Skip to content

Commit cea12db

Browse files
committed
bip352: Check each k value in parallel
Start by checking k=0 and subsequently double the the number values checked in parallel
1 parent 53e4189 commit cea12db

File tree

2 files changed

+123
-81
lines changed

2 files changed

+123
-81
lines changed

src/common/bip352.cpp

Lines changed: 121 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -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;

src/common/bip352.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ namespace bip352 {
2323

2424
using PubKey = std::variant<CPubKey, XOnlyPubKey>;
2525

26+
static size_t SP_RECIPIENT_GROUP_LIMIT = 1000;
27+
2628
class PrevoutsSummaryImpl;
2729
class PrevoutsSummary
2830
{

0 commit comments

Comments
 (0)