Add FundTransaction method to wallet

Some code stolen from Jonas Schnelli <jonas.schnelli@include7.ch>
This commit is contained in:
Matt Corallo 2015-04-24 18:29:00 -07:00
parent 2d84e22703
commit 1e0d1a2ff0
2 changed files with 117 additions and 13 deletions

View File

@ -1509,7 +1509,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
isminetype mine = IsMine(pcoin->vout[i]); isminetype mine = IsMine(pcoin->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
!IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && !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)); vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO));
} }
} }
@ -1669,11 +1669,11 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
AvailableCoins(vCoins, true, coinControl); AvailableCoins(vCoins, true, coinControl);
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure) // 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) BOOST_FOREACH(const COutput& out, vCoins)
{ {
if(!out.fSpendable) if (!out.fSpendable)
continue; continue;
nValueRet += out.tx->vout[out.i].nValue; nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i)); setCoinsRet.insert(make_pair(out.tx, out.i));
@ -1681,13 +1681,96 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*
return (nValueRet >= nTargetValue); return (nValueRet >= nTargetValue);
} }
return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || // calculate value from preset inputs and store them
SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || set<pair<const CWalletTx*, uint32_t> > setPresetCoins;
(bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet))); 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, bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason)
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl) {
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; CAmount nValue = 0;
unsigned int nSubtractFeeFromAmount = 0; unsigned int nSubtractFeeFromAmount = 0;
@ -1890,23 +1973,43 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend,
// Sign // Sign
int nIn = 0; int nIn = 0;
CTransaction txNewConst(txNew);
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) 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"); strFailReason = _("Signing transaction failed");
return false; 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. // Embed the constructed transaction data in wtxNew.
*static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew);
// Limit size // Limit size
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_STANDARD_TX_SIZE) if (nBytes >= MAX_STANDARD_TX_SIZE)
{ {
strFailReason = _("Transaction too large"); strFailReason = _("Transaction too large");
return false; return false;
} }
dPriority = wtxNew.ComputePriority(dPriority, nBytes); dPriority = wtxNew.ComputePriority(dPriority, nBytes);
// Can we complete this as a free transaction? // Can we complete this as a free transaction?

View File

@ -625,8 +625,9 @@ public:
CAmount GetWatchOnlyBalance() const; CAmount GetWatchOnlyBalance() const;
CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const;
bool CreateTransaction(const std::vector<CRecipient>& vecSend, bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason);
CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); 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); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
static CFeeRate minTxFee; static CFeeRate minTxFee;