@@ -50,6 +50,9 @@ static constexpr uint8_t PSBT_IN_TAP_LEAF_SCRIPT = 0x15;
5050static constexpr uint8_t PSBT_IN_TAP_BIP32_DERIVATION = 0x16 ;
5151static constexpr uint8_t PSBT_IN_TAP_INTERNAL_KEY = 0x17 ;
5252static constexpr uint8_t PSBT_IN_TAP_MERKLE_ROOT = 0x18 ;
53+ static constexpr uint8_t PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x1a ;
54+ static constexpr uint8_t PSBT_IN_MUSIG2_PUB_NONCE = 0x1b ;
55+ static constexpr uint8_t PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1c ;
5356static constexpr uint8_t PSBT_IN_PROPRIETARY = 0xFC ;
5457
5558// Output types
@@ -59,6 +62,7 @@ static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
5962static constexpr uint8_t PSBT_OUT_TAP_INTERNAL_KEY = 0x05 ;
6063static constexpr uint8_t PSBT_OUT_TAP_TREE = 0x06 ;
6164static constexpr uint8_t PSBT_OUT_TAP_BIP32_DERIVATION = 0x07 ;
65+ static constexpr uint8_t PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x08 ;
6266static constexpr uint8_t PSBT_OUT_PROPRIETARY = 0xFC ;
6367
6468// The separator is 0x00. Reading this in means that the unserializer can interpret it
@@ -193,6 +197,49 @@ void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_k
193197 }
194198}
195199
200+ // Deserialize a PSBT_{IN/OUT}_MUSIG2_PARTICIPANT_PUBKEYS field
201+ template <typename Stream>
202+ void DeserializeMuSig2ParticipantPubkeys (Stream& s, SpanReader& skey, std::map<CPubKey, std::vector<CPubKey>>& out, std::string context)
203+ {
204+ std::array<unsigned char , CPubKey::COMPRESSED_SIZE> agg_pubkey_bytes;
205+ skey >> std::as_writable_bytes (std::span{agg_pubkey_bytes});
206+ CPubKey agg_pubkey (agg_pubkey_bytes);
207+
208+ std::vector<CPubKey> participants;
209+ std::vector<unsigned char > val;
210+ s >> val;
211+ SpanReader s_val{val};
212+ while (s_val.size () >= CPubKey::COMPRESSED_SIZE) {
213+ std::array<unsigned char , CPubKey::COMPRESSED_SIZE> part_pubkey_bytes;
214+ s_val >> std::as_writable_bytes (std::span{part_pubkey_bytes});
215+ participants.emplace_back (std::span{part_pubkey_bytes});
216+ }
217+ if (!s_val.empty ()) {
218+ throw std::ios_base::failure (context + " musig2 participants pubkeys value size is not a multiple of 33" );
219+ }
220+
221+ out.emplace (agg_pubkey, participants);
222+ }
223+
224+ // Deserialize the MuSig2 participant identifiers from PSBT_MUSIG2_{PUBNONCE/PARTIAL_SIG} fields
225+ // Both fields contain the same data after the type byte - aggregate pubkey | participant pubkey | leaf script hash
226+ template <typename Stream>
227+ void DeserializeMuSig2ParticipantDataIdentifier (Stream& skey, CPubKey& agg_pub, CPubKey& part_pub, uint256& leaf_hash)
228+ {
229+ leaf_hash.SetNull ();
230+
231+ std::array<unsigned char , CPubKey::COMPRESSED_SIZE> part_pubkey_bytes;
232+ std::array<unsigned char , CPubKey::COMPRESSED_SIZE> agg_pubkey_bytes;
233+
234+ skey >> std::as_writable_bytes (std::span{part_pubkey_bytes}) >> std::as_writable_bytes (std::span{agg_pubkey_bytes});
235+ agg_pub.Set (agg_pubkey_bytes.begin (), agg_pubkey_bytes.end ());
236+ part_pub.Set (part_pubkey_bytes.begin (), part_pubkey_bytes.end ());
237+
238+ if (!skey.empty ()) {
239+ skey >> leaf_hash;
240+ }
241+ }
242+
196243/* * A structure for PSBTs which contain per-input information */
197244struct PSBTInput
198245{
@@ -217,6 +264,13 @@ struct PSBTInput
217264 XOnlyPubKey m_tap_internal_key;
218265 uint256 m_tap_merkle_root;
219266
267+ // MuSig2 fields
268+ std::map<CPubKey, std::vector<CPubKey>> m_musig2_participants;
269+ // Key is the aggregate pubkey and the script leaf hash, value is a map of participant pubkey to pubnonce
270+ std::map<std::pair<CPubKey, uint256>, std::map<CPubKey, std::vector<uint8_t >>> m_musig2_pubnonces;
271+ // Key is the aggregate pubkey and the script leaf hash, value is a map of participant pubkey to partial_sig
272+ std::map<std::pair<CPubKey, uint256>, std::map<CPubKey, uint256>> m_musig2_partial_sigs;
273+
220274 std::map<std::vector<unsigned char >, std::vector<unsigned char >> unknown;
221275 std::set<PSBTProprietary> m_proprietary;
222276 std::optional<int > sighash_type;
@@ -337,6 +391,43 @@ struct PSBTInput
337391 SerializeToVector (s, PSBT_IN_TAP_MERKLE_ROOT);
338392 SerializeToVector (s, m_tap_merkle_root);
339393 }
394+
395+ // Write MuSig2 Participants
396+ for (const auto & [agg_pubkey, part_pubs] : m_musig2_participants) {
397+ SerializeToVector (s, CompactSizeWriter (PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS), std::span{agg_pubkey});
398+ std::vector<unsigned char > value;
399+ VectorWriter s_value{value, 0 };
400+ for (auto & pk : part_pubs) {
401+ s_value << std::span{pk};
402+ }
403+ s << value;
404+ }
405+
406+ // Write MuSig2 pubnonces
407+ for (const auto & [agg_pubkey_leaf_hash, pubnonces] : m_musig2_pubnonces) {
408+ const auto & [agg_pubkey, leaf_hash] = agg_pubkey_leaf_hash;
409+ for (const auto & [part_pubkey, pubnonce] : pubnonces) {
410+ if (leaf_hash.IsNull ()) {
411+ SerializeToVector (s, CompactSizeWriter (PSBT_IN_MUSIG2_PUB_NONCE), std::span{part_pubkey}, std::span{agg_pubkey});
412+ } else {
413+ SerializeToVector (s, CompactSizeWriter (PSBT_IN_MUSIG2_PUB_NONCE), std::span{part_pubkey}, std::span{agg_pubkey}, leaf_hash);
414+ }
415+ s << pubnonce;
416+ }
417+ }
418+
419+ // Write MuSig2 partial signatures
420+ for (const auto & [agg_pubkey_leaf_hash, psigs] : m_musig2_partial_sigs) {
421+ const auto & [agg_pubkey, leaf_hash] = agg_pubkey_leaf_hash;
422+ for (const auto & [pubkey, psig] : psigs) {
423+ if (leaf_hash.IsNull ()) {
424+ SerializeToVector (s, CompactSizeWriter (PSBT_IN_MUSIG2_PARTIAL_SIG), std::span{pubkey}, std::span{agg_pubkey});
425+ } else {
426+ SerializeToVector (s, CompactSizeWriter (PSBT_IN_MUSIG2_PARTIAL_SIG), std::span{pubkey}, std::span{agg_pubkey}, leaf_hash);
427+ }
428+ SerializeToVector (s, psig);
429+ }
430+ }
340431 }
341432
342433 // Write script sig
@@ -672,6 +763,53 @@ struct PSBTInput
672763 UnserializeFromVector (s, m_tap_merkle_root);
673764 break ;
674765 }
766+ case PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS:
767+ {
768+ if (!key_lookup.emplace (key).second ) {
769+ throw std::ios_base::failure (" Duplicate Key, input participant pubkeys for an aggregate key already provided" );
770+ } else if (key.size () != CPubKey::COMPRESSED_SIZE + 1 ) {
771+ throw std::ios_base::failure (" Input musig2 participants pubkeys aggregate key is not 34 bytes" );
772+ }
773+ DeserializeMuSig2ParticipantPubkeys (s, skey, m_musig2_participants, std::string{" Input" });
774+ break ;
775+ }
776+ case PSBT_IN_MUSIG2_PUB_NONCE:
777+ {
778+ if (!key_lookup.emplace (key).second ) {
779+ throw std::ios_base::failure (" Duplicate Key, input musig2 pubnonce already provided" );
780+ } else if (key.size () != 2 * CPubKey::COMPRESSED_SIZE + 1 && key.size () != 2 * CPubKey::COMPRESSED_SIZE + CSHA256::OUTPUT_SIZE + 1 ) {
781+ throw std::ios_base::failure (" Input musig2 pubnonce key is not expected size of 67 or 99 bytes" );
782+ }
783+ CPubKey agg_pub, part_pub;
784+ uint256 leaf_hash;
785+ DeserializeMuSig2ParticipantDataIdentifier (skey, agg_pub, part_pub, leaf_hash);
786+
787+ std::vector<uint8_t > pubnonce;
788+ s >> pubnonce;
789+ if (pubnonce.size () != 66 ) {
790+ throw std::ios_base::failure (" Input musig2 pubnonce value is not 66 bytes" );
791+ }
792+
793+ m_musig2_pubnonces[std::make_pair (agg_pub, leaf_hash)].emplace (part_pub, pubnonce);
794+ break ;
795+ }
796+ case PSBT_IN_MUSIG2_PARTIAL_SIG:
797+ {
798+ if (!key_lookup.emplace (key).second ) {
799+ throw std::ios_base::failure (" Duplicate Key, input musig2 partial sig already provided" );
800+ } else if (key.size () != 2 * CPubKey::COMPRESSED_SIZE + 1 && key.size () != 2 * CPubKey::COMPRESSED_SIZE + CSHA256::OUTPUT_SIZE + 1 ) {
801+ throw std::ios_base::failure (" Input musig2 partial sig key is not expected size of 67 or 99 bytes" );
802+ }
803+ CPubKey agg_pub, part_pub;
804+ uint256 leaf_hash;
805+ DeserializeMuSig2ParticipantDataIdentifier (skey, agg_pub, part_pub, leaf_hash);
806+
807+ uint256 partial_sig;
808+ UnserializeFromVector (s, partial_sig);
809+
810+ m_musig2_partial_sigs[std::make_pair (agg_pub, leaf_hash)].emplace (part_pub, partial_sig);
811+ break ;
812+ }
675813 case PSBT_IN_PROPRIETARY:
676814 {
677815 PSBTProprietary this_prop;
@@ -719,6 +857,7 @@ struct PSBTOutput
719857 XOnlyPubKey m_tap_internal_key;
720858 std::vector<std::tuple<uint8_t , uint8_t , std::vector<unsigned char >>> m_tap_tree;
721859 std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> m_tap_bip32_paths;
860+ std::map<CPubKey, std::vector<CPubKey>> m_musig2_participants;
722861 std::map<std::vector<unsigned char >, std::vector<unsigned char >> unknown;
723862 std::set<PSBTProprietary> m_proprietary;
724863
@@ -781,6 +920,17 @@ struct PSBTOutput
781920 s << value;
782921 }
783922
923+ // Write MuSig2 Participants
924+ for (const auto & [agg_pubkey, part_pubs] : m_musig2_participants) {
925+ SerializeToVector (s, CompactSizeWriter (PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS), std::span{agg_pubkey});
926+ std::vector<unsigned char > value;
927+ VectorWriter s_value{value, 0 };
928+ for (auto & pk : part_pubs) {
929+ s_value << std::span{pk};
930+ }
931+ s << value;
932+ }
933+
784934 // Write unknown things
785935 for (auto & entry : unknown) {
786936 s << entry.first ;
@@ -907,6 +1057,16 @@ struct PSBTOutput
9071057 m_tap_bip32_paths.emplace (xonly, std::make_pair (leaf_hashes, DeserializeKeyOrigin (s, origin_len)));
9081058 break ;
9091059 }
1060+ case PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS:
1061+ {
1062+ if (!key_lookup.emplace (key).second ) {
1063+ throw std::ios_base::failure (" Duplicate Key, output participant pubkeys for an aggregate key already provided" );
1064+ } else if (key.size () != CPubKey::COMPRESSED_SIZE + 1 ) {
1065+ throw std::ios_base::failure (" Output musig2 participants pubkeys aggregate key is not 34 bytes" );
1066+ }
1067+ DeserializeMuSig2ParticipantPubkeys (s, skey, m_musig2_participants, std::string{" Output" });
1068+ break ;
1069+ }
9101070 case PSBT_OUT_PROPRIETARY:
9111071 {
9121072 PSBTProprietary this_prop;
0 commit comments