Skip to content

Commit 44bf41d

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 2dab4e9 commit 44bf41d

File tree

2 files changed

+138
-13
lines changed

2 files changed

+138
-13
lines changed

examples/silentpayments.c

Lines changed: 90 additions & 9 deletions
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

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "../../../src/modules/silentpayments/dleq_impl.h"
1111
#include "../../../src/modules/silentpayments/vectors.h"
1212
#include "include/secp256k1.h"
13+
#include <assert.h>
1314

1415
/** Constants
1516
*
@@ -129,6 +130,9 @@ static void test_recipient_sort_helper(unsigned char (*sp_addresses[3])[2][33],
129130
const secp256k1_silentpayments_recipient *recipient_ptrs[3];
130131
secp256k1_xonly_pubkey generated_outputs[3];
131132
secp256k1_xonly_pubkey *generated_output_ptrs[3];
133+
secp256k1_silentpayments_dleq_data dleq_data[2];
134+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[2];
135+
size_t n_dleq_size;
132136
unsigned char xonly_ser[32];
133137
size_t i;
134138
int ret;
@@ -140,15 +144,32 @@ static void test_recipient_sort_helper(unsigned char (*sp_addresses[3])[2][33],
140144
recipients[i].index = i;
141145
recipient_ptrs[i] = &recipients[i];
142146
generated_output_ptrs[i] = &generated_outputs[i];
147+
if (i != 2){
148+
dleq_data_ptrs[i] = &dleq_data[i];
149+
}
143150
}
144-
ret = secp256k1_silentpayments_sender_create_outputs(CTX,
145-
generated_output_ptrs,
151+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(CTX,
152+
generated_output_ptrs, dleq_data_ptrs, &n_dleq_size,
146153
recipient_ptrs, 3,
147154
SMALLEST_OUTPOINT,
148155
NULL, 0,
149156
seckey_ptrs, 1
150157
);
151158
CHECK(ret);
159+
{
160+
secp256k1_pubkey pk;
161+
secp256k1_xonly_pubkey xonly_pk;
162+
secp256k1_silentpayments_public_data public_data;
163+
const secp256k1_xonly_pubkey *tx_input_ptr = &xonly_pk;
164+
CHECK(secp256k1_ec_pubkey_create(CTX, &pk, seckey_ptrs[0]) == 1);
165+
CHECK(secp256k1_xonly_pubkey_from_pubkey(CTX, &xonly_pk, NULL, &pk) == 1);
166+
CHECK(secp256k1_silentpayments_recipient_public_data_create(CTX, &public_data, SMALLEST_OUTPOINT, &tx_input_ptr, 1, NULL, 0) == 1);
167+
for (i = 0; i < 2; i++) {
168+
CHECK(secp256k1_silentpayments_verify_proof(CTX, dleq_data_ptrs[i]->shared_secret, dleq_data_ptrs[i]->proof,
169+
&recipients[dleq_data_ptrs[i]->index].scan_pubkey,
170+
&public_data) == 1);
171+
}
172+
}
152173
for (i = 0; i < 3; i++) {
153174
secp256k1_xonly_pubkey_serialize(CTX, xonly_ser, &generated_outputs[i]);
154175
CHECK(secp256k1_memcmp_var(xonly_ser, (*sp_outputs[i]), 32) == 0);
@@ -373,9 +394,17 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
373394
secp256k1_keypair taproot_keypairs[MAX_INPUTS_PER_TEST_CASE];
374395
secp256k1_keypair const *taproot_keypair_ptrs[MAX_INPUTS_PER_TEST_CASE];
375396
unsigned char const *plain_seckeys[MAX_INPUTS_PER_TEST_CASE];
397+
secp256k1_pubkey plain_pubkeys[MAX_INPUTS_PER_TEST_CASE];
398+
const secp256k1_pubkey *plain_pubkey_ptrs[MAX_INPUTS_PER_TEST_CASE];
399+
secp256k1_xonly_pubkey xonly_pubkeys[MAX_INPUTS_PER_TEST_CASE];
400+
const secp256k1_xonly_pubkey *xonly_pubkey_ptrs[MAX_INPUTS_PER_TEST_CASE];
401+
secp256k1_silentpayments_dleq_data dleq_data[MAX_OUTPUTS_PER_TEST_CASE];
402+
secp256k1_silentpayments_dleq_data *dleq_data_ptrs[MAX_OUTPUTS_PER_TEST_CASE];
376403
unsigned char created_output[32];
377404
size_t i, j, k;
378405
int match, ret;
406+
size_t n_dleq_size;
407+
secp256k1_silentpayments_public_data public_data;
379408

380409
/* Check that sender creates expected outputs */
381410
for (i = 0; i < test->num_outputs; i++) {
@@ -384,16 +413,21 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
384413
recipients[i].index = i;
385414
recipient_ptrs[i] = &recipients[i];
386415
generated_output_ptrs[i] = &generated_outputs[i];
416+
dleq_data_ptrs[i] = &dleq_data[i];
387417
}
388418
for (i = 0; i < test->num_plain_inputs; i++) {
389419
plain_seckeys[i] = test->plain_seckeys[i];
420+
CHECK(secp256k1_ec_pubkey_create(CTX, &plain_pubkeys[i], plain_seckeys[i]) == 1);
421+
plain_pubkey_ptrs[i] = &plain_pubkeys[i];
390422
}
391423
for (i = 0; i < test->num_taproot_inputs; i++) {
392424
CHECK(secp256k1_keypair_create(CTX, &taproot_keypairs[i], test->taproot_seckeys[i]));
393425
taproot_keypair_ptrs[i] = &taproot_keypairs[i];
426+
CHECK(secp256k1_keypair_xonly_pub(CTX, &xonly_pubkeys[i], NULL, &taproot_keypairs[i]) == 1);
427+
xonly_pubkey_ptrs[i] = &xonly_pubkeys[i];
394428
}
395-
ret = secp256k1_silentpayments_sender_create_outputs(CTX,
396-
generated_output_ptrs,
429+
ret = secp256k1_silentpayments_sender_create_outputs_with_proof(CTX,
430+
generated_output_ptrs, dleq_data_ptrs, &n_dleq_size,
397431
recipient_ptrs,
398432
test->num_outputs,
399433
test->outpoint_smallest,
@@ -427,6 +461,16 @@ void run_silentpayments_test_vector_send(const struct bip352_test_vector *test)
427461
}
428462
}
429463
CHECK(match);
464+
465+
/* Verify generated DLEQ proofs*/
466+
CHECK(secp256k1_silentpayments_recipient_public_data_create(CTX, &public_data, test->outpoint_smallest,
467+
test->num_taproot_inputs > 0 ? xonly_pubkey_ptrs : NULL, test->num_taproot_inputs,
468+
test->num_plain_inputs > 0 ? plain_pubkey_ptrs : NULL, test->num_plain_inputs) == 1);
469+
for (i = 0; i < n_dleq_size; i++) {
470+
CHECK(secp256k1_silentpayments_verify_proof(CTX, dleq_data_ptrs[i]->shared_secret, dleq_data_ptrs[i]->proof,
471+
&recipients[dleq_data_ptrs[i]->index].scan_pubkey,
472+
&public_data) == 1);
473+
}
430474
}
431475

432476
void run_silentpayments_test_vector_receive(const struct bip352_test_vector *test) {

0 commit comments

Comments
 (0)