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