Skip to content

Commit

Permalink
musig: add nonces_combine function that adds up the received nonces
Browse files Browse the repository at this point in the history
This is useful for reducing the communcation by having a "combiner" as mentioned
in the MuSig2 paper.
  • Loading branch information
jonasnick committed May 14, 2021
1 parent f9057a1 commit 247316f
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 0 deletions.
25 changes: 25 additions & 0 deletions include/secp256k1_musig.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,31 @@ SECP256K1_API int secp256k1_musig_session_init(
const unsigned char *extra_input32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);

/** Combines received nonces into a single nonce
*
* This is useful to reduce the communication between signers, because instead
* of everyone sending nonces to everyone else, there can be one party
* receiving all nonces, combining the nonces with this function and then
* sending only the combined nonce back to the signers. The pubnonces argument
* of secp256k1_musig_process_nonces then simply becomes an array whose sole
* element is a pointer to this combined nonce.
*
* Returns: 0 if the arguments are invalid or if all signers sent invalid
* pubnonces, 1 otherwise
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: combined_pubnonce66: a 66-byte array to store the combined pubnonce
* In: pubnonces: array of pointers to the 66-byte pubnonces sent
* by the signers
* n_pubnonces: number of elements in the pubnonces array. Must
* be greater than 0.
*/
SECP256K1_API int secp256k1_musig_nonces_combine(
const secp256k1_context* ctx,
unsigned char *combined_pubnonce66,
const unsigned char * const* pubnonces,
size_t n_pubnonces
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Takes the public nonces of all signers and computes a session cache that is
* required for signing and verification of partial signatures and a signature
* template that is required for combining partial signatures.
Expand Down
24 changes: 24 additions & 0 deletions src/modules/musig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,30 @@ static void secp256k1_musig_sum_nonces(const secp256k1_context* ctx, secp256k1_g
}
}


int secp256k1_musig_nonces_combine(const secp256k1_context* ctx, unsigned char *combined_pubnonce66, const unsigned char * const* pubnonces, size_t n_pubnonces) {
secp256k1_gej summed_nonces[2];
int i;

ARG_CHECK(combined_pubnonce66 != NULL);
ARG_CHECK(pubnonces != NULL);
ARG_CHECK(n_pubnonces > 0);

secp256k1_musig_sum_nonces(ctx, summed_nonces, pubnonces, n_pubnonces);
for (i = 0; i < 2; i++) {
int ret;
secp256k1_ge noncep;
size_t len = 33;
if (secp256k1_gej_is_infinity(&summed_nonces[i])) {
return 0;
}
secp256k1_ge_set_gej(&noncep, &summed_nonces[i]);
ret = secp256k1_eckey_pubkey_serialize(&noncep, &combined_pubnonce66[i*33], &len, 1);
VERIFY_CHECK(ret);
}
return 1;
}

static void secp256k1_musig_session_cache_load(secp256k1_scalar *b, secp256k1_scalar *e, int *combined_nonce_parity, const secp256k1_musig_session_cache *cache) {
secp256k1_scalar_set_b32(b, &cache->data[0], NULL);
secp256k1_scalar_set_b32(e, &cache->data[32], NULL);
Expand Down
57 changes: 57 additions & 0 deletions src/modules/musig/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
const unsigned char *invalid_pubnonce_ptr[1];
unsigned char inf_pubnonce[2][66];
const unsigned char *inf_pubnonce_ptr[2];
unsigned char combined_pnonce[66];
unsigned char msg[32];
secp256k1_xonly_pubkey combined_pk;
secp256k1_musig_pre_session pre_session;
Expand Down Expand Up @@ -274,6 +275,19 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_musig_session_init(sign, &secnonce[1], pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1);

/** Receive nonces **/
ecount = 0;
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, pubnonce_ptr, 2) == 1);
CHECK(secp256k1_musig_nonces_combine(none, NULL, pubnonce_ptr, 2) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, NULL, 2) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, pubnonce_ptr, 0) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, invalid_pubnonce_ptr, 1) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, inf_pubnonce_ptr, 2) == 0);
CHECK(ecount == 3);

ecount = 0;
CHECK(secp256k1_musig_process_nonces(none, &session_cache, &sig_template, &nonce_parity, pubnonce_ptr, 2, msg, &combined_pk, &pre_session, &adaptor) == 0);
CHECK(ecount == 1);
Expand Down Expand Up @@ -626,6 +640,48 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1);
}

void musig_combiner_test(secp256k1_scratch_space *scratch) {
unsigned char sk[2][32];
secp256k1_keypair keypair[2];
unsigned char pubnonce[2][66];
const unsigned char *pubnonce_ptr[2];
unsigned char combined_pnonce[66];
const unsigned char *combined_pnonce_ptr[1];
unsigned char msg[32];
secp256k1_xonly_pubkey combined_pk;
secp256k1_musig_pre_session pre_session;
unsigned char session_id[2][32];
secp256k1_musig_secnonce secnonce[2];
secp256k1_xonly_pubkey pk[2];
const secp256k1_xonly_pubkey *pk_ptr[2];
int nonce_parity, nonce_parity2;
secp256k1_musig_template sig_template, sig_template2;
secp256k1_musig_session_cache session_cache, session_cache2;
int i;

secp256k1_testrand256(msg);
for (i = 0; i < 2; i++) {
secp256k1_testrand256(session_id[i]);
secp256k1_testrand256(sk[i]);
pk_ptr[i] = &pk[i];
pubnonce_ptr[i] = pubnonce[i];
CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i]));
}
combined_pnonce_ptr[0] = combined_pnonce;

CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);

CHECK(secp256k1_musig_session_init(ctx, &secnonce[1], pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1);

CHECK(secp256k1_musig_process_nonces(ctx, &session_cache, &sig_template, &nonce_parity, pubnonce_ptr, 2, msg, &combined_pk, &pre_session, NULL) == 1);
/* Check that process_nonces on the result of nonces_combine gives the same result */
CHECK(secp256k1_musig_nonces_combine(ctx, combined_pnonce, pubnonce_ptr, 2) == 1);
CHECK(secp256k1_musig_process_nonces(ctx, &session_cache2, &sig_template2, &nonce_parity2, combined_pnonce_ptr, 1, msg, &combined_pk, &pre_session, NULL) == 1);
CHECK(memcmp(&session_cache, &session_cache2, sizeof(session_cache)) == 0);
CHECK(memcmp(&sig_template, &sig_template2, sizeof(sig_template)) == 0);
CHECK(nonce_parity == nonce_parity2);
}

/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the
* expected state. */
void sha256_tag_test(void) {
Expand Down Expand Up @@ -903,6 +959,7 @@ void run_musig_tests(void) {
/* Run multiple times to ensure that pk and nonce have different y
* parities */
scriptless_atomic_swap(scratch);
musig_combiner_test(scratch);
musig_tweak_test(scratch);
}
musig_test_vectors();
Expand Down

0 comments on commit 247316f

Please sign in to comment.