From bf69442b3f5004dc3df5a1b1d752114ba68fa5f4 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 5 Feb 2024 16:44:03 -0500 Subject: [PATCH] sign: Add CreateMuSig2PartialSig --- src/key.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++ src/key.h | 1 + src/script/sign.cpp | 49 +++++++++++++++++++++++++ src/script/sign.h | 2 + 4 files changed, 141 insertions(+) diff --git a/src/key.cpp b/src/key.cpp index 023983326c0..84e0b821f71 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -383,6 +383,95 @@ std::vector CKey::CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uin return out; } +std::optional CKey::CreateMuSig2PartialSig(const uint256& sighash, const CPubKey& aggregate_pubkey, const std::vector& pubkeys, const std::map>& pubnonces, MuSig2SecNonce& secnonce, const std::vector>& tweaks) +{ + secp256k1_keypair keypair; + if (!secp256k1_keypair_create(secp256k1_context_sign, &keypair, UCharCast(begin()))) return std::nullopt; + + // Get the keyagg cache and aggregate pubkey + secp256k1_musig_keyagg_cache keyagg_cache; + if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt; + + // Check that there are enough pubnonces + if (pubnonces.size() != pubkeys.size()) return std::nullopt; + + // Parse the pubnonces + std::vector> signers_data; + std::vector pubnonce_ptrs; + std::optional our_pubkey_idx; + CPubKey our_pubkey = GetPubKey(); + for (const CPubKey& part_pk : pubkeys) { + const auto& pn_it = pubnonces.find(part_pk); + if (pn_it == pubnonces.end()) return std::nullopt; + const std::vector pubnonce = pn_it->second; + if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt; + if (part_pk == our_pubkey) { + our_pubkey_idx = signers_data.size(); + } + + auto& [secp_pk, secp_pn] = signers_data.emplace_back(); + + if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) { + return std::nullopt; + } + + if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) { + return std::nullopt; + } + } + if (our_pubkey_idx == std::nullopt) { + return std::nullopt; + } + pubnonce_ptrs.reserve(signers_data.size()); + for (auto& [_, pn] : signers_data) { + pubnonce_ptrs.push_back(&pn); + } + + // Aggregate nonces + secp256k1_musig_aggnonce aggnonce; + if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) { + return std::nullopt; + } + + // Apply tweaks + for (const auto& [tweak, xonly] : tweaks) { + if (xonly) { + if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) { + return std::nullopt; + } + } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) { + return std::nullopt; + } + } + + // Create musig_session + secp256k1_musig_session session; + if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) { + return std::nullopt; + } + + // Create partial signature + secp256k1_musig_partial_sig psig; + if (!secp256k1_musig_partial_sign(secp256k1_context_static, &psig, secnonce.Get(), &keypair, &keyagg_cache, &session)) { + return std::nullopt; + } + // The secnonce must be deleted after signing to prevent nonce reuse. + secnonce.Invalidate(); + + // Verify partial signature + if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &psig, &(signers_data.at(*our_pubkey_idx).second), &(signers_data.at(*our_pubkey_idx).first), &keyagg_cache, &session)) { + return std::nullopt; + } + + // Serialize + uint256 sig; + if (!secp256k1_musig_partial_sig_serialize(secp256k1_context_static, sig.data(), &psig)) { + return std::nullopt; + } + + return sig; +} + CKey GenerateRandomKey(bool compressed) noexcept { CKey key; diff --git a/src/key.h b/src/key.h index 97ed27ccfc0..f35f3cc0156 100644 --- a/src/key.h +++ b/src/key.h @@ -223,6 +223,7 @@ public: KeyPair ComputeKeyPair(const uint256* merkle_root) const; std::vector CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CPubKey& aggregate_pubkey, const std::vector& pubkeys); + std::optional CreateMuSig2PartialSig(const uint256& hash, const CPubKey& aggregate_pubkey, const std::vector& pubkeys, const std::map>& pubnonces, MuSig2SecNonce& secnonce, const std::vector>& tweaks); }; CKey GenerateRandomKey(bool compressed = true) noexcept; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 5ca3f98814e..8abc82bcb3c 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -130,6 +130,50 @@ std::vector MutableTransactionSignatureCreator::CreateMuSig2Nonce(const return out; } +bool MutableTransactionSignatureCreator::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>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const +{ + assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT); + + // Retrieve private key + CKey key; + if (!provider.GetKey(part_pubkey.GetID(), key)) return false; + + // Retrieve participant pubkeys + auto it = sigdata.musig2_pubkeys.find(aggregate_pubkey); + if (it == sigdata.musig2_pubkeys.end()) return false; + const std::vector& pubkeys = it->second; + if (std::find(pubkeys.begin(), pubkeys.end(), part_pubkey) == pubkeys.end()) return {}; + + // Retrieve pubnonces + auto this_leaf_aggkey = std::make_pair(script_pubkey, leaf_hash ? *leaf_hash : uint256()); + auto pubnonce_it = sigdata.musig2_pubnonces.find(this_leaf_aggkey); + if (pubnonce_it == sigdata.musig2_pubnonces.end()) return false; + const std::map>& pubnonces = pubnonce_it->second; + + // Check if enough pubnonces + if (pubnonces.size() != pubkeys.size()) return false; + + // Compute sighash + std::optional sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion); + if (!sighash.has_value()) return false; + + // Retrieve the secnonce + uint256 session_id = MuSig2SessionID(script_pubkey, part_pubkey, *sighash); + std::optional> secnonce = provider.GetMuSig2SecNonce(session_id); + if (!secnonce || !secnonce->get().IsValid()) return false; + + // Compute the sig + std::optional sig = key.CreateMuSig2PartialSig(*sighash, aggregate_pubkey, pubkeys, pubnonces, *secnonce, tweaks); + if (!sig) return false; + partial_sig = std::move(*sig); + + // Delete the secnonce now that we're done with it + assert(!secnonce->get().IsValid()); + provider.DeleteMuSig2Session(session_id); + + return true; +} + static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script) { if (provider.GetCScript(scriptid, script)) { @@ -791,6 +835,11 @@ public: out.assign(MUSIG2_PUBNONCE_SIZE, '\000'); return out; } + 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>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override + { + partial_sig = uint256::ONE; + return true; + } }; } diff --git a/src/script/sign.h b/src/script/sign.h index 4a0782daa32..0c9263df0b4 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -35,6 +35,7 @@ public: virtual bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0; virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0; virtual std::vector 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; + 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>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0; }; /** A signature creator for transactions. */ @@ -56,6 +57,7 @@ public: bool CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; bool CreateSchnorrSig(const SigningProvider& provider, std::vector& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override; std::vector 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; + 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>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override; }; /** A signature checker that accepts all signatures */