@@ -398,7 +398,7 @@ std::optional<std::vector<SilentPaymentOutput>> ParallelScanForSilentPaymentOutp
398398 tx_output_objs.reserve (tx_outputs.size ());
399399
400400 Mutex mutex;
401- std::vector<std::future<void >> futures;
401+ std::vector<std::future<bool >> futures;
402402 futures.reserve (tx_outputs.size ());
403403
404404 for (auto & tx_output: tx_outputs) {
@@ -423,95 +423,135 @@ std::optional<std::vector<SilentPaymentOutput>> ParallelScanForSilentPaymentOutp
423423 }
424424
425425 uint32_t k = 0 ;
426- while (k < tx_outputs.size ()) {
427- uint256 tweak ;
428-
429- if (! secp256k1_silentpayments_recipient_create_tweak (
430- secp256k1_context_static,
431- tweak. data (),
432- UCharCast (scan_key. begin ()),
433- prevouts_summary. Get (),
434- &spend_pubkey_obj,
435- k)
436- ) {
437- return {} ;
426+ const size_t max_outputs_size = std::min (SP_RECIPIENT_GROUP_LIMIT, tx_outputs.size ());
427+ const size_t factor = 2 ;
428+ const size_t min_batch_size = 1 ;
429+ while ( true ) {
430+ bool found = true ;
431+ for ( size_t i = 0 ; i < futures. size (); i++) {
432+ auto & future = futures[i];
433+ const bool ready = future. wait_for ( std::chrono::seconds::zero ()) == std::future_status::ready;
434+ if (!ready) continue ;
435+ // set found to false if even one of the futures returns false
436+ found = found & future. get ();
437+ futures. erase (futures. begin () + i) ;
438438 }
439439
440- secp256k1_xonly_pubkey unlabeled_output;
441- if (!secp256k1_silentpayments_recipient_create_output_pubkey (
442- secp256k1_context_static,
443- &unlabeled_output,
444- tweak.data (),
445- &spend_pubkey_obj
446- )) {
447- return {};
440+ if (futures.size () > 0 ) {
441+ // Join in task processing while
442+ // waiting for existing work to complete
443+ threadpool.ProcessTask ();
444+ continue ;
448445 }
449446
450- bool found = false ;
451- uint256 label_tweak;
452- for (size_t i = 0 ; i < tx_output_objs.size (); i++) {
453- if (found) break ;
454- if (secp256k1_xonly_pubkey_cmp (
455- secp256k1_context_static,
456- &tx_output_objs[i],
457- &unlabeled_output
458- ) == 0 ) {
459- found = true ;
460- SilentPaymentOutput found_output{tx_outputs[i], tweak, {}};
461- found_outputs.emplace_back (found_output);
462- break ;
463- }
464- if (labels.size () == 0 ) continue ;
465- base_blob<264 > label_candidates[2 ];
466- std::array<unsigned char *, 2 > label_candidate_ptrs;
467- label_candidate_ptrs[0 ] = label_candidates[0 ].data ();
468- label_candidate_ptrs[1 ] = label_candidates[1 ].data ();
469-
470- secp256k1_silentpayments_recipient_create_output_label (
471- secp256k1_context_static,
472- label_candidate_ptrs.data (),
473- &tx_output_objs[i],
474- &unlabeled_output
475- );
476-
477- for (auto candidate_ptr: label_candidate_ptrs) {
478- secp256k1_silentpayments_label label_obj;
479- bool ret = secp256k1_silentpayments_recipient_label_parse (
480- secp256k1_context_static, &label_obj, candidate_ptr);
481- assert (ret);
482- SilentPaymentLabel label{std::move (label_obj)};
483- // Find the pubkey in the map
484- auto it = labels.find (label);
485- if (it != labels.end ()) {
486- // Return a pointer to the uint256 label tweak if found
487- // so it can be added to t_k
488- label_tweak = it->second ;
489- found = true ;
490- /* This is extremely unlikely to fail in that it can only really fail if label_tweak
491- * is the negation of the shared secret tweak. But since both tweak and label_tweak are
492- * created by hashing data, practically speaking this would only happen if an attacker
493- * tricked us into using a particular label_tweak (deviating from the protocol).
494- *
495- * Furthermore, although technically a failure for ec_seckey_tweak_add, this is not treated
496- * as a failure for Silent Payments because the output is still spendable with just the
497- * spend secret key. We set `tweak = 0` for this case.
498- */
499- if (!secp256k1_ec_seckey_tweak_add (
447+ // Stop if no payment was found for at least one k
448+ // or there are no more k values to test
449+ if (!found || k >= max_outputs_size) break ;
450+ size_t current_batch_size = std::max (k * factor, min_batch_size);
451+ current_batch_size = std::min (current_batch_size, max_outputs_size - k);
452+ for (size_t i = 0 ; i < current_batch_size; i++) {
453+ futures.emplace_back (threadpool.Submit ([&, k] {
454+ uint256 tweak;
455+
456+ if (!secp256k1_silentpayments_recipient_create_tweak (
457+ secp256k1_context_static,
458+ tweak.data (),
459+ UCharCast (scan_key.begin ()),
460+ prevouts_summary.Get (),
461+ &spend_pubkey_obj,
462+ k)
463+ ) {
464+ return false ; // TODO should not fail silently
465+ }
466+
467+ secp256k1_xonly_pubkey unlabeled_output;
468+ if (!secp256k1_silentpayments_recipient_create_output_pubkey (
469+ secp256k1_context_static,
470+ &unlabeled_output,
471+ tweak.data (),
472+ &spend_pubkey_obj
473+ )) {
474+ return false ; // TODO should not fail silently
475+ }
476+
477+ uint256 label_tweak;
478+ std::optional<SilentPaymentOutput> found_output;
479+ secp256k1_xonly_pubkey tx_output_obj;
480+ size_t current_index = 0 ;
481+ while (current_index < tx_output_objs.size ()) {
482+ {
483+ LOCK (mutex);
484+ if (found_output) {
485+ found_outputs.push_back (std::move (*found_output));
486+ return true ;
487+ }
488+ tx_output_obj = tx_output_objs[current_index];
489+ }
490+ if (secp256k1_xonly_pubkey_cmp (
500491 secp256k1_context_static,
501- tweak.data (),
502- label_tweak.data ()
503- )) {
504- tweak.SetNull ();
492+ &tx_output_obj,
493+ &unlabeled_output
494+ ) == 0 ) {
495+ found_output = SilentPaymentOutput{tx_outputs[current_index], tweak, {}};
496+ continue ;
505497 }
506- SilentPaymentOutput found_output{tx_outputs[i], tweak, label};
507- found_outputs.emplace_back (found_output);
508- break ;
498+
499+ if (labels.size () == 0 ) continue ;
500+ base_blob<264 > label_candidates[2 ];
501+ std::array<unsigned char *, 2 > label_candidate_ptrs;
502+ label_candidate_ptrs[0 ] = label_candidates[0 ].data ();
503+ label_candidate_ptrs[1 ] = label_candidates[1 ].data ();
504+
505+ secp256k1_silentpayments_recipient_create_output_label (
506+ secp256k1_context_static,
507+ label_candidate_ptrs.data (),
508+ &tx_output_obj,
509+ &unlabeled_output
510+ );
511+
512+ for (auto candidate_ptr: label_candidate_ptrs) {
513+ secp256k1_silentpayments_label label_obj;
514+ bool ret = secp256k1_silentpayments_recipient_label_parse (
515+ secp256k1_context_static, &label_obj, candidate_ptr);
516+ assert (ret);
517+ SilentPaymentLabel label{std::move (label_obj)};
518+ // Find the pubkey in the map
519+ auto it = labels.find (label);
520+ if (it != labels.end ()) {
521+ // Return a pointer to the uint256 label tweak if found
522+ // so it can be added to t_k
523+ label_tweak = it->second ;
524+ /* This is extremely unlikely to fail in that it can only really fail if label_tweak
525+ * is the negation of the shared secret tweak. But since both tweak and label_tweak are
526+ * created by hashing data, practically speaking this would only happen if an attacker
527+ * tricked us into using a particular label_tweak (deviating from the protocol).
528+ *
529+ * Furthermore, although technically a failure for ec_seckey_tweak_add, this is not treated
530+ * as a failure for Silent Payments because the output is still spendable with just the
531+ * spend secret key. We set `tweak = 0` for this case.
532+ */
533+ if (!secp256k1_ec_seckey_tweak_add (
534+ secp256k1_context_static,
535+ tweak.data (),
536+ label_tweak.data ()
537+ )) {
538+ tweak.SetNull ();
539+ }
540+ found_output = SilentPaymentOutput{tx_outputs[current_index], tweak, label};
541+ break ;
542+ }
543+ }
544+ current_index++;
509545 }
510- }
546+ return false ;
547+ }));
548+
549+ k++;
511550 }
551+ }
512552
513- if (!found) break ;
514- k++ ;
553+ for ( auto & future: futures) {
554+ future. get () ;
515555 }
516556
517557 return found_outputs;
0 commit comments