mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-25 18:53:23 -03:00
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.
This commit is contained in:
parent
612f1e44fe
commit
a0c3afb898
2 changed files with 78 additions and 0 deletions
|
@ -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 <consensus/validation.h>
|
||||
#include <interfaces/chain.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
|
@ -171,6 +172,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
|
|||
// While we're here, calculate the input amount
|
||||
std::map<COutPoint, Coin> coins;
|
||||
CAmount input_value = 0;
|
||||
std::vector<CTxOut> 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);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#ifndef BITCOIN_WALLET_FEEBUMPER_H
|
||||
#define BITCOIN_WALLET_FEEBUMPER_H
|
||||
|
||||
#include <consensus/consensus.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
||||
class uint256;
|
||||
|
@ -55,6 +57,55 @@ Result CommitTransaction(CWallet& wallet,
|
|||
std::vector<bilingual_str>& errors,
|
||||
uint256& bumped_txid);
|
||||
|
||||
struct SignatureWeights
|
||||
{
|
||||
private:
|
||||
int m_sigs_count{0};
|
||||
int64_t m_sigs_weight{0};
|
||||
|
||||
public:
|
||||
void AddSigWeight(const size_t weight, const SigVersion sigversion)
|
||||
{
|
||||
switch (sigversion) {
|
||||
case SigVersion::BASE:
|
||||
m_sigs_weight += weight * WITNESS_SCALE_FACTOR;
|
||||
m_sigs_count += 1 * WITNESS_SCALE_FACTOR;
|
||||
break;
|
||||
case SigVersion::WITNESS_V0:
|
||||
m_sigs_weight += weight;
|
||||
m_sigs_count++;
|
||||
break;
|
||||
case SigVersion::TAPROOT:
|
||||
case SigVersion::TAPSCRIPT:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t GetWeightDiffToMax() const
|
||||
{
|
||||
// Note: the witness scaling factor is already accounted for because the count is multiplied by it.
|
||||
return (/* max signature size=*/ 72 * m_sigs_count) - m_sigs_weight;
|
||||
}
|
||||
};
|
||||
|
||||
class SignatureWeightChecker : public DeferringSignatureChecker
|
||||
{
|
||||
private:
|
||||
SignatureWeights& m_weights;
|
||||
|
||||
public:
|
||||
SignatureWeightChecker(SignatureWeights& weights, const BaseSignatureChecker& checker) : DeferringSignatureChecker(checker), m_weights(weights) {}
|
||||
|
||||
bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& pubkey, const CScript& script, SigVersion sigversion) const override
|
||||
{
|
||||
if (m_checker.CheckECDSASignature(sig, pubkey, script, sigversion)) {
|
||||
m_weights.AddSigWeight(sig.size(), sigversion);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace feebumper
|
||||
} // namespace wallet
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue