@@ -433,4 +433,137 @@ int secp256k1_silentpayments_recipient_public_data_parse(const secp256k1_context
433433 return 1 ;
434434}
435435
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+
436569#endif
0 commit comments