Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/wallet.cpp
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | const uint256 CMerkleTx::ABANDON_HASH(uint256S( | ||||
"0000000000000000000000000000000000000000000000000000000000000001")); | "0000000000000000000000000000000000000000000000000000000000000001")); | ||||
/** @defgroup mapWallet | /** @defgroup mapWallet | ||||
* | * | ||||
* @{ | * @{ | ||||
*/ | */ | ||||
struct CompareValueOnly { | struct CompareValueOnly { | ||||
bool operator()( | bool operator()(const CInputCoin &t1, const CInputCoin &t2) const { | ||||
const std::pair<Amount, std::pair<const CWalletTx *, unsigned int>> &t1, | return t1.txout.nValue < t2.txout.nValue; | ||||
const std::pair<Amount, std::pair<const CWalletTx *, unsigned int>> &t2) | |||||
const { | |||||
return t1.first < t2.first; | |||||
} | } | ||||
}; | }; | ||||
std::string COutput::ToString() const { | std::string COutput::ToString() const { | ||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, | return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i, | ||||
nDepth, FormatMoney(tx->tx->vout[i].nValue)); | nDepth, FormatMoney(tx->tx->vout[i].nValue)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,297 Lines • ▼ Show 20 Lines | for (std::map<TxId, CWalletTx>::const_iterator it = mapWallet.begin(); | ||||
// Checks the maximum number of UTXO's. | // Checks the maximum number of UTXO's. | ||||
if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { | if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static void ApproximateBestSubset( | static void ApproximateBestSubset(const std::vector<CInputCoin> &vValue, | ||||
std::vector<std::pair<Amount, std::pair<const CWalletTx *, unsigned int>>> | const Amount &nTotalLower, | ||||
vValue, | const Amount &nTargetValue, | ||||
const Amount nTotalLower, const Amount nTargetValue, | std::vector<char> &vfBest, Amount &nBest, | ||||
std::vector<char> &vfBest, Amount &nBest, int iterations = 1000) { | int iterations = 1000) { | ||||
std::vector<char> vfIncluded; | std::vector<char> vfIncluded; | ||||
vfBest.assign(vValue.size(), true); | vfBest.assign(vValue.size(), true); | ||||
nBest = nTotalLower; | nBest = nTotalLower; | ||||
FastRandomContext insecure_rand; | FastRandomContext insecure_rand; | ||||
for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { | for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { | ||||
vfIncluded.assign(vValue.size(), false); | vfIncluded.assign(vValue.size(), false); | ||||
Amount nTotal = Amount::zero(); | Amount nTotal = Amount::zero(); | ||||
bool fReachedTarget = false; | bool fReachedTarget = false; | ||||
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) { | for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) { | ||||
for (size_t i = 0; i < vValue.size(); i++) { | for (size_t i = 0; i < vValue.size(); i++) { | ||||
// The solver here uses a randomized algorithm, the randomness | // The solver here uses a randomized algorithm, the randomness | ||||
// serves no real security purpose but is just needed to prevent | // serves no real security purpose but is just needed to prevent | ||||
// degenerate behavior and it is important that the rng is fast. | // degenerate behavior and it is important that the rng is fast. | ||||
// We do not use a constant random sequence, because there may | // We do not use a constant random sequence, because there may | ||||
// be some privacy improvement by making the selection random. | // be some privacy improvement by making the selection random. | ||||
if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i]) { | if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i]) { | ||||
nTotal += vValue[i].first; | nTotal += vValue[i].txout.nValue; | ||||
vfIncluded[i] = true; | vfIncluded[i] = true; | ||||
if (nTotal >= nTargetValue) { | if (nTotal >= nTargetValue) { | ||||
fReachedTarget = true; | fReachedTarget = true; | ||||
if (nTotal < nBest) { | if (nTotal < nBest) { | ||||
nBest = nTotal; | nBest = nTotal; | ||||
vfBest = vfIncluded; | vfBest = vfIncluded; | ||||
} | } | ||||
nTotal -= vValue[i].first; | nTotal -= vValue[i].txout.nValue; | ||||
vfIncluded[i] = false; | vfIncluded[i] = false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool CWallet::SelectCoinsMinConf( | bool CWallet::SelectCoinsMinConf(const Amount nTargetValue, const int nConfMine, | ||||
const Amount nTargetValue, const int nConfMine, const int nConfTheirs, | const int nConfTheirs, | ||||
const uint64_t nMaxAncestors, std::vector<COutput> vCoins, | const uint64_t nMaxAncestors, | ||||
std::set<std::pair<const CWalletTx *, unsigned int>> &setCoinsRet, | std::vector<COutput> vCoins, | ||||
std::set<CInputCoin> &setCoinsRet, | |||||
Amount &nValueRet) const { | Amount &nValueRet) const { | ||||
setCoinsRet.clear(); | setCoinsRet.clear(); | ||||
nValueRet = Amount::zero(); | nValueRet = Amount::zero(); | ||||
// List of values less than target | // List of values less than target | ||||
std::pair<Amount, std::pair<const CWalletTx *, unsigned int>> | boost::optional<CInputCoin> coinLowestLarger; | ||||
coinLowestLarger; | std::vector<CInputCoin> vValue; | ||||
coinLowestLarger.first = MAX_MONEY; | |||||
coinLowestLarger.second.first = nullptr; | |||||
std::vector<std::pair<Amount, std::pair<const CWalletTx *, unsigned int>>> | |||||
vValue; | |||||
Amount nTotalLower = Amount::zero(); | Amount nTotalLower = Amount::zero(); | ||||
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); | random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); | ||||
for (const COutput &output : vCoins) { | for (const COutput &output : vCoins) { | ||||
if (!output.fSpendable) { | if (!output.fSpendable) { | ||||
continue; | continue; | ||||
} | } | ||||
const CWalletTx *pcoin = output.tx; | const CWalletTx *pcoin = output.tx; | ||||
if (output.nDepth < | if (output.nDepth < | ||||
(pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) { | (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!g_mempool.TransactionWithinChainLimit(pcoin->GetId(), | if (!g_mempool.TransactionWithinChainLimit(pcoin->GetId(), | ||||
nMaxAncestors)) { | nMaxAncestors)) { | ||||
continue; | continue; | ||||
} | } | ||||
int i = output.i; | int i = output.i; | ||||
Amount n = pcoin->tx->vout[i].nValue; | CInputCoin coin = CInputCoin(pcoin, i); | ||||
std::pair<Amount, std::pair<const CWalletTx *, unsigned int>> coin = | if (coin.txout.nValue == nTargetValue) { | ||||
std::make_pair(n, std::make_pair(pcoin, i)); | setCoinsRet.insert(coin); | ||||
nValueRet += coin.txout.nValue; | |||||
if (n == nTargetValue) { | |||||
setCoinsRet.insert(coin.second); | |||||
nValueRet += coin.first; | |||||
return true; | return true; | ||||
} | } else if (coin.txout.nValue < nTargetValue + MIN_CHANGE) { | ||||
if (n < nTargetValue + MIN_CHANGE) { | |||||
vValue.push_back(coin); | vValue.push_back(coin); | ||||
nTotalLower += n; | nTotalLower += coin.txout.nValue; | ||||
} else if (n < coinLowestLarger.first) { | } else if (!coinLowestLarger || | ||||
coin.txout.nValue < coinLowestLarger->txout.nValue) { | |||||
coinLowestLarger = coin; | coinLowestLarger = coin; | ||||
} | } | ||||
} | } | ||||
if (nTotalLower == nTargetValue) { | if (nTotalLower == nTargetValue) { | ||||
for (unsigned int i = 0; i < vValue.size(); ++i) { | for (unsigned int i = 0; i < vValue.size(); ++i) { | ||||
setCoinsRet.insert(vValue[i].second); | setCoinsRet.insert(vValue[i]); | ||||
nValueRet += vValue[i].first; | nValueRet += vValue[i].txout.nValue; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
if (nTotalLower < nTargetValue) { | if (nTotalLower < nTargetValue) { | ||||
if (coinLowestLarger.second.first == nullptr) { | if (!coinLowestLarger) { | ||||
return false; | return false; | ||||
} | } | ||||
setCoinsRet.insert(coinLowestLarger.second); | setCoinsRet.insert(coinLowestLarger.get()); | ||||
nValueRet += coinLowestLarger.first; | nValueRet += coinLowestLarger->txout.nValue; | ||||
return true; | return true; | ||||
} | } | ||||
// Solve subset sum by stochastic approximation | // Solve subset sum by stochastic approximation | ||||
std::sort(vValue.begin(), vValue.end(), CompareValueOnly()); | std::sort(vValue.begin(), vValue.end(), CompareValueOnly()); | ||||
std::reverse(vValue.begin(), vValue.end()); | std::reverse(vValue.begin(), vValue.end()); | ||||
std::vector<char> vfBest; | std::vector<char> vfBest; | ||||
Amount nBest; | Amount nBest; | ||||
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest); | ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest); | ||||
if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) { | if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) { | ||||
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, | ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, | ||||
vfBest, nBest); | vfBest, nBest); | ||||
} | } | ||||
// If we have a bigger coin and (either the stochastic approximation didn't | // If we have a bigger coin and (either the stochastic approximation didn't | ||||
// find a good solution, or the next bigger coin is closer), return the | // find a good solution, or the next bigger coin is closer), return the | ||||
// bigger coin. | // bigger coin. | ||||
if (coinLowestLarger.second.first && | if (coinLowestLarger && | ||||
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || | ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || | ||||
coinLowestLarger.first <= nBest)) { | coinLowestLarger->txout.nValue <= nBest)) { | ||||
setCoinsRet.insert(coinLowestLarger.second); | setCoinsRet.insert(coinLowestLarger.get()); | ||||
nValueRet += coinLowestLarger.first; | nValueRet += coinLowestLarger->txout.nValue; | ||||
} else { | } else { | ||||
for (unsigned int i = 0; i < vValue.size(); i++) { | for (unsigned int i = 0; i < vValue.size(); i++) { | ||||
if (vfBest[i]) { | if (vfBest[i]) { | ||||
setCoinsRet.insert(vValue[i].second); | setCoinsRet.insert(vValue[i]); | ||||
nValueRet += vValue[i].first; | nValueRet += vValue[i].txout.nValue; | ||||
} | } | ||||
} | } | ||||
if (LogAcceptCategory(BCLog::SELECTCOINS)) { | if (LogAcceptCategory(BCLog::SELECTCOINS)) { | ||||
LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); | LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: "); | ||||
for (size_t i = 0; i < vValue.size(); i++) { | for (size_t i = 0; i < vValue.size(); i++) { | ||||
if (vfBest[i]) { | if (vfBest[i]) { | ||||
LogPrint(BCLog::SELECTCOINS, "%s ", | LogPrint(BCLog::SELECTCOINS, "%s ", | ||||
FormatMoney(vValue[i].first)); | FormatMoney(vValue[i].txout.nValue)); | ||||
} | } | ||||
} | } | ||||
LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest)); | LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest)); | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CWallet::SelectCoins( | bool CWallet::SelectCoins(const std::vector<COutput> &vAvailableCoins, | ||||
const std::vector<COutput> &vAvailableCoins, const Amount nTargetValue, | const Amount nTargetValue, | ||||
std::set<std::pair<const CWalletTx *, unsigned int>> &setCoinsRet, | std::set<CInputCoin> &setCoinsRet, Amount &nValueRet, | ||||
Amount &nValueRet, const CCoinControl *coinControl) const { | const CCoinControl *coinControl) const { | ||||
std::vector<COutput> vCoins(vAvailableCoins); | std::vector<COutput> vCoins(vAvailableCoins); | ||||
// coin control -> return all selected outputs (we want all selected to go | // coin control -> return all selected outputs (we want all selected to go | ||||
// into the transaction for sure). | // into the transaction for sure). | ||||
if (coinControl && coinControl->HasSelected() && | if (coinControl && coinControl->HasSelected() && | ||||
!coinControl->fAllowOtherInputs) { | !coinControl->fAllowOtherInputs) { | ||||
for (const COutput &out : vCoins) { | for (const COutput &out : vCoins) { | ||||
if (!out.fSpendable) { | if (!out.fSpendable) { | ||||
continue; | continue; | ||||
} | } | ||||
nValueRet += out.tx->tx->vout[out.i].nValue; | nValueRet += out.tx->tx->vout[out.i].nValue; | ||||
setCoinsRet.insert(std::make_pair(out.tx, out.i)); | setCoinsRet.insert(CInputCoin(out.tx, out.i)); | ||||
} | } | ||||
return (nValueRet >= nTargetValue); | return (nValueRet >= nTargetValue); | ||||
} | } | ||||
// Calculate value from preset inputs and store them. | // Calculate value from preset inputs and store them. | ||||
std::set<std::pair<const CWalletTx *, uint32_t>> setPresetCoins; | std::set<CInputCoin> setPresetCoins; | ||||
Amount nValueFromPresetInputs = Amount::zero(); | Amount nValueFromPresetInputs = Amount::zero(); | ||||
std::vector<COutPoint> vPresetInputs; | std::vector<COutPoint> vPresetInputs; | ||||
if (coinControl) { | if (coinControl) { | ||||
coinControl->ListSelected(vPresetInputs); | coinControl->ListSelected(vPresetInputs); | ||||
} | } | ||||
for (const COutPoint &outpoint : vPresetInputs) { | for (const COutPoint &outpoint : vPresetInputs) { | ||||
std::map<TxId, CWalletTx>::const_iterator it = | std::map<TxId, CWalletTx>::const_iterator it = | ||||
mapWallet.find(outpoint.GetTxId()); | mapWallet.find(outpoint.GetTxId()); | ||||
if (it == mapWallet.end()) { | if (it == mapWallet.end()) { | ||||
// TODO: Allow non-wallet inputs | // TODO: Allow non-wallet inputs | ||||
return false; | return false; | ||||
} | } | ||||
const CWalletTx *pcoin = &it->second; | const CWalletTx *pcoin = &it->second; | ||||
// Clearly invalid input, fail. | // Clearly invalid input, fail. | ||||
if (pcoin->tx->vout.size() <= outpoint.GetN()) { | if (pcoin->tx->vout.size() <= outpoint.GetN()) { | ||||
return false; | return false; | ||||
} | } | ||||
nValueFromPresetInputs += pcoin->tx->vout[outpoint.GetN()].nValue; | nValueFromPresetInputs += pcoin->tx->vout[outpoint.GetN()].nValue; | ||||
setPresetCoins.insert(std::make_pair(pcoin, outpoint.GetN())); | setPresetCoins.insert(CInputCoin(pcoin, outpoint.GetN())); | ||||
} | } | ||||
// Remove preset inputs from vCoins. | // Remove preset inputs from vCoins. | ||||
for (std::vector<COutput>::iterator it = vCoins.begin(); | for (std::vector<COutput>::iterator it = vCoins.begin(); | ||||
it != vCoins.end() && coinControl && coinControl->HasSelected();) { | it != vCoins.end() && coinControl && coinControl->HasSelected();) { | ||||
if (setPresetCoins.count(std::make_pair(it->tx, it->i))) { | if (setPresetCoins.count(CInputCoin(it->tx, it->i))) { | ||||
it = vCoins.erase(it); | it = vCoins.erase(it); | ||||
} else { | } else { | ||||
++it; | ++it; | ||||
} | } | ||||
} | } | ||||
size_t nMaxChainLength = std::min( | size_t nMaxChainLength = std::min( | ||||
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), | gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), | ||||
▲ Show 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend, | ||||
if (GetRandInt(10) == 0) { | if (GetRandInt(10) == 0) { | ||||
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | ||||
} | } | ||||
assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | ||||
assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
{ | { | ||||
std::set<std::pair<const CWalletTx *, uint32_t>> setCoins; | std::set<CInputCoin> setCoins; | ||||
LOCK2(cs_main, cs_wallet); | LOCK2(cs_main, cs_wallet); | ||||
std::vector<COutput> vAvailableCoins; | std::vector<COutput> vAvailableCoins; | ||||
AvailableCoins(vAvailableCoins, true, coinControl); | AvailableCoins(vAvailableCoins, true, coinControl); | ||||
nFeeRet = Amount::zero(); | nFeeRet = 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) { | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
setCoins.clear(); | setCoins.clear(); | ||||
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, | if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, | ||||
nValueIn, coinControl)) { | nValueIn, coinControl)) { | ||||
strFailReason = _("Insufficient funds"); | strFailReason = _("Insufficient funds"); | ||||
return false; | return false; | ||||
} | } | ||||
for (const auto &pcoin : setCoins) { | for (const auto &pcoin : setCoins) { | ||||
Amount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; | Amount nCredit = pcoin.txout.nValue; | ||||
// The coin age after the next block (depth+1) is used instead | // The coin age after the next block (depth+1) is used instead | ||||
// of the current, reflecting an assumption the user would | // of the current, reflecting an assumption the user would | ||||
// accept a bit more delay for a chance at a free transaction. | // accept a bit more delay for a chance at a free transaction. | ||||
// But mempool inputs might still be in the mempool, so their | // But mempool inputs might still be in the mempool, so their | ||||
// age stays 0. | // age stays 0. | ||||
int age = pcoin.first->GetDepthInMainChain(); | int age = pcoin.wtx->GetDepthInMainChain(); | ||||
assert(age >= 0); | assert(age >= 0); | ||||
if (age != 0) { | if (age != 0) { | ||||
age += 1; | age += 1; | ||||
} | } | ||||
dPriority += (age * nCredit) / SATOSHI; | dPriority += (age * nCredit) / SATOSHI; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
} | } | ||||
// Fill vin | // Fill vin | ||||
// | // | ||||
// Note how the sequence number is set to non-maxint so that the | // Note how the sequence number is set to non-maxint so that the | ||||
// nLockTime set above actually works. | // nLockTime set above actually works. | ||||
for (const auto &coin : setCoins) { | for (const auto &coin : setCoins) { | ||||
txNew.vin.push_back( | txNew.vin.push_back( | ||||
CTxIn(coin.first->GetId(), coin.second, CScript(), | CTxIn(coin.outpoint, CScript(), | ||||
std::numeric_limits<uint32_t>::max() - 1)); | std::numeric_limits<uint32_t>::max() - 1)); | ||||
} | } | ||||
// Fill in dummy signatures for fee calculation. | // Fill in dummy signatures for fee calculation. | ||||
if (!DummySignTx(txNew, setCoins)) { | if (!DummySignTx(txNew, setCoins)) { | ||||
strFailReason = _("Signing transaction failed"); | strFailReason = _("Signing transaction failed"); | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | ||||
} | } | ||||
if (sign) { | if (sign) { | ||||
SigHashType sigHashType = SigHashType().withForkId(); | SigHashType sigHashType = SigHashType().withForkId(); | ||||
CTransaction txNewConst(txNew); | CTransaction txNewConst(txNew); | ||||
int nIn = 0; | int nIn = 0; | ||||
for (const auto &coin : setCoins) { | for (const auto &coin : setCoins) { | ||||
const CScript &scriptPubKey = | const CScript &scriptPubKey = coin.txout.scriptPubKey; | ||||
coin.first->tx->vout[coin.second].scriptPubKey; | |||||
SignatureData sigdata; | SignatureData sigdata; | ||||
if (!ProduceSignature( | if (!ProduceSignature(TransactionSignatureCreator( | ||||
TransactionSignatureCreator( | |||||
this, &txNewConst, nIn, | this, &txNewConst, nIn, | ||||
coin.first->tx->vout[coin.second].nValue, | coin.txout.nValue, sigHashType), | ||||
sigHashType), | |||||
scriptPubKey, sigdata)) { | scriptPubKey, sigdata)) { | ||||
strFailReason = _("Signing transaction failed"); | strFailReason = _("Signing transaction failed"); | ||||
return false; | return false; | ||||
} | } | ||||
UpdateTransaction(txNew, nIn, sigdata); | UpdateTransaction(txNew, nIn, sigdata); | ||||
nIn++; | nIn++; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,250 Lines • Show Last 20 Lines |