musig: Add MuSig2AggregatePubkeys variant that validates the aggregate

A common pattern that MuSig2 functions will use is to aggregate the
pubkeys to get the keyagg_cache and then validate the aggregated pubkey
against a provided aggregate pubkey. A variant of MuSig2AggregatePubkeys
is added which does that.

The functionality of GetMuSig2KeyAggCache and GetCPubKeyFromMuSig2KeyAggCache
are included in MuSig2AggregatePubkeys (and used internally) so there is
no expectation that callers will need these so they are made static.
This commit is contained in:
Ava Chow 2025-08-05 12:38:29 -07:00
parent d99a081679
commit 82ea67c607
2 changed files with 17 additions and 10 deletions

View File

@ -7,7 +7,7 @@
#include <secp256k1_musig.h>
bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
{
// Parse the pubkeys
std::vector<secp256k1_pubkey> secp_pubkeys;
@ -29,7 +29,7 @@ bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_k
return true;
}
std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
{
// Get the plain aggregated pubkey
secp256k1_pubkey agg_pubkey;
@ -44,13 +44,21 @@ std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_ca
return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);
}
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate)
{
secp256k1_musig_keyagg_cache keyagg_cache;
if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {
return std::nullopt;
}
return GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
if (!agg_key.has_value()) return std::nullopt;
if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt;
return agg_key;
}
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
{
secp256k1_musig_keyagg_cache keyagg_cache;
return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt);
}
CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey)

View File

@ -18,11 +18,10 @@ struct secp256k1_musig_secnonce;
using namespace util::hex_literals;
constexpr uint256 MUSIG_CHAINCODE{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8};
//! Create a secp256k1_musig_keyagg_cache from the pubkeys in their current order. This is necessary for most MuSig2 operations
bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache);
//! Retrieve the full aggregate pubkey from the secp256k1_musig_keyagg_cache
std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& cache);
//! Compute the full aggregate pubkey from the given participant pubkeys in their current order
//! Compute the full aggregate pubkey from the given participant pubkeys in their current order.
//! Outputs the secp256k1_musig_keyagg_cache and validates that the computed aggregate pubkey matches an expected aggregate pubkey.
//! This is necessary for most MuSig2 operations.
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate);
std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys);
//! Construct the BIP 328 synthetic xpub for a pubkey