mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-11 21:20:39 +02:00
Merge bitcoin/bitcoin#35516: rpc: preserve global xpubs and proprietary fields in joinpsbts
436921eb46test: check joinpsbts preserves global xpubs and proprietary fields (Thomas)011094b282rpc: preserve global xpubs and proprietary fields in joinpsbts (Thomas) Pull request description: `joinpsbts` collects the global xpubs of all the joined PSBTs into `merged_psbt`, but returns a separately constructed `shuffled_psbt` into which only the inputs, outputs, and unknown fields are copied. The collected `PSBT_GLOBAL_XPUB` records are silently dropped, and `PSBT_GLOBAL_PROPRIETARY` records are not collected at all. The xpub collection was added in #17034, which was written against a `joinpsbts` that still returned `merged_psbt`, but was merged after #16512 had introduced the `shuffled_psbt` rebuild, so the collected xpubs have never reached the result. Shuffle the inputs and outputs of `merged_psbt` in place instead of rebuilding a new PSBT, so that all global data is preserved, and union the global proprietary records in the merge loop, matching the `combinepsbt` behavior from #34893. ACKs for top commit: jpk68: ACK436921eb46achow101: ACK436921eb46winterrdog: tACK436921eb46Tree-SHA512: d9de34c25aecc29b6b4fb80d6584fa919cc5ff9b7ef2f4d8ce35c4043fe7638fefb8af10448f2cd14021f5d25e149f0efc8798c5b8c9bc8b5582c6152010e891
This commit is contained in:
@@ -1180,6 +1180,45 @@ class PSBTTest(BitcoinTestFramework):
|
||||
break
|
||||
assert shuffled
|
||||
|
||||
# Check that joining preserves global xpub and proprietary records
|
||||
def global_xpub_key(extended_pubkey):
|
||||
xpub_data, xpub_version = base58_to_byte(extended_pubkey)
|
||||
return bytes([PSBT_GLOBAL_XPUB]) + bytes([xpub_version]) + xpub_data
|
||||
|
||||
xpub1 = "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp"
|
||||
xpub_key1 = global_xpub_key(xpub1)
|
||||
xpub_key2 = global_xpub_key("tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B")
|
||||
xpub_value = b"\x00\x00\x00\x00" # master key fingerprint with an empty derivation path
|
||||
global_prop_key = bytes([PSBT_GLOBAL_PROPRIETARY]) + b"\x02\x01\x02\x00" # identifier "0102", subtype 0
|
||||
global_prop_value = b"\xde\xad\xbe\xef"
|
||||
|
||||
psbt1_obj = PSBT.from_base64(psbt1)
|
||||
psbt1_obj.g.map[xpub_key1] = xpub_value
|
||||
psbt1_obj.g.map[global_prop_key] = global_prop_value
|
||||
psbt2_obj = PSBT.from_base64(psbt2)
|
||||
psbt2_obj.g.map[xpub_key2] = xpub_value
|
||||
joined_globals = PSBT.from_base64(self.nodes[0].joinpsbts([psbt1_obj.to_base64(), psbt2_obj.to_base64()]))
|
||||
assert_equal(joined_globals.g.map[xpub_key1], xpub_value)
|
||||
assert_equal(joined_globals.g.map[xpub_key2], xpub_value)
|
||||
assert_equal(joined_globals.g.map[global_prop_key], global_prop_value)
|
||||
|
||||
# Same proprietary key in both PSBTs with different values: the first PSBT's value wins
|
||||
collide_key = bytes([PSBT_GLOBAL_PROPRIETARY]) + b"\x02\x03\x04\x00"
|
||||
psbt_first_obj = PSBT.from_base64(psbt1)
|
||||
psbt_first_obj.g.map[collide_key] = b"\x11\x11\x11\x11"
|
||||
psbt_second_obj = PSBT.from_base64(psbt2)
|
||||
psbt_second_obj.g.map[collide_key] = b"\x22\x22\x22\x22"
|
||||
joined_collision = PSBT.from_base64(self.nodes[0].joinpsbts([psbt_first_obj.to_base64(), psbt_second_obj.to_base64()]))
|
||||
assert_equal(joined_collision.g.map[collide_key], b"\x11\x11\x11\x11")
|
||||
|
||||
# Same xpub with conflicting origins: the first PSBT's origin is kept, avoiding duplicate keys
|
||||
conflict_first_obj = PSBT.from_base64(psbt1)
|
||||
conflict_first_obj.g.map[xpub_key1] = xpub_value
|
||||
conflict_second_obj = PSBT.from_base64(psbt2)
|
||||
conflict_second_obj.g.map[xpub_key1] = b"\x11\x11\x11\x11"
|
||||
joined_conflict = self.nodes[0].joinpsbts([conflict_first_obj.to_base64(), conflict_second_obj.to_base64()])
|
||||
assert_equal(self.nodes[0].decodepsbt(joined_conflict)["global_xpubs"], [{"xpub": xpub1, "master_fingerprint": "00000000", "path": "m"}])
|
||||
|
||||
# Newly created PSBT needs UTXOs and updating
|
||||
addr = self.nodes[1].getnewaddress("", "p2sh-segwit")
|
||||
utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 7}])[0]
|
||||
|
||||
Reference in New Issue
Block a user