Skip to content

Commit dad722c

Browse files
achow101Raimo33
authored andcommitted
sign: Add CreateMuSig2AggregateSig
1 parent fd804f3 commit dad722c

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

src/musig.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

src/musig.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,6 @@ class MuSig2SecNonce
6767

6868
uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash);
6969

70+
std::optional<std::vector<uint8_t>> CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, 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);
71+
7072
#endif // BITCOIN_MUSIG_H

src/script/sign.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,36 @@ bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningPro
174174
return true;
175175
}
176176

177+
bool MutableTransactionSignatureCreator::CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const
178+
{
179+
assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
180+
if (!participants.size()) return false;
181+
182+
// Retrieve pubnonces and partial sigs
183+
auto this_leaf_aggkey = std::make_pair(script_pubkey, leaf_hash ? *leaf_hash : uint256());
184+
auto pubnonce_it = sigdata.musig2_pubnonces.find(this_leaf_aggkey);
185+
if (pubnonce_it == sigdata.musig2_pubnonces.end()) return false;
186+
const std::map<CPubKey, std::vector<uint8_t>>& pubnonces = pubnonce_it->second;
187+
auto partial_sigs_it = sigdata.musig2_partial_sigs.find(this_leaf_aggkey);
188+
if (partial_sigs_it == sigdata.musig2_partial_sigs.end()) return false;
189+
const std::map<CPubKey, uint256>& partial_sigs = partial_sigs_it->second;
190+
191+
// Check if enough pubnonces and partial sigs
192+
if (pubnonces.size() != participants.size()) return false;
193+
if (partial_sigs.size() != participants.size()) return false;
194+
195+
// Compute sighash
196+
std::optional<uint256> sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
197+
if (!sighash.has_value()) return false;
198+
199+
std::optional<std::vector<uint8_t>> res = ::CreateMuSig2AggregateSig(participants, aggregate_pubkey, tweaks, *sighash, pubnonces, partial_sigs);
200+
if (!res) return false;
201+
sig = res.value();
202+
if (nHashType) sig.push_back(nHashType);
203+
204+
return true;
205+
}
206+
177207
static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script)
178208
{
179209
if (provider.GetCScript(scriptid, script)) {
@@ -840,6 +870,11 @@ class DummySignatureCreator final : public BaseSignatureCreator {
840870
partial_sig = uint256::ONE;
841871
return true;
842872
}
873+
bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override
874+
{
875+
sig.assign(64, '\000');
876+
return true;
877+
}
843878
};
844879

845880
}

src/script/sign.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class BaseSignatureCreator {
3636
virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0;
3737
virtual std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const =0;
3838
virtual bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0;
39+
virtual bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0;
3940
};
4041

4142
/** A signature creator for transactions. */
@@ -58,6 +59,7 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator
5859
bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
5960
std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const override;
6061
bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override;
62+
bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override;
6163
};
6264

6365
/** A signature checker that accepts all signatures */

0 commit comments

Comments
 (0)