@@ -111,13 +111,14 @@ const unsigned char* label_lookup(
111111}
112112
113113int 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
0 commit comments