Skip to content

Commit 63e41d6

Browse files
committed
fix: resolve zero-value proof failure in tests
Updated the commitment logic to safely skip empty curve points (Point at Infinity) generated by all-zero bit vectors, rather than treating them as a fatal library error.
1 parent 81e531e commit 63e41d6

File tree

2 files changed

+104
-125
lines changed

2 files changed

+104
-125
lines changed

src/bulletproof_aggregated.c

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,20 +1220,49 @@ int secp256k1_bulletproof_prove_agg(
12201220
{
12211221
secp256k1_pubkey tG, tH, tB;
12221222
const secp256k1_pubkey* pts[3];
1223+
int n_pts = 0;
12231224

1224-
if (!secp256k1_bulletproof_ipa_msm(ctx, &tG, G_vec, al, n)) goto cleanup;
1225-
if (!secp256k1_bulletproof_ipa_msm(ctx, &tH, H_vec, ar, n)) goto cleanup;
1225+
/* A Calculation */
1226+
/* 1. alpha * Base (Always exists) */
12261227
tB = *pk_base;
12271228
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tB, alpha)) goto cleanup;
1228-
pts[0] = &tB; pts[1] = &tG; pts[2] = &tH;
1229-
if (!secp256k1_ec_pubkey_combine(ctx, &A, pts, 3)) goto cleanup;
1229+
pts[n_pts++] = &tB;
12301230

1231-
if (!secp256k1_bulletproof_ipa_msm(ctx, &tG, G_vec, sl, n)) goto cleanup;
1232-
if (!secp256k1_bulletproof_ipa_msm(ctx, &tH, H_vec, sr, n)) goto cleanup;
1231+
/* 2. <al, G> (Only add if al is non-zero) */
1232+
/* Note: ipa_msm returns 0 if result is infinity (all scalars 0). This is valid for value=0. */
1233+
if (secp256k1_bulletproof_ipa_msm(ctx, &tG, G_vec, al, n)) {
1234+
pts[n_pts++] = &tG;
1235+
}
1236+
1237+
/* 3. <ar, H> (Only add if ar is non-zero) */
1238+
if (secp256k1_bulletproof_ipa_msm(ctx, &tH, H_vec, ar, n)) {
1239+
pts[n_pts++] = &tH;
1240+
}
1241+
1242+
/* Combine valid points for A */
1243+
if (!secp256k1_ec_pubkey_combine(ctx, &A, pts, n_pts)) goto cleanup;
1244+
1245+
1246+
/* S Calculation (Follows same logic for consistency) */
1247+
n_pts = 0;
1248+
1249+
/* 1. rho * Base */
12331250
tB = *pk_base;
12341251
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tB, rho)) goto cleanup;
1235-
pts[0] = &tB; pts[1] = &tG; pts[2] = &tH;
1236-
if (!secp256k1_ec_pubkey_combine(ctx, &S, pts, 3)) goto cleanup;
1252+
pts[n_pts++] = &tB;
1253+
1254+
/* 2. <sl, G> */
1255+
if (secp256k1_bulletproof_ipa_msm(ctx, &tG, G_vec, sl, n)) {
1256+
pts[n_pts++] = &tG;
1257+
}
1258+
1259+
/* 3. <sr, H> */
1260+
if (secp256k1_bulletproof_ipa_msm(ctx, &tH, H_vec, sr, n)) {
1261+
pts[n_pts++] = &tH;
1262+
}
1263+
1264+
/* Combine valid points for S */
1265+
if (!secp256k1_ec_pubkey_combine(ctx, &S, pts, n_pts)) goto cleanup;
12371266
}
12381267

12391268
/* ---- 6. Fiat–Shamir y,z ---- */

tests/test_bulletproof_agg.c

Lines changed: 67 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -6,184 +6,134 @@
66
#include "secp256k1_mpt.h"
77
#include "test_utils.h"
88

9-
/* ---- Aggregation parameters ---- */
10-
#define M 2
119
#define BP_VALUE_BITS 64
1210
#define BP_TOTAL_BITS(m) ((size_t)(BP_VALUE_BITS * (m)))
13-
14-
/* ---- Benchmark parameters ---- */
1511
#define VERIFY_RUNS 5
1612

17-
1813
/* ---- Helpers ---- */
19-
2014
static inline double elapsed_ms(struct timespec a, struct timespec b) {
2115
return (b.tv_sec - a.tv_sec) * 1000.0 +
2216
(b.tv_nsec - a.tv_nsec) / 1e6;
2317
}
2418

25-
/* ---- Main ---- */
26-
int main(void) {
27-
printf("[TEST] Aggregated Bulletproof test (m = %d)\n", M);
19+
/* ---- Core Test Logic ---- */
20+
void run_test_case(secp256k1_context* ctx, const char* name, uint64_t* values, size_t m, int run_benchmarks) {
21+
printf("\n[TEST] %s (m = %zu)\n", name, m);
2822

29-
/* ---- Context ---- */
30-
secp256k1_context* ctx =
31-
secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
32-
EXPECT(ctx != NULL);
33-
34-
/* ---- Values ---- */
35-
uint64_t values[M] = { 5000, 123456 };
36-
unsigned char blindings[M][32];
37-
secp256k1_pubkey commitments[M];
38-
39-
/* ---- Context Binding ---- */
23+
unsigned char blindings[m][32];
24+
secp256k1_pubkey commitments[m];
4025
unsigned char context_id[32];
4126
EXPECT(RAND_bytes(context_id, 32) == 1);
4227

4328
secp256k1_pubkey pk_base;
44-
/* Use the standard H generator from the library */
4529
EXPECT(secp256k1_mpt_get_h_generator(ctx, &pk_base));
4630

4731
/* ---- Commitments ---- */
48-
for (size_t i = 0; i < M; i++) {
32+
for (size_t i = 0; i < m; i++) {
4933
random_scalar(ctx, blindings[i]);
5034
EXPECT(secp256k1_bulletproof_create_commitment(
51-
ctx,
52-
&commitments[i],
53-
values[i],
54-
blindings[i],
55-
&pk_base));
35+
ctx, &commitments[i], values[i], blindings[i], &pk_base));
5636
}
5737

5838
/* ---- Generator vectors ---- */
59-
const size_t n = BP_TOTAL_BITS(M);
39+
const size_t n = BP_TOTAL_BITS(m);
6040
secp256k1_pubkey* G_vec = malloc(n * sizeof(secp256k1_pubkey));
6141
secp256k1_pubkey* H_vec = malloc(n * sizeof(secp256k1_pubkey));
6242
EXPECT(G_vec && H_vec);
6343

64-
EXPECT(secp256k1_mpt_get_generator_vector(
65-
ctx, G_vec, n, (const unsigned char*)"G", 1));
66-
EXPECT(secp256k1_mpt_get_generator_vector(
67-
ctx, H_vec, n, (const unsigned char*)"H", 1));
44+
EXPECT(secp256k1_mpt_get_generator_vector(ctx, G_vec, n, (const unsigned char*)"G", 1));
45+
EXPECT(secp256k1_mpt_get_generator_vector(ctx, H_vec, n, (const unsigned char*)"H", 1));
6846

69-
/* ---- Prove (timed) ---- */
47+
/* ---- Prove ---- */
7048
unsigned char proof[4096];
7149
size_t proof_len = sizeof(proof);
7250

73-
printf("[TEST] Proving aggregated range proof...\n");
74-
7551
struct timespec t_p_start, t_p_end;
7652
clock_gettime(CLOCK_MONOTONIC, &t_p_start);
7753

78-
/* Note: We cast the 2D array 'blindings' to flat pointer */
7954
EXPECT(secp256k1_bulletproof_prove_agg(
80-
ctx,
81-
proof,
82-
&proof_len,
83-
values,
84-
(const unsigned char*)blindings,
85-
M,
86-
&pk_base,
87-
context_id));
55+
ctx, proof, &proof_len, values, (const unsigned char*)blindings, m, &pk_base, context_id));
8856

8957
clock_gettime(CLOCK_MONOTONIC, &t_p_end);
58+
printf(" Proof size: %zu bytes\n", proof_len);
59+
if (run_benchmarks) printf(" [BENCH] Proving time: %.3f ms\n", elapsed_ms(t_p_start, t_p_end));
9060

91-
printf("[TEST] Proof size = %zu bytes\n", proof_len);
92-
printf("[BENCH] Proving time: %.3f ms\n",
93-
elapsed_ms(t_p_start, t_p_end));
94-
95-
/* ---- Verify (single run, timed) ---- */
96-
printf("[TEST] Verifying aggregated proof...\n");
97-
61+
/* ---- Verify ---- */
9862
struct timespec t_v_start, t_v_end;
9963
clock_gettime(CLOCK_MONOTONIC, &t_v_start);
10064

10165
int ok = secp256k1_bulletproof_verify_agg(
102-
ctx,
103-
G_vec,
104-
H_vec,
105-
proof,
106-
proof_len,
107-
commitments,
108-
M,
109-
&pk_base,
110-
context_id);
66+
ctx, G_vec, H_vec, proof, proof_len, commitments, m, &pk_base, context_id);
11167

11268
clock_gettime(CLOCK_MONOTONIC, &t_v_end);
113-
11469
EXPECT(ok);
115-
116-
printf("PASSED\n");
117-
printf("[BENCH] Verification time (single): %.3f ms\n",
118-
elapsed_ms(t_v_start, t_v_end));
119-
120-
/* ---- Verify benchmark (average) ---- */
121-
double total_ms = 0.0;
122-
123-
for (int i = 0; i < VERIFY_RUNS; i++) {
124-
struct timespec ts, te;
125-
clock_gettime(CLOCK_MONOTONIC, &ts);
126-
127-
ok = secp256k1_bulletproof_verify_agg(
128-
ctx,
129-
G_vec,
130-
H_vec,
131-
proof,
132-
proof_len,
133-
commitments,
134-
M,
135-
&pk_base,
136-
context_id);
137-
138-
clock_gettime(CLOCK_MONOTONIC, &te);
139-
140-
EXPECT(ok);
141-
total_ms += elapsed_ms(ts, te);
70+
printf(" PASSED (Verification)\n");
71+
if (run_benchmarks) printf(" [BENCH] Verification time: %.3f ms\n", elapsed_ms(t_v_start, t_v_end));
72+
73+
/* ---- Benchmark Verify ---- */
74+
if (run_benchmarks) {
75+
double total_ms = 0.0;
76+
for (int i = 0; i < VERIFY_RUNS; i++) {
77+
struct timespec ts, te;
78+
clock_gettime(CLOCK_MONOTONIC, &ts);
79+
ok = secp256k1_bulletproof_verify_agg(
80+
ctx, G_vec, H_vec, proof, proof_len, commitments, m, &pk_base, context_id);
81+
clock_gettime(CLOCK_MONOTONIC, &te);
82+
EXPECT(ok);
83+
total_ms += elapsed_ms(ts, te);
84+
}
85+
printf(" [BENCH] Verification avg over %d runs: %.3f ms\n", VERIFY_RUNS, total_ms / VERIFY_RUNS);
14286
}
14387

144-
printf("[BENCH] Verification avg over %d runs: %.3f ms\n",
145-
VERIFY_RUNS, total_ms / VERIFY_RUNS);
146-
147-
/* ---- Negative test ---- */
148-
printf("[TEST] Tamper test... ");
149-
150-
secp256k1_pubkey bad_commitments[M];
151-
memcpy(bad_commitments, commitments, sizeof(commitments));
152-
88+
/* ---- Negative Test (Tamper) ---- */
89+
secp256k1_pubkey bad_commitments[m];
90+
memcpy(bad_commitments, commitments, sizeof(secp256k1_pubkey) * m);
15391
unsigned char bad_blinding[32];
15492
random_scalar(ctx, bad_blinding);
15593

156-
/* Create a fake commitment to (value + 1) to break the sum */
94+
/* Create fake commitment to (value + 1) */
15795
EXPECT(secp256k1_bulletproof_create_commitment(
158-
ctx,
159-
&bad_commitments[1],
160-
values[M - 1] + 1,
161-
bad_blinding,
162-
&pk_base));
96+
ctx, &bad_commitments[m - 1], values[m - 1] + 1, bad_blinding, &pk_base));
16397

16498
ok = secp256k1_bulletproof_verify_agg(
165-
ctx,
166-
G_vec,
167-
H_vec,
168-
proof,
169-
proof_len,
170-
bad_commitments,
171-
M,
172-
&pk_base,
173-
context_id);
99+
ctx, G_vec, H_vec, proof, proof_len, bad_commitments, m, &pk_base, context_id);
174100

175101
if (ok) {
176102
fprintf(stderr, "FAILED: Accepted invalid proof!\n");
177103
exit(EXIT_FAILURE);
178104
}
105+
printf(" PASSED (Rejected invalid proof)\n");
179106

180-
printf("PASSED (rejected invalid proof)\n");
181-
182-
/* ---- Cleanup ---- */
183107
free(G_vec);
184108
free(H_vec);
185-
secp256k1_context_destroy(ctx);
109+
}
186110

187-
printf("[TEST] Aggregated Bulletproof test completed successfully\n");
111+
/* ---- Main ---- */
112+
int main(void) {
113+
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
114+
EXPECT(ctx != NULL);
115+
116+
/* 1. Single proof, value 0 (The Bug Fix) */
117+
uint64_t v1[] = {0};
118+
run_test_case(ctx, "Single Proof (Value 0)", v1, 1, 0);
119+
120+
/* 2. Single proof, value 1 */
121+
uint64_t v2[] = {1};
122+
run_test_case(ctx, "Single Proof (Value 1)", v2, 1, 0);
123+
124+
/* 3. Single proof, MAX VALUE (Tests opposite vector edge case) */
125+
uint64_t v3[] = {0xFFFFFFFFFFFFFFFF}; // 2^64 - 1
126+
run_test_case(ctx, "Single Proof (MAX Value)", v3, 1, 0);
127+
128+
/* 4. Aggregated proof, {0, 1} */
129+
uint64_t v4[] = {0, 1};
130+
run_test_case(ctx, "Aggregated Proof (0, 1)", v4, 2, 0);
131+
132+
/* 5. Aggregated proof, {0, 0} with Benchmarks */
133+
uint64_t v5[] = {0, 0};
134+
run_test_case(ctx, "Aggregated Proof (0, 0)", v5, 2, 1);
135+
136+
secp256k1_context_destroy(ctx);
137+
printf("\n[TEST] All Bulletproof tests completed successfully\n");
188138
return 0;
189139
}

0 commit comments

Comments
 (0)