Skip to content

Commit bcfbbc9

Browse files
committed
rangeproof: add secp256k1_rangeproof_max_size function to estimate rangeproof size
Provides a method that will give an upper bound on the size of a rangeproof, given an upper bound on the value to be passed in and an upper bound on the min_bits parameter. There is a lot of design freedom here since the actual size of the rangeproof depends on every parameter passed to rangeproof_sign, including the value to be proven, often in quite intricate ways. For the sake of simplicity we assume a nonzero `min_value` and that `exp` will be 0 (the default, and size-maximizing, choice), and provide an exact value for a proof of the given value and min_bits.
1 parent 7ff446d commit bcfbbc9

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

include/secp256k1_rangeproof.h

+26
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,32 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info(
287287
size_t plen
288288
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
289289

290+
/** Returns an upper bound on the size of a rangeproof with the given parameters
291+
*
292+
* An actual rangeproof may be smaller, for example if the actual value
293+
* is less than both the provided `max_value` and 2^`min_bits`, or if
294+
* the `exp` parameter to `secp256k1_rangeproof_sign` is set such that
295+
* the proven range is compressed. In particular this function will always
296+
* overestimate the size of single-value proofs. Also, if `min_value`
297+
* is set to 0 in the proof, the result will usually, but not always,
298+
* be 8 bytes smaller than if a nonzero value had been passed.
299+
*
300+
* The goal of this function is to provide a useful upper bound for
301+
* memory allocation or fee estimation purposes, without requiring
302+
* too many parameters be fixed in advance.
303+
*
304+
* To obtain the size of largest possible proof, set `max_value` to UINT64_MAX.
305+
*
306+
* In: ctx: pointer to a context object
307+
* max_value: the maximum value that might be passed for `value` for the proof.
308+
* min_bits: the value that will be passed as `min_bits` for the proof.
309+
*/
310+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT size_t secp256k1_rangeproof_max_size(
311+
const secp256k1_context* ctx,
312+
uint64_t max_value,
313+
int min_bits
314+
) SECP256K1_ARG_NONNULL(1);
315+
290316
# ifdef __cplusplus
291317
}
292318
# endif

src/modules/rangeproof/main_impl.h

+12
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,16 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof
304304
proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp);
305305
}
306306

307+
size_t secp256k1_rangeproof_max_size(const secp256k1_context* ctx, uint64_t max_value, int min_bits) {
308+
const int val_mantissa = max_value > 0 ? 64 - secp256k1_clz64_var(max_value) : 1;
309+
const int mantissa = min_bits > val_mantissa ? min_bits : val_mantissa;
310+
const size_t rings = (mantissa + 1) / 2;
311+
const size_t npubs = rings * 4 - 2 * (mantissa % 2);
312+
313+
VERIFY_CHECK(ctx != NULL);
314+
(void) ctx;
315+
316+
return 10 + 32 * (npubs + rings - 1) + 32 + ((rings - 1 + 7) / 8);
317+
}
318+
307319
#endif

src/modules/rangeproof/tests_impl.h

+11
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c
225225
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0);
226226
CHECK(*ecount == 29);
227227
}
228+
229+
/* This constant is hardcoded in these tests and elsewhere, so we
230+
* consider it to be part of the API and test it here. */
231+
CHECK(secp256k1_rangeproof_max_size(none, 0, 64) == 5134);
232+
CHECK(secp256k1_rangeproof_max_size(none, UINT64_MAX, 0) == 5134);
228233
}
229234

230235
static void test_api(void) {
@@ -431,6 +436,7 @@ static void test_rangeproof(void) {
431436
len = 5134;
432437
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, 0, 0, v, input_message, input_message_len, NULL, 0, secp256k1_generator_h));
433438
CHECK(len <= 5134);
439+
CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0));
434440
mlen = 4096;
435441
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
436442
if (input_message != NULL) {
@@ -447,6 +453,7 @@ static void test_rangeproof(void) {
447453
len = 5134;
448454
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, NULL, 0, secp256k1_generator_h));
449455
CHECK(len <= 73);
456+
CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0));
450457
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
451458
CHECK(secp256k1_memcmp_var(blindout, blind, 32) == 0);
452459
CHECK(vout == v);
@@ -457,6 +464,7 @@ static void test_rangeproof(void) {
457464
len = 5134;
458465
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, v, &commit, blind, commit.data, -1, 64, v, NULL, 0, message_short, sizeof(message_short), secp256k1_generator_h));
459466
CHECK(len <= 73);
467+
CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0));
460468
CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
461469
CHECK(!secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_long, sizeof(message_long), secp256k1_generator_h));
462470
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, NULL, NULL, commit.data, &minv, &maxv, &commit, proof, len, message_short, sizeof(message_short), secp256k1_generator_h));
@@ -472,6 +480,7 @@ static void test_rangeproof(void) {
472480
for (i = 0; i < 19; i++) {
473481
len = 5134;
474482
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, i, 0, v, NULL, 0, NULL, 0, secp256k1_generator_h));
483+
CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 0));
475484
CHECK(secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
476485
CHECK(len <= 5134);
477486
CHECK(minv <= v);
@@ -487,6 +496,7 @@ static void test_rangeproof(void) {
487496
len = 5134;
488497
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, 0, &commit, blind, commit.data, 0, 3, v, NULL, 0, NULL, 0, secp256k1_generator_h));
489498
CHECK(len <= 5134);
499+
CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, 3));
490500
/* Test if trailing bytes are rejected. */
491501
proof[len] = v;
492502
CHECK(!secp256k1_rangeproof_verify(ctx, &minv, &maxv, &commit, proof, len + 1, NULL, 0, secp256k1_generator_h));
@@ -521,6 +531,7 @@ static void test_rangeproof(void) {
521531
}
522532
CHECK(secp256k1_rangeproof_sign(ctx, proof, &len, vmin, &commit, blind, commit.data, exp, min_bits, v, NULL, 0, NULL, 0, secp256k1_generator_h));
523533
CHECK(len <= 5134);
534+
CHECK(len <= secp256k1_rangeproof_max_size(ctx, v, min_bits));
524535
mlen = 4096;
525536
CHECK(secp256k1_rangeproof_rewind(ctx, blindout, &vout, message, &mlen, commit.data, &minv, &maxv, &commit, proof, len, NULL, 0, secp256k1_generator_h));
526537
for (j = 0; j < mlen; j++) {

0 commit comments

Comments
 (0)