Skip to content

Commit 46ceff9

Browse files
committed
psbt: add MuSig2 fields
1 parent 5f533aa commit 46ceff9

File tree

1 file changed

+44
-0
lines changed

1 file changed

+44
-0
lines changed

hwilib/psbt.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ class PartiallySignedInput:
103103
PSBT_IN_TAP_BIP32_DERIVATION = 0x16
104104
PSBT_IN_TAP_INTERNAL_KEY = 0x17
105105
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
106109

107110
def __init__(self, version: int) -> None:
108111
self.non_witness_utxo: Optional[CTransaction] = None
@@ -125,6 +128,9 @@ def __init__(self, version: int) -> None:
125128
self.tap_bip32_paths: Dict[bytes, Tuple[Set[bytes], KeyOriginInfo]] = {}
126129
self.tap_internal_key = b""
127130
self.tap_merkle_root = b""
131+
self.musig2_participant_pubkeys = {}
132+
self.musig2_pub_nonce = {}
133+
self.musig2_partial_sig = {}
128134
self.unknown: Dict[bytes, bytes] = {}
129135

130136
self.version: int = version
@@ -153,6 +159,9 @@ def set_null(self) -> None:
153159
self.sequence = None
154160
self.time_locktime = None
155161
self.height_locktime = None
162+
self.musig2_participant_pubkeys.clear()
163+
self.musig2_pub_nonce.clear()
164+
self.musig2_partial_sig.clear()
156165
self.unknown.clear()
157166

158167
def deserialize(self, f: Readable) -> None:
@@ -351,6 +360,18 @@ def deserialize(self, f: Readable) -> None:
351360
self.tap_merkle_root = deser_string(f)
352361
if len(self.tap_merkle_root) != 32:
353362
raise PSBTSerializationError("Input Taproot merkle root is not 32 bytes")
363+
elif key_type == PartiallySignedInput.PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS:
364+
if key in key_lookup:
365+
raise PSBTSerializationError("Duplicate key, input MuSig2 Participant Public Keys already provided")
366+
self.musig2_participant_pubkeys = deser_string(f)
367+
elif key_type == PartiallySignedInput.PSBT_IN_MUSIG2_PUB_NONCE:
368+
if key in key_lookup:
369+
raise PSBTSerializationError("Duplicate key, input MuSig2 Public Nonce already provided")
370+
self.musig2_pub_nonce = deser_string(f)
371+
elif key_type == PartiallySignedInput.PSBT_IN_MUSIG2_PARTIAL_SIG:
372+
if key in key_lookup:
373+
raise PSBTSerializationError("Duplicate key, input MuSig2 Participant Partial Signature already provided")
374+
self.musig2_partial_sig = deser_string(f)
354375
else:
355376
if key in self.unknown:
356377
raise PSBTSerializationError("Duplicate key, key for unknown value already provided")
@@ -441,6 +462,18 @@ def serialize(self) -> bytes:
441462
witstack = self.final_script_witness.serialize()
442463
r += ser_string(witstack)
443464

465+
if len(self.musig2_participant_pubkeys) != 0:
466+
r += ser_string(ser_compact_size(PartiallySignedInput.PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS))
467+
r += ser_string(self.musig2_participant_pubkeys)
468+
469+
if len(self.musig2_pub_nonce) != 0:
470+
r += ser_string(ser_compact_size(PartiallySignedInput.PSBT_IN_MUSIG2_PUB_NONCE))
471+
r += ser_string(self.musig2_pub_nonce)
472+
473+
if len(self.musig2_partial_sig) != 0:
474+
r += ser_string(ser_compact_size(PartiallySignedInput.PSBT_IN_MUSIG2_PARTIAL_SIG))
475+
r += ser_string(self.musig2_partial_sig)
476+
444477
if self.version >= 2:
445478
if len(self.prev_txid) != 0:
446479
r += ser_string(ser_compact_size(PartiallySignedInput.PSBT_IN_PREVIOUS_TXID))
@@ -483,6 +516,7 @@ class PartiallySignedOutput:
483516
PSBT_OUT_TAP_INTERNAL_KEY = 0x05
484517
PSBT_OUT_TAP_TREE = 0x06
485518
PSBT_OUT_TAP_BIP32_DERIVATION = 0x07
519+
PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x08
486520

487521
def __init__(self, version: int) -> None:
488522
self.redeem_script = b""
@@ -493,6 +527,7 @@ def __init__(self, version: int) -> None:
493527
self.tap_internal_key = b""
494528
self.tap_tree = b""
495529
self.tap_bip32_paths: Dict[bytes, Tuple[Set[bytes], KeyOriginInfo]] = {}
530+
self.musig2_participant_pubkeys = {}
496531
self.unknown: Dict[bytes, bytes] = {}
497532

498533
self.version: int = version
@@ -509,6 +544,7 @@ def set_null(self) -> None:
509544
self.tap_bip32_paths.clear()
510545
self.amount = None
511546
self.script = b""
547+
self.musig2_participant_pubkeys = {}
512548
self.unknown.clear()
513549

514550
def deserialize(self, f: Readable) -> None:
@@ -589,6 +625,10 @@ def deserialize(self, f: Readable) -> None:
589625
for i in range(0, num_hashes):
590626
leaf_hashes.add(vs.read(32))
591627
self.tap_bip32_paths[xonly] = (leaf_hashes, KeyOriginInfo.deserialize(vs.read()))
628+
elif key_type == PartiallySignedOutput.PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS:
629+
if key in key_lookup:
630+
raise PSBTSerializationError("Duplicate key, output MuSig2 Participant Public Keys already provided")
631+
self.musig2_participant_pubkeys = deser_string(f)
592632
else:
593633
if key in self.unknown:
594634
raise PSBTSerializationError("Duplicate key, key for unknown value already provided")
@@ -646,6 +686,10 @@ def serialize(self) -> bytes:
646686
value += origin.serialize()
647687
r += ser_string(value)
648688

689+
if len(self.musig2_participant_pubkeys) != 0:
690+
r += ser_string(ser_compact_size(PartiallySignedOutput.PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS))
691+
r += ser_string(self.musig2_participant_pubkeys)
692+
649693
for key, value in sorted(self.unknown.items()):
650694
r += ser_string(key)
651695
r += ser_string(value)

0 commit comments

Comments
 (0)