Repository: bitcoin/bitcoin
File: test/functional/rpc_psbt.py (Python)

Code before:
        _, out_fake_agg_pubkey = generate_keypair()
        psbt.o = [PSBTMap({
                    bytes([PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS]) + out_fake_agg_pubkey: [out_pubkey1, out_pubkey2],
                 })]
        res = self.nodes[0].decodepsbt(psbt.to_base64())
        assert_equal(len(res["inputs"]), 1)
        res_input = res["inputs"][0]
        assert_equal(len(res["outputs"]), 1)
        res_output = res["outputs"][0]

        assert "musig2_participant_pubkeys" in res_input
        in_participant_pks = res_input["musig2_participant_pubkeys"][0]
        assert "aggregate_pubkey" in in_participant_pks
        assert_equal(in_participant_pks["aggregate_pubkey"], in_fake_agg_pubkey.hex())
        assert "participant_pubkeys" in in_participant_pks
        assert_equal(in_participant_pks["participant_pubkeys"], [in_pubkey1.hex(), in_pubkey2.hex()])

        assert "musig2_pubnonces" in res_input
        in_pubnonce = res_input["musig2_pubnonces"][0]
        assert "participant_pubkey" in in_pubnonce
        assert_equal(in_pubnonce["participant_pubkey"], in_pubkey1.hex())
        assert "aggregate_pubkey" in in_pubnonce
        assert_equal(in_pubnonce["aggregate_pubkey"], in_fake_agg_pubkey.hex())
        assert "leaf_hash" in in_pubnonce
        assert_equal(in_pubnonce["leaf_hash"], fake_leaf_hash.hex())
        assert "pubnonce" in in_pubnonce
        assert_equal(in_pubnonce["pubnonce"], fake_pubnonce.hex())

        assert "musig2_partial_sigs" in res_input
        in_partialsig = res_input["musig2_partial_sigs"][0]
        assert "participant_pubkey" in in_partialsig
        assert_equal(in_partialsig["participant_pubkey"], in_pubkey1.hex())
        assert "aggregate_pubkey" in in_partialsig
        assert_equal(in_partialsig["aggregate_pubkey"], in_fake_agg_pubkey.hex())
        assert "leaf_hash" in in_partialsig
        assert_equal(in_partialsig["leaf_hash"], fake_leaf_hash.hex())
        assert "partial_sig" in in_partialsig
        assert_equal(in_partialsig["partial_sig"], fake_partialsig.hex())

        assert "musig2_participant_pubkeys" in res_output
        out_participant_pks = res_output["musig2_participant_pubkeys"][0]
        assert "aggregate_pubkey" in out_participant_pks
        assert_equal(out_participant_pks["aggregate_pubkey"], out_fake_agg_pubkey.hex())
        assert "participant_pubkeys" in out_participant_pks
        assert_equal(out_participant_pks["participant_pubkeys"], [out_pubkey1.hex(), out_pubkey2.hex()])

    def assert_change_type(self, psbtx, expected_type):
        """Assert that the given PSBT has a change output with the given type."""

        # The decodepsbt RPC is stateless and independent of any settings, we can always just call it on the first node
        decoded_psbt = self.nodes[0].decodepsbt(psbtx["psbt"])

Review comment (suggestion):
b70e83da4210e8098cbf3fc3b2289fc710828455: let's echo the specified sighash  value, otherwise the error is confusing in this default case.

We should probably also echo what's in the PSBT.

I think it's a good safety feature that the default behaviour is _not_ to just mimic what's in the PSBT but to insist on `DEFAULT` / `ALL`. The error message could warn against the temptation to call the RPC again with the "right" value.

Code after:
        _, out_pubkey2 = generate_keypair()
        _, out_fake_agg_pubkey = generate_keypair()
        psbt.o = [PSBTMap({
                    bytes([PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS]) + out_fake_agg_pubkey: [out_pubkey1, out_pubkey2],
                 })]
        res = self.nodes[0].decodepsbt(psbt.to_base64())
        assert_equal(len(res["inputs"]), 1)
        res_input = res["inputs"][0]
        assert_equal(len(res["outputs"]), 1)
        res_output = res["outputs"][0]

        assert "musig2_participant_pubkeys" in res_input
        in_participant_pks = res_input["musig2_participant_pubkeys"][0]
        assert "aggregate_pubkey" in in_participant_pks
        assert_equal(in_participant_pks["aggregate_pubkey"], in_fake_agg_pubkey.hex())
        assert "participant_pubkeys" in in_participant_pks
        assert_equal(in_participant_pks["participant_pubkeys"], [in_pubkey1.hex(), in_pubkey2.hex()])

        assert "musig2_pubnonces" in res_input
        in_pubnonce = res_input["musig2_pubnonces"][0]
        assert "participant_pubkey" in in_pubnonce
        assert_equal(in_pubnonce["participant_pubkey"], in_pubkey1.hex())
        assert "aggregate_pubkey" in in_pubnonce
        assert_equal(in_pubnonce["aggregate_pubkey"], in_fake_agg_pubkey.hex())
        assert "leaf_hash" in in_pubnonce
        assert_equal(in_pubnonce["leaf_hash"], fake_leaf_hash.hex())
        assert "pubnonce" in in_pubnonce
        assert_equal(in_pubnonce["pubnonce"], fake_pubnonce.hex())

        assert "musig2_partial_sigs" in res_input
        in_partialsig = res_input["musig2_partial_sigs"][0]
        assert "participant_pubkey" in in_partialsig
        assert_equal(in_partialsig["participant_pubkey"], in_pubkey1.hex())
        assert "aggregate_pubkey" in in_partialsig
        assert_equal(in_partialsig["aggregate_pubkey"], in_fake_agg_pubkey.hex())
        assert "leaf_hash" in in_partialsig
        assert_equal(in_partialsig["leaf_hash"], fake_leaf_hash.hex())
        assert "partial_sig" in in_partialsig
        assert_equal(in_partialsig["partial_sig"], fake_partialsig.hex())

        assert "musig2_participant_pubkeys" in res_output
        out_participant_pks = res_output["musig2_participant_pubkeys"][0]
        assert "aggregate_pubkey" in out_participant_pks
        assert_equal(out_participant_pks["aggregate_pubkey"], out_fake_agg_pubkey.hex())
        assert "participant_pubkeys" in out_participant_pks
        assert_equal(out_participant_pks["participant_pubkeys"], [out_pubkey1.hex(), out_pubkey2.hex()])

    def test_sighash_mismatch(self):
        self.log.info("Test sighash type mismatches")
        self.nodes[0].createwallet("sighash_mismatch")
        wallet = self.nodes[0].get_wallet_rpc("sighash_mismatch")