aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorismaelsadeeq <ask4ismailsadiq@gmail.com>2024-02-29 12:43:52 +0100
committerismaelsadeeq <ask4ismailsadiq@gmail.com>2024-06-27 15:31:21 +0100
commit734076c6de1781f957c8bc3bf7ed6951920cfcf6 (patch)
tree1102525c0ba39be6d66671ea3b17fd9524bbdfc3 /test
parentb6fc5043c16c2467a2a6768a6ca9b18035fc400f (diff)
[wallet, rpc]: add `max_tx_weight` to tx funding options
This allows a transaction's weight to be bound under a certain weight if possible and desired. This can be beneficial for future RBF attempts, or whenever a more restricted spend topology is desired. Co-authored-by: Greg Sanders <gsanders87@gmail.com>
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/rpc_psbt.py45
-rw-r--r--test/functional/test_framework/blocktools.py1
2 files changed, 46 insertions, 0 deletions
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 6ee7e56886..111ca63618 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,
@@ -186,6 +191,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