From a0c3afb898016c2e0a76dc48f68eaa5c3ae6282c Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 5 Oct 2021 21:41:54 -0400 Subject: [PATCH] bumpfee: extract weights of external inputs when bumping fee When bumping the fee of a transaction containing external inputs, determine the weights of those inputs. Because signatures can have a variable size, the script is executed with a special SignatureChecker which will compute the total weight of the signatures in the transaction and the weight if they were all maximum size signatures. This allows us to compute the maximum weight of the input for use during coin selection. --- src/wallet/feebumper.cpp | 27 +++++++++++++++++++++ src/wallet/feebumper.h | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 637153ec66..d2e3f07301 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -171,6 +172,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo // While we're here, calculate the input amount std::map coins; CAmount input_value = 0; + std::vector spent_outputs; for (const CTxIn& txin : wtx.tx->vin) { coins[txin.prevout]; // Create empty map entry keyed by prevout. } @@ -187,6 +189,31 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo new_coin_control.SelectExternal(txin.prevout, coin.out); } input_value += coin.out.nValue; + spent_outputs.push_back(coin.out); + } + + // Figure out if we need to compute the input weight, and do so if necessary + PrecomputedTransactionData txdata; + txdata.Init(*wtx.tx, std::move(spent_outputs), /* force=*/ true); + for (unsigned int i = 0; i < wtx.tx->vin.size(); ++i) { + const CTxIn& txin = wtx.tx->vin.at(i); + const Coin& coin = coins.at(txin.prevout); + + if (new_coin_control.IsExternalSelected(txin.prevout)) { + // For external inputs, we estimate the size using the size of this input + int64_t input_weight = GetTransactionInputWeight(txin); + // Because signatures can have different sizes, we need to figure out all of the + // signature sizes and replace them with the max sized signature. + // In order to do this, we verify the script with a special SignatureChecker which + // will observe the signatures verified and record their sizes. + SignatureWeights weights; + TransactionSignatureChecker tx_checker(wtx.tx.get(), i, coin.out.nValue, txdata, MissingDataBehavior::FAIL); + SignatureWeightChecker size_checker(weights, tx_checker); + VerifyScript(txin.scriptSig, coin.out.scriptPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, size_checker); + // Add the difference between max and current to input_weight so that it represents the largest the input could be + input_weight += weights.GetWeightDiffToMax(); + new_coin_control.SetInputWeight(txin.prevout, input_weight); + } } Result result = PreconditionChecks(wallet, wtx, errors); diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index 191878a137..908ef1117a 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_WALLET_FEEBUMPER_H #define BITCOIN_WALLET_FEEBUMPER_H +#include +#include