Skip to content

Commit d37296b

Browse files
committed
musig: add nonces_combine function that adds up the received nonces
This is useful for reducing the communcation by having a "combiner" as mentioned in the MuSig2 paper.
1 parent eaf9a9c commit d37296b

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed

include/secp256k1_musig.h

+25
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,31 @@ SECP256K1_API int secp256k1_musig_session_init(
205205
const unsigned char *extra_input32
206206
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
207207

208+
/** Combines received nonces into a single nonce
209+
*
210+
* This is useful to reduce the communication between signers, because instead
211+
* of everyone sending nonces to everyone else, there can be one party
212+
* receiving all nonces, combining the nonces with this function and then
213+
* sending only the combined nonce back to the signers. The pubnonces argument
214+
* of secp256k1_musig_process_nonces then simply becomes an array whose sole
215+
* element is a pointer to this combined nonce.
216+
*
217+
* Returns: 0 if the arguments are invalid or if all signers sent invalid
218+
* pubnonces, 1 otherwise
219+
* Args: ctx: pointer to a context object (cannot be NULL)
220+
* Out: combined_pubnonce66: a 66-byte array to store the combined pubnonce
221+
* In: pubnonces: array of pointers to the 66-byte pubnonces sent
222+
* by the signers
223+
* n_pubnonces: number of elements in the pubnonces array. Must
224+
* be greater than 0.
225+
*/
226+
SECP256K1_API int secp256k1_musig_nonces_combine(
227+
const secp256k1_context* ctx,
228+
unsigned char *combined_pubnonce66,
229+
const unsigned char * const* pubnonces,
230+
size_t n_pubnonces
231+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
232+
208233
/** Takes the public nonces of all signers and computes a session cache that is
209234
* required for signing and verification of partial signatures and a signature
210235
* template that is required for combining partial signatures.

src/modules/musig/main_impl.h

+24
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,30 @@ static void secp256k1_musig_sum_nonces(const secp256k1_context* ctx, secp256k1_g
295295
}
296296
}
297297

298+
299+
int secp256k1_musig_nonces_combine(const secp256k1_context* ctx, unsigned char *combined_pubnonce66, const unsigned char * const* pubnonces, size_t n_pubnonces) {
300+
secp256k1_gej summed_nonces[2];
301+
int i;
302+
303+
ARG_CHECK(combined_pubnonce66 != NULL);
304+
ARG_CHECK(pubnonces != NULL);
305+
ARG_CHECK(n_pubnonces > 0);
306+
307+
secp256k1_musig_sum_nonces(ctx, summed_nonces, pubnonces, n_pubnonces);
308+
for (i = 0; i < 2; i++) {
309+
int ret;
310+
secp256k1_ge noncep;
311+
size_t len = 33;
312+
if (secp256k1_gej_is_infinity(&summed_nonces[i])) {
313+
return 0;
314+
}
315+
secp256k1_ge_set_gej(&noncep, &summed_nonces[i]);
316+
ret = secp256k1_eckey_pubkey_serialize(&noncep, &combined_pubnonce66[i*33], &len, 1);
317+
VERIFY_CHECK(ret);
318+
}
319+
return 1;
320+
}
321+
298322
static void secp256k1_musig_session_cache_load(secp256k1_scalar *b, secp256k1_scalar *e, int *combined_nonce_parity, const secp256k1_musig_session_cache *cache) {
299323
secp256k1_scalar_set_b32(b, &cache->data[0], NULL);
300324
secp256k1_scalar_set_b32(e, &cache->data[32], NULL);

src/modules/musig/tests_impl.h

+58
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
110110
const unsigned char *invalid_pubnonce_ptr[1];
111111
unsigned char inf_pubnonce[2][66];
112112
const unsigned char *inf_pubnonce_ptr[2];
113+
unsigned char combined_pnonce[66];
113114
unsigned char msg[32];
114115
secp256k1_xonly_pubkey combined_pk;
115116
secp256k1_musig_pre_session pre_session;
@@ -274,6 +275,19 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
274275
CHECK(secp256k1_musig_session_init(sign, &secnonce[1], pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1);
275276

276277
/** Receive nonces **/
278+
ecount = 0;
279+
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, pubnonce_ptr, 2) == 1);
280+
CHECK(secp256k1_musig_nonces_combine(none, NULL, pubnonce_ptr, 2) == 0);
281+
CHECK(ecount == 1);
282+
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, NULL, 2) == 0);
283+
CHECK(ecount == 2);
284+
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, pubnonce_ptr, 0) == 0);
285+
CHECK(ecount == 3);
286+
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, invalid_pubnonce_ptr, 1) == 0);
287+
CHECK(ecount == 3);
288+
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, inf_pubnonce_ptr, 2) == 0);
289+
CHECK(ecount == 3);
290+
277291
ecount = 0;
278292
CHECK(secp256k1_musig_process_nonces(none, &session_cache, &sig_template, &nonce_parity, pubnonce_ptr, 2, msg, &combined_pk, &pre_session, &adaptor) == 0);
279293
CHECK(ecount == 1);
@@ -626,6 +640,49 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
626640
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1);
627641
}
628642

643+
void musig_combiner_test(secp256k1_scratch_space *scratch) {
644+
unsigned char sk[2][32];
645+
secp256k1_keypair keypair[2];
646+
unsigned char pubnonce[2][66];
647+
const unsigned char *pubnonce_ptr[2];
648+
unsigned char combined_pnonce[66];
649+
const unsigned char *combined_pnonce_ptr[1];
650+
unsigned char msg[32];
651+
secp256k1_xonly_pubkey combined_pk;
652+
secp256k1_musig_pre_session pre_session;
653+
unsigned char session_id[2][32];
654+
secp256k1_musig_secnonce secnonce[2];
655+
secp256k1_xonly_pubkey pk[2];
656+
const secp256k1_xonly_pubkey *pk_ptr[2];
657+
int nonce_parity, nonce_parity2;
658+
secp256k1_musig_template sig_template, sig_template2;
659+
secp256k1_musig_session_cache session_cache, session_cache2;
660+
int i;
661+
662+
secp256k1_testrand256(msg);
663+
for (i = 0; i < 2; i++) {
664+
secp256k1_testrand256(session_id[i]);
665+
secp256k1_testrand256(sk[i]);
666+
pk_ptr[i] = &pk[i];
667+
pubnonce_ptr[i] = pubnonce[i];
668+
CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i]));
669+
}
670+
combined_pnonce_ptr[0] = combined_pnonce;
671+
672+
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);
673+
674+
CHECK(secp256k1_musig_session_init(ctx, &secnonce[0], pubnonce[0], session_id[0], sk[0], NULL, NULL, NULL) == 1);
675+
CHECK(secp256k1_musig_session_init(ctx, &secnonce[1], pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1);
676+
677+
CHECK(secp256k1_musig_process_nonces(ctx, &session_cache, &sig_template, &nonce_parity, pubnonce_ptr, 2, msg, &combined_pk, &pre_session, NULL) == 1);
678+
/* Check that process_nonces on the result of nonces_combine gives the same result */
679+
CHECK(secp256k1_musig_nonces_combine(ctx, combined_pnonce, pubnonce_ptr, 2) == 1);
680+
CHECK(secp256k1_musig_process_nonces(ctx, &session_cache2, &sig_template2, &nonce_parity2, combined_pnonce_ptr, 1, msg, &combined_pk, &pre_session, NULL) == 1);
681+
CHECK(memcmp(&session_cache, &session_cache2, sizeof(session_cache)) == 0);
682+
CHECK(memcmp(&sig_template, &sig_template2, sizeof(sig_template)) == 0);
683+
CHECK(nonce_parity == nonce_parity2);
684+
}
685+
629686
/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the
630687
* expected state. */
631688
void sha256_tag_test(void) {
@@ -903,6 +960,7 @@ void run_musig_tests(void) {
903960
/* Run multiple times to ensure that pk and nonce have different y
904961
* parities */
905962
scriptless_atomic_swap(scratch);
963+
musig_combiner_test(scratch);
906964
musig_tweak_test(scratch);
907965
}
908966
musig_test_vectors();

0 commit comments

Comments
 (0)