Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 2,769 Lines • ▼ Show 20 Lines | bool CWallet::FundTransaction(CMutableTransaction &tx, Amount &nFeeRet, | ||||
coinControl.fAllowOtherInputs = true; | coinControl.fAllowOtherInputs = true; | ||||
for (const CTxIn &txin : tx.vin) { | for (const CTxIn &txin : tx.vin) { | ||||
coinControl.Select(txin.prevout); | coinControl.Select(txin.prevout); | ||||
} | } | ||||
CReserveKey reservekey(this); | CReserveKey reservekey(this); | ||||
CWalletTx wtx; | CTransactionRef tx_new; | ||||
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, | if (!CreateTransaction(vecSend, tx_new, reservekey, nFeeRet, nChangePosInOut, | ||||
strFailReason, coinControl, false)) { | strFailReason, coinControl, false)) { | ||||
return false; | return false; | ||||
} | } | ||||
if (nChangePosInOut != -1) { | if (nChangePosInOut != -1) { | ||||
tx.vout.insert(tx.vout.begin() + nChangePosInOut, | tx.vout.insert(tx.vout.begin() + nChangePosInOut, | ||||
wtx.tx->vout[nChangePosInOut]); | tx_new->vout[nChangePosInOut]); | ||||
} | } | ||||
// Copy output sizes from new transaction; they may have had the fee | // Copy output sizes from new transaction; they may have had the fee | ||||
// subtracted from them. | // subtracted from them. | ||||
for (size_t idx = 0; idx < tx.vout.size(); idx++) { | for (size_t idx = 0; idx < tx.vout.size(); idx++) { | ||||
tx.vout[idx].nValue = wtx.tx->vout[idx].nValue; | tx.vout[idx].nValue = tx_new->vout[idx].nValue; | ||||
} | } | ||||
// Add new txins (keeping original txin scriptSig/order) | // Add new txins (keeping original txin scriptSig/order) | ||||
for (const CTxIn &txin : wtx.tx->vin) { | for (const CTxIn &txin : tx_new->vin) { | ||||
if (!coinControl.IsSelected(txin.prevout)) { | if (!coinControl.IsSelected(txin.prevout)) { | ||||
tx.vin.push_back(txin); | tx.vin.push_back(txin); | ||||
if (lockUnspents) { | if (lockUnspents) { | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
LockCoin(txin.prevout); | LockCoin(txin.prevout); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// Optionally keep the change output key. | // Optionally keep the change output key. | ||||
if (keepReserveKey) { | if (keepReserveKey) { | ||||
reservekey.KeepKey(); | reservekey.KeepKey(); | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend, | bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend, | ||||
CWalletTx &wtxNew, CReserveKey &reservekey, | CTransactionRef &tx, CReserveKey &reservekey, | ||||
Amount &nFeeRet, int &nChangePosInOut, | Amount &nFeeRet, int &nChangePosInOut, | ||||
std::string &strFailReason, | std::string &strFailReason, | ||||
const CCoinControl &coinControl, bool sign) { | const CCoinControl &coinControl, bool sign) { | ||||
Amount nValue = Amount::zero(); | Amount nValue = Amount::zero(); | ||||
int nChangePosRequest = nChangePosInOut; | int nChangePosRequest = nChangePosInOut; | ||||
unsigned int nSubtractFeeFromAmount = 0; | unsigned int nSubtractFeeFromAmount = 0; | ||||
for (const auto &recipient : vecSend) { | for (const auto &recipient : vecSend) { | ||||
if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) { | if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) { | ||||
strFailReason = _("Transaction amounts must not be negative"); | strFailReason = _("Transaction amounts must not be negative"); | ||||
return false; | return false; | ||||
} | } | ||||
nValue += recipient.nAmount; | nValue += recipient.nAmount; | ||||
if (recipient.fSubtractFeeFromAmount) { | if (recipient.fSubtractFeeFromAmount) { | ||||
nSubtractFeeFromAmount++; | nSubtractFeeFromAmount++; | ||||
} | } | ||||
} | } | ||||
if (vecSend.empty()) { | if (vecSend.empty()) { | ||||
strFailReason = _("Transaction must have at least one recipient"); | strFailReason = _("Transaction must have at least one recipient"); | ||||
return false; | return false; | ||||
} | } | ||||
wtxNew.fTimeReceivedIsTxTime = true; | |||||
wtxNew.BindWallet(this); | |||||
CMutableTransaction txNew; | CMutableTransaction txNew; | ||||
// Discourage fee sniping. | // Discourage fee sniping. | ||||
// | // | ||||
// For a large miner the value of the transactions in the best block and the | // For a large miner the value of the transactions in the best block and the | ||||
// mempool can exceed the cost of deliberately attempting to mine two blocks | // mempool can exceed the cost of deliberately attempting to mine two blocks | ||||
// to orphan the current best block. By setting nLockTime such that only the | // to orphan the current best block. By setting nLockTime such that only the | ||||
// next block can include the transaction, we discourage this practice as | // next block can include the transaction, we discourage this practice as | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
nFeeRet = Amount::zero(); | nFeeRet = Amount::zero(); | ||||
bool pick_new_inputs = true; | bool pick_new_inputs = true; | ||||
Amount nValueIn = Amount::zero(); | Amount nValueIn = Amount::zero(); | ||||
// Start with no fee and loop until there is enough fee | // Start with no fee and loop until there is enough fee | ||||
while (true) { | while (true) { | ||||
nChangePosInOut = nChangePosRequest; | nChangePosInOut = nChangePosRequest; | ||||
txNew.vin.clear(); | txNew.vin.clear(); | ||||
txNew.vout.clear(); | txNew.vout.clear(); | ||||
wtxNew.fFromMe = true; | |||||
bool fFirst = true; | bool fFirst = true; | ||||
Amount nValueToSelect = nValue; | Amount nValueToSelect = nValue; | ||||
if (nSubtractFeeFromAmount == 0) { | if (nSubtractFeeFromAmount == 0) { | ||||
nValueToSelect += nFeeRet; | nValueToSelect += nFeeRet; | ||||
} | } | ||||
double dPriority = 0; | double dPriority = 0; | ||||
▲ Show 20 Lines • Show All 245 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
return false; | return false; | ||||
} | } | ||||
UpdateTransaction(txNew, nIn, sigdata); | UpdateTransaction(txNew, nIn, sigdata); | ||||
nIn++; | nIn++; | ||||
} | } | ||||
} | } | ||||
// Embed the constructed transaction data in wtxNew. | // Return the constructed transaction data. | ||||
wtxNew.SetTx(MakeTransactionRef(std::move(txNew))); | tx = MakeTransactionRef(std::move(txNew)); | ||||
// Limit size. | // Limit size. | ||||
if (CTransaction(wtxNew).GetTotalSize() >= MAX_STANDARD_TX_SIZE) { | if (tx->GetTotalSize() >= MAX_STANDARD_TX_SIZE) { | ||||
strFailReason = _("Transaction too large"); | strFailReason = _("Transaction too large"); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
if (gArgs.GetBoolArg("-walletrejectlongchains", | if (gArgs.GetBoolArg("-walletrejectlongchains", | ||||
DEFAULT_WALLET_REJECT_LONG_CHAINS)) { | DEFAULT_WALLET_REJECT_LONG_CHAINS)) { | ||||
// Lastly, ensure this tx will pass the mempool's chain limits. | // Lastly, ensure this tx will pass the mempool's chain limits. | ||||
LockPoints lp; | LockPoints lp; | ||||
CTxMemPoolEntry entry(wtxNew.tx, Amount::zero(), 0, 0, 0, | CTxMemPoolEntry entry(tx, Amount::zero(), 0, 0, 0, Amount::zero(), | ||||
Amount::zero(), false, 0, lp); | false, 0, lp); | ||||
CTxMemPool::setEntries setAncestors; | CTxMemPool::setEntries setAncestors; | ||||
size_t nLimitAncestors = | size_t nLimitAncestors = | ||||
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); | gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); | ||||
size_t nLimitAncestorSize = | size_t nLimitAncestorSize = | ||||
gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * | gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * | ||||
1000; | 1000; | ||||
size_t nLimitDescendants = | size_t nLimitDescendants = | ||||
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); | gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); | ||||
Show All 11 Lines | bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend, | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Call after CreateTransaction unless you want to abort | * Call after CreateTransaction unless you want to abort | ||||
*/ | */ | ||||
bool CWallet::CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey, | bool CWallet::CommitTransaction( | ||||
CConnman *connman, CValidationState &state) { | CTransactionRef tx, mapValue_t mapValue, | ||||
std::vector<std::pair<std::string, std::string>> orderForm, | |||||
std::string fromAccount, CReserveKey &reservekey, CConnman *connman, | |||||
CValidationState &state) { | |||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
CWalletTx wtxNew(this, std::move(tx)); | |||||
wtxNew.mapValue = std::move(mapValue); | |||||
wtxNew.vOrderForm = std::move(orderForm); | |||||
wtxNew.strFromAccount = std::move(fromAccount); | |||||
wtxNew.fTimeReceivedIsTxTime = true; | |||||
wtxNew.fFromMe = true; | |||||
LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); | LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); | ||||
// Take key pair from key pool so it won't be used again. | // Take key pair from key pool so it won't be used again. | ||||
reservekey.KeepKey(); | reservekey.KeepKey(); | ||||
// Add tx to wallet, because if it has change it's also ours, otherwise just | // Add tx to wallet, because if it has change it's also ours, otherwise just | ||||
// for transaction history. | // for transaction history. | ||||
AddToWallet(wtxNew); | AddToWallet(wtxNew); | ||||
▲ Show 20 Lines • Show All 1,209 Lines • Show Last 20 Lines |