diff options
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r-- | src/wallet/wallet.cpp | 1443 |
1 files changed, 0 insertions, 1443 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1bb2438d88..b87ae4ab4f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -53,8 +53,6 @@ const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{ }, }; -static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100}; - RecursiveMutex cs_wallets; static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets); static std::list<LoadWalletFn> g_load_wallet_fns GUARDED_BY(cs_wallets); @@ -351,11 +349,6 @@ std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::strin * @{ */ -std::string COutput::ToString() const -{ - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); -} - const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const { AssertLockHeld(cs_wallet); @@ -1283,20 +1276,6 @@ void CWallet::BlockUntilSyncedToCurrentChain() const { chain().waitForNotificationsIfTipChanged(last_block_hash); } - -isminetype CWallet::IsMine(const CTxIn &txin) const -{ - AssertLockHeld(cs_wallet); - std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); - if (mi != mapWallet.end()) - { - const CWalletTx& prev = (*mi).second; - if (txin.prevout.n < prev.tx->vout.size()) - return IsMine(prev.tx->vout[txin.prevout.n]); - } - return ISMINE_NO; -} - // Note that this function doesn't distinguish between a 0-valued input, // and a not-"is mine" (according to the filter) input. CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const @@ -1337,49 +1316,6 @@ isminetype CWallet::IsMine(const CScript& script) const return result; } -CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const -{ - if (!MoneyRange(txout.nValue)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - LOCK(cs_wallet); - return ((IsMine(txout) & filter) ? txout.nValue : 0); -} - -bool CWallet::IsChange(const CTxOut& txout) const -{ - return IsChange(txout.scriptPubKey); -} - -bool CWallet::IsChange(const CScript& script) const -{ - // TODO: fix handling of 'change' outputs. The assumption is that any - // payment to a script that is ours, but is not in the address book - // is change. That assumption is likely to break when we implement multisignature - // wallets that return change back into a multi-signature-protected address; - // a better way of identifying which outputs are 'the send' and which are - // 'the change' will need to be implemented (maybe extend CWalletTx to remember - // which output, if any, was change). - AssertLockHeld(cs_wallet); - if (IsMine(script)) - { - CTxDestination address; - if (!ExtractDestination(script, address)) - return true; - if (!FindAddressBookEntry(address)) { - return true; - } - } - return false; -} - -CAmount CWallet::GetChange(const CTxOut& txout) const -{ - AssertLockHeld(cs_wallet); - if (!MoneyRange(txout.nValue)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - return (IsChange(txout) ? txout.nValue : 0); -} - bool CWallet::IsMine(const CTransaction& tx) const { AssertLockHeld(cs_wallet); @@ -1406,52 +1342,6 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co return nDebit; } -bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const -{ - LOCK(cs_wallet); - - for (const CTxIn& txin : tx.vin) - { - auto mi = mapWallet.find(txin.prevout.hash); - if (mi == mapWallet.end()) - return false; // any unknown inputs can't be from us - - const CWalletTx& prev = (*mi).second; - - if (txin.prevout.n >= prev.tx->vout.size()) - return false; // invalid input! - - if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) - return false; - } - return true; -} - -CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const -{ - CAmount nCredit = 0; - for (const CTxOut& txout : tx.vout) - { - nCredit += GetCredit(txout, filter); - if (!MoneyRange(nCredit)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - } - return nCredit; -} - -CAmount CWallet::GetChange(const CTransaction& tx) const -{ - LOCK(cs_wallet); - CAmount nChange = 0; - for (const CTxOut& txout : tx.vout) - { - nChange += GetChange(txout); - if (!MoneyRange(nChange)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); - } - return nChange; -} - bool CWallet::IsHDEnabled() const { // All Active ScriptPubKeyMans must be HD for this to be true @@ -1531,12 +1421,6 @@ bool CWallet::AddWalletFlags(uint64_t flags) return LoadWalletFlags(flags); } -int64_t CWalletTx::GetTxTime() const -{ - int64_t n = nTimeSmart; - return n ? n : nTimeReceived; -} - // Helper for producing a max-sized low-S low-R signature (eg 71 bytes) // or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig) const @@ -1627,100 +1511,6 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri return true; } -TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig) -{ - std::vector<CTxOut> txouts; - for (const CTxIn& input : tx.vin) { - const auto mi = wallet->mapWallet.find(input.prevout.hash); - // Can not estimate size without knowing the input details - if (mi == wallet->mapWallet.end()) { - return TxSize{-1, -1}; - } - assert(input.prevout.n < mi->second.tx->vout.size()); - txouts.emplace_back(mi->second.tx->vout[input.prevout.n]); - } - return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig); -} - -// txouts needs to be in the order of tx.vin -TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig) -{ - CMutableTransaction txNew(tx); - if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) { - return TxSize{-1, -1}; - } - CTransaction ctx(txNew); - int64_t vsize = GetVirtualTransactionSize(ctx); - int64_t weight = GetTransactionWeight(ctx); - return TxSize{vsize, weight}; -} - -int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig) -{ - CMutableTransaction txn; - txn.vin.push_back(CTxIn(COutPoint())); - if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) { - return -1; - } - return GetVirtualTransactionInputSize(txn.vin[0]); -} - -void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, - std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const -{ - nFee = 0; - listReceived.clear(); - listSent.clear(); - - // Compute fee: - CAmount nDebit = GetDebit(filter); - if (nDebit > 0) // debit>0 means we signed/sent this transaction - { - CAmount nValueOut = tx->GetValueOut(); - nFee = nDebit - nValueOut; - } - - LOCK(pwallet->cs_wallet); - // Sent/received. - for (unsigned int i = 0; i < tx->vout.size(); ++i) - { - const CTxOut& txout = tx->vout[i]; - isminetype fIsMine = pwallet->IsMine(txout); - // Only need to handle txouts if AT LEAST one of these is true: - // 1) they debit from us (sent) - // 2) the output is to us (received) - if (nDebit > 0) - { - // Don't report 'change' txouts - if (pwallet->IsChange(txout)) - continue; - } - else if (!(fIsMine & filter)) - continue; - - // In either case, we need to get the destination address - CTxDestination address; - - if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable()) - { - pwallet->WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", - this->GetHash().ToString()); - address = CNoDestination(); - } - - COutputEntry output = {address, txout.nValue, (int)i}; - - // If we are debited by the transaction, add the output as a "sent" entry - if (nDebit > 0) - listSent.push_back(output); - - // If we are receiving the output, add it as a "received" entry - if (fIsMine & filter) - listReceived.push_back(output); - } - -} - /** * Scan active chain for relevant transactions after importing keys. This should * be called whenever new keys are added to the wallet, with the oldest key @@ -1943,165 +1733,6 @@ std::set<uint256> CWalletTx::GetConflicts() const return result; } -CAmount CWalletTx::GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate) const -{ - auto& amount = m_amounts[type]; - if (recalculate || !amount.m_cached[filter]) { - amount.Set(filter, type == DEBIT ? pwallet->GetDebit(*tx, filter) : pwallet->GetCredit(*tx, filter)); - m_is_cache_empty = false; - } - return amount.m_value[filter]; -} - -CAmount CWalletTx::GetDebit(const isminefilter& filter) const -{ - if (tx->vin.empty()) - return 0; - - CAmount debit = 0; - if (filter & ISMINE_SPENDABLE) { - debit += GetCachableAmount(DEBIT, ISMINE_SPENDABLE); - } - if (filter & ISMINE_WATCH_ONLY) { - debit += GetCachableAmount(DEBIT, ISMINE_WATCH_ONLY); - } - return debit; -} - -CAmount CWalletTx::GetCredit(const isminefilter& filter) const -{ - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsImmatureCoinBase()) - return 0; - - CAmount credit = 0; - if (filter & ISMINE_SPENDABLE) { - // GetBalance can assume transactions in mapWallet won't change - credit += GetCachableAmount(CREDIT, ISMINE_SPENDABLE); - } - if (filter & ISMINE_WATCH_ONLY) { - credit += GetCachableAmount(CREDIT, ISMINE_WATCH_ONLY); - } - return credit; -} - -CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const -{ - if (IsImmatureCoinBase() && IsInMainChain()) { - return GetCachableAmount(IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache); - } - - return 0; -} - -CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter) const -{ - if (pwallet == nullptr) - return 0; - - // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future). - bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL; - - // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsImmatureCoinBase()) - return 0; - - if (fUseCache && allow_cache && m_amounts[AVAILABLE_CREDIT].m_cached[filter]) { - return m_amounts[AVAILABLE_CREDIT].m_value[filter]; - } - - bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); - CAmount nCredit = 0; - uint256 hashTx = GetHash(); - for (unsigned int i = 0; i < tx->vout.size(); i++) - { - if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) { - const CTxOut &txout = tx->vout[i]; - nCredit += pwallet->GetCredit(txout, filter); - if (!MoneyRange(nCredit)) - throw std::runtime_error(std::string(__func__) + " : value out of range"); - } - } - - if (allow_cache) { - m_amounts[AVAILABLE_CREDIT].Set(filter, nCredit); - m_is_cache_empty = false; - } - - return nCredit; -} - -CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const -{ - if (IsImmatureCoinBase() && IsInMainChain()) { - return GetCachableAmount(IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache); - } - - return 0; -} - -CAmount CWalletTx::GetChange() const -{ - if (fChangeCached) - return nChangeCached; - nChangeCached = pwallet->GetChange(*tx); - fChangeCached = true; - return nChangeCached; -} - -bool CWalletTx::InMempool() const -{ - return fInMempool; -} - -bool CWalletTx::IsTrusted() const -{ - std::set<uint256> trusted_parents; - LOCK(pwallet->cs_wallet); - return pwallet->IsTrusted(*this, trusted_parents); -} - -bool CWallet::IsTrusted(const CWalletTx& wtx, std::set<uint256>& trusted_parents) const -{ - AssertLockHeld(cs_wallet); - // Quick answer in most cases - if (!chain().checkFinalTx(*wtx.tx)) return false; - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth >= 1) return true; - if (nDepth < 0) return false; - // using wtx's cached debit - if (!m_spend_zero_conf_change || !wtx.IsFromMe(ISMINE_ALL)) return false; - - // Don't trust unconfirmed transactions from us unless they are in the mempool. - if (!wtx.InMempool()) return false; - - // Trusted if all inputs are from us and are in the mempool: - for (const CTxIn& txin : wtx.tx->vin) - { - // Transactions not sent by us: not trusted - const CWalletTx* parent = GetWalletTx(txin.prevout.hash); - if (parent == nullptr) return false; - const CTxOut& parentOut = parent->tx->vout[txin.prevout.n]; - // Check that this specific input being spent is trusted - if (IsMine(parentOut) != ISMINE_SPENDABLE) return false; - // If we've already trusted this parent, continue - if (trusted_parents.count(parent->GetHash())) continue; - // Recurse to check that the parent is also trusted - if (!IsTrusted(*parent, trusted_parents)) return false; - trusted_parents.insert(parent->GetHash()); - } - return true; -} - -bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const -{ - CMutableTransaction tx1 {*this->tx}; - CMutableTransaction tx2 {*_tx.tx}; - for (auto& txin : tx1.vin) txin.scriptSig = CScript(); - for (auto& txin : tx2.vin) txin.scriptSig = CScript(); - return CTransaction(tx1) == CTransaction(tx2); -} - // Rebroadcast transactions from the wallet. We do this on a random timer // to slightly obfuscate which transactions come from our wallet. // @@ -2162,394 +1793,6 @@ void MaybeResendWalletTxs() * @{ */ - -CWallet::Balance CWallet::GetBalance(const int min_depth, bool avoid_reuse) const -{ - Balance ret; - isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED; - { - LOCK(cs_wallet); - std::set<uint256> trusted_parents; - for (const auto& entry : mapWallet) - { - const CWalletTx& wtx = entry.second; - const bool is_trusted{IsTrusted(wtx, trusted_parents)}; - const int tx_depth{wtx.GetDepthInMainChain()}; - const CAmount tx_credit_mine{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_SPENDABLE | reuse_filter)}; - const CAmount tx_credit_watchonly{wtx.GetAvailableCredit(/* fUseCache */ true, ISMINE_WATCH_ONLY | reuse_filter)}; - if (is_trusted && tx_depth >= min_depth) { - ret.m_mine_trusted += tx_credit_mine; - ret.m_watchonly_trusted += tx_credit_watchonly; - } - if (!is_trusted && tx_depth == 0 && wtx.InMempool()) { - ret.m_mine_untrusted_pending += tx_credit_mine; - ret.m_watchonly_untrusted_pending += tx_credit_watchonly; - } - ret.m_mine_immature += wtx.GetImmatureCredit(); - ret.m_watchonly_immature += wtx.GetImmatureWatchOnlyCredit(); - } - } - return ret; -} - -CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const -{ - LOCK(cs_wallet); - - CAmount balance = 0; - std::vector<COutput> vCoins; - AvailableCoins(vCoins, coinControl); - for (const COutput& out : vCoins) { - if (out.fSpendable) { - balance += out.tx->tx->vout[out.i].nValue; - } - } - return balance; -} - -void CWallet::AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) const -{ - AssertLockHeld(cs_wallet); - - vCoins.clear(); - CAmount nTotal = 0; - // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where - // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses - bool allow_used_addresses = !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || (coinControl && !coinControl->m_avoid_address_reuse); - const int min_depth = {coinControl ? coinControl->m_min_depth : DEFAULT_MIN_DEPTH}; - const int max_depth = {coinControl ? coinControl->m_max_depth : DEFAULT_MAX_DEPTH}; - const bool only_safe = {coinControl ? !coinControl->m_include_unsafe_inputs : true}; - - std::set<uint256> trusted_parents; - for (const auto& entry : mapWallet) - { - const uint256& wtxid = entry.first; - const CWalletTx& wtx = entry.second; - - if (!chain().checkFinalTx(*wtx.tx)) { - continue; - } - - if (wtx.IsImmatureCoinBase()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < 0) - continue; - - // We should not consider coins which aren't at least in our mempool - // It's possible for these to be conflicted via ancestors which we may never be able to detect - if (nDepth == 0 && !wtx.InMempool()) - continue; - - bool safeTx = IsTrusted(wtx, trusted_parents); - - // We should not consider coins from transactions that are replacing - // other transactions. - // - // Example: There is a transaction A which is replaced by bumpfee - // transaction B. In this case, we want to prevent creation of - // a transaction B' which spends an output of B. - // - // Reason: If transaction A were initially confirmed, transactions B - // and B' would no longer be valid, so the user would have to create - // a new transaction C to replace B'. However, in the case of a - // one-block reorg, transactions B' and C might BOTH be accepted, - // when the user only wanted one of them. Specifically, there could - // be a 1-block reorg away from the chain where transactions A and C - // were accepted to another chain where B, B', and C were all - // accepted. - if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) { - safeTx = false; - } - - // Similarly, we should not consider coins from transactions that - // have been replaced. In the example above, we would want to prevent - // creation of a transaction A' spending an output of A, because if - // transaction B were initially confirmed, conflicting with A and - // A', we wouldn't want to the user to create a transaction D - // intending to replace A', but potentially resulting in a scenario - // where A, A', and D could all be accepted (instead of just B and - // D, or just A and A' like the user would want). - if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) { - safeTx = false; - } - - if (only_safe && !safeTx) { - continue; - } - - if (nDepth < min_depth || nDepth > max_depth) { - continue; - } - - for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { - // Only consider selected coins if add_inputs is false - if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) { - continue; - } - - if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount) - continue; - - if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i))) - continue; - - if (IsLockedCoin(entry.first, i)) - continue; - - if (IsSpent(wtxid, i)) - continue; - - isminetype mine = IsMine(wtx.tx->vout[i]); - - if (mine == ISMINE_NO) { - continue; - } - - if (!allow_used_addresses && IsSpentKey(wtxid, i)) { - continue; - } - - std::unique_ptr<SigningProvider> provider = GetSolvingProvider(wtx.tx->vout[i].scriptPubKey); - - bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false; - bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); - - vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly))); - - // Checks the sum amount of all UTXO's. - if (nMinimumSumAmount != MAX_MONEY) { - nTotal += wtx.tx->vout[i].nValue; - - if (nTotal >= nMinimumSumAmount) { - return; - } - } - - // Checks the maximum number of UTXO's. - if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { - return; - } - } - } -} - -std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const -{ - AssertLockHeld(cs_wallet); - - std::map<CTxDestination, std::vector<COutput>> result; - std::vector<COutput> availableCoins; - - AvailableCoins(availableCoins); - - for (const COutput& coin : availableCoins) { - CTxDestination address; - if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) && - ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) { - result[address].emplace_back(std::move(coin)); - } - } - - std::vector<COutPoint> lockedCoins; - ListLockedCoins(lockedCoins); - // Include watch-only for LegacyScriptPubKeyMan wallets without private keys - const bool include_watch_only = GetLegacyScriptPubKeyMan() && IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); - const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; - for (const COutPoint& output : lockedCoins) { - auto it = mapWallet.find(output.hash); - if (it != mapWallet.end()) { - int depth = it->second.GetDepthInMainChain(); - if (depth >= 0 && output.n < it->second.tx->vout.size() && - IsMine(it->second.tx->vout[output.n]) == is_mine_filter - ) { - CTxDestination address; - if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) { - result[address].emplace_back( - &it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */); - } - } - } - } - - return result; -} - -const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const -{ - AssertLockHeld(cs_wallet); - const CTransaction* ptx = &tx; - int n = output; - while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) { - const COutPoint& prevout = ptx->vin[0].prevout; - auto it = mapWallet.find(prevout.hash); - if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n || - !IsMine(it->second.tx->vout[prevout.n])) { - break; - } - ptx = it->second.tx.get(); - n = prevout.n; - } - return ptx->vout[n]; -} - -bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins, - std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const -{ - setCoinsRet.clear(); - nValueRet = 0; - - // Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output. - std::vector<OutputGroup> positive_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, true /* positive_only */); - if (SelectCoinsBnB(positive_groups, nTargetValue, coin_selection_params.m_cost_of_change, setCoinsRet, nValueRet)) { - return true; - } - // The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here. - std::vector<OutputGroup> all_groups = GroupOutputs(coins, coin_selection_params, eligibility_filter, false /* positive_only */); - // While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output. - // So we need to include that for KnapsackSolver as well, as we are expecting to create a change output. - return KnapsackSolver(nTargetValue + coin_selection_params.m_change_fee, all_groups, setCoinsRet, nValueRet); -} - -bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const -{ - std::vector<COutput> vCoins(vAvailableCoins); - CAmount value_to_select = nTargetValue; - - // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) - if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs) - { - for (const COutput& out : vCoins) - { - if (!out.fSpendable) - continue; - nValueRet += out.tx->tx->vout[out.i].nValue; - setCoinsRet.insert(out.GetInputCoin()); - } - return (nValueRet >= nTargetValue); - } - - // calculate value from preset inputs and store them - std::set<CInputCoin> setPresetCoins; - CAmount nValueFromPresetInputs = 0; - - std::vector<COutPoint> vPresetInputs; - coin_control.ListSelected(vPresetInputs); - for (const COutPoint& outpoint : vPresetInputs) - { - std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); - if (it != mapWallet.end()) - { - const CWalletTx& wtx = it->second; - // Clearly invalid input, fail - if (wtx.tx->vout.size() <= outpoint.n) { - return false; - } - // Just to calculate the marginal byte size - CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false)); - nValueFromPresetInputs += coin.txout.nValue; - if (coin.m_input_bytes <= 0) { - return false; // Not solvable, can't estimate size for fee - } - coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes); - if (coin_selection_params.m_subtract_fee_outputs) { - value_to_select -= coin.txout.nValue; - } else { - value_to_select -= coin.effective_value; - } - setPresetCoins.insert(coin); - } else { - return false; // TODO: Allow non-wallet inputs - } - } - - // remove preset inputs from vCoins so that Coin Selection doesn't pick them. - for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();) - { - if (setPresetCoins.count(it->GetInputCoin())) - it = vCoins.erase(it); - else - ++it; - } - - unsigned int limit_ancestor_count = 0; - unsigned int limit_descendant_count = 0; - chain().getPackageLimits(limit_ancestor_count, limit_descendant_count); - const size_t max_ancestors = (size_t)std::max<int64_t>(1, limit_ancestor_count); - const size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count); - const bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); - - // form groups from remaining coins; note that preset coins will not - // automatically have their associated (same address) coins included - if (coin_control.m_avoid_partial_spends && vCoins.size() > OUTPUT_GROUP_MAX_ENTRIES) { - // Cases where we have 101+ outputs all pointing to the same destination may result in - // privacy leaks as they will potentially be deterministically sorted. We solve that by - // explicitly shuffling the outputs before processing - Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext()); - } - - // Coin Selection attempts to select inputs from a pool of eligible UTXOs to fund the - // transaction at a target feerate. If an attempt fails, more attempts may be made using a more - // permissive CoinEligibilityFilter. - const bool res = [&] { - // Pre-selected inputs already cover the target amount. - if (value_to_select <= 0) return true; - - // If possible, fund the transaction with confirmed UTXOs only. Prefer at least six - // confirmations on outputs received from other wallets and only spend confirmed change. - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; - - // Fall back to using zero confirmation change (but with as few ancestors in the mempool as - // possible) if we cannot fund the transaction otherwise. - if (m_spend_zero_conf_change) { - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params)) return true; - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), - vCoins, setCoinsRet, nValueRet, coin_selection_params)) { - return true; - } - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), - vCoins, setCoinsRet, nValueRet, coin_selection_params)) { - return true; - } - // If partial groups are allowed, relax the requirement of spending OutputGroups (groups - // of UTXOs sent to the same address, which are obviously controlled by a single wallet) - // in their entirety. - if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), - vCoins, setCoinsRet, nValueRet, coin_selection_params)) { - return true; - } - // Try with unsafe inputs if they are allowed. This may spend unconfirmed outputs - // received from other wallets. - if (coin_control.m_include_unsafe_inputs - && SelectCoinsMinConf(value_to_select, - CoinEligibilityFilter(0 /* conf_mine */, 0 /* conf_theirs */, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), - vCoins, setCoinsRet, nValueRet, coin_selection_params)) { - return true; - } - // Try with unlimited ancestors/descendants. The transaction will still need to meet - // mempool ancestor/descendant policy to be accepted to mempool and broadcasted, but - // OutputGroups use heuristics that may overestimate ancestor/descendant counts. - if (!fRejectLongChains && SelectCoinsMinConf(value_to_select, - CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), true /* include_partial_groups */), - vCoins, setCoinsRet, nValueRet, coin_selection_params)) { - return true; - } - } - // Coin Selection failed. - return false; - }(); - - // SelectCoinsMinConf clears setCoinsRet, so add the preset inputs from coin_control to the coinset - util::insert(setCoinsRet, setPresetCoins); - - // add preset inputs to the total value selected - nValueRet += nValueFromPresetInputs; - - return res; -} - bool CWallet::SignTransaction(CMutableTransaction& tx) const { AssertLockHeld(cs_wallet); @@ -2645,118 +1888,6 @@ SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkh return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl coinControl) -{ - std::vector<CRecipient> vecSend; - - // Turn the txout set into a CRecipient vector. - for (size_t idx = 0; idx < tx.vout.size(); idx++) { - const CTxOut& txOut = tx.vout[idx]; - CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; - vecSend.push_back(recipient); - } - - coinControl.fAllowOtherInputs = true; - - for (const CTxIn& txin : tx.vin) { - coinControl.Select(txin.prevout); - } - - // Acquire the locks to prevent races to the new locked unspents between the - // CreateTransaction call and LockCoin calls (when lockUnspents is true). - LOCK(cs_wallet); - - CTransactionRef tx_new; - FeeCalculation fee_calc_out; - if (!CreateTransaction(vecSend, tx_new, nFeeRet, nChangePosInOut, error, coinControl, fee_calc_out, false)) { - return false; - } - - if (nChangePosInOut != -1) { - tx.vout.insert(tx.vout.begin() + nChangePosInOut, tx_new->vout[nChangePosInOut]); - } - - // Copy output sizes from new transaction; they may have had the fee - // subtracted from them. - for (unsigned int idx = 0; idx < tx.vout.size(); idx++) { - tx.vout[idx].nValue = tx_new->vout[idx].nValue; - } - - // Add new txins while keeping original txin scriptSig/order. - for (const CTxIn& txin : tx_new->vin) { - if (!coinControl.IsSelected(txin.prevout)) { - tx.vin.push_back(txin); - - } - if (lockUnspents) { - LockCoin(txin.prevout); - } - - } - - return true; -} - -static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash) -{ - if (chain.isInitialBlockDownload()) { - return false; - } - constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds - int64_t block_time; - CHECK_NONFATAL(chain.findBlock(block_hash, FoundBlock().time(block_time))); - if (block_time < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) { - return false; - } - return true; -} - -/** - * Return a height-based locktime for new transactions (uses the height of the - * current chain tip unless we are not synced with the current chain - */ -static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height) -{ - uint32_t locktime; - // Discourage fee sniping. - // - // 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 to orphan the current best block. By setting nLockTime such that - // only the next block can include the transaction, we discourage this - // practice as the height restricted and limited blocksize gives miners - // considering fee sniping fewer options for pulling off this attack. - // - // A simple way to think about this is from the wallet's point of view we - // always want the blockchain to move forward. By setting nLockTime this - // way we're basically making the statement that we only want this - // transaction to appear in the next block; we don't want to potentially - // encourage reorgs by allowing transactions to appear at lower heights - // than the next block in forks of the best chain. - // - // Of course, the subsidy is high enough, and transaction volume low - // enough, that fee sniping isn't a problem yet, but by implementing a fix - // now we ensure code won't be written that makes assumptions about - // nLockTime that preclude a fix later. - if (IsCurrentForAntiFeeSniping(chain, block_hash)) { - locktime = block_height; - - // Secondly occasionally randomly pick a nLockTime even further back, so - // that transactions that are delayed after signing for whatever reason, - // e.g. high-latency mix networks and some CoinJoin implementations, have - // better privacy. - if (GetRandInt(10) == 0) - locktime = std::max(0, (int)locktime - GetRandInt(100)); - } else { - // If our chain is lagging behind, we can't discourage fee sniping nor help - // the privacy of high-latency transactions. To avoid leaking a potentially - // unique "nLockTime fingerprint", set nLockTime to a constant. - locktime = 0; - } - assert(locktime < LOCKTIME_THRESHOLD); - return locktime; -} - OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const { // If -changetype is specified, always use that change type. @@ -2785,363 +1916,6 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang return m_default_address_type; } -bool CWallet::CreateTransactionInternal( - const std::vector<CRecipient>& vecSend, - CTransactionRef& tx, - CAmount& nFeeRet, - int& nChangePosInOut, - bilingual_str& error, - const CCoinControl& coin_control, - FeeCalculation& fee_calc_out, - bool sign) -{ - CAmount nValue = 0; - const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); - ReserveDestination reservedest(this, change_type); - unsigned int nSubtractFeeFromAmount = 0; - for (const auto& recipient : vecSend) - { - if (nValue < 0 || recipient.nAmount < 0) - { - error = _("Transaction amounts must not be negative"); - return false; - } - nValue += recipient.nAmount; - - if (recipient.fSubtractFeeFromAmount) - nSubtractFeeFromAmount++; - } - if (vecSend.empty()) - { - error = _("Transaction must have at least one recipient"); - return false; - } - - CMutableTransaction txNew; - FeeCalculation feeCalc; - TxSize tx_sizes; - int nBytes; - { - std::set<CInputCoin> setCoins; - LOCK(cs_wallet); - txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight()); - { - std::vector<COutput> vAvailableCoins; - AvailableCoins(vAvailableCoins, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0); - CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy - coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends; - - // Create change script that will be used if we need change - // TODO: pass in scriptChange instead of reservedest so - // change transaction isn't always pay-to-bitcoin-address - CScript scriptChange; - - // coin control: send change to custom address - if (!std::get_if<CNoDestination>(&coin_control.destChange)) { - scriptChange = GetScriptForDestination(coin_control.destChange); - } else { // no coin control: send change to newly generated address - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool. If it fails, provide a dummy - // destination in case we don't need change. - CTxDestination dest; - if (!reservedest.GetReservedDestination(dest, true)) { - error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first."); - } - scriptChange = GetScriptForDestination(dest); - // A valid destination implies a change script (and - // vice-versa). An empty change script will abort later, if the - // change keypool ran out, but change is required. - CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty()); - } - CTxOut change_prototype_txout(0, scriptChange); - coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); - - // Get size of spending the change output - int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this); - // If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh - // as lower-bound to allow BnB to do it's thing - if (change_spend_size == -1) { - coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE; - } else { - coin_selection_params.change_spend_size = (size_t)change_spend_size; - } - - // Set discard feerate - coin_selection_params.m_discard_feerate = GetDiscardRate(*this); - - // Get the fee rate to use effective values in coin selection - coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc); - // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly - // provided one - if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) { - error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB)); - return false; - } - if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) { - // eventually allow a fallback fee - error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); - return false; - } - - // Get long term estimate - CCoinControl cc_temp; - cc_temp.m_confirm_target = chain().estimateMaxBlocks(); - coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr); - - // Calculate the cost of change - // Cost of change is the cost of creating the change output + cost of spending the change output in the future. - // For creating the change output now, we use the effective feerate. - // For spending the change output in the future, we use the discard feerate for now. - // So cost of change = (change output size * effective feerate) + (size of spending change output * discard feerate) - coin_selection_params.m_change_fee = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size); - coin_selection_params.m_cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_change_fee; - - coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values - - // vouts to the payees - if (!coin_selection_params.m_subtract_fee_outputs) { - coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size) - } - for (const auto& recipient : vecSend) - { - CTxOut txout(recipient.nAmount, recipient.scriptPubKey); - - // Include the fee cost for outputs. - if (!coin_selection_params.m_subtract_fee_outputs) { - coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION); - } - - if (IsDust(txout, chain().relayDustFee())) - { - error = _("Transaction amount too small"); - return false; - } - txNew.vout.push_back(txout); - } - - // Include the fees for things that aren't inputs, excluding the change output - const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size); - CAmount nValueToSelect = nValue + not_input_fees; - - // Choose coins to use - CAmount inputs_sum = 0; - setCoins.clear(); - if (!SelectCoins(vAvailableCoins, /* nTargetValue */ nValueToSelect, setCoins, inputs_sum, coin_control, coin_selection_params)) - { - error = _("Insufficient funds"); - return false; - } - - // Always make a change output - // We will reduce the fee from this change output later, and remove the output if it is too small. - const CAmount change_and_fee = inputs_sum - nValue; - assert(change_and_fee >= 0); - CTxOut newTxOut(change_and_fee, scriptChange); - - if (nChangePosInOut == -1) - { - // Insert change txn at random position: - nChangePosInOut = GetRandInt(txNew.vout.size()+1); - } - else if ((unsigned int)nChangePosInOut > txNew.vout.size()) - { - error = _("Change index out of range"); - return false; - } - - assert(nChangePosInOut != -1); - auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut); - - // Dummy fill vin for maximum size estimation - // - for (const auto& coin : setCoins) { - txNew.vin.push_back(CTxIn(coin.outpoint,CScript())); - } - - // Calculate the transaction fee - tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); - nBytes = tx_sizes.vsize; - if (nBytes < 0) { - error = _("Signing transaction failed"); - return false; - } - nFeeRet = coin_selection_params.m_effective_feerate.GetFee(nBytes); - - // Subtract fee from the change output if not subtrating it from recipient outputs - CAmount fee_needed = nFeeRet; - if (nSubtractFeeFromAmount == 0) { - change_position->nValue -= fee_needed; - } - - // We want to drop the change to fees if: - // 1. The change output would be dust - // 2. The change is within the (almost) exact match window, i.e. it is less than or equal to the cost of the change output (cost_of_change) - CAmount change_amount = change_position->nValue; - if (IsDust(*change_position, coin_selection_params.m_discard_feerate) || change_amount <= coin_selection_params.m_cost_of_change) - { - nChangePosInOut = -1; - change_amount = 0; - txNew.vout.erase(change_position); - - // Because we have dropped this change, the tx size and required fee will be different, so let's recalculate those - tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly); - nBytes = tx_sizes.vsize; - fee_needed = coin_selection_params.m_effective_feerate.GetFee(nBytes); - } - - // Update nFeeRet in case fee_needed changed due to dropping the change output - if (fee_needed <= change_and_fee - change_amount) { - nFeeRet = change_and_fee - change_amount; - } - - // Reduce output values for subtractFeeFromAmount - if (nSubtractFeeFromAmount != 0) { - CAmount to_reduce = fee_needed + change_amount - change_and_fee; - int i = 0; - bool fFirst = true; - for (const auto& recipient : vecSend) - { - if (i == nChangePosInOut) { - ++i; - } - CTxOut& txout = txNew.vout[i]; - - if (recipient.fSubtractFeeFromAmount) - { - txout.nValue -= to_reduce / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient - - if (fFirst) // first receiver pays the remainder not divisible by output count - { - fFirst = false; - txout.nValue -= to_reduce % nSubtractFeeFromAmount; - } - - // Error if this output is reduced to be below dust - if (IsDust(txout, chain().relayDustFee())) { - if (txout.nValue < 0) { - error = _("The transaction amount is too small to pay the fee"); - } else { - error = _("The transaction amount is too small to send after the fee has been deducted"); - } - return false; - } - } - ++i; - } - nFeeRet = fee_needed; - } - - // Give up if change keypool ran out and change is required - if (scriptChange.empty() && nChangePosInOut != -1) { - return false; - } - } - - // Shuffle selected coins and fill in final vin - txNew.vin.clear(); - std::vector<CInputCoin> selected_coins(setCoins.begin(), setCoins.end()); - Shuffle(selected_coins.begin(), selected_coins.end(), FastRandomContext()); - - // Note how the sequence number is set to non-maxint so that - // the nLockTime set above actually works. - // - // BIP125 defines opt-in RBF as any nSequence < maxint-1, so - // we use the highest possible value in that range (maxint-2) - // to avoid conflicting with other possible uses of nSequence, - // and in the spirit of "smallest possible change from prior - // behavior." - const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); - for (const auto& coin : selected_coins) { - txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); - } - - if (sign && !SignTransaction(txNew)) { - error = _("Signing transaction failed"); - return false; - } - - // Return the constructed transaction data. - tx = MakeTransactionRef(std::move(txNew)); - - // Limit size - if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) || - (!sign && tx_sizes.weight > MAX_STANDARD_TX_WEIGHT)) - { - error = _("Transaction too large"); - return false; - } - } - - if (nFeeRet > m_default_max_tx_fee) { - error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED); - return false; - } - - if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) { - // Lastly, ensure this tx will pass the mempool's chain limits - if (!chain().checkChainLimits(tx)) { - error = _("Transaction has too long of a mempool chain"); - return false; - } - } - - // Before we return success, we assume any change key will be used to prevent - // accidental re-use. - reservedest.KeepDestination(); - fee_calc_out = feeCalc; - - WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", - nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, - feeCalc.est.pass.start, feeCalc.est.pass.end, - (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) > 0.0 ? 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool) : 0.0, - feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, - feeCalc.est.fail.start, feeCalc.est.fail.end, - (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) > 0.0 ? 100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool) : 0.0, - feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool); - return true; -} - -bool CWallet::CreateTransaction( - const std::vector<CRecipient>& vecSend, - CTransactionRef& tx, - CAmount& nFeeRet, - int& nChangePosInOut, - bilingual_str& error, - const CCoinControl& coin_control, - FeeCalculation& fee_calc_out, - bool sign) -{ - int nChangePosIn = nChangePosInOut; - Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr) - bool res = CreateTransactionInternal(vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign); - // try with avoidpartialspends unless it's enabled already - if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) { - CCoinControl tmp_cc = coin_control; - tmp_cc.m_avoid_partial_spends = true; - CAmount nFeeRet2; - CTransactionRef tx2; - int nChangePosInOut2 = nChangePosIn; - bilingual_str error2; // fired and forgotten; if an error occurs, we discard the results - if (CreateTransactionInternal(vecSend, tx2, nFeeRet2, nChangePosInOut2, error2, tmp_cc, fee_calc_out, sign)) { - // if fee of this alternative one is within the range of the max fee, we use this one - const bool use_aps = nFeeRet2 <= nFeeRet + m_max_aps_fee; - WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped"); - if (use_aps) { - tx = tx2; - nFeeRet = nFeeRet2; - nChangePosInOut = nChangePosInOut2; - } - } - } - return res; -} - void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm) { LOCK(cs_wallet); @@ -3385,137 +2159,6 @@ void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations } } -std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() const -{ - std::map<CTxDestination, CAmount> balances; - - { - LOCK(cs_wallet); - std::set<uint256> trusted_parents; - for (const auto& walletEntry : mapWallet) - { - const CWalletTx& wtx = walletEntry.second; - - if (!IsTrusted(wtx, trusted_parents)) - continue; - - if (wtx.IsImmatureCoinBase()) - continue; - - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1)) - continue; - - for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) - { - CTxDestination addr; - if (!IsMine(wtx.tx->vout[i])) - continue; - if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr)) - continue; - - CAmount n = IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue; - balances[addr] += n; - } - } - } - - return balances; -} - -std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() const -{ - AssertLockHeld(cs_wallet); - std::set< std::set<CTxDestination> > groupings; - std::set<CTxDestination> grouping; - - for (const auto& walletEntry : mapWallet) - { - const CWalletTx& wtx = walletEntry.second; - - if (wtx.tx->vin.size() > 0) - { - bool any_mine = false; - // group all input addresses with each other - for (const CTxIn& txin : wtx.tx->vin) - { - CTxDestination address; - if(!IsMine(txin)) /* If this input isn't mine, ignore it */ - continue; - if(!ExtractDestination(mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address)) - continue; - grouping.insert(address); - any_mine = true; - } - - // group change with input addresses - if (any_mine) - { - for (const CTxOut& txout : wtx.tx->vout) - if (IsChange(txout)) - { - CTxDestination txoutAddr; - if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) - continue; - grouping.insert(txoutAddr); - } - } - if (grouping.size() > 0) - { - groupings.insert(grouping); - grouping.clear(); - } - } - - // group lone addrs by themselves - for (const auto& txout : wtx.tx->vout) - if (IsMine(txout)) - { - CTxDestination address; - if(!ExtractDestination(txout.scriptPubKey, address)) - continue; - grouping.insert(address); - groupings.insert(grouping); - grouping.clear(); - } - } - - std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses - std::map< CTxDestination, std::set<CTxDestination>* > setmap; // map addresses to the unique group containing it - for (std::set<CTxDestination> _grouping : groupings) - { - // make a set of all the groups hit by this new group - std::set< std::set<CTxDestination>* > hits; - std::map< CTxDestination, std::set<CTxDestination>* >::iterator it; - for (const CTxDestination& address : _grouping) - if ((it = setmap.find(address)) != setmap.end()) - hits.insert((*it).second); - - // merge all hit groups into a new single group and delete old groups - std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping); - for (std::set<CTxDestination>* hit : hits) - { - merged->insert(hit->begin(), hit->end()); - uniqueGroupings.erase(hit); - delete hit; - } - uniqueGroupings.insert(merged); - - // update setmap - for (const CTxDestination& element : *merged) - setmap[element] = merged; - } - - std::set< std::set<CTxDestination> > ret; - for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings) - { - ret.insert(*uniqueGrouping); - delete uniqueGrouping; - } - - return ret; -} - std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) const { LOCK(cs_wallet); @@ -4231,92 +2874,6 @@ bool CWalletTx::IsImmatureCoinBase() const return GetBlocksToMaturity() > 0; } -std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const -{ - std::vector<OutputGroup> groups_out; - - if (!coin_sel_params.m_avoid_partial_spends) { - // Allowing partial spends means no grouping. Each COutput gets its own OutputGroup. - for (const COutput& output : outputs) { - // Skip outputs we cannot spend - if (!output.fSpendable) continue; - - size_t ancestors, descendants; - chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); - CInputCoin input_coin = output.GetInputCoin(); - - // Make an OutputGroup containing just this output - OutputGroup group{coin_sel_params}; - group.Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); - - // Check the OutputGroup's eligibility. Only add the eligible ones. - if (positive_only && group.GetSelectionAmount() <= 0) continue; - if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); - } - return groups_out; - } - - // We want to combine COutputs that have the same scriptPubKey into single OutputGroups - // except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup. - // To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups. - // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added - // to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has - // OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector. - std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map; - for (const auto& output : outputs) { - // Skip outputs we cannot spend - if (!output.fSpendable) continue; - - size_t ancestors, descendants; - chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants); - CInputCoin input_coin = output.GetInputCoin(); - CScript spk = input_coin.txout.scriptPubKey; - - std::vector<OutputGroup>& groups = spk_to_groups_map[spk]; - - if (groups.size() == 0) { - // No OutputGroups for this scriptPubKey yet, add one - groups.emplace_back(coin_sel_params); - } - - // Get the last OutputGroup in the vector so that we can add the CInputCoin to it - // A pointer is used here so that group can be reassigned later if it is full. - OutputGroup* group = &groups.back(); - - // Check if this OutputGroup is full. We limit to OUTPUT_GROUP_MAX_ENTRIES when using -avoidpartialspends - // to avoid surprising users with very high fees. - if (group->m_outputs.size() >= OUTPUT_GROUP_MAX_ENTRIES) { - // The last output group is full, add a new group to the vector and use that group for the insertion - groups.emplace_back(coin_sel_params); - group = &groups.back(); - } - - // Add the input_coin to group - group->Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants, positive_only); - } - - // Now we go through the entire map and pull out the OutputGroups - for (const auto& spk_and_groups_pair: spk_to_groups_map) { - const std::vector<OutputGroup>& groups_per_spk= spk_and_groups_pair.second; - - // Go through the vector backwards. This allows for the first item we deal with being the partial group. - for (auto group_it = groups_per_spk.rbegin(); group_it != groups_per_spk.rend(); group_it++) { - const OutputGroup& group = *group_it; - - // Don't include partial groups if there are full groups too and we don't want partial groups - if (group_it == groups_per_spk.rbegin() && groups_per_spk.size() > 1 && !filter.m_include_partial_groups) { - continue; - } - - // Check the OutputGroup's eligibility. Only add the eligible ones. - if (positive_only && group.GetSelectionAmount() <= 0) continue; - if (group.m_outputs.size() > 0 && group.EligibleForSpending(filter)) groups_out.push_back(group); - } - } - - return groups_out; -} - bool CWallet::IsCrypted() const { return HasEncryptionKeys(); |