@@ -304,4 +304,97 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof
304
304
proof , plen , min_value , & commitp , blind , nonce , exp , min_bits , value , message , msg_len , extra_commit , extra_commit_len , & genp );
305
305
}
306
306
307
+ int secp256k1_rangeproof_verify_value (const secp256k1_context * ctx , const unsigned char * proof , size_t plen , uint64_t value , const secp256k1_pedersen_commitment * commit , const secp256k1_generator * gen ) {
308
+ secp256k1_ge commitp ;
309
+ secp256k1_ge genp ;
310
+ secp256k1_gej tmpj ;
311
+ secp256k1_gej xj ;
312
+ secp256k1_ge rp ;
313
+ secp256k1_scalar es ;
314
+ secp256k1_scalar ss ;
315
+ secp256k1_sha256 sha2 ;
316
+ unsigned char tmpch [33 ];
317
+ unsigned char pp_comm [32 ];
318
+ size_t offset ;
319
+ size_t sz ;
320
+ int overflow ;
321
+
322
+ VERIFY_CHECK (ctx != NULL );
323
+ ARG_CHECK (proof != NULL );
324
+ ARG_CHECK (commit != NULL );
325
+ ARG_CHECK (gen != NULL );
326
+
327
+ if (plen != 73 && plen != 65 ) {
328
+ return 0 ;
329
+ }
330
+ /* 0x80 must be unset for any rangeproof; 0x40 indicates "has nonzero range"
331
+ * so must also be unset for single-value proofs */
332
+ if ((proof [0 ] & 0xC0 ) != 0x00 ) {
333
+ return 0 ;
334
+ }
335
+
336
+ secp256k1_pedersen_commitment_load (& commitp , commit );
337
+ secp256k1_generator_load (& genp , gen );
338
+ /* Verify that value in the header is what we expect; 0x20 is "has nonzero min-value" */
339
+ if ((proof [0 ] & 0x20 ) == 0x00 ) {
340
+ if (value != 0 ) {
341
+ return 0 ;
342
+ }
343
+ offset = 1 ;
344
+ } else {
345
+ uint64_t claimed = 0 ;
346
+ /* Iterate from 0 to 8, setting `offset` to 9 as a side-effect */
347
+ for (offset = 1 ; offset < 9 ; offset ++ ) {
348
+ claimed = (claimed << 8 ) | proof [offset ];
349
+ }
350
+ if (value != claimed ) {
351
+ return 0 ;
352
+ }
353
+ }
354
+ /* Subtract value from commitment; store modified commitment in xj */
355
+ secp256k1_pedersen_ecmult_small (& tmpj , value , & genp );
356
+ secp256k1_gej_neg (& tmpj , & tmpj );
357
+ secp256k1_gej_add_ge_var (& xj , & tmpj , & commitp , NULL );
358
+
359
+ /* Now we just have a Schnorr signature in (e, s) form. The verification
360
+ * equation is e == H(sG - eX || proof params) */
361
+
362
+ /* 1. Compute slow/overwrought commitment to proof params */
363
+ secp256k1_sha256_initialize (& sha2 );
364
+ secp256k1_rangeproof_serialize_point (tmpch , & commitp );
365
+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
366
+ secp256k1_rangeproof_serialize_point (tmpch , & genp );
367
+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
368
+ secp256k1_sha256_write (& sha2 , proof , offset ); /* lol we commit to one extra byte here */
369
+ secp256k1_sha256_finalize (& sha2 , pp_comm );
370
+
371
+ /* ... feed this into our hash */
372
+ secp256k1_borromean_hash (tmpch , pp_comm , 32 , & proof [offset ], 32 , 0 , 0 );
373
+ secp256k1_scalar_set_b32 (& es , tmpch , & overflow );
374
+ if (overflow || secp256k1_scalar_is_zero (& es )) {
375
+ return 0 ;
376
+ }
377
+
378
+ /* 1. Compute R = sG - eX */
379
+ secp256k1_scalar_set_b32 (& ss , & proof [offset + 32 ], & overflow );
380
+ if (overflow || secp256k1_scalar_is_zero (& ss )) {
381
+ return 0 ;
382
+ }
383
+ secp256k1_ecmult (& tmpj , & xj , & es , & ss );
384
+ if (secp256k1_gej_is_infinity (& tmpj )) {
385
+ return 0 ;
386
+ }
387
+ secp256k1_ge_set_gej (& rp , & tmpj );
388
+ secp256k1_eckey_pubkey_serialize (& rp , tmpch , & sz , 1 );
389
+
390
+ /* 2. Compute e = H(R || proof params) */
391
+ secp256k1_sha256_initialize (& sha2 );
392
+ secp256k1_sha256_write (& sha2 , tmpch , sz );
393
+ secp256k1_sha256_write (& sha2 , pp_comm , sizeof (pp_comm ));
394
+ secp256k1_sha256_finalize (& sha2 , tmpch );
395
+
396
+ /* 3. Check computed e against original e */
397
+ return !memcmp (tmpch , & proof [offset ], 32 );
398
+ }
399
+
307
400
#endif
0 commit comments