mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-19 23:03:45 +01:00
Merge bitcoin/bitcoin#29523: Wallet: Add max_tx_weight to transaction funding options (take 2)
734076c6de[wallet, rpc]: add `max_tx_weight` to tx funding options (ismaelsadeeq)b6fc5043c1[wallet]: update the data type of `change_output_size`, `change_spend_size` and `tx_noinputs_size` to `int` (ismaelsadeeq)baab0d2d43[doc]: update reason for deducting change output weight (ismaelsadeeq)7f61d31a5c[refactor]: update coin selection algorithms input parameter `max_weight` name (ismaelsadeeq) Pull request description: This PR taken over from #29264 The PR added an option `max_tx_weight` to transaction funding RPC's that ensures the resulting transaction weight does not exceed the specified `max_tx_weight` limit. If `max_tx_weight` is not given `MAX_STANDARD_TX_WEIGHT` is used as the max threshold. This PR addressed outstanding review comments in #29264 For more context and rationale behind this PR see https://delvingbitcoin.org/t/lightning-transactions-with-v3-and-ephemeral-anchors/418/11?u=instagibbs ACKs for top commit: achow101: ACK734076c6defurszy: utACK734076c6derkrux: reACK [734076c](734076c6de) Tree-SHA512: 013501aa443d239ee2ac01bccfc5296490c27b4edebe5cfca6b96c842375e895e5cfeb5424e82e359be581460f8be92095855763a62779a18ccd5bdfdd7ddce7
This commit is contained in:
@@ -695,26 +695,35 @@ util::Result<SelectionResult> ChooseSelectionResult(interfaces::Chain& chain, co
|
||||
}
|
||||
};
|
||||
|
||||
// Maximum allowed weight
|
||||
int max_inputs_weight = MAX_STANDARD_TX_WEIGHT - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
|
||||
// Maximum allowed weight for selected coins.
|
||||
int max_transaction_weight = coin_selection_params.m_max_tx_weight.value_or(MAX_STANDARD_TX_WEIGHT);
|
||||
int tx_weight_no_input = coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR;
|
||||
int max_selection_weight = max_transaction_weight - tx_weight_no_input;
|
||||
if (max_selection_weight <= 0) {
|
||||
return util::Error{_("Maximum transaction weight is less than transaction weight without inputs")};
|
||||
}
|
||||
|
||||
// SFFO frequently causes issues in the context of changeless input sets: skip BnB when SFFO is active
|
||||
if (!coin_selection_params.m_subtract_fee_outputs) {
|
||||
if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change, max_inputs_weight)}) {
|
||||
if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change, max_selection_weight)}) {
|
||||
results.push_back(*bnb_result);
|
||||
} else append_error(std::move(bnb_result));
|
||||
}
|
||||
|
||||
// As Knapsack and SRD can create change, also deduce change weight.
|
||||
max_inputs_weight -= (coin_selection_params.change_output_size * WITNESS_SCALE_FACTOR);
|
||||
// Deduct change weight because remaining Coin Selection algorithms can create change output
|
||||
int change_outputs_weight = coin_selection_params.change_output_size * WITNESS_SCALE_FACTOR;
|
||||
max_selection_weight -= change_outputs_weight;
|
||||
if (max_selection_weight < 0 && results.empty()) {
|
||||
return util::Error{_("Maximum transaction weight is too low, can not accommodate change output")};
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast, max_inputs_weight)}) {
|
||||
if (auto knapsack_result{KnapsackSolver(groups.mixed_group, nTargetValue, coin_selection_params.m_min_change_target, coin_selection_params.rng_fast, max_selection_weight)}) {
|
||||
results.push_back(*knapsack_result);
|
||||
} else append_error(std::move(knapsack_result));
|
||||
|
||||
if (coin_selection_params.m_effective_feerate > CFeeRate{3 * coin_selection_params.m_long_term_feerate}) { // Minimize input set for feerates of at least 3×LTFRE (default: 30 ṩ/vB+)
|
||||
if (auto cg_result{CoinGrinder(groups.positive_group, nTargetValue, coin_selection_params.m_min_change_target, max_inputs_weight)}) {
|
||||
if (auto cg_result{CoinGrinder(groups.positive_group, nTargetValue, coin_selection_params.m_min_change_target, max_selection_weight)}) {
|
||||
cg_result->RecalculateWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
|
||||
results.push_back(*cg_result);
|
||||
} else {
|
||||
@@ -722,7 +731,7 @@ util::Result<SelectionResult> ChooseSelectionResult(interfaces::Chain& chain, co
|
||||
}
|
||||
}
|
||||
|
||||
if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.m_change_fee, coin_selection_params.rng_fast, max_inputs_weight)}) {
|
||||
if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.m_change_fee, coin_selection_params.rng_fast, max_selection_weight)}) {
|
||||
results.push_back(*srd_result);
|
||||
} else append_error(std::move(srd_result));
|
||||
|
||||
@@ -801,7 +810,7 @@ util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& av
|
||||
coin_selection_params.m_change_fee);
|
||||
|
||||
// Verify we haven't exceeded the maximum allowed weight
|
||||
int max_inputs_weight = MAX_STANDARD_TX_WEIGHT - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
|
||||
int max_inputs_weight = coin_selection_params.m_max_tx_weight.value_or(MAX_STANDARD_TX_WEIGHT) - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
|
||||
if (op_selection_result->GetWeight() > max_inputs_weight) {
|
||||
return util::Error{_("The combination of the pre-selected inputs and the wallet automatic inputs selection exceeds the transaction maximum weight. "
|
||||
"Please try sending a smaller amount or manually consolidating your wallet's UTXOs")};
|
||||
@@ -1012,7 +1021,11 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
||||
CoinSelectionParams coin_selection_params{rng_fast}; // Parameters for coin selection, init with dummy
|
||||
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
|
||||
coin_selection_params.m_include_unsafe_inputs = coin_control.m_include_unsafe_inputs;
|
||||
|
||||
coin_selection_params.m_max_tx_weight = coin_control.m_max_tx_weight.value_or(MAX_STANDARD_TX_WEIGHT);
|
||||
int minimum_tx_weight = MIN_STANDARD_TX_NONWITNESS_SIZE * WITNESS_SCALE_FACTOR;
|
||||
if (coin_selection_params.m_max_tx_weight.value() < minimum_tx_weight || coin_selection_params.m_max_tx_weight.value() > MAX_STANDARD_TX_WEIGHT) {
|
||||
return util::Error{strprintf(_("Maximum transaction weight must be between %d and %d"), minimum_tx_weight, MAX_STANDARD_TX_WEIGHT)};
|
||||
}
|
||||
// Set the long term feerate estimate to the wallet's consolidate feerate
|
||||
coin_selection_params.m_long_term_feerate = wallet.m_consolidate_feerate;
|
||||
// Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 witness overhead (dummy, flag, stack size)
|
||||
@@ -1077,7 +1090,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
|
||||
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;
|
||||
coin_selection_params.change_spend_size = change_spend_size;
|
||||
}
|
||||
|
||||
// Set discard feerate
|
||||
|
||||
Reference in New Issue
Block a user