From be9781d8b4b975382ffddf7632302b1334bb80e5 Mon Sep 17 00:00:00 2001 From: fanquake Date: Thu, 29 Apr 2021 22:04:30 +0800 Subject: [PATCH] Merge bitcoin/bitcoin#21759: wallet: document coin selection code 6ba892126d354219b146f0c7f35d472f9c14bdac refactor + document coin selection strategy (glozow) 58ea324fdd906204bb77ea4be1c01a3ab56cf86f [docs] add doxygen comments to wallet code (glozow) 0c74716c50384677724247e05e6592f845fc8635 [docs] format existing comments as doxygen (glozow) Pull request description: I think it would help code review to have more documentation + doxygen comments ACKs for top commit: Xekyo: ReACK https://github.com/bitcoin/bitcoin/commit/6ba892126d354219b146f0c7f35d472f9c14bdac achow101: ACK 6ba892126d354219b146f0c7f35d472f9c14bdac Tree-SHA512: 74a78d9b0e0c1d5659bed566432a5b3511511d8b2432f440565f443da7b8257a1b90e70aa7505a7f8abf618748eeb43d166e84f278bdee3d34ce5d5c37dc573a --- src/wallet/coinselection.h | 30 ++++++++++++++- src/wallet/wallet.cpp | 62 ++++++++++++++++++++++++------- src/wallet/wallet.h | 76 +++++++++++++++++++++++++++++--------- 3 files changed, 136 insertions(+), 32 deletions(-) diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index 1620d54572553..6070a43b8ebd2 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -15,6 +15,7 @@ static constexpr CAmount MIN_CHANGE{COIN / 100}; //! final minimum change amount after paying for fees static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2; +/** A UTXO under consideration for use in funding a new transaction. */ class CInputCoin { public: CInputCoin(const CTransactionRef& tx, unsigned int i) @@ -56,31 +57,58 @@ class CInputCoin { } }; +/** Parameters for filtering which OutputGroups we may use in coin selection. + * We start by being very selective and requiring multiple confirmations and + * then get more permissive if we cannot fund the transaction. */ struct CoinEligibilityFilter { + /** Minimum number of confirmations for outputs that we sent to ourselves. + * We may use unconfirmed UTXOs sent from ourselves, e.g. change outputs. */ const int conf_mine; + /** Minimum number of confirmations for outputs received from a different + * wallet. We never spend unconfirmed foreign outputs as we cannot rely on these funds yet. */ const int conf_theirs; + /** Maximum number of unconfirmed ancestors aggregated across all UTXOs in an OutputGroup. */ const uint64_t max_ancestors; + /** Maximum number of descendants that a single UTXO in the OutputGroup may have. */ const uint64_t max_descendants; - const bool m_include_partial_groups{false}; //! Include partial destination groups when avoid_reuse and there are full groups + /** When avoid_reuse=true and there are full groups (OUTPUT_GROUP_MAX_ENTRIES), whether or not to use any partial groups.*/ + const bool m_include_partial_groups{false}; CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {} CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {} CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants), m_include_partial_groups(include_partial) {} }; +/** A group of UTXOs paid to the same output script. */ struct OutputGroup { + /** The list of UTXOs contained in this output group. */ std::vector m_outputs; + /** Whether the UTXOs were sent by the wallet to itself. This is relevant because we may want at + * least a certain number of confirmations on UTXOs received from outside wallets while trusting + * our own UTXOs more. */ bool m_from_me{true}; + /** The total value of the UTXOs in sum. */ CAmount m_value{0}; + /** The minimum number of confirmations the UTXOs in the group have. Unconfirmed is 0. */ int m_depth{999}; + /** The aggregated count of unconfirmed ancestors of all UTXOs in this + * group. Not deduplicated and may overestimate when ancestors are shared. */ size_t m_ancestors{0}; + /** The maximum count of descendants of a single UTXO in this output group. */ size_t m_descendants{0}; + /** The value of the UTXOs after deducting the cost of spending them at the effective feerate. */ CAmount effective_value{0}; + /** The fee to spend these UTXOs at the effective feerate. */ CAmount fee{0}; + /** The target feerate of the transaction we're trying to build. */ CFeeRate m_effective_feerate{0}; + /** The fee to spend these UTXOs at the long term feerate. */ CAmount long_term_fee{0}; + /** The feerate for spending a created change output eventually (i.e. not urgently, and thus at + * a lower feerate). Calculated using long term fee estimate. This is used to decide whether + * it could be economical to create a change output. */ CFeeRate m_long_term_feerate{0}; OutputGroup() {} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b4c400aa04ab0..545a0ddf829d9 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2877,7 +2877,7 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm } } - // remove preset inputs from vCoins + // remove preset inputs from vCoins so that Coin Selection doesn't pick them. for (std::vector::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();) { if (setPresetCoins.count(it->GetInputCoin())) @@ -2889,9 +2889,9 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm unsigned int limit_ancestor_count = 0; unsigned int limit_descendant_count = 0; chain().getPackageLimits(limit_ancestor_count, limit_descendant_count); - size_t max_ancestors = (size_t)std::max(1, limit_ancestor_count); - size_t max_descendants = (size_t)std::max(1, limit_descendant_count); - bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + const size_t max_ancestors = (size_t)std::max(1, limit_ancestor_count); + const size_t max_descendants = (size_t)std::max(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 @@ -2901,16 +2901,52 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm // explicitly shuffling the outputs before processing Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext()); } - bool res = value_to_select <= 0 || - SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType) || - SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType) || - (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)) || - (m_spend_zero_conf_change && 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, bnb_used, nCoinType)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1, true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)) || - (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits::max(), std::numeric_limits::max(), true /* include_partial_groups */), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)); + // 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, bnb_used, nCoinType)) return true; + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)) 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. We never spend unconfirmed + // outputs received from other wallets. + if (m_spend_zero_conf_change) { + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)) 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, bnb_used, nCoinType)) { + return true; + } + if (SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), + vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)) { + 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, bnb_used, nCoinType)) { + 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::max(), std::numeric_limits::max(), true /* include_partial_groups */), + vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used, nCoinType)) { + return true; + } + } + // Coin Selection failed. + return false; + }(); - // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset + // 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 diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 6a80b3690ccdd..7cb32f4c0a736 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -393,7 +393,7 @@ class CWalletTx CTransactionRef tx; - /* New transactions start as UNCONFIRMED. At BlockConnected, + /** New transactions start as UNCONFIRMED. At BlockConnected, * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected, * they roll back to UNCONFIRMED. If we detect a conflicting transaction at * block connection, we update conflicted tx and its dependencies as CONFLICTED. @@ -408,7 +408,7 @@ class CWalletTx ABANDONED }; - /* Confirmation includes tx status and a triplet of {block height/block hash/tx index in block} + /** Confirmation includes tx status and a triplet of {block height/block hash/tx index in block} * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned. * Meaning of these fields changes with CONFLICTED state where they instead point to block hash * and block height of the deepest conflicting tx. @@ -517,7 +517,7 @@ class CWalletTx CAmount GetAnonymizedCredit(const CCoinControl* coinControl = nullptr) const; CAmount GetDenominatedCredit(bool unconfirmed, bool fUseCache=true) const; - // Get the marginal bytes if spending the specified output from this transaction + /** Get the marginal bytes if spending the specified output from this transaction */ int GetSpendSize(unsigned int out, bool use_max_sig = false) const { return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig); @@ -531,7 +531,7 @@ class CWalletTx return (GetDebit(filter) > 0); } - // True if only scriptSigs are different + /** True if only scriptSigs are different */ bool IsEquivalentTo(const CWalletTx& tx) const; bool InMempool() const; @@ -541,7 +541,7 @@ class CWalletTx bool CanBeResent() const; - // Pass this transaction to node for mempool insertion and relay to peers if flag set to true + /** Pass this transaction to node for mempool insertion and relay to peers if flag set to true */ bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay); // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct @@ -623,7 +623,15 @@ class COutput { public: const CWalletTx *tx; + + /** Index in tx->vout. */ int i; + + /** + * Depth in block chain. + * If > 0: the tx is on chain and has this many confirmations. + * If = 0: the tx is waiting confirmation. + * If < 0: a conflicting tx is on chain and has this many confirmations. */ int nDepth; /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */ @@ -663,15 +671,23 @@ class COutput } }; +/** Parameters for one iteration of Coin Selection. */ struct CoinSelectionParams { + /** Toggles use of Branch and Bound instead of Knapsack solver. */ bool use_bnb = true; + /** Size of a change output in bytes, determined by the output type. */ size_t change_output_size = 0; + /** Size of the input to spend a change output in virtual bytes. */ size_t change_spend_size = 0; + /** The targeted feerate of the transaction being built. */ CFeeRate effective_fee = CFeeRate(0); size_t tx_noinputs_size = 0; - //! Indicate that we are subtracting the fee from outputs + /** Indicate that we are subtracting the fee from outputs */ bool m_subtract_fee_outputs = false; + /** When true, always spend all (up to OUTPUT_GROUP_MAX_ENTRIES) or none of the outputs + * associated with the same address. This helps reduce privacy leaks resulting from address + * reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */ bool m_avoid_partial_spends = false; CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size, bool avoid_partial) : @@ -709,6 +725,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE}; int64_t nNextResend = 0; + /** Whether this wallet will submit newly created transactions to the node's mempool and + * prompt rebroadcasts (see ResendWalletTransactions()). */ bool fBroadcastTransactions = false; // Local time that the tip block was received. Used to schedule wallet rebroadcasts. std::atomic m_best_block_time {0}; @@ -746,10 +764,10 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati */ bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, WalletBatch& batch, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ + /** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */ void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx); - /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */ + /** Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */ void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void SyncMetaData(std::pair) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -758,6 +776,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */ void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, WalletBatch& batch, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + /** WalletFlags set on this wallet. */ std::atomic m_wallet_flags{0}; bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose); @@ -809,7 +828,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati */ void InitCoinJoinSalt(); - /* Height of last block processed is used by wallet to know depth of transactions + /** Height of last block processed is used by wallet to know depth of transactions * without relying on Chain interface beyond asynchronous updates. For safety, we * initialize it to -1. Height is a pointer on node's tip and doesn't imply * that the wallet has scanned sequentially all blocks up to this one. @@ -826,7 +845,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati bool CreateTransactionInternal(const std::vector& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, bool sign, int nExtraPayloadSize); public: - /* + /** * Main wallet lock. * This lock protects all the fields added by CWallet. */ @@ -840,8 +859,11 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati /** * Select a set of coins such that nValueRet >= nTargetValue and at least - * all coins from coinControl are selected; Never select unconfirmed coins - * if they are not ours + * all coins from coin_control are selected; never select unconfirmed coins if they are not ours + * param@[out] setCoinsRet Populated with inputs including pre-selected inputs from + * coin_control and Coin Selection if successful. + * param@[out] nValueRet Total value of selected coins including pre-selected ones + * from coin_control and Coin Selection if successful. */ bool SelectCoins(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -881,6 +903,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati void UpdateProgress(const std::string& title, int nProgress) override; + /** Map from txid to CWalletTx for all transactions this wallet is + * interested in, including received and sent transactions. */ std::map mapWallet GUARDED_BY(cs_wallet); typedef std::multimap TxItems; @@ -892,6 +916,10 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati std::map m_address_book GUARDED_BY(cs_wallet); const CAddressBookData* FindAddressBookEntry(const CTxDestination&, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + /** Set of Coins owned by this wallet that we won't try to spend from. A + * Coin may be locked if it has already been used to fund a transaction + * that hasn't confirmed yet. We wouldn't consider the Coin spent already, + * but also shouldn't try to use it again. */ std::set setLockedCoins GUARDED_BY(cs_wallet); int64_t nKeysLeftSinceAutoBackup; @@ -933,6 +961,11 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * small change; This method is stochastic for some inputs and upon * completion the coin set and corresponding actual target value is * assembled + * param@[in] coins Set of UTXOs to consider. These will be categorized into + * OutputGroups and filtered using eligibility_filter before + * selecting coins. + * param@[out] setCoinsRet Populated with the coins selected if successful. + * param@[out] nValueRet Used to return the total value of selected coins. */ bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector coins, std::set& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used, CoinType nCoinType = CoinType::ALL_COINS) const; @@ -1069,9 +1102,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * calling CreateTransaction(); */ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, CCoinControl); - // Fetch the inputs and sign with SIGHASH_ALL. + /** Fetch the inputs and sign with SIGHASH_ALL. */ bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - // Sign the tx given the input coins and sighash. + /** Sign the tx given the input coins and sighash. */ bool SignTransaction(CMutableTransaction& tx, const std::map& coins, int sighash, std::map& input_errors) const; SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const; @@ -1128,6 +1161,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE}; unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET}; + /** Allow Coin Selection to pick unconfirmed UTXOs that were sent from our own wallet if it + * cannot fund the transaction otherwise. */ bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE}; bool m_allow_fallback_fee{true}; //!< will be false if -fallbackfee=0 CFeeRate m_min_fee{DEFAULT_TRANSACTION_MINFEE}; //!< Override with -mintxfee @@ -1137,7 +1172,12 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * Override with -fallbackfee */ CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE}; + + /** If the cost to spend a change output at this feerate is greater than the value of the + * output itself, just drop it to fees. */ CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE}; + + /** The maximum fee amount we're willing to pay to prioritize partial spend avoidance. */ CAmount m_max_aps_fee{DEFAULT_MAX_AVOIDPARTIALSPEND_FEE}; //!< note: this is absolute fee, not fee rate /** Absolute maximum transaction fee (in satoshis) used by default for the wallet */ CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE}; @@ -1477,10 +1517,10 @@ class WalletRescanReserver }; -// Calculate the size of the transaction assuming all signatures are max size -// Use DummySignatureCreator, which inserts 71 byte signatures everywhere. -// NOTE: this requires that all inputs must be in mapWallet (eg the tx should -// be IsAllFromMe). +/** Calculate the size of the transaction assuming all signatures are max size +* Use DummySignatureCreator, which inserts 71 byte signatures everywhere. +* NOTE: this requires that all inputs must be in mapWallet (eg the tx should +* be IsAllFromMe). */ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector& txouts, bool use_max_sig = false);