Skip to content

Commit 1d212a1

Browse files
Merge dashpay#7225: fix: collect sigshares synchronously in dispatcher for parallel verification
018298b fix: collect sigshares synchronously in dispatcher for parallel verification (PastaClaw) Pull request description: ## Summary The dispatcher thread in `WorkThreadDispatcher()` unconditionally enqueued a `ProcessPendingSigShares` worker into the thread pool every 10ms whenever any `pendingIncomingSigShares` existed. Since the workers would then find nothing to do and immediately exit, this wasted allocations and queue operations. This was flagged during review of dashpay#7004 by CodeRabbit as a nitpick but was not addressed at the time. ## Fix Move `CollectPendingSigSharesToVerify` out of `ProcessPendingSigShares` and into the dispatcher loop, where it runs synchronously (it holds `cs` briefly and does no BLS work). Each collected batch is moved into its own worker for parallel BLS verification. This approach (suggested knst) has two advantages over the initial atomic-guard implementation: 1. **Parallel verification**: Multiple verification batches can run concurrently across the thread pool (up to 4 workers), instead of being serialised to a single worker. 2. **Natural queue bounding**: Each `Collect` call dequeues its batch from the pending queue, so only as many tasks are enqueued as there are actual batches of work — no redundant empty-exit tasks. **Performance impact: slight improvement.** Multiple BLS verification batches can now run in parallel when there are more than 32 pending sig shares (`nMaxBatchSize`). The collection step is cheap (no BLS ops). ## Validation - Code review of the dispatch path confirms collect-then-dispatch preserves the existing drain behavior - Each `CollectPendingSigSharesToVerify` call removes its batch from the pending queue, preventing duplicate work across workers - No functional change to sigshare processing semantics ACKs for top commit: UdjinM6: utACK 018298b knst: utACK 018298b Tree-SHA512: 667f510f7b4a75025cf840bc2835895bd09c25563da1617116b5bb721bf950e9823a4b911f6e86b5a6b8096e70d08d8238e2a2a80f5c2f06db221bdc2a0b2b68
2 parents 26aab22 + 018298b commit 1d212a1

File tree

2 files changed

+26
-23
lines changed

2 files changed

+26
-23
lines changed

src/llmq/net_signing.cpp

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -306,17 +306,25 @@ void NetSigning::WorkThreadDispatcher()
306306
}
307307
}
308308

309-
if (m_shares_manager->IsAnyPendingProcessing()) {
310-
// If there's processing work, spawn a helper worker
311-
worker_pool.push([this](int) {
312-
while (!workInterrupt) {
313-
bool moreWork = ProcessPendingSigShares();
314-
315-
if (!moreWork) {
316-
return; // No work found, exit immediately
317-
}
318-
}
309+
// Collect pending sig shares synchronously and dispatch each batch to a worker for parallel BLS verification
310+
while (!workInterrupt) {
311+
std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesByNodes;
312+
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
313+
314+
const size_t nMaxBatchSize{32};
315+
bool more_work = m_shares_manager->CollectPendingSigSharesToVerify(nMaxBatchSize, sigSharesByNodes, quorums);
316+
317+
if (sigSharesByNodes.empty()) {
318+
break;
319+
}
320+
321+
worker_pool.push([this, sigSharesByNodes = std::move(sigSharesByNodes), quorums = std::move(quorums)](int) mutable {
322+
ProcessPendingSigShares(std::move(sigSharesByNodes), std::move(quorums));
319323
});
324+
325+
if (!more_work) {
326+
break;
327+
}
320328
}
321329

322330
// Always sleep briefly between checks
@@ -329,17 +337,10 @@ void NetSigning::NotifyRecoveredSig(const std::shared_ptr<const CRecoveredSig>&
329337
m_peer_manager->PeerRelayRecoveredSig(*sig, proactive_relay);
330338
}
331339

332-
bool NetSigning::ProcessPendingSigShares()
340+
void NetSigning::ProcessPendingSigShares(
341+
std::unordered_map<NodeId, std::vector<CSigShare>>&& sigSharesByNodes,
342+
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>&& quorums)
333343
{
334-
std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesByNodes;
335-
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
336-
337-
const size_t nMaxBatchSize{32};
338-
bool more_work = m_shares_manager->CollectPendingSigSharesToVerify(nMaxBatchSize, sigSharesByNodes, quorums);
339-
if (sigSharesByNodes.empty()) {
340-
return false;
341-
}
342-
343344
// It's ok to perform insecure batched verification here as we verify against the quorum public key shares,
344345
// which are not craftable by individual entities, making the rogue public key attack impossible
345346
CBLSBatchVerifier<NodeId, SigShareKey> batchVerifier(false, true);
@@ -400,8 +401,6 @@ bool NetSigning::ProcessPendingSigShares()
400401
ProcessRecoveredSig(rs, true);
401402
}
402403
}
403-
404-
return more_work;
405404
}
406405

407406
} // namespace llmq

src/llmq/net_signing.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#define BITCOIN_LLMQ_NET_SIGNING_H
77

88
#include <ctpl_stl.h>
9+
#include <llmq/signing_shares.h>
10+
#include <llmq/types.h>
911
#include <net_processing.h>
1012
#include <util/threadinterrupt.h>
1113
#include <util/time.h>
@@ -52,7 +54,9 @@ class NetSigning final : public NetHandler, public CValidationInterface
5254
void WorkThreadCleaning();
5355
void WorkThreadDispatcher();
5456

55-
bool ProcessPendingSigShares();
57+
void ProcessPendingSigShares(
58+
std::unordered_map<NodeId, std::vector<CSigShare>>&& sigSharesByNodes,
59+
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>&& quorums);
5660

5761
void RemoveBannedNodeStates();
5862
void BanNode(NodeId nodeid);

0 commit comments

Comments
 (0)