Skip to content

Commit ed10948

Browse files
committed
add tests for creating and verifying proofs
- in examples/silentpayments.c - sender now generates outputs with proof and verifies the proof - proof can be serialised to bytes and sent to recipient - bytes can be parsed back to proof by recipient as well - recipient can verify proof - in tests_recipients_helper, run_silentpayments_test_vector_send - along with output checks, generate DLEQ proofs and verify them
1 parent c70d0e5 commit ed10948

File tree

2 files changed

+140
-15
lines changed

2 files changed

+140
-15
lines changed

examples/silentpayments.c

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#define N_INPUTS 2
2020
#define N_OUTPUTS 3
21+
#define N_RECIPIENTS 2
2122

2223
/* Static data for Bob and Carol's silent payment addresses */
2324
static unsigned char smallest_outpoint[36] = {
@@ -127,6 +128,7 @@ int main(void) {
127128
secp256k1_xonly_pubkey *tx_output_ptrs[N_OUTPUTS];
128129
int ret;
129130
size_t i;
131+
unsigned char dleq_proof[N_RECIPIENTS][64];
130132

131133
/* Before we can call actual API functions, we need to create a "context" */
132134
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
@@ -229,18 +231,66 @@ int main(void) {
229231
for (i = 0; i < N_OUTPUTS; i++) {
230232
tx_output_ptrs[i] = &tx_outputs[i];
231233
}
232-
ret = secp256k1_silentpayments_sender_create_outputs(ctx,
233-
tx_output_ptrs,
234-
recipient_ptrs, N_OUTPUTS,
235-
smallest_outpoint,
236-
sender_keypair_ptrs, N_INPUTS,
237-
NULL, 0
238-
);
239-
if (!ret) {
240-
printf("Something went wrong, try again with different inputs.\n");
241-
return EXIT_FAILURE;
234+
235+
/* Sender can perform 1 of the following options:
236+
* Option 1: generate outputs without DLEQ proofs
237+
ret = secp256k1_silentpayments_sender_create_outputs(ctx,
238+
tx_output_ptrs,
239+
recipient_ptrs, N_OUTPUTS,
240+
smallest_outpoint,
241+
sender_keypair_ptrs, N_INPUTS,
242+
NULL, 0
243+
);
244+
if (!ret) {
245+
printf("Something went wrong, try again with different inputs.\n");
246+
return EXIT_FAILURE;
247+
}
248+
*/
249+
{
250+
/* Option 2: generate outputs with DLEQ proofs*/
251+
secp256k1_silentpayments_prevouts_summary prevouts_summary;
252+
size_t n_dleq_size;
253+
secp256k1_silentpayments_dleq_data dleq_data[N_RECIPIENTS];
254+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[N_RECIPIENTS];
255+
for (i = 0; i < N_RECIPIENTS; i++) {
256+
dleq_data_ptrs[i] = &dleq_data[i];
257+
}
258+
for (i = 0; i < N_INPUTS; i++) {
259+
tx_input_ptrs[i] = &tx_inputs[i];
260+
}
261+
ret = secp256k1_silentpayments_recipient_prevouts_summary_create(ctx, &prevouts_summary, smallest_outpoint,
262+
tx_input_ptrs, N_INPUTS, NULL, 0);
263+
assert(ret);
264+
265+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(ctx,
266+
tx_output_ptrs, dleq_data_ptrs, &n_dleq_size,
267+
recipient_ptrs, N_OUTPUTS,
268+
smallest_outpoint,
269+
sender_keypair_ptrs, N_INPUTS,
270+
NULL, 0
271+
);
272+
assert(n_dleq_size == N_RECIPIENTS);
273+
assert(ret);
274+
/* Ensure that outputs are generated correctly at the sender side by verifying the DLEQ proof */
275+
for (i = 0; i < N_RECIPIENTS; i++) {
276+
/* Serialized form of proof can be sent from 1 sender side device to another sender side device.
277+
* ex: hardware wallet (which can do ECDH + proof calculation) to wallet application. */
278+
unsigned char ss_proof_index_bytes[33 + 64 + 4];
279+
secp256k1_silentpayments_dleq_data data;
280+
secp256k1_silentpayments_dleq_data_serialize(ss_proof_index_bytes, &dleq_data[i]);
281+
/* Parse the serialized proof on the second device. (ex: wallet application) */
282+
secp256k1_silentpayments_dleq_data_parse(&data, ss_proof_index_bytes);
283+
/* Proof verification can be done on the second device. */
284+
ret = secp256k1_silentpayments_verify_proof(ctx, data.shared_secret, data.proof,
285+
&recipients[data.index].scan_pubkey,
286+
&prevouts_summary);
287+
assert(ret);
288+
/* Store proof to send to different receivers (Bob, Carol) later. */
289+
memcpy(dleq_proof[i], ss_proof_index_bytes + 33, 64);
290+
}
242291
}
243-
printf("Alice created the following outputs for Bob and Carol:\n");
292+
293+
printf("Alice created the following outputs for Bob and Carol: \n");
244294
for (i = 0; i < N_OUTPUTS; i++) {
245295
printf(" ");
246296
ret = secp256k1_xonly_pubkey_serialize(ctx,
@@ -481,6 +531,25 @@ int main(void) {
481531
} else {
482532
printf("Bob did not find any outputs in this transaction.\n");
483533
}
534+
{
535+
/* Optionally, Bob can use DLEQ proof to prove ownership of his address without revealing private keys
536+
* DLEQ proof verification needs proof, input pubkey sum, Bob's scan pubkey and shared secret as inputs. */
537+
unsigned char shared_secret[33];
538+
secp256k1_pubkey scan_pubkey;
539+
/* 1. Get Bob's scan pubkey */
540+
ret = secp256k1_ec_pubkey_parse(ctx, &scan_pubkey, bob_address[0], 33);
541+
assert(ret);
542+
/* 2. Compute input pubkey sum */
543+
ret = secp256k1_silentpayments_recipient_prevouts_summary_serialize(ctx, light_client_data33, &prevouts_summary);
544+
assert(ret);
545+
/* 3. Bob computes shared secret */
546+
ret = secp256k1_silentpayments_recipient_create_shared_secret(ctx, shared_secret, bob_scan_key,
547+
&prevouts_summary);
548+
assert(ret);
549+
/* 4. Use proof we obtained from Alice for verification */
550+
ret &= secp256k1_silentpayments_verify_proof(ctx, shared_secret, dleq_proof[0], &scan_pubkey, &prevouts_summary);
551+
assert(ret);
552+
}
484553
}
485554
{
486555
/*** Scanning as a light client (Carol) ***
@@ -608,6 +677,18 @@ int main(void) {
608677
} else {
609678
printf("Carol did not find any outputs in this transaction.\n");
610679
}
680+
{
681+
/* Optionally, Carol can use DLEQ proof to prove ownership of her address without revealing private keys
682+
* DLEQ proof verification needs proof, input pubkey sum, Carol's scan pubkey and shared secret as inputs. */
683+
/* 1. Get Carol's scan pubkey */
684+
secp256k1_pubkey scan_pubkey;
685+
ret = secp256k1_ec_pubkey_parse(ctx, &scan_pubkey, carol_address[0], 33);
686+
assert(ret);
687+
/* 2. Input pubkey sum and shared secret already computed at this point, so verify_proof directly */
688+
/* 3. Use proof we obtained from Alice for verification */
689+
ret &= secp256k1_silentpayments_verify_proof(ctx, shared_secret, dleq_proof[1], &scan_pubkey, &prevouts_summary);
690+
assert(ret);
691+
}
611692
}
612693
}
613694

src/modules/silentpayments/tests_impl.h

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "../../../src/modules/silentpayments/vectors.h"
1212
#include "../../../src/modules/silentpayments/dleq_vectors.h"
1313
#include "../../../include/secp256k1.h"
14+
#include <assert.h>
1415

1516
/** Constants
1617
*
@@ -132,6 +133,9 @@ static void test_recipient_sort_helper(unsigned char (*sp_addresses[3])[2][33],
132133
const secp256k1_silentpayments_recipient *recipient_ptrs[3];
133134
secp256k1_xonly_pubkey generated_outputs[3];
134135
secp256k1_xonly_pubkey *generated_output_ptrs[3];
136+
secp256k1_silentpayments_dleq_data dleq_data[2];
137+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[2];
138+
size_t n_dleq_size;
135139
unsigned char xonly_ser[32];
136140
size_t i;
137141
int ret;
@@ -143,15 +147,32 @@ static void test_recipient_sort_helper(unsigned char (*sp_addresses[3])[2][33],
143147
recipients[i].index = i;
144148
recipient_ptrs[i] = &recipients[i];
145149
generated_output_ptrs[i] = &generated_outputs[i];
150+
if (i != 2){
151+
dleq_data_ptrs[i] = &dleq_data[i];
152+
}
146153
}
147-
ret = secp256k1_silentpayments_sender_create_outputs(CTX,
148-
generated_output_ptrs,
154+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(CTX,
155+
generated_output_ptrs, dleq_data_ptrs, &n_dleq_size,
149156
recipient_ptrs, 3,
150157
SMALLEST_OUTPOINT,
151158
NULL, 0,
152159
seckey_ptrs, 1
153160
);
154161
CHECK(ret == 1);
162+
{
163+
secp256k1_pubkey pk;
164+
secp256k1_xonly_pubkey xonly_pk;
165+
secp256k1_silentpayments_prevouts_summary prevouts_summary;
166+
const secp256k1_xonly_pubkey *tx_input_ptr = &xonly_pk;
167+
CHECK(secp256k1_ec_pubkey_create(CTX, &pk, seckey_ptrs[0]) == 1);
168+
CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk) == 1);
169+
CHECK(secp256k1_silentpayments_recipient_prevouts_summary_create(CTX, &prevouts_summary, SMALLEST_OUTPOINT, &tx_input_ptr, 1, NULL, 0) == 1);
170+
for (i = 0; i < 2; i++) {
171+
CHECK(secp256k1_silentpayments_verify_proof(CTX, dleq_data_ptrs[i]->shared_secret, dleq_data_ptrs[i]->proof,
172+
&recipients[dleq_data_ptrs[i]->index].scan_pubkey,
173+
&prevouts_summary) == 1);
174+
}
175+
}
155176
for (i = 0; i < 3; i++) {
156177
secp256k1_xonly_pubkey_serialize(CTX, xonly_ser, &generated_outputs[i]);
157178
CHECK(secp256k1_memcmp_var(xonly_ser, (*sp_outputs[i]), 32) == 0);
@@ -505,9 +526,17 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
505526
secp256k1_keypair taproot_keypairs[MAX_INPUTS_PER_TEST_CASE];
506527
secp256k1_keypair const *taproot_keypair_ptrs[MAX_INPUTS_PER_TEST_CASE];
507528
unsigned char const *plain_seckeys[MAX_INPUTS_PER_TEST_CASE];
529+
secp256k1_pubkey plain_pubkeys[MAX_INPUTS_PER_TEST_CASE];
530+
const secp256k1_pubkey *plain_pubkey_ptrs[MAX_INPUTS_PER_TEST_CASE];
531+
secp256k1_xonly_pubkey xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE];
532+
const secp256k1_xonly_pubkey *xonly_pubkey_ptrs[MAX_INPUTS_PER_TEST_CASE];
533+
secp256k1_silentpayments_dleq_data dleq_data[MAX_OUTPUTS_PER_TEST_CASE];
534+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[MAX_OUTPUTS_PER_TEST_CASE];
508535
unsigned char created_output[32];
509536
size_t i, j, k;
510537
int match, ret;
538+
size_t n_dleq_size;
539+
secp256k1_silentpayments_prevouts_summary prevouts_summary;
511540

512541
/* Check that sender creates expected outputs */
513542
for (i = 0; i < test->num_outputs; i++) {
@@ -516,19 +545,24 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
516545
recipients[i].index = i;
517546
recipient_ptrs[i] = &recipients[i];
518547
generated_output_ptrs[i] = &generated_outputs[i];
548+
dleq_data_ptrs[i] = &dleq_data[i];
519549
}
520550
for (i = 0; i < test->num_plain_inputs; i++) {
521551
plain_seckeys[i] = test->plain_seckeys[i];
552+
CHECK(secp256k1_ec_pubkey_create(CTX, &plain_pubkeys[i], plain_seckeys[i]) == 1);
553+
plain_pubkey_ptrs[i] = &plain_pubkeys[i];
522554
}
523555
for (i = 0; i < test->num_taproot_inputs; i++) {
524556
CHECK(secp256k1_keypair_create(CTX, &taproot_keypairs[i], test->taproot_seckeys[i]));
525557
taproot_keypair_ptrs[i] = &taproot_keypairs[i];
558+
CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pubkeys[i], NULL, &taproot_keypairs[i]) == 1);
559+
xonly_pubkey_ptrs[i] = &xonly_pubkeys[i];
526560
}
527561
{
528562
int32_t ecount = 0;
529563
secp256k1_context_set_illegal_callback(CTX, counting_callback_fn, &ecount);
530-
ret = secp256k1_silentpayments_sender_create_outputs(CTX,
531-
generated_output_ptrs,
564+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(CTX,
565+
generated_output_ptrs, dleq_data_ptrs, &n_dleq_size,
532566
recipient_ptrs,
533567
test->num_outputs,
534568
test->outpoint_smallest,
@@ -566,6 +600,16 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
566600
}
567601
}
568602
CHECK(match);
603+
604+
/* Verify generated DLEQ proofs*/
605+
CHECK(secp256k1_silentpayments_recipient_prevouts_summary_create(CTX, &prevouts_summary, test->outpoint_smallest,
606+
test->num_taproot_inputs > 0 ? xonly_pubkey_ptrs : NULL, test->num_taproot_inputs,
607+
test->num_plain_inputs > 0 ? plain_pubkey_ptrs : NULL, test->num_plain_inputs) == 1);
608+
for (i = 0; i < n_dleq_size; i++) {
609+
CHECK(secp256k1_silentpayments_verify_proof(CTX, dleq_data_ptrs[i]->shared_secret, dleq_data_ptrs[i]->proof,
610+
&recipients[dleq_data_ptrs[i]->index].scan_pubkey,
611+
&prevouts_summary) == 1);
612+
}
569613
}
570614

571615
void run_silentpayments_test_vector_receive(const struct bip352_test_vector *test) {

0 commit comments

Comments
 (0)