@@ -304,4 +304,98 @@ 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
+ int i ;
346
+ uint64_t claimed = 0 ;
347
+ for (i = 0 ; i < 8 ; i ++ ) {
348
+ claimed = (claimed << 8 ) | proof [1 + i ];
349
+ }
350
+ if (value != claimed ) {
351
+ return 0 ;
352
+ }
353
+ offset = 9 ;
354
+ }
355
+ /* Subtract value from commitment; store modified commitment in xj */
356
+ secp256k1_pedersen_ecmult_small (& tmpj , value , & genp );
357
+ secp256k1_gej_neg (& tmpj , & tmpj );
358
+ secp256k1_gej_add_ge_var (& xj , & tmpj , & commitp , NULL );
359
+
360
+ /* Now we just have a Schnorr signature in (e, s) form. The verification
361
+ * equation is e == H(sG - eX || proof params) */
362
+
363
+ /* 1. Compute slow/overwrought commitment to proof params */
364
+ secp256k1_sha256_initialize (& sha2 );
365
+ secp256k1_rangeproof_serialize_point (tmpch , & commitp );
366
+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
367
+ secp256k1_rangeproof_serialize_point (tmpch , & genp );
368
+ secp256k1_sha256_write (& sha2 , tmpch , 33 );
369
+ secp256k1_sha256_write (& sha2 , proof , offset ); /* lol we commit to one extra byte here */
370
+ secp256k1_sha256_finalize (& sha2 , pp_comm );
371
+
372
+ /* ... feed this into our hash */
373
+ secp256k1_borromean_hash (tmpch , pp_comm , 32 , & proof [offset ], 32 , 0 , 0 );
374
+ secp256k1_scalar_set_b32 (& es , tmpch , & overflow );
375
+ if (overflow || secp256k1_scalar_is_zero (& es )) {
376
+ return 0 ;
377
+ }
378
+
379
+ /* 1. Compute R = sG - eX */
380
+ secp256k1_scalar_set_b32 (& ss , & proof [offset + 32 ], & overflow );
381
+ if (overflow || secp256k1_scalar_is_zero (& ss )) {
382
+ return 0 ;
383
+ }
384
+ secp256k1_ecmult (& tmpj , & xj , & es , & ss );
385
+ if (secp256k1_gej_is_infinity (& tmpj )) {
386
+ return 0 ;
387
+ }
388
+ secp256k1_ge_set_gej (& rp , & tmpj );
389
+ secp256k1_eckey_pubkey_serialize (& rp , tmpch , & sz , 1 );
390
+
391
+ /* 2. Compute e = H(R || proof params) */
392
+ secp256k1_sha256_initialize (& sha2 );
393
+ secp256k1_sha256_write (& sha2 , tmpch , sz );
394
+ secp256k1_sha256_write (& sha2 , pp_comm , sizeof (pp_comm ));
395
+ secp256k1_sha256_finalize (& sha2 , tmpch );
396
+
397
+ /* 3. Check computed e against original e */
398
+ return !memcmp (tmpch , & proof [offset ], 32 );
399
+ }
400
+
307
401
#endif
0 commit comments