Skip to content

Commit 41ee3e8

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 add detailed examples showing parsing/serialisation
1 parent 91aa8c3 commit 41ee3e8

File tree

2 files changed

+138
-13
lines changed

2 files changed

+138
-13
lines changed

examples/silentpayments.c

+90-9
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,14 @@ const unsigned char* label_lookup(
111111
}
112112

113113
int main(void) {
114-
enum { N_INPUTS = 2, N_OUTPUTS = 3 };
114+
enum { N_INPUTS = 2, N_OUTPUTS = 3, N_RECIPIENTS = 2 };
115115
unsigned char randomize[32];
116116
unsigned char xonly_print[32];
117117
secp256k1_xonly_pubkey tx_inputs[N_INPUTS];
118118
secp256k1_xonly_pubkey tx_outputs[N_OUTPUTS];
119119
int ret;
120120
size_t i;
121+
unsigned char dleq_proof[N_RECIPIENTS][64];
121122

122123
/* Before we can call actual API functions, we need to create a "context" */
123124
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
@@ -218,14 +219,63 @@ int main(void) {
218219
for (i = 0; i < N_OUTPUTS; i++) {
219220
generated_output_ptrs[i] = &generated_outputs[i];
220221
}
221-
ret = secp256k1_silentpayments_sender_create_outputs(ctx,
222-
generated_output_ptrs,
223-
recipient_ptrs, N_OUTPUTS,
224-
smallest_outpoint,
225-
sender_seckey_ptrs, N_INPUTS,
226-
NULL, 0
227-
);
228-
assert(ret);
222+
223+
/* Sender can perform 1 of the following options:
224+
* Option 1: generate outputs without DLEQ proofs
225+
ret = secp256k1_silentpayments_sender_create_outputs(ctx,
226+
generated_output_ptrs,
227+
recipient_ptrs, N_OUTPUTS,
228+
smallest_outpoint,
229+
sender_seckey_ptrs, N_INPUTS,
230+
NULL, 0
231+
);
232+
assert(ret);
233+
*/
234+
{
235+
/* Option 2: generate outputs with DLEQ proofs*/
236+
secp256k1_silentpayments_public_data public_data;
237+
const secp256k1_xonly_pubkey *tx_input_ptrs[N_INPUTS];
238+
size_t n_dleq_size;
239+
secp256k1_silentpayments_dleq_data dleq_data[N_RECIPIENTS];
240+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[N_RECIPIENTS];
241+
for (i = 0; i < N_RECIPIENTS; i++) {
242+
dleq_data_ptrs[i] = &dleq_data[i];
243+
}
244+
for (i = 0; i < N_INPUTS; i++) {
245+
tx_input_ptrs[i] = &tx_inputs[i];
246+
}
247+
ret = secp256k1_silentpayments_recipient_public_data_create(ctx, &public_data, smallest_outpoint,
248+
tx_input_ptrs, N_INPUTS, NULL, 0);
249+
assert(ret);
250+
251+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(ctx,
252+
generated_output_ptrs, dleq_data_ptrs, &n_dleq_size,
253+
recipient_ptrs, N_OUTPUTS,
254+
smallest_outpoint,
255+
sender_seckey_ptrs, N_INPUTS,
256+
NULL, 0
257+
);
258+
assert(n_dleq_size == N_RECIPIENTS);
259+
assert(ret);
260+
/* Ensure that outputs are generated correctly at the sender side by verifying the DLEQ proof */
261+
for (i = 0; i < N_RECIPIENTS; i++) {
262+
/* Serialized form of proof can be sent from 1 sender side device to another sender side device.
263+
* ex: hardware wallet (which can do ECDH + proof calculation) to wallet application. */
264+
unsigned char ss_proof_index_bytes[33 + 64 + 4];
265+
secp256k1_silentpayments_dleq_data data;
266+
secp256k1_silentpayments_dleq_data_serialize(ss_proof_index_bytes, &dleq_data[i]);
267+
/* Parse the serialized proof on the second device. (ex: wallet application) */
268+
secp256k1_silentpayments_dleq_data_parse(&data, ss_proof_index_bytes);
269+
/* Proof verification can be done on the second device. */
270+
ret = secp256k1_silentpayments_verify_proof(ctx, data.shared_secret, data.proof,
271+
&recipients[data.index].scan_pubkey,
272+
&public_data);
273+
assert(ret);
274+
/* Store proof to send to different receivers (Bob, Carol) later. */
275+
memcpy(dleq_proof[i], ss_proof_index_bytes + 33, 64);
276+
}
277+
}
278+
229279
printf("Alice created the following outputs for Bob and Carol: \n");
230280
for (i = 0; i < N_OUTPUTS; i++) {
231281
printf(" ");
@@ -400,6 +450,25 @@ int main(void) {
400450
);
401451
print_hex(xonly_print, sizeof(xonly_print));
402452
}
453+
{
454+
/* Optionally, Bob can use DLEQ proof to prove ownership of his address without revealing private keys
455+
* DLEQ proof verification needs proof, input pubkey sum, Bob's scan pubkey and shared secret as inputs. */
456+
unsigned char shared_secret[33];
457+
secp256k1_pubkey scan_pubkey;
458+
/* 1. Get Bob's scan pubkey */
459+
ret = secp256k1_ec_pubkey_parse(ctx, &scan_pubkey, bob_address[0], 33);
460+
assert(ret);
461+
/* 2. Compute input pubkey sum */
462+
ret = secp256k1_silentpayments_recipient_public_data_parse(ctx, &public_data, light_client_data33);
463+
assert(ret);
464+
/* 3. Bob computes shared secret */
465+
ret = secp256k1_silentpayments_recipient_create_shared_secret(ctx, shared_secret, bob_scan_key,
466+
&public_data);
467+
assert(ret);
468+
/* 4. Use proof we obtained from Alice for verification */
469+
ret &= secp256k1_silentpayments_verify_proof(ctx, shared_secret, dleq_proof[0], &scan_pubkey, &public_data);
470+
assert(ret);
471+
}
403472
}
404473
{
405474
/*** Scanning as a light client (Carol) ***
@@ -494,6 +563,18 @@ int main(void) {
494563
printf(" ");
495564
print_hex(ser_found_outputs[i], 32);
496565
}
566+
{
567+
/* Optionally, Carol can use DLEQ proof to prove ownership of her address without revealing private keys
568+
* DLEQ proof verification needs proof, input pubkey sum, Carol's scan pubkey and shared secret as inputs. */
569+
/* 1. Get Carol's scan pubkey */
570+
secp256k1_pubkey scan_pubkey;
571+
ret = secp256k1_ec_pubkey_parse(ctx, &scan_pubkey, carol_address[0], 33);
572+
assert(ret);
573+
/* 2. Input pubkey sum and shared secret already computed at this point, so verify_proof directly */
574+
/* 3. Use proof we obtained from Alice for verification */
575+
ret &= secp256k1_silentpayments_verify_proof(ctx, shared_secret, dleq_proof[1], &scan_pubkey, &public_data);
576+
assert(ret);
577+
}
497578
}
498579
}
499580

src/modules/silentpayments/tests_impl.h

+48-4
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
*
@@ -130,6 +131,9 @@ static void test_recipient_sort_helper(unsigned char (*sp_addresses[3])[2][33],
130131
const secp256k1_silentpayments_recipient *recipient_ptrs[3];
131132
secp256k1_xonly_pubkey generated_outputs[3];
132133
secp256k1_xonly_pubkey *generated_output_ptrs[3];
134+
secp256k1_silentpayments_dleq_data dleq_data[2];
135+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[2];
136+
size_t n_dleq_size;
133137
unsigned char xonly_ser[32];
134138
size_t i;
135139
int ret;
@@ -141,15 +145,32 @@ static void test_recipient_sort_helper(unsigned char (*sp_addresses[3])[2][33],
141145
recipients[i].index = i;
142146
recipient_ptrs[i] = &recipients[i];
143147
generated_output_ptrs[i] = &generated_outputs[i];
148+
if (i != 2){
149+
dleq_data_ptrs[i] = &dleq_data[i];
150+
}
144151
}
145-
ret = secp256k1_silentpayments_sender_create_outputs(CTX,
146-
generated_output_ptrs,
152+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(CTX,
153+
generated_output_ptrs, dleq_data_ptrs, &n_dleq_size,
147154
recipient_ptrs, 3,
148155
SMALLEST_OUTPOINT,
149156
NULL, 0,
150157
seckey_ptrs, 1
151158
);
152159
CHECK(ret);
160+
{
161+
secp256k1_pubkey pk;
162+
secp256k1_xonly_pubkey xonly_pk;
163+
secp256k1_silentpayments_public_data public_data;
164+
const secp256k1_xonly_pubkey *tx_input_ptr = &xonly_pk;
165+
CHECK(secp256k1_ec_pubkey_create(CTX, &pk, seckey_ptrs[0]) == 1);
166+
CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk) == 1);
167+
CHECK(secp256k1_silentpayments_recipient_public_data_create(CTX, &public_data, SMALLEST_OUTPOINT, &tx_input_ptr, 1, NULL, 0) == 1);
168+
for (i = 0; i < 2; i++) {
169+
CHECK(secp256k1_silentpayments_verify_proof(CTX, dleq_data_ptrs[i]->shared_secret, dleq_data_ptrs[i]->proof,
170+
&recipients[dleq_data_ptrs[i]->index].scan_pubkey,
171+
&public_data) == 1);
172+
}
173+
}
153174
for (i = 0; i < 3; i++) {
154175
secp256k1_xonly_pubkey_serialize(CTX, xonly_ser, &generated_outputs[i]);
155176
CHECK(secp256k1_memcmp_var(xonly_ser, (*sp_outputs[i]), 32) == 0);
@@ -374,9 +395,17 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
374395
secp256k1_keypair taproot_keypairs[MAX_INPUTS_PER_TEST_CASE];
375396
secp256k1_keypair const *taproot_keypair_ptrs[MAX_INPUTS_PER_TEST_CASE];
376397
unsigned char const *plain_seckeys[MAX_INPUTS_PER_TEST_CASE];
398+
secp256k1_pubkey plain_pubkeys[MAX_INPUTS_PER_TEST_CASE];
399+
const secp256k1_pubkey *plain_pubkey_ptrs[MAX_INPUTS_PER_TEST_CASE];
400+
secp256k1_xonly_pubkey xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE];
401+
const secp256k1_xonly_pubkey *xonly_pubkey_ptrs[MAX_INPUTS_PER_TEST_CASE];
402+
secp256k1_silentpayments_dleq_data dleq_data[MAX_OUTPUTS_PER_TEST_CASE];
403+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[MAX_OUTPUTS_PER_TEST_CASE];
377404
unsigned char created_output[32];
378405
size_t i, j, k;
379406
int match, ret;
407+
size_t n_dleq_size;
408+
secp256k1_silentpayments_public_data public_data;
380409

381410
/* Check that sender creates expected outputs */
382411
for (i = 0; i < test->num_outputs; i++) {
@@ -385,16 +414,21 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
385414
recipients[i].index = i;
386415
recipient_ptrs[i] = &recipients[i];
387416
generated_output_ptrs[i] = &generated_outputs[i];
417+
dleq_data_ptrs[i] = &dleq_data[i];
388418
}
389419
for (i = 0; i < test->num_plain_inputs; i++) {
390420
plain_seckeys[i] = test->plain_seckeys[i];
421+
CHECK(secp256k1_ec_pubkey_create(CTX, &plain_pubkeys[i], plain_seckeys[i]) == 1);
422+
plain_pubkey_ptrs[i] = &plain_pubkeys[i];
391423
}
392424
for (i = 0; i < test->num_taproot_inputs; i++) {
393425
CHECK(secp256k1_keypair_create(CTX, &taproot_keypairs[i], test->taproot_seckeys[i]));
394426
taproot_keypair_ptrs[i] = &taproot_keypairs[i];
427+
CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pubkeys[i], NULL, &taproot_keypairs[i]) == 1);
428+
xonly_pubkey_ptrs[i] = &xonly_pubkeys[i];
395429
}
396-
ret = secp256k1_silentpayments_sender_create_outputs(CTX,
397-
generated_output_ptrs,
430+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(CTX,
431+
generated_output_ptrs, dleq_data_ptrs, &n_dleq_size,
398432
recipient_ptrs,
399433
test->num_outputs,
400434
test->outpoint_smallest,
@@ -428,6 +462,16 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
428462
}
429463
}
430464
CHECK(match);
465+
466+
/* Verify generated DLEQ proofs*/
467+
CHECK(secp256k1_silentpayments_recipient_public_data_create(CTX, &public_data, test->outpoint_smallest,
468+
test->num_taproot_inputs > 0 ? xonly_pubkey_ptrs : NULL, test->num_taproot_inputs,
469+
test->num_plain_inputs > 0 ? plain_pubkey_ptrs : NULL, test->num_plain_inputs) == 1);
470+
for (i = 0; i < n_dleq_size; i++) {
471+
CHECK(secp256k1_silentpayments_verify_proof(CTX, dleq_data_ptrs[i]->shared_secret, dleq_data_ptrs[i]->proof,
472+
&recipients[dleq_data_ptrs[i]->index].scan_pubkey,
473+
&public_data) == 1);
474+
}
431475
}
432476

433477
void run_silentpayments_test_vector_receive(const struct bip352_test_vector *test) {

0 commit comments

Comments
 (0)