Add FundTransaction method to wallet
Some code stolen from Jonas Schnelli <jonas.schnelli@include7.ch>
This commit is contained in:
parent
2d84e22703
commit
1e0d1a2ff0
@ -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,25 +1669,108 @@ 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));
|
||||||
}
|
}
|
||||||
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?
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user