Skip to content

Commit

Permalink
rangeproof: add a "net blinding factor" API for Elements
Browse files Browse the repository at this point in the history
Our original API for Confidential Assets transaction balancing was the
single function `secp256k1_pedersen_blind_generator_blind_sum` which
attempts to take a complete list of vbfs and abfs and modifies a single
abf at the end. However this API has a number of shortcomings:

   * it is really confusing
   * it assumes that the user has all the abfs and vbfs in convenient
     arrays, requiring marshalling on the C++ side
   * it does not support partial computations, as are needed by PSET
   * there is no easy/sensible way to extend this API to allow more
     interesting of transaction balancing (e.g. by blinding only an
     asset, leaving the value explicit)

The hope is that by exposing the arithmetic at a more fine-grained
level, these issues will be fixed. These methods can be abused to do
arithmetic on arbitrary scalars, but this is already possible (in an
ugly manner) by using secp256k1_seckey_tweak_add and explicit 0-checks.
  • Loading branch information
apoelstra committed Sep 11, 2022
1 parent d22774e commit 14620d1
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
43 changes: 43 additions & 0 deletions include/secp256k1_rangeproof.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,49 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally(
size_t ncnt
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);

/** Compute the "net blinding factor" for an asset/amount pair of Pedersen commitments
*
* Returns 0 if either input is out of range, otherwise 1
* Args: ctx: a secp256k1 context object.
* Out: output: 32-byte array into which the result will be written
* In: val: the value of the amount commitment
* vbf: the amount commitment's blinding factor
* abf: the asset commitment's blinding factor
*
* This computse val*abf + vbf
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_netbf_compute(
const secp256k1_context* ctx,
unsigned char* output,
uint64_t val,
const unsigned char* vbf,
const unsigned char* abf
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Accumulate a net blinding factor
*
* Returns 0 if the input is out of range, otherwise 1
* Args: ctx: a secp256k1 context object.
* In/Out: acc: initially set to the current state of the accumulator; updated in place
* In: nbf: the net blinding factor to add to the accumulator
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_netbf_acc(
const secp256k1_context* ctx,
unsigned char* acc,
const unsigned char* nbf
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Negate a(n accumulated) net blinding factor
*
* Returns 0 if the input is out of range, otherwise 1
* Args: ctx: a secp256k1 context object.
* In/Out: acc: initially set to the bf to negate; changed to the negated version
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_netbf_neg(
const secp256k1_context* ctx,
unsigned char* output
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);

/** Sets the final Pedersen blinding factor correctly when the generators themselves
* have blinding factors.
*
Expand Down
68 changes: 68 additions & 0 deletions src/modules/rangeproof/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,74 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k
return secp256k1_gej_is_infinity(&accj);
}

int secp256k1_netbf_compute(const secp256k1_context* ctx, unsigned char* output, uint64_t val, const unsigned char* vbf, const unsigned char* abf) {
int overflow = 0;
secp256k1_scalar vbf_s;
secp256k1_scalar abf_s;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(vbf != NULL);
ARG_CHECK(abf != NULL);
(void) ctx;

secp256k1_scalar_set_b32(&abf_s, abf, &overflow);
if (overflow == 1) {
return 0;
}
secp256k1_scalar_set_u64(&vbf_s, val);
secp256k1_scalar_mul(&abf_s, &abf_s, &vbf_s);

secp256k1_scalar_set_b32(&vbf_s, vbf, &overflow);
if (overflow == 1) {
return 0;
}
secp256k1_scalar_add(&vbf_s, &vbf_s, &abf_s);

secp256k1_scalar_get_b32(output, &vbf_s);
return 1;
}

int secp256k1_netbf_acc(const secp256k1_context* ctx, unsigned char* acc, const unsigned char* nbf) {
int overflow = 0;
secp256k1_scalar ret_s;
secp256k1_scalar nbf_s;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(acc != NULL);
ARG_CHECK(nbf != NULL);

secp256k1_scalar_set_b32(&ret_s, acc, &overflow);
if (overflow == 1) {
return 0;
}
secp256k1_scalar_set_b32(&nbf_s, nbf, &overflow);
if (overflow == 1) {
return 0;
}

secp256k1_scalar_add(&ret_s, &ret_s, &nbf_s);
secp256k1_scalar_get_b32(acc, &ret_s);
return 1;
}

int secp256k1_netbf_neg(const secp256k1_context* ctx, unsigned char* acc) {
int overflow = 0;
secp256k1_scalar ret_s;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(acc != NULL);

secp256k1_scalar_set_b32(&ret_s, acc, &overflow);
if (overflow == 1) {
return 0;
}

secp256k1_scalar_negate(&ret_s, &ret_s);
secp256k1_scalar_get_b32(acc, &ret_s);
return 1;
}

int secp256k1_pedersen_blind_generator_blind_sum(const secp256k1_context* ctx, const uint64_t *value, const unsigned char* const* generator_blind, unsigned char* const* blinding_factor, size_t n_total, size_t n_inputs) {
secp256k1_scalar sum;
secp256k1_scalar tmp;
Expand Down
77 changes: 77 additions & 0 deletions src/modules/rangeproof/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,81 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c
CHECK(secp256k1_rangeproof_max_size(none, UINT64_MAX, 0) == 5134);
}

#define N_COMMITS 8
static void test_nbf(const secp256k1_context *none, const int32_t *ecount) {
unsigned char abf[N_COMMITS][32];
unsigned char vbf[N_COMMITS][32];
unsigned char nbf[N_COMMITS][32];
uint64_t val[N_COMMITS];

unsigned char acc_p[32];
unsigned char acc_n[32];
secp256k1_scalar target;

size_t i;
VERIFY_CHECK(N_COMMITS >= 2); /* better to trigger this than to trigger UB */

/* Check that zeros are ok; check NULL checks */
memset(abf[0], 0x00, sizeof(abf[0]));
memset(vbf[0], 0x00, sizeof(abf[0]));
val[0] = 0;

CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[0], abf[0]));
CHECK(secp256k1_memcmp_var(nbf[0], vbf[0], sizeof(nbf[0])) == 0);
CHECK(*ecount == 0);
CHECK(secp256k1_netbf_compute(none, NULL, val[0], vbf[0], abf[0]) == 0);
CHECK(*ecount == 1);
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], NULL, abf[0]) == 0);
CHECK(*ecount == 2);
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[0], NULL) == 0);
CHECK(*ecount == 3);

memset(vbf[1], 0xff, sizeof(vbf[1])); /* out of range */
memset(abf[1], 0xff, sizeof(abf[1])); /* out of range */
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[0], abf[1]) == 0);
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[1], abf[0]) == 0);
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[1], abf[1]) == 0);
CHECK(*ecount == 3); /* not API errors */

memset(acc_p, 0x00, sizeof(acc_p)); /* FIXME should we expose a nbf_clear method for this? */
memset(acc_n, 0x00, sizeof(acc_n));
secp256k1_scalar_clear(&target);
for (i = 0; i < N_COMMITS; i++) {
val[i] = ((uint64_t) secp256k1_testrand32() << 32) + secp256k1_testrand32();
/* nb these theoretically could go out of range but not in this universe's lifetime */
secp256k1_testrand256_test(abf[i]);
secp256k1_testrand256_test(vbf[i]);
CHECK(secp256k1_netbf_compute(none, nbf[i], val[i], vbf[i], abf[i]));

if (secp256k1_testrand32() & 1) {
int overflow;
secp256k1_scalar tmps;
/* positive */
CHECK(secp256k1_netbf_acc(none, acc_p, nbf[i]));

secp256k1_scalar_set_b32(&tmps, nbf[i], &overflow);
CHECK(overflow == 0); /* actually unreachable, not just cryptographically */
secp256k1_scalar_add(&target, &target, &tmps);
} else {
int overflow;
secp256k1_scalar tmps;
/* negative */
CHECK(secp256k1_netbf_acc(none, acc_n, nbf[i]));

secp256k1_scalar_set_b32(&tmps, nbf[i], &overflow);
CHECK(overflow == 0); /* actually unreachable, not just cryptographically */
secp256k1_scalar_negate(&tmps, &tmps);
secp256k1_scalar_add(&target, &target, &tmps);
}
}
CHECK(secp256k1_netbf_neg(none, acc_n));
CHECK(secp256k1_netbf_acc(none, acc_p, acc_n));

secp256k1_scalar_get_b32(acc_n, &target);
CHECK(secp256k1_memcmp_var(acc_p, acc_n, sizeof(acc_p)) == 0);
}
#undef N_COMMITS

static void test_api(void) {
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
Expand All @@ -257,6 +332,8 @@ static void test_api(void) {
test_pedersen_api(none, sign, vrfy, sttc, &ecount);
ecount = 0;
test_rangeproof_api(none, sign, vrfy, both, sttc, &ecount);
ecount = 0;
test_nbf(none, &ecount);
}

secp256k1_context_destroy(none);
Expand Down

0 comments on commit 14620d1

Please sign in to comment.