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