Skip to content

Commit 14620d1

Browse files
committed
rangeproof: add a "net blinding factor" API for Elements
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.
1 parent d22774e commit 14620d1

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

include/secp256k1_rangeproof.h

+43
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,49 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally(
128128
size_t ncnt
129129
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
130130

131+
/** Compute the "net blinding factor" for an asset/amount pair of Pedersen commitments
132+
*
133+
* Returns 0 if either input is out of range, otherwise 1
134+
* Args: ctx: a secp256k1 context object.
135+
* Out: output: 32-byte array into which the result will be written
136+
* In: val: the value of the amount commitment
137+
* vbf: the amount commitment's blinding factor
138+
* abf: the asset commitment's blinding factor
139+
*
140+
* This computse val*abf + vbf
141+
*/
142+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_netbf_compute(
143+
const secp256k1_context* ctx,
144+
unsigned char* output,
145+
uint64_t val,
146+
const unsigned char* vbf,
147+
const unsigned char* abf
148+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
149+
150+
/** Accumulate a net blinding factor
151+
*
152+
* Returns 0 if the input is out of range, otherwise 1
153+
* Args: ctx: a secp256k1 context object.
154+
* In/Out: acc: initially set to the current state of the accumulator; updated in place
155+
* In: nbf: the net blinding factor to add to the accumulator
156+
*/
157+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_netbf_acc(
158+
const secp256k1_context* ctx,
159+
unsigned char* acc,
160+
const unsigned char* nbf
161+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
162+
163+
/** Negate a(n accumulated) net blinding factor
164+
*
165+
* Returns 0 if the input is out of range, otherwise 1
166+
* Args: ctx: a secp256k1 context object.
167+
* In/Out: acc: initially set to the bf to negate; changed to the negated version
168+
*/
169+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_netbf_neg(
170+
const secp256k1_context* ctx,
171+
unsigned char* output
172+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
173+
131174
/** Sets the final Pedersen blinding factor correctly when the generators themselves
132175
* have blinding factors.
133176
*

src/modules/rangeproof/main_impl.h

+68
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,74 @@ int secp256k1_pedersen_verify_tally(const secp256k1_context* ctx, const secp256k
165165
return secp256k1_gej_is_infinity(&accj);
166166
}
167167

168+
int secp256k1_netbf_compute(const secp256k1_context* ctx, unsigned char* output, uint64_t val, const unsigned char* vbf, const unsigned char* abf) {
169+
int overflow = 0;
170+
secp256k1_scalar vbf_s;
171+
secp256k1_scalar abf_s;
172+
173+
VERIFY_CHECK(ctx != NULL);
174+
ARG_CHECK(output != NULL);
175+
ARG_CHECK(vbf != NULL);
176+
ARG_CHECK(abf != NULL);
177+
(void) ctx;
178+
179+
secp256k1_scalar_set_b32(&abf_s, abf, &overflow);
180+
if (overflow == 1) {
181+
return 0;
182+
}
183+
secp256k1_scalar_set_u64(&vbf_s, val);
184+
secp256k1_scalar_mul(&abf_s, &abf_s, &vbf_s);
185+
186+
secp256k1_scalar_set_b32(&vbf_s, vbf, &overflow);
187+
if (overflow == 1) {
188+
return 0;
189+
}
190+
secp256k1_scalar_add(&vbf_s, &vbf_s, &abf_s);
191+
192+
secp256k1_scalar_get_b32(output, &vbf_s);
193+
return 1;
194+
}
195+
196+
int secp256k1_netbf_acc(const secp256k1_context* ctx, unsigned char* acc, const unsigned char* nbf) {
197+
int overflow = 0;
198+
secp256k1_scalar ret_s;
199+
secp256k1_scalar nbf_s;
200+
201+
VERIFY_CHECK(ctx != NULL);
202+
ARG_CHECK(acc != NULL);
203+
ARG_CHECK(nbf != NULL);
204+
205+
secp256k1_scalar_set_b32(&ret_s, acc, &overflow);
206+
if (overflow == 1) {
207+
return 0;
208+
}
209+
secp256k1_scalar_set_b32(&nbf_s, nbf, &overflow);
210+
if (overflow == 1) {
211+
return 0;
212+
}
213+
214+
secp256k1_scalar_add(&ret_s, &ret_s, &nbf_s);
215+
secp256k1_scalar_get_b32(acc, &ret_s);
216+
return 1;
217+
}
218+
219+
int secp256k1_netbf_neg(const secp256k1_context* ctx, unsigned char* acc) {
220+
int overflow = 0;
221+
secp256k1_scalar ret_s;
222+
223+
VERIFY_CHECK(ctx != NULL);
224+
ARG_CHECK(acc != NULL);
225+
226+
secp256k1_scalar_set_b32(&ret_s, acc, &overflow);
227+
if (overflow == 1) {
228+
return 0;
229+
}
230+
231+
secp256k1_scalar_negate(&ret_s, &ret_s);
232+
secp256k1_scalar_get_b32(acc, &ret_s);
233+
return 1;
234+
}
235+
168236
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) {
169237
secp256k1_scalar sum;
170238
secp256k1_scalar tmp;

src/modules/rangeproof/tests_impl.h

+77
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,81 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c
232232
CHECK(secp256k1_rangeproof_max_size(none, UINT64_MAX, 0) == 5134);
233233
}
234234

235+
#define N_COMMITS 8
236+
static void test_nbf(const secp256k1_context *none, const int32_t *ecount) {
237+
unsigned char abf[N_COMMITS][32];
238+
unsigned char vbf[N_COMMITS][32];
239+
unsigned char nbf[N_COMMITS][32];
240+
uint64_t val[N_COMMITS];
241+
242+
unsigned char acc_p[32];
243+
unsigned char acc_n[32];
244+
secp256k1_scalar target;
245+
246+
size_t i;
247+
VERIFY_CHECK(N_COMMITS >= 2); /* better to trigger this than to trigger UB */
248+
249+
/* Check that zeros are ok; check NULL checks */
250+
memset(abf[0], 0x00, sizeof(abf[0]));
251+
memset(vbf[0], 0x00, sizeof(abf[0]));
252+
val[0] = 0;
253+
254+
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[0], abf[0]));
255+
CHECK(secp256k1_memcmp_var(nbf[0], vbf[0], sizeof(nbf[0])) == 0);
256+
CHECK(*ecount == 0);
257+
CHECK(secp256k1_netbf_compute(none, NULL, val[0], vbf[0], abf[0]) == 0);
258+
CHECK(*ecount == 1);
259+
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], NULL, abf[0]) == 0);
260+
CHECK(*ecount == 2);
261+
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[0], NULL) == 0);
262+
CHECK(*ecount == 3);
263+
264+
memset(vbf[1], 0xff, sizeof(vbf[1])); /* out of range */
265+
memset(abf[1], 0xff, sizeof(abf[1])); /* out of range */
266+
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[0], abf[1]) == 0);
267+
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[1], abf[0]) == 0);
268+
CHECK(secp256k1_netbf_compute(none, nbf[0], val[0], vbf[1], abf[1]) == 0);
269+
CHECK(*ecount == 3); /* not API errors */
270+
271+
memset(acc_p, 0x00, sizeof(acc_p)); /* FIXME should we expose a nbf_clear method for this? */
272+
memset(acc_n, 0x00, sizeof(acc_n));
273+
secp256k1_scalar_clear(&target);
274+
for (i = 0; i < N_COMMITS; i++) {
275+
val[i] = ((uint64_t) secp256k1_testrand32() << 32) + secp256k1_testrand32();
276+
/* nb these theoretically could go out of range but not in this universe's lifetime */
277+
secp256k1_testrand256_test(abf[i]);
278+
secp256k1_testrand256_test(vbf[i]);
279+
CHECK(secp256k1_netbf_compute(none, nbf[i], val[i], vbf[i], abf[i]));
280+
281+
if (secp256k1_testrand32() & 1) {
282+
int overflow;
283+
secp256k1_scalar tmps;
284+
/* positive */
285+
CHECK(secp256k1_netbf_acc(none, acc_p, nbf[i]));
286+
287+
secp256k1_scalar_set_b32(&tmps, nbf[i], &overflow);
288+
CHECK(overflow == 0); /* actually unreachable, not just cryptographically */
289+
secp256k1_scalar_add(&target, &target, &tmps);
290+
} else {
291+
int overflow;
292+
secp256k1_scalar tmps;
293+
/* negative */
294+
CHECK(secp256k1_netbf_acc(none, acc_n, nbf[i]));
295+
296+
secp256k1_scalar_set_b32(&tmps, nbf[i], &overflow);
297+
CHECK(overflow == 0); /* actually unreachable, not just cryptographically */
298+
secp256k1_scalar_negate(&tmps, &tmps);
299+
secp256k1_scalar_add(&target, &target, &tmps);
300+
}
301+
}
302+
CHECK(secp256k1_netbf_neg(none, acc_n));
303+
CHECK(secp256k1_netbf_acc(none, acc_p, acc_n));
304+
305+
secp256k1_scalar_get_b32(acc_n, &target);
306+
CHECK(secp256k1_memcmp_var(acc_p, acc_n, sizeof(acc_p)) == 0);
307+
}
308+
#undef N_COMMITS
309+
235310
static void test_api(void) {
236311
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
237312
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
@@ -257,6 +332,8 @@ static void test_api(void) {
257332
test_pedersen_api(none, sign, vrfy, sttc, &ecount);
258333
ecount = 0;
259334
test_rangeproof_api(none, sign, vrfy, both, sttc, &ecount);
335+
ecount = 0;
336+
test_nbf(none, &ecount);
260337
}
261338

262339
secp256k1_context_destroy(none);

0 commit comments

Comments
 (0)