@@ -429,4 +429,139 @@ int secp256k1_silentpayments_recipient_public_data_parse(const secp256k1_context
429
429
return 1 ;
430
430
}
431
431
432
+ int secp256k1_silentpayments_recipient_scan_outputs (
433
+ const secp256k1_context * ctx ,
434
+ secp256k1_silentpayments_found_output * * found_outputs , size_t * n_found_outputs ,
435
+ const secp256k1_xonly_pubkey * const * tx_outputs , size_t n_tx_outputs ,
436
+ const unsigned char * scan_key ,
437
+ const secp256k1_silentpayments_public_data * public_data ,
438
+ const secp256k1_pubkey * receiver_spend_pubkey ,
439
+ const secp256k1_silentpayments_label_lookup label_lookup ,
440
+ const void * label_context
441
+ ) {
442
+ secp256k1_scalar t_k_scalar ;
443
+ secp256k1_ge receiver_spend_pubkey_ge ;
444
+ secp256k1_xonly_pubkey P_output_xonly ;
445
+ secp256k1_pubkey A_sum ;
446
+ unsigned char shared_secret [33 ];
447
+ size_t i , k , n_found ;
448
+ int found , combined ;
449
+
450
+ /* Sanity check inputs */
451
+ VERIFY_CHECK (ctx != NULL );
452
+ ARG_CHECK (found_outputs != NULL );
453
+ ARG_CHECK (tx_outputs != NULL );
454
+ ARG_CHECK (scan_key != NULL );
455
+ ARG_CHECK (public_data != NULL );
456
+ combined = (int )public_data -> data [0 ];
457
+ {
458
+ unsigned char input_hash [32 ];
459
+ unsigned char * input_hash_ptr ;
460
+ if (combined ) {
461
+ input_hash_ptr = NULL ;
462
+ } else {
463
+ memset (input_hash , 0 , 32 );
464
+ input_hash_ptr = input_hash ;
465
+ }
466
+ if (!secp256k1_silentpayments_recipient_public_data_load (ctx , & A_sum , input_hash_ptr , public_data )) {
467
+ return 0 ;
468
+ }
469
+ secp256k1_pubkey_load (ctx , & receiver_spend_pubkey_ge , receiver_spend_pubkey );
470
+ if (!secp256k1_silentpayments_create_shared_secret (ctx , shared_secret , scan_key , & A_sum , input_hash_ptr )) {
471
+ return 0 ;
472
+ }
473
+ }
474
+
475
+ n_found = 0 ;
476
+ k = 0 ;
477
+ while (1 ) {
478
+ secp256k1_ge P_output_ge = receiver_spend_pubkey_ge ;
479
+ /* Calculate t_k = hash(shared_secret || ser_32(k)) */
480
+ secp256k1_silentpayments_create_t_k (& t_k_scalar , shared_secret , k );
481
+
482
+ /* Calculate P_output = B_spend + t_k * G */
483
+ if (!secp256k1_eckey_pubkey_tweak_add (& P_output_ge , & t_k_scalar )) {
484
+ return 0 ;
485
+ }
486
+
487
+ /* If the calculated output matches the one from the tx, we have a direct match and can
488
+ * return without labels calculation (one of the two would result in point of infinity) */
489
+ secp256k1_xonly_pubkey_save (& P_output_xonly , & P_output_ge );
490
+ found = 0 ;
491
+ for (i = 0 ; i < n_tx_outputs ; i ++ ) {
492
+ if (secp256k1_xonly_pubkey_cmp (ctx , & P_output_xonly , tx_outputs [i ]) == 0 ) {
493
+ secp256k1_xonly_pubkey_save (& found_outputs [n_found ]-> output , & P_output_ge );
494
+ secp256k1_scalar_get_b32 (found_outputs [n_found ]-> tweak , & t_k_scalar );
495
+ found_outputs [n_found ]-> found_with_label = 0 ;
496
+ secp256k1_pubkey_save (& found_outputs [n_found ]-> label , & P_output_ge );
497
+ found = 1 ;
498
+ n_found ++ ;
499
+ k ++ ;
500
+ break ;
501
+ }
502
+
503
+ /* If desired, also calculate label candidates */
504
+ if (label_lookup != NULL ) {
505
+ secp256k1_pubkey label_pubkey ;
506
+ secp256k1_ge P_output_negated_ge , tx_output_ge ;
507
+ secp256k1_ge label_ge ;
508
+ secp256k1_gej label_gej ;
509
+ const unsigned char * label_tweak ;
510
+
511
+ /* Calculate negated P_output (common addend) first */
512
+ secp256k1_ge_neg (& P_output_negated_ge , & P_output_ge );
513
+
514
+ /* Calculate first scan label candidate: label1 = tx_output - P_output */
515
+ secp256k1_xonly_pubkey_load (ctx , & tx_output_ge , tx_outputs [i ]);
516
+ secp256k1_gej_set_ge (& label_gej , & tx_output_ge );
517
+ secp256k1_gej_add_ge_var (& label_gej , & label_gej , & P_output_negated_ge , NULL );
518
+ secp256k1_ge_set_gej (& label_ge , & label_gej );
519
+ secp256k1_pubkey_save (& label_pubkey , & label_ge );
520
+
521
+ label_tweak = label_lookup (& label_pubkey , label_context );
522
+ if (label_tweak != NULL ) {
523
+ secp256k1_xonly_pubkey_save (& found_outputs [n_found ]-> output , & tx_output_ge );
524
+ found_outputs [n_found ]-> found_with_label = 1 ;
525
+ secp256k1_pubkey_save (& found_outputs [n_found ]-> label , & label_ge );
526
+ secp256k1_scalar_get_b32 (found_outputs [n_found ]-> tweak , & t_k_scalar );
527
+ if (!secp256k1_ec_seckey_tweak_add (ctx , found_outputs [n_found ]-> tweak , label_tweak )) {
528
+ return 0 ;
529
+ }
530
+ found = 1 ;
531
+ n_found ++ ;
532
+ k ++ ;
533
+ break ;
534
+ }
535
+
536
+ /* Calculate second scan label candidate: label2 = -tx_output - P_output */
537
+ secp256k1_gej_set_ge (& label_gej , & tx_output_ge );
538
+ secp256k1_gej_neg (& label_gej , & label_gej );
539
+ secp256k1_gej_add_ge_var (& label_gej , & label_gej , & P_output_negated_ge , NULL );
540
+ secp256k1_ge_set_gej (& label_ge , & label_gej );
541
+ secp256k1_pubkey_save (& label_pubkey , & label_ge );
542
+
543
+ label_tweak = label_lookup (& label_pubkey , label_context );
544
+ if (label_tweak != NULL ) {
545
+ secp256k1_xonly_pubkey_save (& found_outputs [n_found ]-> output , & tx_output_ge );
546
+ found_outputs [n_found ]-> found_with_label = 1 ;
547
+ secp256k1_pubkey_save (& found_outputs [n_found ]-> label , & label_ge );
548
+ secp256k1_scalar_get_b32 (found_outputs [n_found ]-> tweak , & t_k_scalar );
549
+ if (!secp256k1_ec_seckey_tweak_add (ctx , found_outputs [n_found ]-> tweak , label_tweak )) {
550
+ return 0 ;
551
+ }
552
+ found = 1 ;
553
+ n_found ++ ;
554
+ k ++ ;
555
+ break ;
556
+ }
557
+ }
558
+ }
559
+ if (!found ) {
560
+ break ;
561
+ }
562
+ }
563
+ * n_found_outputs = n_found ;
564
+ return 1 ;
565
+ }
566
+
432
567
#endif
0 commit comments