diff --git a/src/psbt.cpp b/src/psbt.cpp index c9f0311634e..18e2d0a7f84 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -464,6 +464,7 @@ bool PSBTInput::Merge(const PSBTInput& input) for (const auto& [agg_key_lh, psigs] : input.m_musig2_partial_sigs) { m_musig2_partial_sigs[agg_key_lh].insert(psigs.begin(), psigs.end()); } + if (sighash_type == std::nullopt && input.sighash_type != std::nullopt) sighash_type = input.sighash_type; if (sequence == std::nullopt && input.sequence != std::nullopt) sequence = input.sequence; if (time_locktime == std::nullopt && input.time_locktime != std::nullopt) time_locktime = input.time_locktime; if (height_locktime == std::nullopt && input.height_locktime != std::nullopt) height_locktime = input.height_locktime; diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 84c11278b92..8641417cfdc 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -565,6 +565,34 @@ class PSBTTest(BitcoinTestFramework): psbt.i[0].map[PSBT_IN_SIGHASH_TYPE] = (0x101).to_bytes(4, "little") assert_equal(node.decodepsbt(psbt.to_base64())["inputs"][0]["sighash"], "") + def test_combinepsbt_sighash_type(self): + self.log.info("Test that combining PSBTs preserves the sighash type field regardless of order") + node = self.nodes[0] + node.createwallet("combine_sighash") + wallet = node.get_wallet_rpc("combine_sighash") + def_wallet = node.get_wallet_rpc(self.default_wallet_name) + + def_wallet.send([{wallet.getnewaddress(address_type="bech32"): 1}]) + self.generate(node, 1) + psbt = wallet.walletcreatefundedpsbt(wallet.listunspent(), [{def_wallet.getnewaddress(): 0.5}])["psbt"] + + signed = wallet.walletprocesspsbt(psbt=psbt, sighashtype="ALL|ANYONECANPAY", finalize=False)["psbt"] + assert_equal(node.decodepsbt(signed)["inputs"][0].get("sighash"), "ALL|ANYONECANPAY") + updated = wallet.walletprocesspsbt(psbt=psbt, sign=False)["psbt"] + assert "sighash" not in node.decodepsbt(updated)["inputs"][0] + + finalized = [] + for psbts in [[signed, updated], [updated, signed]]: + combined = node.combinepsbt(psbts) + assert_equal(node.decodepsbt(combined)["inputs"][0].get("sighash"), "ALL|ANYONECANPAY") + fin_res = node.finalizepsbt(combined) + assert_equal(fin_res["complete"], True) + assert_equal(node.testmempoolaccept([fin_res["hex"]])[0]["allowed"], True) + finalized.append(fin_res["hex"]) + assert_equal(finalized[0], finalized[1]) + + wallet.unloadwallet() + def assert_change_type(self, psbtx, expected_type): """Assert that the given PSBT has a change output with the given type.""" @@ -1628,6 +1656,7 @@ class PSBTTest(BitcoinTestFramework): self.test_sighash_mismatch() self.test_sighash_adding() self.test_decodepsbt_long_sighash_type() + self.test_combinepsbt_sighash_type() self.test_psbt_named_parameter_handling() self.test_psbt_roundtrip() self.test_psbt_version()