diff --git a/src/key.cpp b/src/key.cpp index f17a286750e4..cc03df1cd946 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -350,128 +349,6 @@ KeyPair CKey::ComputeKeyPair(const uint256* merkle_root) const return KeyPair(*this, merkle_root); } -std::vector CKey::CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CPubKey& aggregate_pubkey, const std::vector& pubkeys) -{ - // Get the keyagg cache and aggregate pubkey - secp256k1_musig_keyagg_cache keyagg_cache; - if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return {}; - - // Parse participant pubkey - CPubKey our_pubkey = GetPubKey(); - secp256k1_pubkey pubkey; - if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &pubkey, our_pubkey.data(), our_pubkey.size())) { - return {}; - } - - // Generate randomness for nonce - uint256 rand; - GetStrongRandBytes(rand); - - // Generate nonce - secp256k1_musig_pubnonce pubnonce; - if (!secp256k1_musig_nonce_gen(secp256k1_context_sign, secnonce.Get(), &pubnonce, rand.data(), UCharCast(begin()), &pubkey, sighash.data(), &keyagg_cache, nullptr)) { - return {}; - } - - // Serialize pubnonce - std::vector out; - out.resize(MUSIG2_PUBNONCE_SIZE); - if (!secp256k1_musig_pubnonce_serialize(secp256k1_context_static, out.data(), &pubnonce)) { - return {}; - } - - 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; @@ -568,6 +445,11 @@ bool ECC_InitSanityCheck() { return key.VerifyPubKey(pubkey); } +secp256k1_context* GetSecp256k1SignContext() +{ + return secp256k1_context_sign; +} + /** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */ static void ECC_Start() { assert(secp256k1_context_sign == nullptr); diff --git a/src/key.h b/src/key.h index f35f3cc01562..cd77dcd0ee2a 100644 --- a/src/key.h +++ b/src/key.h @@ -7,7 +7,6 @@ #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H -#include #include #include #include @@ -16,6 +15,8 @@ #include #include +struct secp256k1_context_struct; +typedef struct secp256k1_context_struct secp256k1_context; /** * CPrivKey is a serialized private key, with all parameters included @@ -221,9 +222,6 @@ class CKey * Merkle root of the script tree). */ 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; @@ -315,6 +313,9 @@ class KeyPair /** Check that required EC support is available at runtime. */ bool ECC_InitSanityCheck(); +/** Access the secp256k1 context used for signing and MuSig2 nonce generation. */ +secp256k1_context* GetSecp256k1SignContext(); + /** * RAII class initializing and deinitializing global state for elliptic curve support. * Only one instance may be initialized at a time. diff --git a/src/musig.cpp b/src/musig.cpp index 706874be2cf5..9a1b34421cd2 100644 --- a/src/musig.cpp +++ b/src/musig.cpp @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include +#include #include #include @@ -126,6 +128,128 @@ uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey return hasher.GetSHA256(); } +std::vector CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector& pubkeys) +{ + // Get the keyagg cache and aggregate pubkey + secp256k1_musig_keyagg_cache keyagg_cache; + if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return {}; + + // Parse participant pubkey + CPubKey our_pubkey = our_seckey.GetPubKey(); + secp256k1_pubkey pubkey; + if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &pubkey, our_pubkey.data(), our_pubkey.size())) { + return {}; + } + + // Generate randomness for nonce + uint256 rand; + GetStrongRandBytes(rand); + + // Generate nonce + secp256k1_musig_pubnonce pubnonce; + if (!secp256k1_musig_nonce_gen(GetSecp256k1SignContext(), secnonce.Get(), &pubnonce, rand.data(), UCharCast(our_seckey.begin()), &pubkey, sighash.data(), &keyagg_cache, nullptr)) { + return {}; + } + + // Serialize pubnonce + std::vector out; + out.resize(MUSIG2_PUBNONCE_SIZE); + if (!secp256k1_musig_pubnonce_serialize(secp256k1_context_static, out.data(), &pubnonce)) { + return {}; + } + + return out; +} + +std::optional CreateMuSig2PartialSig(const uint256& sighash, const CKey& our_seckey, 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(GetSecp256k1SignContext(), &keypair, UCharCast(our_seckey.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 = our_seckey.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; +} + std::optional> CreateMuSig2AggregateSig(const std::vector& part_pubkeys, const CPubKey& aggregate_pubkey, const std::vector>& tweaks, const uint256& sighash, const std::map>& pubnonces, const std::map& partial_sigs) { if (!part_pubkeys.size()) return std::nullopt; diff --git a/src/musig.h b/src/musig.h index f518ae81ba29..b61c2f3f49b8 100644 --- a/src/musig.h +++ b/src/musig.h @@ -10,6 +10,7 @@ #include #include +class CKey; struct secp256k1_musig_keyagg_cache; class MuSig2SecNonceImpl; struct secp256k1_musig_secnonce; @@ -58,6 +59,8 @@ class MuSig2SecNonce uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash); +std::vector CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector& pubkeys); +std::optional CreateMuSig2PartialSig(const uint256& hash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector& pubkeys, const std::map>& pubnonces, MuSig2SecNonce& secnonce, const std::vector>& tweaks); std::optional> CreateMuSig2AggregateSig(const std::vector& participants, const CPubKey& aggregate_pubkey, const std::vector>& tweaks, const uint256& sighash, const std::map>& pubnonces, const std::map& partial_sigs); #endif // BITCOIN_MUSIG_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 6cbcf07e5e2a..e8a0071ee966 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -119,7 +119,7 @@ std::vector MutableTransactionSignatureCreator::CreateMuSig2Nonce(const if (!sighash.has_value()) return {}; MuSig2SecNonce secnonce; - std::vector out = key.CreateMuSig2Nonce(secnonce, *sighash, aggregate_pubkey, pubkeys); + std::vector out = ::CreateMuSig2Nonce(secnonce, *sighash, key, aggregate_pubkey, pubkeys); if (out.empty()) return {}; // Store the secnonce in the SigningProvider @@ -161,7 +161,7 @@ bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningPro if (!secnonce || !secnonce->get().IsValid()) return false; // Compute the sig - std::optional sig = key.CreateMuSig2PartialSig(*sighash, aggregate_pubkey, pubkeys, pubnonces, *secnonce, tweaks); + std::optional sig = ::CreateMuSig2PartialSig(*sighash, key, aggregate_pubkey, pubkeys, pubnonces, *secnonce, tweaks); if (!sig) return false; partial_sig = std::move(*sig); diff --git a/src/test/fuzz/txorphan.cpp b/src/test/fuzz/txorphan.cpp index 8a21a506cbae..394aed30ef65 100644 --- a/src/test/fuzz/txorphan.cpp +++ b/src/test/fuzz/txorphan.cpp @@ -197,9 +197,13 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage) [&] { // Make a block out of txs and then EraseForBlock CBlock block; + int64_t block_weight{0}; int num_txs = fuzzed_data_provider.ConsumeIntegralInRange(0, 1000); for (int i{0}; i < num_txs; ++i) { auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history); + const auto tx_weight = GetTransactionWeight(*tx_to_remove); + if (block_weight + tx_weight > MAX_BLOCK_WEIGHT) break; + block_weight += tx_weight; block.vtx.push_back(tx_to_remove); } orphanage->EraseForBlock(block);