diff options
author | Ava Chow <github@achow101.com> | 2024-07-17 18:27:59 -0400 |
---|---|---|
committer | Ava Chow <github@achow101.com> | 2024-07-17 18:27:59 -0400 |
commit | efbf4e71ce8e3cd49ccdfb5e55e14fa4b338453c (patch) | |
tree | 57672ef15f50c33654c39ef83f48a5f262e3e81a /test/functional | |
parent | 3679fa167f7d198d9287f05f210f03e83b76f3b8 (diff) | |
parent | 734076c6de1781f957c8bc3bf7ed6951920cfcf6 (diff) |
Merge bitcoin/bitcoin#29523: Wallet: Add `max_tx_weight` to transaction funding options (take 2)
734076c6de1781f957c8bc3bf7ed6951920cfcf6 [wallet, rpc]: add `max_tx_weight` to tx funding options (ismaelsadeeq)
b6fc5043c16c2467a2a6768a6ca9b18035fc400f [wallet]: update the data type of `change_output_size`, `change_spend_size` and `tx_noinputs_size` to `int` (ismaelsadeeq)
baab0d2d43049a71dc90176bc4d72062f7b2ce19 [doc]: update reason for deducting change output weight (ismaelsadeeq)
7f61d31a5cec8fc61328bee43f90d3f1dcb0a035 [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:
ACK 734076c6de1781f957c8bc3bf7ed6951920cfcf6
furszy:
utACK 734076c6de1781f957c8bc3bf7ed6951920cfcf6
rkrux:
reACK [734076c](https://github.com/bitcoin/bitcoin/pull/29523/commits/734076c6de1781f957c8bc3bf7ed6951920cfcf6)
Tree-SHA512: 013501aa443d239ee2ac01bccfc5296490c27b4edebe5cfca6b96c842375e895e5cfeb5424e82e359be581460f8be92095855763a62779a18ccd5bdfdd7ddce7
Diffstat (limited to 'test/functional')
-rwxr-xr-x | test/functional/rpc_psbt.py | 45 | ||||
-rw-r--r-- | test/functional/test_framework/blocktools.py | 1 |
2 files changed, 46 insertions, 0 deletions
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 8065eefdba..8c3adff3cf 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -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. " diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index f0dc866f69..338b7fa3b0 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -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 |