aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2015-06-22 17:10:09 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2015-06-23 12:40:00 +0200
commit91389e51c78ae3565b037e31dd5382b52bd75136 (patch)
treee38a1457340ce17036b9e257d75fb46958fa6699 /src/wallet/wallet.cpp
parentb77fbe095f44e182c484625a47940148d7c3b1e4 (diff)
parent208589514cdc344c2d33228dba7f00b305d0174b (diff)
downloadbitcoin-91389e51c78ae3565b037e31dd5382b52bd75136.tar.xz
Merge pull request #6088
2085895 fundrawtransaction tests (Jonas Schnelli) 21bbd92 Add fundrawtransaction RPC method (Matt Corallo) 1e0d1a2 Add FundTransaction method to wallet (Matt Corallo) 2d84e22 Small tweaks to CCoinControl for fundrawtransaction (Matt Corallo) 9b4e7d9 Add DummySignatureCreator which just creates zeroed sigs (Pieter Wuille)
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp125
1 files changed, 114 insertions, 11 deletions
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?