Skip to content

Commit 0aee4dd

Browse files
achow101Raimo33
authored andcommitted
sign: Create MuSig2 signatures for known MuSig2 aggregate keys
When creating Taproot signatures, if the key being signed for is known to be a MuSig2 aggregate key, do the MuSig2 signing algorithms. First try to create the aggregate signature. This will fail if there are not enough partial signatures or public nonces. If it does fail, try to create a partial signature with all participant keys. This will fail for those keys that we do not have the private keys for, and if there are not enough public nonces. Lastly, if the partial signatures could not be created, add our own public nonces for the private keys that we know, if they do not yet exist.
1 parent dad722c commit 0aee4dd

File tree

3 files changed

+128
-8
lines changed

3 files changed

+128
-8
lines changed

src/script/sign.cpp

Lines changed: 115 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,100 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
265265
return false;
266266
}
267267

268+
static bool SignMuSig2(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& script_pubkey, const uint256* merkle_root, const uint256* leaf_hash, SigVersion sigversion)
269+
{
270+
Assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
271+
272+
// Lookup derivation paths for the script pubkey
273+
KeyOriginInfo agg_info;
274+
auto misc_pk_it = sigdata.taproot_misc_pubkeys.find(script_pubkey);
275+
if (misc_pk_it != sigdata.taproot_misc_pubkeys.end()) {
276+
agg_info = misc_pk_it->second.second;
277+
}
278+
279+
for (const auto& [agg_pub, part_pks] : sigdata.musig2_pubkeys) {
280+
if (part_pks.empty()) continue;
281+
282+
// Fill participant derivation path info
283+
for (const auto& part_pk : part_pks) {
284+
KeyOriginInfo part_info;
285+
if (provider.GetKeyOrigin(part_pk.GetID(), part_info)) {
286+
XOnlyPubKey xonly_part(part_pk);
287+
auto it = sigdata.taproot_misc_pubkeys.find(xonly_part);
288+
if (it == sigdata.taproot_misc_pubkeys.end()) {
289+
it = sigdata.taproot_misc_pubkeys.emplace(xonly_part, std::make_pair(std::set<uint256>(), part_info)).first;
290+
}
291+
if (leaf_hash) it->second.first.insert(*leaf_hash);
292+
}
293+
}
294+
295+
// The pubkey in the script may not be the actual aggregate of the participants, but derived from it.
296+
// Check the derivation, and compute the BIP 32 derivation tweaks
297+
std::vector<std::pair<uint256, bool>> tweaks;
298+
CPubKey plain_pub = agg_pub;
299+
if (XOnlyPubKey(agg_pub) != script_pubkey) {
300+
if (agg_info.path.empty()) continue;
301+
// Compute and compare fingerprint
302+
CKeyID keyid = agg_pub.GetID();
303+
if (!std::equal(agg_info.fingerprint, agg_info.fingerprint + sizeof(agg_info.fingerprint), keyid.data())) {
304+
continue;
305+
}
306+
// Get the BIP32 derivation tweaks
307+
CExtPubKey extpub = CreateMuSig2SyntheticXpub(agg_pub);
308+
for (const int i : agg_info.path) {
309+
auto& [t, xonly] = tweaks.emplace_back();
310+
xonly = false;
311+
if (!extpub.Derive(extpub, i, &t)) {
312+
return false;
313+
}
314+
}
315+
Assert(XOnlyPubKey(extpub.pubkey) == script_pubkey);
316+
plain_pub = extpub.pubkey;
317+
}
318+
319+
// Add the merkle root tweak
320+
if (sigversion == SigVersion::TAPROOT && merkle_root) {
321+
tweaks.emplace_back(script_pubkey.ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root), true);
322+
std::optional<std::pair<XOnlyPubKey, bool>> tweaked = script_pubkey.CreateTapTweak(merkle_root->IsNull() ? nullptr : merkle_root);
323+
if (!Assume(tweaked)) return false;
324+
plain_pub = tweaked->first.GetCPubKeys().at(tweaked->second ? 1 : 0);
325+
}
326+
327+
// First try to aggregate
328+
if (creator.CreateMuSig2AggregateSig(part_pks, sig_out, agg_pub, plain_pub, leaf_hash, tweaks, sigversion, sigdata)) {
329+
if (sigversion == SigVersion::TAPROOT) {
330+
sigdata.taproot_key_path_sig = sig_out;
331+
} else {
332+
auto lookup_key = std::make_pair(script_pubkey, leaf_hash ? *leaf_hash : uint256());
333+
sigdata.taproot_script_sigs[lookup_key] = sig_out;
334+
}
335+
continue;
336+
}
337+
// Cannot aggregate, try making partial sigs for every participant
338+
auto pub_key_leaf_hash = std::make_pair(plain_pub, leaf_hash ? *leaf_hash : uint256());
339+
for (const CPubKey& part_pk : part_pks) {
340+
uint256 partial_sig;
341+
if (creator.CreateMuSig2PartialSig(provider, partial_sig, agg_pub, plain_pub, part_pk, leaf_hash, tweaks, sigversion, sigdata) && Assume(!partial_sig.IsNull())) {
342+
sigdata.musig2_partial_sigs[pub_key_leaf_hash].emplace(part_pk, partial_sig);
343+
}
344+
}
345+
// If there are any partial signatures, exit early
346+
auto partial_sigs_it = sigdata.musig2_partial_sigs.find(pub_key_leaf_hash);
347+
if (partial_sigs_it != sigdata.musig2_partial_sigs.end() && !partial_sigs_it->second.empty()) {
348+
continue;
349+
}
350+
// No partial sigs, try to make pubnonces
351+
std::map<CPubKey, std::vector<uint8_t>>& pubnonces = sigdata.musig2_pubnonces[pub_key_leaf_hash];
352+
for (const CPubKey& part_pk : part_pks) {
353+
if (pubnonces.contains(part_pk)) continue;
354+
std::vector<uint8_t> pubnonce = creator.CreateMuSig2Nonce(provider, agg_pub, plain_pub, part_pk, leaf_hash, merkle_root, sigversion, sigdata);
355+
if (pubnonce.empty()) continue;
356+
pubnonces[part_pk] = std::move(pubnonce);
357+
}
358+
}
359+
return true;
360+
}
361+
268362
static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, SignatureData& sigdata, const SigningProvider& provider, std::vector<unsigned char>& sig_out, const XOnlyPubKey& pubkey, const uint256& leaf_hash, SigVersion sigversion)
269363
{
270364
KeyOriginInfo info;
@@ -283,11 +377,14 @@ static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, Signatur
283377
sig_out = it->second;
284378
return true;
285379
}
380+
286381
if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) {
287382
sigdata.taproot_script_sigs[lookup_key] = sig_out;
288-
return true;
383+
} else if (!SignMuSig2(creator, sigdata, provider, sig_out, pubkey, /*merkle_root=*/nullptr, &leaf_hash, sigversion)) {
384+
return false;
289385
}
290-
return false;
386+
387+
return sigdata.taproot_script_sigs.contains(lookup_key);
291388
}
292389

293390
template<typename M, typename K, typename V>
@@ -456,6 +553,10 @@ static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCrea
456553
if (provider.GetTaprootBuilder(output, builder)) {
457554
sigdata.tr_builder = builder;
458555
}
556+
if (auto agg_keys = provider.GetAllMuSig2ParticipantPubkeys(); !agg_keys.empty()) {
557+
sigdata.musig2_pubkeys.insert(agg_keys.begin(), agg_keys.end());
558+
}
559+
459560

460561
// Try key path spending.
461562
{
@@ -475,16 +576,22 @@ static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCrea
475576
}
476577
}
477578

478-
std::vector<unsigned char> sig;
479-
if (sigdata.taproot_key_path_sig.size() == 0) {
480-
if (creator.CreateSchnorrSig(provider, sig, sigdata.tr_spenddata.internal_key, nullptr, &sigdata.tr_spenddata.merkle_root, SigVersion::TAPROOT)) {
579+
auto make_keypath_sig = [&](const XOnlyPubKey& pk, const uint256* merkle_root) {
580+
std::vector<unsigned char> sig;
581+
if (creator.CreateSchnorrSig(provider, sig, pk, nullptr, merkle_root, SigVersion::TAPROOT)) {
481582
sigdata.taproot_key_path_sig = sig;
583+
} else {
584+
SignMuSig2(creator, sigdata, provider, sig, pk, merkle_root, /*leaf_hash=*/nullptr, SigVersion::TAPROOT);
482585
}
586+
};
587+
588+
// First try signing with internal key
589+
if (sigdata.taproot_key_path_sig.size() == 0) {
590+
make_keypath_sig(sigdata.tr_spenddata.internal_key, &sigdata.tr_spenddata.merkle_root);
483591
}
592+
// Try signing with output key if still no signature
484593
if (sigdata.taproot_key_path_sig.size() == 0) {
485-
if (creator.CreateSchnorrSig(provider, sig, output, nullptr, nullptr, SigVersion::TAPROOT)) {
486-
sigdata.taproot_key_path_sig = sig;
487-
}
594+
make_keypath_sig(output, nullptr);
488595
}
489596
if (sigdata.taproot_key_path_sig.size()) {
490597
result = Vector(sigdata.taproot_key_path_sig);

src/script/signingprovider.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ std::vector<CPubKey> HidingSigningProvider::GetMuSig2ParticipantPubkeys(const CP
5858
return m_provider->GetMuSig2ParticipantPubkeys(pubkey);
5959
}
6060

61+
std::map<CPubKey, std::vector<CPubKey>> HidingSigningProvider::GetAllMuSig2ParticipantPubkeys() const
62+
{
63+
return m_provider->GetAllMuSig2ParticipantPubkeys();
64+
}
65+
6166
void HidingSigningProvider::SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const
6267
{
6368
m_provider->SetMuSig2SecNonce(id, std::move(nonce));
@@ -109,6 +114,11 @@ std::vector<CPubKey> FlatSigningProvider::GetMuSig2ParticipantPubkeys(const CPub
109114
return participant_pubkeys;
110115
}
111116

117+
std::map<CPubKey, std::vector<CPubKey>> FlatSigningProvider::GetAllMuSig2ParticipantPubkeys() const
118+
{
119+
return aggregate_pubkeys;
120+
}
121+
112122
void FlatSigningProvider::SetMuSig2SecNonce(const uint256& session_id, MuSig2SecNonce&& nonce) const
113123
{
114124
if (!Assume(musig2_secnonces)) return;

src/script/signingprovider.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ class SigningProvider
166166
virtual bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { return false; }
167167
virtual bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const { return false; }
168168
virtual std::vector<CPubKey> GetMuSig2ParticipantPubkeys(const CPubKey& pubkey) const { return {}; }
169+
virtual std::map<CPubKey, std::vector<CPubKey>> GetAllMuSig2ParticipantPubkeys() const {return {}; }
169170
virtual void SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const {}
170171
virtual std::optional<std::reference_wrapper<MuSig2SecNonce>> GetMuSig2SecNonce(const uint256& session_id) const { return std::nullopt; }
171172
virtual void DeleteMuSig2Session(const uint256& session_id) const {}
@@ -213,6 +214,7 @@ class HidingSigningProvider : public SigningProvider
213214
bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
214215
bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override;
215216
std::vector<CPubKey> GetMuSig2ParticipantPubkeys(const CPubKey& pubkey) const override;
217+
std::map<CPubKey, std::vector<CPubKey>> GetAllMuSig2ParticipantPubkeys() const override;
216218
void SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const override;
217219
std::optional<std::reference_wrapper<MuSig2SecNonce>> GetMuSig2SecNonce(const uint256& session_id) const override;
218220
void DeleteMuSig2Session(const uint256& session_id) const override;
@@ -236,6 +238,7 @@ struct FlatSigningProvider final : public SigningProvider
236238
bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
237239
bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override;
238240
std::vector<CPubKey> GetMuSig2ParticipantPubkeys(const CPubKey& pubkey) const override;
241+
std::map<CPubKey, std::vector<CPubKey>> GetAllMuSig2ParticipantPubkeys() const override;
239242
void SetMuSig2SecNonce(const uint256& id, MuSig2SecNonce&& nonce) const override;
240243
std::optional<std::reference_wrapper<MuSig2SecNonce>> GetMuSig2SecNonce(const uint256& session_id) const override;
241244
void DeleteMuSig2Session(const uint256& session_id) const override;

0 commit comments

Comments
 (0)