From 8403e0e845396f0d57431353e4dbd3b36dade702 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Mon, 1 Dec 2025 12:01:08 -0600 Subject: [PATCH 1/3] Merge bitcoin/bitcoin#25773: test: Target exact weight in MiniWallet _bulk_tx fa2537cf0a7629d81df1bc5b4ae6a22dc572647b test: Target exact weight in MiniWallet _bulk_tx (MacroFake) Pull request description: Seems better to target the exact weight than a weight that is up to more than 2000 WU larger. Also, replace a broad `-acceptnonstdtxn=1` with `-datacarriersize=100000` to document the test assumptions better. ACKs for top commit: theStack: Code-review ACK fa2537cf0a7629d81df1bc5b4ae6a22dc572647b Tree-SHA512: cf02c3082a13195b8aa730866aeaf2575ce01974ae2b0244739d8cfc12e60c66312729ed703bb3214651744166a3b560bfaa8dc302ef46ed79fc4d1fe7fcc214 --- test/functional/mempool_package_limits.py | 4 ++-- test/functional/test_framework/wallet.py | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/test/functional/mempool_package_limits.py b/test/functional/mempool_package_limits.py index f324c4de73ce..010c9539cd1b 100755 --- a/test/functional/mempool_package_limits.py +++ b/test/functional/mempool_package_limits.py @@ -33,8 +33,8 @@ def run_test(self): self.test_anc_count_limits_2() self.test_anc_count_limits_bushy() - # The node will accept our (nonstandard) extra large OP_RETURN outputs - self.restart_node(0, extra_args=["-acceptnonstdtxn=1"]) + # The node will accept (nonstandard) extra large OP_RETURN outputs + self.restart_node(0, extra_args=["-datacarriersize=100000"]) self.test_anc_size_limits() self.test_desc_size_limits() diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 277a795bbc73..d64333f3ec3a 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -29,9 +29,10 @@ ) from test_framework.script import ( CScript, - SignatureHash, - OP_TRUE, OP_NOP, + OP_RETURN, + OP_TRUE, + SignatureHash, SIGHASH_ALL, ) from test_framework.script_util import ( @@ -97,11 +98,13 @@ def _bulk_tx(self, tx, target_weight): """Pad a transaction with extra outputs until it reaches a target weight (or higher). returns the tx """ - assert_greater_than_or_equal(target_weight, tx.get_weight()) - while tx.get_weight() < target_weight: - script_pubkey = ( b"6a4d0200" # OP_RETURN OP_PUSH2 512 bytes - + b"01" * 512 ) - tx.vout.append(CTxOut(0, script_pubkey)) + tx.vout.append(CTxOut(nValue=0, scriptPubKey=CScript([OP_RETURN, b'a']))) + dummy_vbytes = (target_weight - tx.get_weight() + 3) // 4 + tx.vout[-1].scriptPubKey = CScript([OP_RETURN, b'a' * dummy_vbytes]) + # Lower bound should always be off by at most 3 + assert_greater_than_or_equal(tx.get_weight(), target_weight) + # Higher bound should always be off by at most 3 + 12 weight (for encoding the length) + assert_greater_than_or_equal(target_weight + 15, tx.get_weight()) def get_balance(self): return sum(u['value'] for u in self._utxos) From 221ace0c973379ecf372147f11bfdf4a43347892 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 2 Dec 2025 11:03:15 -0600 Subject: [PATCH 2/3] validation: Fix _bulk_tx for Dash's weight calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Dash, get_weight() returns vsize directly (no witness scaling factor), while Bitcoin's get_weight() returns weight units where 4 units = 1 vbyte. The original Bitcoin code divided by 4 to convert weight units to vbytes. In Dash, this division is incorrect since get_weight() already returns vbytes. This caused _bulk_tx to create transactions much smaller than the target weight. Fix: Remove the division by 4 and adjust directly for Dash's vsize-based weight calculation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- test/functional/test_framework/wallet.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index d64333f3ec3a..296d9bb99344 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -99,7 +99,12 @@ def _bulk_tx(self, tx, target_weight): returns the tx """ tx.vout.append(CTxOut(nValue=0, scriptPubKey=CScript([OP_RETURN, b'a']))) - dummy_vbytes = (target_weight - tx.get_weight() + 3) // 4 + # In Dash, get_weight() returns vsize directly (no witness scaling) + # We need to account for compact size encoding overhead + current_weight = tx.get_weight() + # Estimate bytes needed, accounting for potential compact size encoding changes + # The +3 accounts for worst-case rounding in compact size encoding + dummy_vbytes = max(0, target_weight - current_weight + 3) tx.vout[-1].scriptPubKey = CScript([OP_RETURN, b'a' * dummy_vbytes]) # Lower bound should always be off by at most 3 assert_greater_than_or_equal(tx.get_weight(), target_weight) From 72699930cfbd962abddeded277d75ce0d2e7de3b Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 2 Dec 2025 15:24:09 -0600 Subject: [PATCH 3/3] validation: Correct _bulk_tx formula for Dash's weight calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix removed the division by 4 (correct for Dash since get_weight() returns vsize, not weight units), but incorrectly kept the +3 offset from Bitcoin's formula. In Bitcoin: (target - current + 3) // 4 - The +3 ensures rounding up - After division, adds 0-3 vbytes based on remainder In Dash: target - current - No division needed since get_weight() = vsize - The +3 was causing transactions to be 3 bytes too large - This caused insufficient fees (min relay fee not met) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- test/functional/test_framework/wallet.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 296d9bb99344..d17562a1843d 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -99,12 +99,12 @@ def _bulk_tx(self, tx, target_weight): returns the tx """ tx.vout.append(CTxOut(nValue=0, scriptPubKey=CScript([OP_RETURN, b'a']))) - # In Dash, get_weight() returns vsize directly (no witness scaling) - # We need to account for compact size encoding overhead - current_weight = tx.get_weight() - # Estimate bytes needed, accounting for potential compact size encoding changes - # The +3 accounts for worst-case rounding in compact size encoding - dummy_vbytes = max(0, target_weight - current_weight + 3) + # In Dash, get_weight() returns vsize directly (no witness scaling factor like Bitcoin's 4) + # Bitcoin's formula: (target_weight - tx.get_weight() + 3) // 4 + # - The +3 ensures rounding up when dividing by 4 + # - After division, this adds 0-3 vbytes depending on remainder + # For Dash (no division by 4), we calculate bytes needed directly + dummy_vbytes = target_weight - tx.get_weight() tx.vout[-1].scriptPubKey = CScript([OP_RETURN, b'a' * dummy_vbytes]) # Lower bound should always be off by at most 3 assert_greater_than_or_equal(tx.get_weight(), target_weight)