Skip to content

Commit 3662a01

Browse files
committed
surjectionproof: add method to verify single-input proofs
1 parent c6222c9 commit 3662a01

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

include/secp256k1_surjectionproof.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,24 @@ SECP256K1_API int secp256k1_surjectionproof_verify(
263263
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
264264
#endif
265265

266+
/** Verify a single-input surjectionproof. Such proofs are sometimes useful to
267+
* prove in zero knowledge that a given commitment commits to a specific asset.
268+
* They can be verified with much less memory than general proofs.
269+
* Returns 0: proof was invalid
270+
* 1: proof was valid
271+
*
272+
* In: ctx: pointer to a context object, initialized for signing and verification
273+
* proof: proof to be verified
274+
* input_tag: the ephemeral asset tag of the sole input
275+
* output_tag: the ephemeral asset tag of the output
276+
*/
277+
SECP256K1_API int secp256k1_surjectionproof_verify_single(
278+
const secp256k1_context* ctx,
279+
const secp256k1_surjectionproof* proof,
280+
const secp256k1_generator* input_tag,
281+
const secp256k1_generator* output_tag
282+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
283+
266284
#ifdef __cplusplus
267285
}
268286
#endif

src/modules/surjection/main_impl.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,4 +397,74 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256
397397
return secp256k1_borromean_verify(NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32);
398398
}
399399

400+
int secp256k1_surjectionproof_verify_single(const secp256k1_context* ctx, const secp256k1_surjectionproof* proof, const secp256k1_generator* input_tag, const secp256k1_generator* output_tag) {
401+
secp256k1_ge inputp;
402+
secp256k1_ge outputp;
403+
secp256k1_gej tmpj;
404+
secp256k1_gej xj;
405+
secp256k1_ge rp;
406+
secp256k1_scalar es;
407+
secp256k1_scalar ss;
408+
secp256k1_sha256 sha2;
409+
unsigned char tmpch[33];
410+
unsigned char pp_comm[32];
411+
size_t sz;
412+
int overflow;
413+
414+
/* Validate and decode surjectionproof data */
415+
VERIFY_CHECK(ctx != NULL);
416+
ARG_CHECK(proof != NULL);
417+
ARG_CHECK(input_tag != NULL);
418+
ARG_CHECK(output_tag != NULL);
419+
#ifdef VERIFY
420+
CHECK(proof->initialized == 1);
421+
#endif
422+
423+
if (proof->n_inputs != 1 || proof->used_inputs[0] != 1) {
424+
return 0;
425+
}
426+
427+
secp256k1_generator_load(&inputp, input_tag);
428+
secp256k1_generator_load(&outputp, output_tag);
429+
secp256k1_ge_neg(&inputp, &inputp);
430+
secp256k1_gej_set_ge(&xj, &inputp);
431+
secp256k1_gej_add_ge(&xj, &xj, &outputp);
432+
433+
/* Now we just have a Schnorr signature in (e, s) form. The verification
434+
* equation is e == H(sG - eX || proof params), where X is the difference
435+
* between the output and input. */
436+
437+
/* 1. Compute slow/overwrought commitment to proof params */
438+
secp256k1_surjection_genmessage(pp_comm, input_tag, 1, output_tag);
439+
/* (past this point the code is identical to rangeproof_verify_value) */
440+
441+
/* ... feed this into our hash */
442+
secp256k1_borromean_hash(tmpch, pp_comm, 32, &proof->data[0], 32, 0, 0);
443+
secp256k1_scalar_set_b32(&es, tmpch, &overflow);
444+
if (overflow || secp256k1_scalar_is_zero(&es)) {
445+
return 0;
446+
}
447+
448+
/* 1. Compute R = sG - eX */
449+
secp256k1_scalar_set_b32(&ss, &proof->data[32], &overflow);
450+
if (overflow || secp256k1_scalar_is_zero(&ss)) {
451+
return 0;
452+
}
453+
secp256k1_ecmult(&tmpj, &xj, &es, &ss);
454+
if (secp256k1_gej_is_infinity(&tmpj)) {
455+
return 0;
456+
}
457+
secp256k1_ge_set_gej(&rp, &tmpj);
458+
secp256k1_eckey_pubkey_serialize(&rp, tmpch, &sz, 1);
459+
460+
/* 2. Compute e = H(R || proof params) */
461+
secp256k1_sha256_initialize(&sha2);
462+
secp256k1_sha256_write(&sha2, tmpch, sz);
463+
secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm));
464+
secp256k1_sha256_finalize(&sha2, tmpch);
465+
466+
/* 3. Check computed e against original e */
467+
return !secp256k1_memcmp_var(tmpch, &proof->data[0], 32);
468+
}
469+
400470
#endif

src/modules/surjection/tests_impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,10 @@ static void test_gen_verify(size_t n_inputs, size_t n_used) {
431431
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof, serialized_len));
432432
result = secp256k1_surjectionproof_verify(ctx, &proof, ephemeral_input_tags, n_inputs, &ephemeral_input_tags[n_inputs]);
433433
CHECK(result == 1);
434+
if (n_inputs == 1) {
435+
result = secp256k1_surjectionproof_verify_single(ctx, &proof, ephemeral_input_tags, &ephemeral_input_tags[n_inputs]);
436+
CHECK(result == 1);
437+
}
434438

435439
/* various fail cases */
436440
if (n_inputs > 1) {
@@ -706,6 +710,7 @@ void run_surjection_tests(void) {
706710
test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
707711

708712
test_input_selection_distribution();
713+
test_gen_verify(1, 1);
709714
test_gen_verify(10, 3);
710715
test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
711716
test_no_used_inputs_verify();

0 commit comments

Comments
 (0)