From 76e4e726fe7b1626e1226bba3994c7b9303662c7 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 23 May 2025 21:14:52 +0700 Subject: [PATCH 1/7] refactor: split quorum commitment verification and its signatures --- src/llmq/blockprocessor.cpp | 15 ++++++- src/llmq/commitment.cpp | 84 +++++++++++++++++++++++-------------- src/llmq/commitment.h | 2 + 3 files changed, 68 insertions(+), 33 deletions(-) diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 046b4d56febd..1aef5ecb1d80 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -196,8 +196,21 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, gsl::not_nullnHeight, ToUnderlying(qc.llmqType), qc.nVersion, qc.quorumIndex, + qc.quorumHash.ToString()); + return false; + } + } + } + for (const auto& [_, qc] : qcs) { - if (!ProcessCommitment(pindex->nHeight, blockHash, qc, state, fJustCheck, fBLSChecks)) { + if (!ProcessCommitment(pindex->nHeight, blockHash, qc, state, fJustCheck, false)) { LogPrintf("[ProcessBlock] failed h[%d] llmqType[%d] version[%d] quorumIndex[%d] quorumHash[%s]\n", pindex->nHeight, ToUnderlying(qc.llmqType), qc.nVersion, qc.quorumIndex, qc.quorumHash.ToString()); return false; } diff --git a/src/llmq/commitment.cpp b/src/llmq/commitment.cpp index 345a113219de..8f46e94b50a6 100644 --- a/src/llmq/commitment.cpp +++ b/src/llmq/commitment.cpp @@ -27,6 +27,57 @@ CFinalCommitment::CFinalCommitment(const Consensus::LLMQParams& params, const ui { } +bool CFinalCommitment::VerifySignature(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, + gsl::not_null pQuorumBaseBlockIndex) const +{ + auto members = utils::GetAllQuorumMembers(llmqType, dmnman, qsnapman, pQuorumBaseBlockIndex); + const auto& llmq_params_opt = Params().GetLLMQ(llmqType); + if (!llmq_params_opt.has_value()) { + LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid llmqType=%d\n", quorumHash.ToString(), + ToUnderlying(llmqType)); + return false; + } + const auto& llmq_params = llmq_params_opt.value(); + + uint256 commitmentHash = BuildCommitmentHash(llmq_params.type, quorumHash, validMembers, quorumPublicKey, + quorumVvecHash); + if (LogAcceptDebug(BCLog::LLMQ)) { + std::stringstream ss3; + for (const auto& mn : members) { + ss3 << mn->proTxHash.ToString().substr(0, 4) << " | "; + } + LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", __func__, + ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString()); + } + if (llmq_params.size == 1) { + LogPrintf("pubkey operator: %s\n", members[0]->pdmnState->pubKeyOperator.Get().ToString()); + if (!membersSig.VerifyInsecure(members[0]->pdmnState->pubKeyOperator.Get(), commitmentHash)) { + LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid member signature\n", quorumHash.ToString()); + return false; + } + } else { + std::vector memberPubKeys; + for (const auto i : irange::range(members.size())) { + if (!signers[i]) { + continue; + } + memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get()); + } + + if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) { + LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid aggregated members signature\n", + quorumHash.ToString()); + return false; + } + } + if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) { + LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorum signature\n", quorumHash.ToString()); + return false; + } + return true; +} + + bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, gsl::not_null pQuorumBaseBlockIndex, bool checkSigs) const { @@ -106,38 +157,7 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, CQuorumSnapshotMa // sigs are only checked when the block is processed if (checkSigs) { - uint256 commitmentHash = BuildCommitmentHash(llmq_params.type, quorumHash, validMembers, quorumPublicKey, quorumVvecHash); - if (LogAcceptDebug(BCLog::LLMQ)) { - std::stringstream ss3; - for (const auto &mn: members) { - ss3 << mn->proTxHash.ToString().substr(0, 4) << " | "; - } - LogPrint(BCLog::LLMQ, "CFinalCommitment::%s members[%s] quorumPublicKey[%s] commitmentHash[%s]\n", - __func__, ss3.str(), quorumPublicKey.ToString(), commitmentHash.ToString()); - } - if (llmq_params.size == 1) { - LogPrintf("pubkey operator: %s\n", members[0]->pdmnState->pubKeyOperator.Get().ToString()); - if (!membersSig.VerifyInsecure(members[0]->pdmnState->pubKeyOperator.Get(), commitmentHash)) { - LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid member signature\n", quorumHash.ToString()); - return false; - } - } else { - std::vector memberPubKeys; - for (const auto i : irange::range(members.size())) { - if (!signers[i]) { - continue; - } - memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get()); - } - - if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) { - LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid aggregated members signature\n", - quorumHash.ToString()); - return false; - } - } - if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) { - LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorum signature\n", quorumHash.ToString()); + if (!VerifySignature(dmnman, qsnapman, pQuorumBaseBlockIndex)) { return false; } } diff --git a/src/llmq/commitment.h b/src/llmq/commitment.h index 2add8ca47ad0..b5f3cc096b0d 100644 --- a/src/llmq/commitment.h +++ b/src/llmq/commitment.h @@ -67,6 +67,8 @@ class CFinalCommitment return int(std::count(validMembers.begin(), validMembers.end(), true)); } + bool VerifySignature(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, + gsl::not_null pQuorumBaseBlockIndex) const; bool Verify(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, gsl::not_null pQuorumBaseBlockIndex, bool checkSigs) const; bool VerifyNull() const; From 0f3a8e4257e9be85c37d9ac0d26a1cbf6290fef0 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 24 May 2025 23:51:19 +0700 Subject: [PATCH 2/7] feat: add -parbls and validate async quorumSig and membersSig in QC It introduces new commandline argument -parbls to set up amount of parallel threads for BLS validation New parallel BlsChecker validates asynchonously quorumSig and membersSig in Quorum Commitment --- src/init.cpp | 2 ++ src/llmq/blockprocessor.cpp | 32 ++++++++++++++++++++++------- src/llmq/blockprocessor.h | 7 +++++++ src/llmq/commitment.cpp | 41 ++++++++++++++++++++++++++----------- src/llmq/commitment.h | 10 +++++++-- src/llmq/options.h | 5 +++++ src/llmq/utils.cpp | 19 +++++++++++++++++ src/llmq/utils.h | 29 ++++++++++++++++++++++++++ 8 files changed, 124 insertions(+), 21 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 8d9acab7bc9f..e7e99a39a9b8 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -529,6 +529,8 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-minimumchainwork=", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-par=", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)", -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-parbls=", strprintf("Set the number of bls verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)", + -GetNumCores(), llmq::MAX_BLSCHECK_THREADS, llmq::DEFAULT_BLSCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-pid=", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-prune=", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -rescan and -disablegovernance=false. " diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 1aef5ecb1d80..6b862fe4b90b 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -52,8 +53,25 @@ CQuorumBlockProcessor::CQuorumBlockProcessor(CChainState& chainstate, CDetermini m_qsnapman(qsnapman) { utils::InitQuorumsCache(mapHasMinedCommitmentCache); + + int bls_threads = gArgs.GetIntArg("-parbls", DEFAULT_BLSCHECK_THREADS); + if (bls_threads <= 0) { + // -parbls=0 means autodetect (number of cores - 1 validator threads) + // -parbls=-n means "leave n cores free" (number of cores - n - 1 validator threads) + bls_threads += GetNumCores(); + } + // Subtract 1 because the main thread counts towards the par threads + bls_threads = std::max(bls_threads - 1, 0); + + // Number of script-checking threads <= MAX_BLSCHECK_THREADS + bls_threads = std::min(bls_threads, MAX_BLSCHECK_THREADS); + + LogPrintf("Bls verification uses %d additional threads\n", bls_threads); + m_bls_queue.StartWorkerThreads(bls_threads); } +CQuorumBlockProcessor::~CQuorumBlockProcessor() { m_bls_queue.StopWorkerThreads(); } + MessageProcessingResult CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv) { @@ -197,18 +215,18 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, gsl::not_null queue_control(&m_bls_queue); for (const auto& [_, qc] : qcs) { if (qc.IsNull()) continue; const auto* pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash); - if (!qc.VerifySignature(m_dmnman, m_qsnapman, pQuorumBaseBlockIndex)) { - LogPrintf("[ProcessBlock] failed h[%d] llmqType[%d] version[%d] quorumIndex[%d] quorumHash[%s]\n", - pindex->nHeight, ToUnderlying(qc.llmqType), qc.nVersion, qc.quorumIndex, - qc.quorumHash.ToString()); - return false; - } + qc.VerifySignatureAsync(m_dmnman, m_qsnapman, pQuorumBaseBlockIndex, &queue_control); } - } + if (!queue_control.Wait()) { + // at least one check failed + return false; + } + } for (const auto& [_, qc] : qcs) { if (!ProcessCommitment(pindex->nHeight, blockHash, qc, state, fJustCheck, false)) { LogPrintf("[ProcessBlock] failed h[%d] llmqType[%d] version[%d] quorumIndex[%d] quorumHash[%s]\n", pindex->nHeight, ToUnderlying(qc.llmqType), qc.nVersion, qc.quorumIndex, qc.quorumHash.ToString()); diff --git a/src/llmq/blockprocessor.h b/src/llmq/blockprocessor.h index 5adaaf18099b..382d1a681475 100644 --- a/src/llmq/blockprocessor.h +++ b/src/llmq/blockprocessor.h @@ -7,7 +7,10 @@ #include +#include +#include #include +#include #include #include #include @@ -19,6 +22,7 @@ class BlockValidationState; class CBlock; class CBlockIndex; +class CBLSSignature; class CChain; class CChainState; class CDataStream; @@ -41,6 +45,8 @@ class CQuorumBlockProcessor CEvoDB& m_evoDb; CQuorumSnapshotManager& m_qsnapman; + CCheckQueue m_bls_queue{4}; + mutable Mutex minableCommitmentsCs; std::map, uint256> minableCommitmentsByQuorum GUARDED_BY(minableCommitmentsCs); std::map minableCommitments GUARDED_BY(minableCommitmentsCs); @@ -50,6 +56,7 @@ class CQuorumBlockProcessor public: explicit CQuorumBlockProcessor(CChainState& chainstate, CDeterministicMNManager& dmnman, CEvoDB& evoDb, CQuorumSnapshotManager& qsnapman); + ~CQuorumBlockProcessor(); [[nodiscard]] MessageProcessingResult ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv); diff --git a/src/llmq/commitment.cpp b/src/llmq/commitment.cpp index 8f46e94b50a6..9d3d1f62153d 100644 --- a/src/llmq/commitment.cpp +++ b/src/llmq/commitment.cpp @@ -8,13 +8,14 @@ #include #include +#include #include #include #include #include #include -#include #include +#include namespace llmq { @@ -27,8 +28,9 @@ CFinalCommitment::CFinalCommitment(const Consensus::LLMQParams& params, const ui { } -bool CFinalCommitment::VerifySignature(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, - gsl::not_null pQuorumBaseBlockIndex) const +bool CFinalCommitment::VerifySignatureAsync(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, + gsl::not_null pQuorumBaseBlockIndex, + CCheckQueueControl* queue_control) const { auto members = utils::GetAllQuorumMembers(llmqType, dmnman, qsnapman, pQuorumBaseBlockIndex); const auto& llmq_params_opt = Params().GetLLMQ(llmqType); @@ -63,16 +65,31 @@ bool CFinalCommitment::VerifySignature(CDeterministicMNManager& dmnman, CQuorumS } memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator.Get()); } - - if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) { - LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid aggregated members signature\n", - quorumHash.ToString()); - return false; + std::string members_id_string{ + strprintf("CFinalCommitment -- q[%s] invalid aggregated members signature", quorumHash.ToString())}; + if (queue_control) { + std::vector vChecks; + vChecks.emplace_back(membersSig, memberPubKeys, commitmentHash, members_id_string); + queue_control->Add(vChecks); + } else { + if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) { + LogPrint(BCLog::LLMQ, "%s\n", members_id_string); + return false; + } } } - if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) { - LogPrint(BCLog::LLMQ, "CFinalCommitment -- q[%s] invalid quorum signature\n", quorumHash.ToString()); - return false; + std::string qsig_id_string{strprintf("CFinalCommitment -- q[%s] invalid quorum signature", quorumHash.ToString())}; + if (queue_control) { + std::vector vChecks; + std::vector public_keys; + public_keys.push_back(quorumPublicKey); + vChecks.emplace_back(quorumSig, public_keys, commitmentHash, qsig_id_string); + queue_control->Add(vChecks); + } else { + if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) { + LogPrint(BCLog::LLMQ, "%s\n", qsig_id_string); + return false; + } } return true; } @@ -157,7 +174,7 @@ bool CFinalCommitment::Verify(CDeterministicMNManager& dmnman, CQuorumSnapshotMa // sigs are only checked when the block is processed if (checkSigs) { - if (!VerifySignature(dmnman, qsnapman, pQuorumBaseBlockIndex)) { + if (!VerifySignatureAsync(dmnman, qsnapman, pQuorumBaseBlockIndex, nullptr)) { return false; } } diff --git a/src/llmq/commitment.h b/src/llmq/commitment.h index b5f3cc096b0d..c1e78c6e3459 100644 --- a/src/llmq/commitment.h +++ b/src/llmq/commitment.h @@ -25,11 +25,16 @@ class CBlockIndex; class CDeterministicMNManager; class ChainstateManager; class TxValidationState; +template +class CCheckQueueControl; namespace llmq { class CQuorumSnapshotManager; +namespace utils { +struct BlsCheck; +} // namespace utils // This message is an aggregation of all received premature commitments and only valid if // enough (>=threshold) premature commitments were aggregated // This is mined on-chain as part of TRANSACTION_QUORUM_COMMITMENT @@ -67,8 +72,9 @@ class CFinalCommitment return int(std::count(validMembers.begin(), validMembers.end(), true)); } - bool VerifySignature(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, - gsl::not_null pQuorumBaseBlockIndex) const; + bool VerifySignatureAsync(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, + gsl::not_null pQuorumBaseBlockIndex, + CCheckQueueControl* queue_control) const; bool Verify(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, gsl::not_null pQuorumBaseBlockIndex, bool checkSigs) const; bool VerifyNull() const; diff --git a/src/llmq/options.h b/src/llmq/options.h index f78f2362fdfc..7bd0f1e59e58 100644 --- a/src/llmq/options.h +++ b/src/llmq/options.h @@ -24,6 +24,11 @@ enum class QvvecSyncMode { OnlyIfTypeMember = 1, }; +/** Maximum number of dedicated script-checking threads allowed */ +static const int MAX_BLSCHECK_THREADS = 31; +/** -parbls default (number of bls-checking threads, 0 = auto) */ +static const int DEFAULT_BLSCHECK_THREADS = 0; + static constexpr bool DEFAULT_ENABLE_QUORUM_DATA_RECOVERY{true}; // If true, we will connect to all new quorums and watch their communication diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 49b6f82334ab..3a3a2c733c45 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -940,6 +940,25 @@ void AddQuorumProbeConnections(const Consensus::LLMQParams& llmqParams, CConnman } } +bool BlsCheck::operator()() +{ + if (m_pubkeys.size() > 1) { + if (!m_sig.VerifySecureAggregated(m_pubkeys, m_msg_hash)) { + LogPrint(BCLog::LLMQ, "%s\n", m_id_string); + return false; + } + } else if (m_pubkeys.size() == 1) { + if (!m_sig.VerifyInsecure(m_pubkeys.back(), m_msg_hash)) { + LogPrint(BCLog::LLMQ, "%s\n", m_id_string); + return false; + } + } else { + // It is supposed to be at least one public key! + return false; + } + return true; +} + template void InitQuorumsCache(CacheType& cache, bool limit_by_connections) { diff --git a/src/llmq/utils.h b/src/llmq/utils.h index 88cbdd174789..e34b2137e275 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_LLMQ_UTILS_H #define BITCOIN_LLMQ_UTILS_H +#include #include #include #include @@ -56,8 +57,36 @@ void AddQuorumProbeConnections(const Consensus::LLMQParams& llmqParams, CConnman const CSporkManager& sporkman, const CDeterministicMNList& tip_mn_list, gsl::not_null pQuorumBaseBlockIndex, const uint256& myProTxHash); +struct BlsCheck { + CBLSSignature m_sig; + std::vector m_pubkeys; + uint256 m_msg_hash; + std::string m_id_string; + + BlsCheck() = default; + + BlsCheck(CBLSSignature sig, std::vector pubkeys, uint256 msg_hash, std::string id_string) : + m_sig(sig), + m_pubkeys(pubkeys), + m_msg_hash(msg_hash), + m_id_string(id_string) + { + } + + void swap(BlsCheck& obj) + { + std::swap(m_sig, obj.m_sig); + std::swap(m_pubkeys, obj.m_pubkeys); + std::swap(m_msg_hash, obj.m_msg_hash); + std::swap(m_id_string, obj.m_id_string); + } + + bool operator()(); +}; + template void InitQuorumsCache(CacheType& cache, bool limit_by_connections = true); + } // namespace utils } // namespace llmq From 7a86e19201a53eb544798468178048d53a12d263 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 24 Jul 2025 23:04:46 +0700 Subject: [PATCH 3/7] refactor: drop unused flag from ProcessCommitment --- src/llmq/blockprocessor.cpp | 8 +++++--- src/llmq/blockprocessor.h | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 6b862fe4b90b..2ce8cb26a0ec 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -228,7 +228,7 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, gsl::not_nullnHeight, blockHash, qc, state, fJustCheck, false)) { + if (!ProcessCommitment(pindex->nHeight, blockHash, qc, state, fJustCheck)) { LogPrintf("[ProcessBlock] failed h[%d] llmqType[%d] version[%d] quorumIndex[%d] quorumHash[%s]\n", pindex->nHeight, ToUnderlying(qc.llmqType), qc.nVersion, qc.quorumIndex, qc.quorumHash.ToString()); return false; } @@ -268,7 +268,8 @@ static bool IsMiningPhase(const Consensus::LLMQParams& llmqParams, const CChain& return nHeight >= quorumCycleMiningStartHeight && nHeight <= quorumCycleMiningEndHeight; } -bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, BlockValidationState& state, bool fJustCheck, bool fBLSChecks) +bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, + BlockValidationState& state, bool fJustCheck) { AssertLockHeld(::cs_main); @@ -334,7 +335,8 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH const auto* pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash); - if (!qc.Verify(m_dmnman, m_qsnapman, pQuorumBaseBlockIndex, /*checkSigs=*/fBLSChecks)) { + // we don't validate signatures here; they already validated on previous step + if (!qc.Verify(m_dmnman, m_qsnapman, pQuorumBaseBlockIndex, /*checksigs=*/false)) { LogPrint(BCLog::LLMQ, /* Continued */ "%s -- height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, " "quorumPublicKey=%s qc verify failed.\n", diff --git a/src/llmq/blockprocessor.h b/src/llmq/blockprocessor.h index 382d1a681475..b080d1afe8c3 100644 --- a/src/llmq/blockprocessor.h +++ b/src/llmq/blockprocessor.h @@ -82,7 +82,8 @@ class CQuorumBlockProcessor std::optional GetLastMinedCommitmentsByQuorumIndexUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, int quorumIndex, size_t cycle) const; private: static bool GetCommitmentsFromBlock(const CBlock& block, gsl::not_null pindex, std::multimap& ret, BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - bool ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, BlockValidationState& state, bool fJustCheck, bool fBLSChecks) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + bool ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, + BlockValidationState& state, bool fJustCheck) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); size_t GetNumCommitmentsRequired(const Consensus::LLMQParams& llmqParams, int nHeight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); static uint256 GetQuorumBlockHash(const Consensus::LLMQParams& llmqParams, const CChain& active_chain, int nHeight, int quorumIndex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); }; From 8530c921179a553c5f91692b4876bd9453564c39 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 24 Jul 2025 23:52:40 +0700 Subject: [PATCH 4/7] fix: replace strings Bls to BLS in command line help Co-authored-by: UdjinM6 --- src/init.cpp | 2 +- src/llmq/blockprocessor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index e7e99a39a9b8..b76c5c1f1831 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -529,7 +529,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-minimumchainwork=", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-par=", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)", -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-parbls=", strprintf("Set the number of bls verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)", + argsman.AddArg("-parbls=", strprintf("Set the number of BLS verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)", -GetNumCores(), llmq::MAX_BLSCHECK_THREADS, llmq::DEFAULT_BLSCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-pid=", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 2ce8cb26a0ec..34ff37f721d5 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -66,7 +66,7 @@ CQuorumBlockProcessor::CQuorumBlockProcessor(CChainState& chainstate, CDetermini // Number of script-checking threads <= MAX_BLSCHECK_THREADS bls_threads = std::min(bls_threads, MAX_BLSCHECK_THREADS); - LogPrintf("Bls verification uses %d additional threads\n", bls_threads); + LogPrintf("BLS verification uses %d additional threads\n", bls_threads); m_bls_queue.StartWorkerThreads(bls_threads); } From 2d4fe1b80985beb3eed985f4c25db8675bed70f0 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 25 Jul 2025 12:29:47 +0700 Subject: [PATCH 5/7] fix: set proper state after async signatures validation in block processor --- src/llmq/blockprocessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 34ff37f721d5..17c7d562c7d9 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -224,7 +224,7 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, gsl::not_null Date: Tue, 5 Aug 2025 00:11:58 +0700 Subject: [PATCH 6/7] fix: increased "max amount of threads" for bls check from 31 to 33 Blocks could have 0, 2 or 32 commitments currently; further benchmarking is needed to find a point where is a balance point, but likely it's somewhere between 32 and 64; because each quorum commitment have 2 BLS signatures --- src/llmq/options.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llmq/options.h b/src/llmq/options.h index 7bd0f1e59e58..b8c02804bde2 100644 --- a/src/llmq/options.h +++ b/src/llmq/options.h @@ -25,7 +25,7 @@ enum class QvvecSyncMode { }; /** Maximum number of dedicated script-checking threads allowed */ -static const int MAX_BLSCHECK_THREADS = 31; +static const int MAX_BLSCHECK_THREADS = 33; /** -parbls default (number of bls-checking threads, 0 = auto) */ static const int DEFAULT_BLSCHECK_THREADS = 0; From 77337406c9c8d8b0c73cac450f1e2c63a1e81093 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 5 Aug 2025 00:13:49 +0700 Subject: [PATCH 7/7] fix: add missing log for case of no public key is provided --- src/llmq/utils.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 3a3a2c733c45..245c4d17ec38 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -953,7 +953,8 @@ bool BlsCheck::operator()() return false; } } else { - // It is supposed to be at least one public key! + // we should not get there ever + LogPrint(BCLog::LLMQ, "%s - no public keys are provided\n", m_id_string); return false; } return true;