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