Skip to content

Commit

Permalink
surjectionproof: add method to verify single-input proofs
Browse files Browse the repository at this point in the history
  • Loading branch information
apoelstra committed Jul 26, 2022
1 parent 5e90403 commit 593686c
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
18 changes: 18 additions & 0 deletions include/secp256k1_surjectionproof.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,24 @@ SECP256K1_API int secp256k1_surjectionproof_verify(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
#endif

/** Verify a single-input surjectionproof. Such proofs are sometimes useful to
* prove in zero knowledge that a given commitment commits to a specific asset.
* They can be verified with much less memory than general proofs.
* Returns 0: proof was invalid
* 1: proof was valid
*
* In: ctx: pointer to a context object, initialized for signing and verification
* proof: proof to be verified
* input_tag: the ephemeral asset tag of the sole input
* output_tag: the ephemeral asset tag of the output
*/
SECP256K1_API int secp256k1_surjectionproof_verify_single(
const secp256k1_context* ctx,
const secp256k1_surjectionproof* proof,
const secp256k1_generator* input_tag,
const secp256k1_generator* output_tag
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

#ifdef __cplusplus
}
#endif
Expand Down
70 changes: 70 additions & 0 deletions src/modules/surjection/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,74 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256
return secp256k1_borromean_verify(NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32);
}

int secp256k1_surjectionproof_verify_single(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* input_tag, const secp256k1_generator* output_tag) {
secp256k1_ge inputp;
secp256k1_ge outputp;
secp256k1_gej tmpj;
secp256k1_gej xj;
secp256k1_ge rp;
secp256k1_scalar es;
secp256k1_scalar ss;
secp256k1_sha256 sha2;
unsigned char tmpch[33];
unsigned char pp_comm[32];
size_t sz;
int overflow;

/* Validate and decode surjectionproof data */
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(proof != NULL);
ARG_CHECK(input_tag != NULL);
ARG_CHECK(output_tag != NULL);
#ifdef VERIFY
CHECK(proof->initialized == 1);
#endif

if (proof->n_inputs != 1 || proof->used_inputs[0] != 1) {
return 0;
}

secp256k1_generator_load(&inputp, input_tag);
secp256k1_generator_load(&outputp, output_tag);
secp256k1_ge_neg(&inputp, &inputp);
secp256k1_gej_set_ge(&xj, &inputp);
secp256k1_gej_add_ge(&xj, &xj, &outputp);

/* Now we just have a Schnorr signature in (e, s) form. The verification
* equation is e == H(sG - eX || proof params), where X is the difference
* between the output and input. */

/* 1. Compute slow/overwrought commitment to proof params */
secp256k1_surjection_genmessage(pp_comm, input_tag, 1, output_tag);
/* (past this point the code is identical to rangeproof_verify_value) */

/* ... feed this into our hash */
secp256k1_borromean_hash(tmpch, pp_comm, 32, &proof->data[0], 32, 0, 0);
secp256k1_scalar_set_b32(&es, tmpch, &overflow);
if (overflow || secp256k1_scalar_is_zero(&es)) {
return 0;
}

/* 1. Compute R = sG - eX */
secp256k1_scalar_set_b32(&ss, &proof->data[32], &overflow);
if (overflow || secp256k1_scalar_is_zero(&ss)) {
return 0;
}
secp256k1_ecmult(&tmpj, &xj, &es, &ss);
if (secp256k1_gej_is_infinity(&tmpj)) {
return 0;
}
secp256k1_ge_set_gej(&rp, &tmpj);
secp256k1_eckey_pubkey_serialize(&rp, tmpch, &sz, 1);

/* 2. Compute e = H(R || proof params) */
secp256k1_sha256_initialize(&sha2);
secp256k1_sha256_write(&sha2, tmpch, sz);
secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm));
secp256k1_sha256_finalize(&sha2, tmpch);

/* 3. Check computed e against original e */
return !memcmp(tmpch, &proof->data[0], 32);
}

#endif
5 changes: 5 additions & 0 deletions src/modules/surjection/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,10 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) {
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len));
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]);
CHECK(result == 1);
if (n_inputs == 1) {
result = secp256k1_surjectionproof_verify_single(ctx, &proof, ephemeral_input_tags, &ephemeral_input_tags[n_inputs]);
CHECK(result == 1);
}

/* various fail cases */
if (n_inputs > 1) {
Expand Down Expand Up @@ -680,6 +684,7 @@ void run_surjection_tests(void) {
test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);

test_input_selection_distribution();
test_gen_verify(1, 1);
test_gen_verify(10, 3);
test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
test_no_used_inputs_verify();
Expand Down

0 comments on commit 593686c

Please sign in to comment.