@@ -103,6 +103,9 @@ class PartiallySignedInput:
103
103
PSBT_IN_TAP_BIP32_DERIVATION = 0x16
104
104
PSBT_IN_TAP_INTERNAL_KEY = 0x17
105
105
PSBT_IN_TAP_MERKLE_ROOT = 0x18
106
+ PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x1a
107
+ PSBT_IN_MUSIG2_PUB_NONCE = 0x1b
108
+ PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1c
106
109
107
110
def __init__ (self , version : int ) -> None :
108
111
self .non_witness_utxo : Optional [CTransaction ] = None
@@ -125,6 +128,9 @@ def __init__(self, version: int) -> None:
125
128
self .tap_bip32_paths : Dict [bytes , Tuple [Set [bytes ], KeyOriginInfo ]] = {}
126
129
self .tap_internal_key = b""
127
130
self .tap_merkle_root = b""
131
+ self .musig2_participant_pubkeys = Dict [bytes , List [bytes ]] = {}
132
+ self .musig2_pub_nonce = Dict [Tuple [bytes , bytes , Optional [bytes ]], bytes ] = {}
133
+ self .musig2_partial_sig = Dict [Tuple [bytes , bytes , Optional [bytes ]], bytes ] = {}
128
134
self .unknown : Dict [bytes , bytes ] = {}
129
135
130
136
self .version : int = version
@@ -153,6 +159,9 @@ def set_null(self) -> None:
153
159
self .sequence = None
154
160
self .time_locktime = None
155
161
self .height_locktime = None
162
+ self .musig2_participant_pubkeys .clear ()
163
+ self .musig2_pub_nonce .clear ()
164
+ self .musig2_partial_sig .clear ()
156
165
self .unknown .clear ()
157
166
158
167
def deserialize (self , f : Readable ) -> None :
@@ -335,22 +344,51 @@ def deserialize(self, f: Readable) -> None:
335
344
for i in range (0 , num_hashes ):
336
345
leaf_hashes .add (vs .read (32 ))
337
346
self .tap_bip32_paths [xonly ] = (leaf_hashes , KeyOriginInfo .deserialize (vs .read ()))
338
- elif key_type == PartiallySignedInput .PSBT_IN_TAP_INTERNAL_KEY :
347
+ elif key_type == PartiallySignedInput .PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS :
339
348
if key in key_lookup :
340
- raise PSBTSerializationError ("Duplicate key, input Taproot internal key already provided" )
341
- elif len (key ) != 1 :
342
- raise PSBTSerializationError ("Input Taproot internal key key is more than one byte type" )
343
- self .tap_internal_key = deser_string (f )
344
- if len (self .tap_internal_key ) != 32 :
345
- raise PSBTSerializationError ("Input Taproot internal key is not 32 bytes" )
346
- elif key_type == PartiallySignedInput .PSBT_IN_TAP_MERKLE_ROOT :
349
+ raise PSBTSerializationError ("Duplicate key, input Musig2 participant pubkeys already provided" )
350
+ elif len (key ) != 1 + 33 :
351
+ raise PSBTSerializationError ("Input Musig2 aggregate compressed pubkey is not 33 bytes" )
352
+
353
+ pubkeys_cat = deser_string (f )
354
+ if len (pubkeys_cat ) == 0 :
355
+ raise PSBTSerializationError ("The list of compressed pubkeys for Musig2 cannot be empty" )
356
+ if (len (pubkeys_cat ) % 33 ) != 0 :
357
+ raise PSBTSerializationError ("The compressed pubkeys for Musig2 must be exactly 33 bytes long" )
358
+ pubkeys = []
359
+ for i in range (0 , len (pubkeys_cat ), 33 ):
360
+ pubkeys .append (pubkeys_cat [i : i + 33 ])
361
+
362
+ self .musig2_participant_pubkeys [key [1 :]] = pubkeys
363
+ elif key_type == PartiallySignedInput .PSBT_IN_MUSIG2_PUB_NONCE :
347
364
if key in key_lookup :
348
- raise PSBTSerializationError ("Duplicate key, input Taproot merkle root already provided" )
349
- elif len (key ) != 1 :
350
- raise PSBTSerializationError ("Input Taproot merkle root key is more than one byte type" )
351
- self .tap_merkle_root = deser_string (f )
352
- if len (self .tap_merkle_root ) != 32 :
353
- raise PSBTSerializationError ("Input Taproot merkle root is not 32 bytes" )
365
+ raise PSBTSerializationError ("Duplicate key, Musig2 public nonce already provided" )
366
+ elif len (key ) not in [1 + 33 + 33 , 1 + 33 + 33 + 32 ]:
367
+ raise PSBTSerializationError ("Invalid key length for Musig2 public nonce" )
368
+
369
+ providing_pubkey = key [1 :1 + 33 ]
370
+ aggregate_pubkey = key [1 + 33 :1 + 33 + 33 ]
371
+ tapleaf_hash = None if len (key ) == 1 + 33 + 33 else key [1 + 33 + 33 :]
372
+
373
+ public_nonces = deser_string (f )
374
+ if len (public_nonces ) != 66 :
375
+ raise PSBTSerializationError ("The length of the public nonces in Musig2 must be exactly 66 bytes" )
376
+
377
+ self .musig2_pub_nonces [(providing_pubkey , aggregate_pubkey , tapleaf_hash )] = public_nonces
378
+ elif key_type == PartiallySignedInput .PSBT_IN_MUSIG2_PARTIAL_SIG :
379
+ if key in key_lookup :
380
+ raise PSBTSerializationError ("Duplicate key, Musig2 partial signature already provided" )
381
+ elif len (key ) not in [1 + 33 + 33 , 1 + 33 + 33 + 32 ]:
382
+ raise PSBTSerializationError ("Invalid key length for Musig2 partial signature" )
383
+
384
+ providing_pubkey = key [1 :1 + 33 ]
385
+ aggregate_pubkey = key [1 + 33 :1 + 33 + 33 ]
386
+ tapleaf_hash = None if len (key ) == 1 + 33 + 33 else key [1 + 33 + 33 :]
387
+
388
+ partial_sig = deser_string (f )
389
+ if len (partial_sig ) != 32 :
390
+ raise PSBTSerializationError ("The length of the partial signature in Musig2 must be exactly 32 bytes" )
391
+ self .musig2_partial_sigs [(providing_pubkey , aggregate_pubkey , tapleaf_hash )] = partial_sig
354
392
else :
355
393
if key in self .unknown :
356
394
raise PSBTSerializationError ("Duplicate key, key for unknown value already provided" )
@@ -441,6 +479,20 @@ def serialize(self) -> bytes:
441
479
witstack = self .final_script_witness .serialize ()
442
480
r += ser_string (witstack )
443
481
482
+ for pk , pubkeys in self .musig2_participant_pubkeys .items ():
483
+ r += ser_string (ser_compact_size (PartiallySignedInput .PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS ) + pk )
484
+ r += ser_string (b'' .join (pubkeys ))
485
+
486
+ for (pk , aggpk , hash ), pubnonce in self .musig2_pub_nonces .items ():
487
+ key_value = pk + aggpk + (hash or b'' )
488
+ r += ser_string (ser_compact_size (PartiallySignedInput .PSBT_IN_MUSIG2_PUB_NONCE ) + key_value )
489
+ r += ser_string (pubnonce )
490
+
491
+ for (pk , aggpk , hash ), partial_sig in self .musig2_partial_sigs .items ():
492
+ key_value = pk + aggpk + (hash or b'' )
493
+ r += ser_string (ser_compact_size (PartiallySignedInput .PSBT_IN_MUSIG2_PARTIAL_SIG ) + key_value )
494
+ r += ser_string (partial_sig )
495
+
444
496
if self .version >= 2 :
445
497
if len (self .prev_txid ) != 0 :
446
498
r += ser_string (ser_compact_size (PartiallySignedInput .PSBT_IN_PREVIOUS_TXID ))
@@ -483,6 +535,7 @@ class PartiallySignedOutput:
483
535
PSBT_OUT_TAP_INTERNAL_KEY = 0x05
484
536
PSBT_OUT_TAP_TREE = 0x06
485
537
PSBT_OUT_TAP_BIP32_DERIVATION = 0x07
538
+ PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x08
486
539
487
540
def __init__ (self , version : int ) -> None :
488
541
self .redeem_script = b""
@@ -493,6 +546,7 @@ def __init__(self, version: int) -> None:
493
546
self .tap_internal_key = b""
494
547
self .tap_tree = b""
495
548
self .tap_bip32_paths : Dict [bytes , Tuple [Set [bytes ], KeyOriginInfo ]] = {}
549
+ self .musig2_participant_pubkeys = Dict [bytes , List [bytes ]] = {}
496
550
self .unknown : Dict [bytes , bytes ] = {}
497
551
498
552
self .version : int = version
@@ -509,6 +563,7 @@ def set_null(self) -> None:
509
563
self .tap_bip32_paths .clear ()
510
564
self .amount = None
511
565
self .script = b""
566
+ self .musig2_participant_pubkeys = {}
512
567
self .unknown .clear ()
513
568
514
569
def deserialize (self , f : Readable ) -> None :
@@ -589,6 +644,22 @@ def deserialize(self, f: Readable) -> None:
589
644
for i in range (0 , num_hashes ):
590
645
leaf_hashes .add (vs .read (32 ))
591
646
self .tap_bip32_paths [xonly ] = (leaf_hashes , KeyOriginInfo .deserialize (vs .read ()))
647
+ elif key_type == PartiallySignedOutput .PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS :
648
+ if key in key_lookup :
649
+ raise PSBTSerializationError ("Duplicate key, output Musig2 participant pubkeys already provided" )
650
+ elif len (key ) != 1 + 33 :
651
+ raise PSBTSerializationError ("Output Musig2 aggregate compressed pubkey is not 33 bytes" )
652
+
653
+ pubkeys_cat = deser_string (f )
654
+ if len (pubkeys_cat ) == 0 :
655
+ raise PSBTSerializationError ("The list of compressed pubkeys for Musig2 cannot be empty" )
656
+ if (len (pubkeys_cat ) % 33 ) != 0 :
657
+ raise PSBTSerializationError ("The compressed pubkeys for Musig2 must be exactly 33 bytes long" )
658
+ pubkeys = []
659
+ for i in range (0 , len (pubkeys_cat ), 33 ):
660
+ pubkeys .append (pubkeys_cat [i : i + 33 ])
661
+
662
+ self .musig2_participant_pubkeys [key [1 :]] = pubkeys
592
663
else :
593
664
if key in self .unknown :
594
665
raise PSBTSerializationError ("Duplicate key, key for unknown value already provided" )
@@ -646,6 +717,10 @@ def serialize(self) -> bytes:
646
717
value += origin .serialize ()
647
718
r += ser_string (value )
648
719
720
+ if len (self .musig2_participant_pubkeys ) != 0 :
721
+ r += ser_string (ser_compact_size (PartiallySignedOutput .PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS ))
722
+ r += ser_string (self .musig2_participant_pubkeys )
723
+
649
724
for key , value in sorted (self .unknown .items ()):
650
725
r += ser_string (key )
651
726
r += ser_string (value )
0 commit comments