Skip to content

Fix incorrect check in cache_unsigned_tx_pieces#830

Open
bigspider wants to merge 1 commit into
bitcoin-core:masterfrom
bigspider:psbt_fix
Open

Fix incorrect check in cache_unsigned_tx_pieces#830
bigspider wants to merge 1 commit into
bitcoin-core:masterfrom
bigspider:psbt_fix

Conversation

@bigspider
Copy link
Copy Markdown
Contributor

self.tx is not None is always true because the constructor sets self.tx = CTransaction() when None is passed, which causes a call to setup_from_tx also for PSBT version 2, contrarily to the function's stated intent in the documentation.

`self.tx is not None` is always true because the constructor sets
`self.tx = CTransaction()` when `None` is passed, which causes a
call to `setup_from_tx` also for PSBT version 2, contrarily to the
function's stated intent in the documentation.
Copy link
Copy Markdown
Contributor

@brunoerg brunoerg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 13715e8

Can be tested with this (vibe-coded) test:

diff --git a/test/test_psbt.py b/test/test_psbt.py
index 3a585b0..2c0c009 100755
--- a/test/test_psbt.py
+++ b/test/test_psbt.py
@@ -1,6 +1,6 @@
 #! /usr/bin/env python3

-from hwilib.psbt import PSBT
+from hwilib.psbt import PSBT, PartiallySignedInput, PartiallySignedOutput
 from hwilib.errors import PSBTSerializationError
 import json
 import os
@@ -28,5 +28,40 @@ class TestPSBT(unittest.TestCase):
                 serd = psbt.serialize()
                 self.assertEqual(valid, serd)

+    def test_v2_roundtrip_preserves_global_fields(self):
+        # Regression test: deserializing a PSBTv2 must not clobber the global
+        # tx_version and fallback_locktime fields. cache_unsigned_tx_pieces()
+        # used to call setup_from_tx() unconditionally, which for a v2 PSBT
+        # (whose self.tx is a default-constructed null CTransaction) overwrote
+        # tx_version with 1 and fallback_locktime with 0.
+        psbt = PSBT()
+        psbt.version = 2
+        psbt.explicit_version = True
+        psbt.tx_version = 2
+        psbt.fallback_locktime = 500000
+
+        psin = PartiallySignedInput(2)
+        psin.prev_txid = bytes.fromhex("11" * 32)
+        psin.prev_out = 0
+        psin.sequence = 0xFFFFFFFF
+        psbt.inputs.append(psin)
+
+        psout = PartiallySignedOutput(2)
+        psout.amount = 12345
+        psout.script = b"\x00\x14" + b"\x22" * 20
+        psbt.outputs.append(psout)
+
+        serd = psbt.serialize()
+
+        deser = PSBT()
+        deser.deserialize(serd)
+
+        self.assertEqual(deser.version, 2)
+        self.assertEqual(deser.tx_version, 2)
+        self.assertEqual(deser.fallback_locktime, 500000)
+        self.assertEqual(deser.inputs[0].prev_out, 0)
+        self.assertEqual(deser.outputs[0].amount, 12345)
+        self.assertEqual(serd, deser.serialize())
+
 if __name__ == "__main__":
     unittest.main()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants