@@ -111,13 +111,14 @@ const unsigned char* label_lookup(
111
111
}
112
112
113
113
int main (void ) {
114
- enum { N_INPUTS = 2 , N_OUTPUTS = 3 };
114
+ enum { N_INPUTS = 2 , N_OUTPUTS = 3 , N_RECIPIENTS = 2 };
115
115
unsigned char randomize [32 ];
116
116
unsigned char xonly_print [32 ];
117
117
secp256k1_xonly_pubkey tx_inputs [N_INPUTS ];
118
118
secp256k1_xonly_pubkey tx_outputs [N_OUTPUTS ];
119
119
int ret ;
120
120
size_t i ;
121
+ unsigned char dleq_proof [N_RECIPIENTS ][64 ];
121
122
122
123
/* Before we can call actual API functions, we need to create a "context" */
123
124
secp256k1_context * ctx = secp256k1_context_create (SECP256K1_CONTEXT_NONE );
@@ -218,14 +219,63 @@ int main(void) {
218
219
for (i = 0 ; i < N_OUTPUTS ; i ++ ) {
219
220
generated_output_ptrs [i ] = & generated_outputs [i ];
220
221
}
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
+
229
279
printf ("Alice created the following outputs for Bob and Carol: \n" );
230
280
for (i = 0 ; i < N_OUTPUTS ; i ++ ) {
231
281
printf (" " );
@@ -400,6 +450,25 @@ int main(void) {
400
450
);
401
451
print_hex (xonly_print , sizeof (xonly_print ));
402
452
}
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
+ }
403
472
}
404
473
{
405
474
/*** Scanning as a light client (Carol) ***
@@ -494,6 +563,18 @@ int main(void) {
494
563
printf (" " );
495
564
print_hex (ser_found_outputs [i ], 32 );
496
565
}
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
+ }
497
578
}
498
579
}
499
580
0 commit comments