test: Test that metadata is synced to malleated transactions
What changed, and why it matters
This commit only adds a new test to Bitcoin Core. It checks that when a transaction is 'malleated' (changed slightly without invalidating it, for example by changing the signature type), any user-provided metadata such as a comment is still correctly copied to the modified transaction in the wallet. There is no code fix or behavior change in the main Bitcoin software—only a new automated test.
No action required. This is a test-only addition and does not change production code or fix a vulnerability.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds a test case in test/functional/wallet_txn_clone.py. The new test creates a transaction, strips its witness data, re-signs it with a different sighash (ALL|ANYONECANPAY) to produce a malleated version with a different txid, mines that malleated version, and then verifies that the wallet still associates the original user comment with the malleated txid. This is purely a regression/behavior test; no wallet logic is modified.
Changed components
test/functional/wallet_txn_clone.pyInspect captured patch +36 / −0
### test/functional/wallet_txn_clone.py
@@ -7,6 +7,7 @@
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_not_equal,
)
from test_framework.messages import (
COIN,
@@ -144,6 +145,41 @@ def run_test(self):
expected -= 50
assert_equal(self.nodes[0].getbalance(), expected)
+ self.test_malleated_metadata_synced()
+
+ def malleate_tx(self, wallet, txid):
+ rawtx = wallet.getrawtransaction(txid)
+ tx = tx_from_hex(rawtx)
+ for txin in tx.vin:
+ txin.scriptSig = b""
+ for wit in tx.wit.vtxinwit:
+ wit.scriptWitness.stack.clear()
+ unsigned_tx = tx.serialize_without_witness().hex()
+
+ # malleate the tx by signing with a different sighash
+ malleated_tx = wallet.signrawtransactionwithwallet(hexstring=unsigned_tx, sighashtype="ALL|ANYONECANPAY")["hex"]
+ malleated_txid = wallet.decoderawtransaction(malleated_tx)["txid"]
+ assert_not_equal(malleated_txid, txid)
+ return malleated_tx, malleated_txid
+
+
+ def test_malleated_metadata_synced(self):
+ self.log.info("Test malleated tx has copied user provided metadata")
+ self.nodes[0].createwallet("metadata_clone")
+ wallet = self.nodes[0].get_wallet_rpc("metadata_clone")
+ def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+
+ def_wallet.sendtoaddress(wallet.getnewaddress(address_type="legacy"), 1)
+
+ self.generate(self.nodes[0], 1)
+
+ original_txid = wallet.sendtoaddress(def_wallet.getnewaddress(), 0.5, comment="testing", fee_rate=1)
+ malleated_tx, malleated_txid = self.malleate_tx(wallet, original_txid)
+
+ self.generateblock(self.nodes[0], def_wallet.getnewaddress(), [malleated_tx])
+
+ assert_equal(wallet.gettransaction(malleated_txid)["comment"], "testing")
+
if __name__ == '__main__':
TxnMallTest(__file__).main()Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.