diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/rpcwallet.cpp | 54 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 125 | ||||
-rw-r--r-- | src/wallet/wallet.h | 5 |
3 files changed, 171 insertions, 13 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 5404dd4aa0..8d88933878 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2361,3 +2361,57 @@ UniValue listunspent(const UniValue& params, bool fHelp) return results; } + +UniValue fundrawtransaction(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() != 1) + throw runtime_error( + "fundrawtransaction \"hexstring\"\n" + "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" + "This will not modify existing inputs, and will add one change output to the outputs.\n" + "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" + "The inputs added will not be signed, use signrawtransaction for that.\n" + "\nArguments:\n" + "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" + "\nResult:\n" + "{\n" + " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" + " \"fee\": n, (numeric) The fee added to the transaction\n" + " \"changepos\": n (numeric) The position of the added change output, or -1\n" + "}\n" + "\"hex\" \n" + "\nExamples:\n" + "\nCreate a transaction with no inputs\n" + + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + + "\nAdd sufficient unsigned inputs to meet the output value\n" + + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") + + "\nSign the transaction\n" + + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + + "\nSend the transaction\n" + + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") + ); + + RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); + + // parse hex string from parameter + CTransaction origTx; + if (!DecodeHexTx(origTx, params[0].get_str())) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + CMutableTransaction tx(origTx); + CAmount nFee; + string strFailReason; + int nChangePos = -1; + if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason)) + throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("hex", EncodeHexTx(tx))); + result.push_back(Pair("changepos", nChangePos)); + result.push_back(Pair("fee", ValueFromAmount(nFee))); + + return result; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3f12d88e79..eee57900b5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1509,7 +1509,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const isminetype mine = IsMine(pcoin->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && - (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i))) vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); } } @@ -1669,25 +1669,108 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx* AvailableCoins(vCoins, true, coinControl); // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) - if (coinControl && coinControl->HasSelected()) + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) { BOOST_FOREACH(const COutput& out, vCoins) { - if(!out.fSpendable) - continue; + if (!out.fSpendable) + continue; nValueRet += out.tx->vout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } return (nValueRet >= nTargetValue); } - return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || - SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || - (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet))); + // calculate value from preset inputs and store them + set<pair<const CWalletTx*, uint32_t> > setPresetCoins; + CAmount nValueFromPresetInputs = 0; + + std::vector<COutPoint> vPresetInputs; + if (coinControl) + coinControl->ListSelected(vPresetInputs); + BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) + { + map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); + if (it != mapWallet.end()) + { + const CWalletTx* pcoin = &it->second; + // Clearly invalid input, fail + if (pcoin->vout.size() <= outpoint.n) + return false; + nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue; + setPresetCoins.insert(make_pair(pcoin, outpoint.n)); + } else + return false; // TODO: Allow non-wallet inputs + } + + // remove preset inputs from vCoins + for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) + { + if (setPresetCoins.count(make_pair(it->tx, it->i))) + it = vCoins.erase(it); + else + ++it; + } + + bool res = nTargetValue <= nValueFromPresetInputs || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) || + (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet)); + + // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset + setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); + + // add preset inputs to the total value selected + nValueRet += nValueFromPresetInputs; + + return res; } -bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason) +{ + vector<CRecipient> vecSend; + + // Turn the txout set into a CRecipient vector + BOOST_FOREACH(const CTxOut& txOut, tx.vout) + { + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false}; + vecSend.push_back(recipient); + } + + CCoinControl coinControl; + coinControl.fAllowOtherInputs = true; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + coinControl.Select(txin.prevout); + + CReserveKey reservekey(this); + CWalletTx wtx; + if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false)) + return false; + + if (nChangePosRet != -1) + tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]); + + // Add new txins (keeping original txin scriptSig/order) + BOOST_FOREACH(const CTxIn& txin, wtx.vin) + { + bool found = false; + BOOST_FOREACH(const CTxIn& origTxIn, tx.vin) + { + if (txin.prevout.hash == origTxIn.prevout.hash && txin.prevout.n == origTxIn.prevout.n) + { + found = true; + break; + } + } + if (!found) + tx.vin.push_back(txin); + } + + return true; +} + +bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, + int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign) { CAmount nValue = 0; unsigned int nSubtractFeeFromAmount = 0; @@ -1890,23 +1973,43 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, // Sign int nIn = 0; + CTransaction txNewConst(txNew); BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) - if (!SignSignature(*this, *coin.first, txNew, nIn++)) + { + bool signSuccess; + const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; + CScript& scriptSigRes = txNew.vin[nIn].scriptSig; + if (sign) + signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes); + else + signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, scriptSigRes); + + if (!signSuccess) { strFailReason = _("Signing transaction failed"); return false; } + nIn++; + } + + unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); + + // Remove scriptSigs if we used dummy signatures for fee calculation + if (!sign) { + BOOST_FOREACH (CTxIn& vin, txNew.vin) + vin.scriptSig = CScript(); + } // Embed the constructed transaction data in wtxNew. *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); // Limit size - unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); if (nBytes >= MAX_STANDARD_TX_SIZE) { strFailReason = _("Transaction too large"); return false; } + dPriority = wtxNew.ComputePriority(dPriority, nBytes); // Can we complete this as a free transaction? diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9f3f08d117..b6a8e8671f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -625,8 +625,9 @@ public: CAmount GetWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; - bool CreateTransaction(const std::vector<CRecipient>& vecSend, - CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason); + bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, + std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); static CFeeRate minTxFee; |