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