Skip to content

Commit 593686c

Browse files
committed
surjectionproof: add method to verify single-input proofs
1 parent 5e90403 commit 593686c

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

include/secp256k1_surjectionproof.h

+18
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

+70
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,74 @@ int secp256k1_surjectionproof_verify(const secp256k1_context* ctx, const secp256
393393
return secp256k1_borromean_verify(NULL, &proof->data[0], borromean_s, ring_pubkeys, rsizes, 1, msg32, 32);
394394
}
395395

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

src/modules/surjection/tests_impl.h

+5
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) {
@@ -680,6 +684,7 @@ void run_surjection_tests(void) {
680684
test_input_selection(SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
681685

682686
test_input_selection_distribution();
687+
test_gen_verify(1, 1);
683688
test_gen_verify(10, 3);
684689
test_gen_verify(SECP256K1_SURJECTIONPROOF_MAX_N_INPUTS, SECP256K1_SURJECTIONPROOF_MAX_USED_INPUTS);
685690
test_no_used_inputs_verify();

0 commit comments

Comments
 (0)