mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-12-08 11:44:14 +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:
@@ -8,6 +8,9 @@ from decimal import Decimal
|
||||
from itertools import product
|
||||
from random import randbytes
|
||||
|
||||
from test_framework.blocktools import (
|
||||
MAX_STANDARD_TX_WEIGHT,
|
||||
)
|
||||
from test_framework.descriptors import descsum_create
|
||||
from test_framework.key import H_POINT
|
||||
from test_framework.messages import (
|
||||
@@ -16,6 +19,7 @@ from test_framework.messages import (
|
||||
CTxIn,
|
||||
CTxOut,
|
||||
MAX_BIP125_RBF_SEQUENCE,
|
||||
WITNESS_SCALE_FACTOR,
|
||||
)
|
||||
from test_framework.psbt import (
|
||||
PSBT,
|
||||
@@ -30,6 +34,7 @@ from test_framework.psbt import (
|
||||
PSBT_OUT_TAP_TREE,
|
||||
)
|
||||
from test_framework.script import CScript, OP_TRUE
|
||||
from test_framework.script_util import MIN_STANDARD_TX_NONWITNESS_SIZE
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_approx,
|
||||
@@ -208,6 +213,46 @@ class PSBTTest(BitcoinTestFramework):
|
||||
# Create and fund a raw tx for sending 10 BTC
|
||||
psbtx1 = self.nodes[0].walletcreatefundedpsbt([], {self.nodes[2].getnewaddress():10})['psbt']
|
||||
|
||||
self.log.info("Test for invalid maximum transaction weights")
|
||||
dest_arg = [{self.nodes[0].getnewaddress(): 1}]
|
||||
min_tx_weight = MIN_STANDARD_TX_NONWITNESS_SIZE * WITNESS_SCALE_FACTOR
|
||||
assert_raises_rpc_error(-4, f"Maximum transaction weight must be between {min_tx_weight} and {MAX_STANDARD_TX_WEIGHT}", self.nodes[0].walletcreatefundedpsbt, [], dest_arg, 0, {"max_tx_weight": -1})
|
||||
assert_raises_rpc_error(-4, f"Maximum transaction weight must be between {min_tx_weight} and {MAX_STANDARD_TX_WEIGHT}", self.nodes[0].walletcreatefundedpsbt, [], dest_arg, 0, {"max_tx_weight": 0})
|
||||
assert_raises_rpc_error(-4, f"Maximum transaction weight must be between {min_tx_weight} and {MAX_STANDARD_TX_WEIGHT}", self.nodes[0].walletcreatefundedpsbt, [], dest_arg, 0, {"max_tx_weight": MAX_STANDARD_TX_WEIGHT + 1})
|
||||
|
||||
# Base transaction vsize: version (4) + locktime (4) + input count (1) + witness overhead (1) = 10 vbytes
|
||||
base_tx_vsize = 10
|
||||
# One P2WPKH output vsize: outpoint (31 vbytes)
|
||||
p2wpkh_output_vsize = 31
|
||||
# 1 vbyte for output count
|
||||
output_count = 1
|
||||
tx_weight_without_inputs = (base_tx_vsize + output_count + p2wpkh_output_vsize) * WITNESS_SCALE_FACTOR
|
||||
# min_tx_weight is greater than transaction weight without inputs
|
||||
assert_greater_than(min_tx_weight, tx_weight_without_inputs)
|
||||
|
||||
# In order to test for when the passed max weight is less than the transaction weight without inputs
|
||||
# Define destination with two outputs.
|
||||
dest_arg_large = [{self.nodes[0].getnewaddress(): 1}, {self.nodes[0].getnewaddress(): 1}]
|
||||
large_tx_vsize_without_inputs = base_tx_vsize + output_count + (p2wpkh_output_vsize * 2)
|
||||
large_tx_weight_without_inputs = large_tx_vsize_without_inputs * WITNESS_SCALE_FACTOR
|
||||
assert_greater_than(large_tx_weight_without_inputs, min_tx_weight)
|
||||
# Test for max_tx_weight less than Transaction weight without inputs
|
||||
assert_raises_rpc_error(-4, "Maximum transaction weight is less than transaction weight without inputs", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": min_tx_weight})
|
||||
assert_raises_rpc_error(-4, "Maximum transaction weight is less than transaction weight without inputs", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": large_tx_weight_without_inputs})
|
||||
|
||||
# Test for max_tx_weight just enough to include inputs but not change output
|
||||
assert_raises_rpc_error(-4, "Maximum transaction weight is too low, can not accommodate change output", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": (large_tx_vsize_without_inputs + 1) * WITNESS_SCALE_FACTOR})
|
||||
self.log.info("Test that a funded PSBT is always faithful to max_tx_weight option")
|
||||
large_tx_vsize_with_change = large_tx_vsize_without_inputs + p2wpkh_output_vsize
|
||||
# It's enough but won't accommodate selected input size
|
||||
assert_raises_rpc_error(-4, "The inputs size exceeds the maximum weight", self.nodes[0].walletcreatefundedpsbt, [], dest_arg_large, 0, {"max_tx_weight": (large_tx_vsize_with_change) * WITNESS_SCALE_FACTOR})
|
||||
|
||||
max_tx_weight_sufficient = 1000 # 1k vbytes is enough
|
||||
psbt = self.nodes[0].walletcreatefundedpsbt(outputs=dest_arg,locktime=0, options={"max_tx_weight": max_tx_weight_sufficient})["psbt"]
|
||||
weight = self.nodes[0].decodepsbt(psbt)["tx"]["weight"]
|
||||
# ensure the transaction's weight is below the specified max_tx_weight.
|
||||
assert_greater_than_or_equal(max_tx_weight_sufficient, weight)
|
||||
|
||||
# If inputs are specified, do not automatically add more:
|
||||
utxo1 = self.nodes[0].listunspent()[0]
|
||||
assert_raises_rpc_error(-4, "The preselected coins total amount does not cover the transaction target. "
|
||||
|
||||
@@ -48,6 +48,7 @@ from .util import assert_equal
|
||||
|
||||
MAX_BLOCK_SIGOPS = 20000
|
||||
MAX_BLOCK_SIGOPS_WEIGHT = MAX_BLOCK_SIGOPS * WITNESS_SCALE_FACTOR
|
||||
MAX_STANDARD_TX_WEIGHT = 400000
|
||||
|
||||
# Genesis block time (regtest)
|
||||
TIME_GENESIS_BLOCK = 1296688602
|
||||
|
||||
Reference in New Issue
Block a user