|
29 | 29 | PSBT_IN_SHA256,
|
30 | 30 | PSBT_IN_HASH160,
|
31 | 31 | PSBT_IN_HASH256,
|
| 32 | + PSBT_IN_MUSIG2_PARTIAL_SIG, |
| 33 | + PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS, |
| 34 | + PSBT_IN_MUSIG2_PUB_NONCE, |
32 | 35 | PSBT_IN_NON_WITNESS_UTXO,
|
33 | 36 | PSBT_IN_WITNESS_UTXO,
|
| 37 | + PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS, |
34 | 38 | PSBT_OUT_TAP_TREE,
|
35 | 39 | )
|
36 | 40 | from test_framework.script import CScript, OP_TRUE
|
@@ -199,6 +203,75 @@ def test_input_confs_control(self):
|
199 | 203 |
|
200 | 204 | wallet.unloadwallet()
|
201 | 205 |
|
| 206 | + def test_decodepsbt_musig2_input_output_types(self): |
| 207 | + self.log.info("Test decoding PSBT with MuSig2 per-input and per-output types") |
| 208 | + # create 2-of-2 musig2 using fake aggregate key, leaf hash, pubnonce, and partial sig |
| 209 | + # TODO: actually implement MuSig2 aggregation (for decoding only it doesn't matter though) |
| 210 | + _, in_pubkey1 = generate_keypair() |
| 211 | + _, in_pubkey2 = generate_keypair() |
| 212 | + _, in_fake_agg_pubkey = generate_keypair() |
| 213 | + fake_leaf_hash = randbytes(32) |
| 214 | + fake_pubnonce = randbytes(66) |
| 215 | + fake_partialsig = randbytes(32) |
| 216 | + tx = CTransaction() |
| 217 | + tx.vin = [CTxIn(outpoint=COutPoint(hash=int('ee' * 32, 16), n=0), scriptSig=b"")] |
| 218 | + tx.vout = [CTxOut(nValue=0, scriptPubKey=b"")] |
| 219 | + psbt = PSBT() |
| 220 | + psbt.g = PSBTMap({PSBT_GLOBAL_UNSIGNED_TX: tx.serialize()}) |
| 221 | + participant1_keydata = in_pubkey1 + in_fake_agg_pubkey + fake_leaf_hash |
| 222 | + psbt.i = [PSBTMap({ |
| 223 | + bytes([PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS]) + in_fake_agg_pubkey: [in_pubkey1, in_pubkey2], |
| 224 | + bytes([PSBT_IN_MUSIG2_PUB_NONCE]) + participant1_keydata: fake_pubnonce, |
| 225 | + bytes([PSBT_IN_MUSIG2_PARTIAL_SIG]) + participant1_keydata: fake_partialsig, |
| 226 | + })] |
| 227 | + _, out_pubkey1 = generate_keypair() |
| 228 | + _, out_pubkey2 = generate_keypair() |
| 229 | + _, out_fake_agg_pubkey = generate_keypair() |
| 230 | + psbt.o = [PSBTMap({ |
| 231 | + bytes([PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS]) + out_fake_agg_pubkey: [out_pubkey1, out_pubkey2], |
| 232 | + })] |
| 233 | + res = self.nodes[0].decodepsbt(psbt.to_base64()) |
| 234 | + assert_equal(len(res["inputs"]), 1) |
| 235 | + res_input = res["inputs"][0] |
| 236 | + assert_equal(len(res["outputs"]), 1) |
| 237 | + res_output = res["outputs"][0] |
| 238 | + |
| 239 | + assert "musig2_participant_pubkeys" in res_input |
| 240 | + in_participant_pks = res_input["musig2_participant_pubkeys"][0] |
| 241 | + assert "aggregate_pubkey" in in_participant_pks |
| 242 | + assert_equal(in_participant_pks["aggregate_pubkey"], in_fake_agg_pubkey.hex()) |
| 243 | + assert "participant_pubkeys" in in_participant_pks |
| 244 | + assert_equal(in_participant_pks["participant_pubkeys"], [in_pubkey1.hex(), in_pubkey2.hex()]) |
| 245 | + |
| 246 | + assert "musig2_pubnonces" in res_input |
| 247 | + in_pubnonce = res_input["musig2_pubnonces"][0] |
| 248 | + assert "participant_pubkey" in in_pubnonce |
| 249 | + assert_equal(in_pubnonce["participant_pubkey"], in_pubkey1.hex()) |
| 250 | + assert "aggregate_pubkey" in in_pubnonce |
| 251 | + assert_equal(in_pubnonce["aggregate_pubkey"], in_fake_agg_pubkey.hex()) |
| 252 | + assert "leaf_hash" in in_pubnonce |
| 253 | + assert_equal(in_pubnonce["leaf_hash"], fake_leaf_hash.hex()) |
| 254 | + assert "pubnonce" in in_pubnonce |
| 255 | + assert_equal(in_pubnonce["pubnonce"], fake_pubnonce.hex()) |
| 256 | + |
| 257 | + assert "musig2_partial_sigs" in res_input |
| 258 | + in_partialsig = res_input["musig2_partial_sigs"][0] |
| 259 | + assert "participant_pubkey" in in_partialsig |
| 260 | + assert_equal(in_partialsig["participant_pubkey"], in_pubkey1.hex()) |
| 261 | + assert "aggregate_pubkey" in in_partialsig |
| 262 | + assert_equal(in_partialsig["aggregate_pubkey"], in_fake_agg_pubkey.hex()) |
| 263 | + assert "leaf_hash" in in_partialsig |
| 264 | + assert_equal(in_partialsig["leaf_hash"], fake_leaf_hash.hex()) |
| 265 | + assert "partial_sig" in in_partialsig |
| 266 | + assert_equal(in_partialsig["partial_sig"], fake_partialsig.hex()) |
| 267 | + |
| 268 | + assert "musig2_participant_pubkeys" in res_output |
| 269 | + out_participant_pks = res_output["musig2_participant_pubkeys"][0] |
| 270 | + assert "aggregate_pubkey" in out_participant_pks |
| 271 | + assert_equal(out_participant_pks["aggregate_pubkey"], out_fake_agg_pubkey.hex()) |
| 272 | + assert "participant_pubkeys" in out_participant_pks |
| 273 | + assert_equal(out_participant_pks["participant_pubkeys"], [out_pubkey1.hex(), out_pubkey2.hex()]) |
| 274 | + |
202 | 275 | def assert_change_type(self, psbtx, expected_type):
|
203 | 276 | """Assert that the given PSBT has a change output with the given type."""
|
204 | 277 |
|
@@ -959,6 +1032,8 @@ def test_psbt_input_keys(psbt_input, keys):
|
959 | 1032 | assert hash.hex() in res_input[preimage_key]
|
960 | 1033 | assert_equal(res_input[preimage_key][hash.hex()], preimage.hex())
|
961 | 1034 |
|
| 1035 | + self.test_decodepsbt_musig2_input_output_types() |
| 1036 | + |
962 | 1037 | self.log.info("Test that combining PSBTs with different transactions fails")
|
963 | 1038 | tx = CTransaction()
|
964 | 1039 | tx.vin = [CTxIn(outpoint=COutPoint(hash=int('aa' * 32, 16), n=0), scriptSig=b"")]
|
|
0 commit comments