Thank you for the detailed insights and suggestions.
To make the discussion more concrete, I believe it would be best to present a practical example. This will outline all the data that is publicly available.
For clarity and practical implementation, I will use the data structures provided by the secp256k1 library.
Below is a general idea of how the data was generated. Note that this code is illustrative and not the exact implementation:
secp256k1_keypair secp256k1Keypair{};
if (secp256k1_keypair_create(
Secp256k1::context().get(), &secp256k1Keypair, privateKey) == 0)
throw std::runtime_error{"failed to create key pair"};
}
secp256k1_pubkey secp256k1PublicKey{};
if (secp256k1_keypair_pub(
Secp256k1::context().get(), &secp256k1PublicKey,
&secp256k1Keypair) == 0) {
throw std::runtime_error{"failed to extract public key"};
}
secp256k1_musig_secnonce secp256k1MusigSecretNonce{};
secp256k1_musig_pubnonce secp256k1MusigPublicNonce{};
if (secp256k1_musig_nonce_gen(
Secp256k1::context().get(), &secp256k1MusigSecretNonce,
&secp256k1MusigPublicNonce, deterministicValue, nullptr,
&secp256k1PublicKey, message, nullptr, nullptr) == 0) {
throw std::runtime_error{"failed to generate nonces"};
}
secp256k1_musig_keyagg_cache secp256k1MusigKeyAggregationCache{};
// ... code to aggregate the public keys ...
secp256k1_musig_aggnonce secp256k1MusigAggregatedNonce{};
// ... code to aggregate the public nonces ...
secp256k1_musig_session secp256k1MusigSession{};
if (secp256k1_musig_nonce_process(
Secp256k1::context().get(), &secp256k1MusigSession,
&secp256k1MusigAggregatedNonce, message,
&secp256k1MusigKeyAggregationCache) == 0) {
throw std::runtime_error{"failed to process nonce"};
}
if (secp256k1_musig_partial_sign(
Secp256k1::context().get(), &secp256k1MusigPartialSignature,
&secp256k1MusigSecretNonce, &secp256k1Keypair,
&secp256k1MusigKeyAggregationCache,
&secp256k1MusigSession) == 0) {
throw std::runtime_error{"failed to partially sign"};
}
const constexpr std::size_t schnorrSignatureSize{64};
CryptoArray<schnorrSignatureSize> aggregatedSignature{};
// ... code to aggregate the partial signatures ...
secp256k1_pubkey secp256k1AggregatedPublicKey;
if (secp256k1_musig_pubkey_get(
Secp256k1::context().get(), &secp256k1AggregatedPublicKey,
&secp256k1MusigKeyAggregationCache) == 0) {
throw std::runtime_error{"failed to get aggregated public key"};
}
secp256k1_xonly_pubkey aggregatedSecp256k1XonlyPublicKey{};
if (secp256k1_xonly_pubkey_from_pubkey(
Secp256k1::context().get(), &aggregatedSecp256k1XonlyPublicKey,
nullptr, &secp256k1AggregatedPublicKey) == 0) {
throw std::runtime_error{
"failed to convert public key to xonly public key"};
}
if (secp256k1_schnorrsig_verify(
Secp256k1::context().get(),
reinterpret_cast<const std::uint8_t *>(
aggregatedSignature.data()),
message, message.size(),
&aggregatedSecp256k1XonlyPublicKey) == 0) {
throw std::runtime_error{"failed to verify signature"};
}
Here is the data generated for the example:
message: 45F8481247B5C84C48A9952CC7B9AA9B27EAD6A90B2D250E24F81152FB25DECE
Key Pair 1:
privateKey: 6942F99EDEE91D2B59780FC865016CC7F452973C3D7A8735AE9F76CAACB57CCD
secp256k1PublicKey: 02A3D48DC0B2BA69F340F68FB4D8BEEF5BE361FB64DAB71EF74ACCF6ACABF70B01
secp256k1MusigPublicNonce: F57A3DA0D4A492010CF9777E237D4EA7005212072958248A624E761F70A0027AD78EB2A99784232661BEFD6CA2CF17517E49DAFFD7AA6C5BD69FE93A4FC841CB835123BC3404FB9CBEC7BA112C6D0F6800247DB3382619DA497D11E08869DD926DE9457DEF6E9B9197BD44A5DF599CD0260851F0707C693B8FBE58914C1A219BBE888765
secp256k1MusigSecretNonce: 220EDCF13B5E1B766BF04082984E34CD17575158C7F7F25DC4A81D1C0A9A8E7E33CAD3BD00A362F6E9AF37DC10EAE6162C6FDF8C0074A050025CD1BB0D2BF14010DB8722010BF7ABACF6CC4AF71EB7DA64FB61E35BEFBED8B48FF640F369BAB2C08DD4A340C4EB116E73B2F1C7B4D2FB188BAB910934212CAF70F1175228FAC79D8F5488
secp256k1MusigPartialSignature: EBFB1A3284DDE05BA570956916623030D11F266A8260A8C4F074190793F088E12EABB41F
Key Pair 2:
privateKey: 6942F99EDEE91D2B59780FC865016CC7F452973C3D7A8735AE9F76CAACB57E4A
secp256k1PublicKey: 027FA2874F02C66B1B7778FD178E8332CF113FDB5536F2005E83DD1F6E77037A89
secp256k1MusigPublicNonce: F57A3DA05150DF05A15EE6CECB86218C9300090AF710EB2B8C6E51CBF3D0B8EB7235E240249F1B51F53F956FA4609B707AED531CCB37854A8FB9AB0E703CDB34BCABA310846018549CE3086CCB7BB0DC25ABB59D79205D918F6911EBF1B10AFE1516CB7F849E04BA945332881E13A3E5C63AC05120F52FABD248F03D5A5D45ECBEC37538
secp256k1MusigSecretNonce: 220EDCF12372A9189B273F7EE4E6D63CF919F3735C93AE0D4898F2099F986CA572097D2BB6C5A742EDE67454BD1F20B2071A6772529F7EC8815E817BA33A51BFD9BD2B6A897A03776E1FDD835E00F23655DB3F11CF32838E17FD78771B6BC6024F87A27FF224968B7AE9ECD15FCE3965A05C6E90C78D8527F01744204BE48161B6FEA8C7
secp256k1MusigPartialSignature: EBFB1A321C984FD7C278DC61F18C80A68DA4B231B08337C39A6F508E27EA184989190027
Key Pair 3:
privateKey: 6942F99EDEE91D2B59780FC865016CC7F452973C3D7A8735AE9F76CAACB58011
secp256k1PublicKey: 024CBBD03BC9B079BC360A26A034A298EB4520333F87DA4A3D195F390B50B7BD27
secp256k1MusigPublicNonce: F57A3DA0D30E12A1367AE49B87EAA99FA678DEAD817221DBAC9BB0AFA70D9A08AB36B147D3B03A46C163E0401898FA6D209B6F139680A4E95D07A9A4E501106725C1671B8B916792FBE092441290E2300FC5C093DD40118D29DFF8F25A9C7413B4D86D26E75DAD92CD3A94455B9DDD94673F283B7E0438652FA5FCC91B03C3B44DA39130
secp256k1MusigSecretNonce: 220EDCF1E48E62BE3329485818C71B5D129F0294E3C7E899AA584B6940FD9737CFDD2EE3483F714997C706C3F984E35CB405F869E3D477EA479E42B300C0EC82B145F0D027BDB7500B395F193D4ADA873F332045EB98A234A0260A36BC79B0C93BD0BB4C940202A0A14E78D617DD2CD39E62C10A2D43EBC66E5962179C7C85D062401979
secp256k1MusigPartialSignature: EBFB1A32894F0D7D27ACE83FD53B0E14B3F65F963BB62F1F1F29B89B25E5DF3B96174160
Aggregated Data:
secp256k1XonlyPublicKeyAggregated: 6BF1779F97025AFD66240B3CDDB97DED15B6D44902CBDC98FE8BA5E18F4F37A894B88F128CDDFB02C265154E17E30D09D82A5528F123046235124D34E2F8262C
aggregatedSignature: BE99342E3F9A4F4EDC5815C90270B32D29DB7B7F65F39DE33FCFA82E9C19EA6B2AC53DB08F965A0ADD29BEEC12BA3833B3EB32C0FAC481F521EE21D97DA5B465
So, given the above data, what specific operations need to be performed to confirm that the public key:
024CBBD03BC9B079BC360A26A034A298EB4520333F87DA4A3D195F390B50B7BD27 contributed to the aggregated signature or the aggregated public key?