@@ -118,3 +118,89 @@ uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey
118118 hasher << script_pubkey << part_pubkey << sighash;
119119 return hasher.GetSHA256 ();
120120}
121+
122+ std::optional<std::vector<uint8_t >> CreateMuSig2AggregateSig (const std::vector<CPubKey>& part_pubkeys, const CPubKey& aggregate_pubkey, const std::vector<std::pair<uint256, bool >>& tweaks, const uint256& sighash, const std::map<CPubKey, std::vector<uint8_t >>& pubnonces, const std::map<CPubKey, uint256>& partial_sigs)
123+ {
124+ if (!part_pubkeys.size ()) return std::nullopt ;
125+
126+ // Get the keyagg cache and aggregate pubkey
127+ secp256k1_musig_keyagg_cache keyagg_cache;
128+ if (!MuSig2AggregatePubkeys (part_pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt ;
129+
130+ // Check if enough pubnonces and partial sigs
131+ if (pubnonces.size () != part_pubkeys.size ()) return std::nullopt ;
132+ if (partial_sigs.size () != part_pubkeys.size ()) return std::nullopt ;
133+
134+ // Parse the pubnonces and partial sigs
135+ std::vector<std::tuple<secp256k1_pubkey, secp256k1_musig_pubnonce, secp256k1_musig_partial_sig>> signers_data;
136+ std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
137+ std::vector<const secp256k1_musig_partial_sig*> partial_sig_ptrs;
138+ for (const CPubKey& part_pk : part_pubkeys) {
139+ const auto & pn_it = pubnonces.find (part_pk);
140+ if (pn_it == pubnonces.end ()) return std::nullopt ;
141+ const std::vector<uint8_t > pubnonce = pn_it->second ;
142+ if (pubnonce.size () != MUSIG2_PUBNONCE_SIZE) return std::nullopt ;
143+ const auto & it = partial_sigs.find (part_pk);
144+ if (it == partial_sigs.end ()) return std::nullopt ;
145+ const uint256& partial_sig = it->second ;
146+
147+ auto & [secp_pk, secp_pn, secp_ps] = signers_data.emplace_back ();
148+
149+ if (!secp256k1_ec_pubkey_parse (secp256k1_context_static, &secp_pk, part_pk.data (), part_pk.size ())) {
150+ return std::nullopt ;
151+ }
152+
153+ if (!secp256k1_musig_pubnonce_parse (secp256k1_context_static, &secp_pn, pubnonce.data ())) {
154+ return std::nullopt ;
155+ }
156+
157+ if (!secp256k1_musig_partial_sig_parse (secp256k1_context_static, &secp_ps, partial_sig.data ())) {
158+ return std::nullopt ;
159+ }
160+ }
161+ pubnonce_ptrs.reserve (signers_data.size ());
162+ partial_sig_ptrs.reserve (signers_data.size ());
163+ for (auto & [_, pn, ps] : signers_data) {
164+ pubnonce_ptrs.push_back (&pn);
165+ partial_sig_ptrs.push_back (&ps);
166+ }
167+
168+ // Aggregate nonces
169+ secp256k1_musig_aggnonce aggnonce;
170+ if (!secp256k1_musig_nonce_agg (secp256k1_context_static, &aggnonce, pubnonce_ptrs.data (), pubnonce_ptrs.size ())) {
171+ return std::nullopt ;
172+ }
173+
174+ // Apply tweaks
175+ for (const auto & [tweak, xonly] : tweaks) {
176+ if (xonly) {
177+ if (!secp256k1_musig_pubkey_xonly_tweak_add (secp256k1_context_static, nullptr , &keyagg_cache, tweak.data ())) {
178+ return std::nullopt ;
179+ }
180+ } else if (!secp256k1_musig_pubkey_ec_tweak_add (secp256k1_context_static, nullptr , &keyagg_cache, tweak.data ())) {
181+ return std::nullopt ;
182+ }
183+ }
184+
185+ // Create musig_session
186+ secp256k1_musig_session session;
187+ if (!secp256k1_musig_nonce_process (secp256k1_context_static, &session, &aggnonce, sighash.data (), &keyagg_cache)) {
188+ return std::nullopt ;
189+ }
190+
191+ // Verify partial sigs
192+ for (const auto & [pk, pb, ps] : signers_data) {
193+ if (!secp256k1_musig_partial_sig_verify (secp256k1_context_static, &ps, &pb, &pk, &keyagg_cache, &session)) {
194+ return std::nullopt ;
195+ }
196+ }
197+
198+ // Aggregate partial sigs
199+ std::vector<uint8_t > sig;
200+ sig.resize (64 );
201+ if (!secp256k1_musig_partial_sig_agg (secp256k1_context_static, sig.data (), &session, partial_sig_ptrs.data (), partial_sig_ptrs.size ())) {
202+ return std::nullopt ;
203+ }
204+
205+ return sig;
206+ }
0 commit comments