Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <llmq/blockprocessor.h>
6 :
7 : #include <evo/evodb.h>
8 : #include <evo/specialtx.h>
9 : #include <llmq/commitment.h>
10 : #include <llmq/options.h>
11 : #include <llmq/utils.h>
12 : #include <util/helpers.h>
13 : #include <util/std23.h>
14 :
15 : #include <chain.h>
16 : #include <chainparams.h>
17 : #include <checkqueue.h>
18 : #include <consensus/params.h>
19 : #include <consensus/validation.h>
20 : #include <deploymentstatus.h>
21 : #include <net.h>
22 : #include <primitives/block.h>
23 : #include <primitives/transaction.h>
24 : #include <saltedhasher.h>
25 : #include <sync.h>
26 : #include <validation.h>
27 :
28 : #include <map>
29 :
30 194079 : static void PreComputeQuorumMembers(CDeterministicMNManager& dmnman, llmq::CQuorumSnapshotManager& qsnapman,
31 : const ChainstateManager& chainman, const CBlockIndex* pindex, bool reset_cache)
32 : {
33 826478 : for (const Consensus::LLMQParams& params : llmq::GetEnabledQuorumParams(chainman, pindex->pprev)) {
34 632399 : if (llmq::IsQuorumRotationEnabled(params, pindex) && (pindex->nHeight % params.dkgInterval == 0)) {
35 7236 : llmq::utils::GetAllQuorumMembers(params.type, {dmnman, qsnapman, chainman, pindex}, reset_cache);
36 7236 : }
37 : }
38 194079 : }
39 :
40 : namespace llmq
41 : {
42 : static const std::string DB_MINED_COMMITMENT = "q_mc";
43 : static const std::string DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT = "q_mcih";
44 : static const std::string DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT_Q_INDEXED = "q_mcihi";
45 :
46 : static const std::string DB_BEST_BLOCK_UPGRADE = "q_bbu2";
47 :
48 6126 : CQuorumBlockProcessor::CQuorumBlockProcessor(CChainState& chainstate, CDeterministicMNManager& dmnman, CEvoDB& evoDb,
49 : CQuorumSnapshotManager& qsnapman, int8_t bls_threads) :
50 3063 : m_chainstate{chainstate},
51 3063 : m_dmnman{dmnman},
52 3063 : m_evoDb{evoDb},
53 3063 : m_qsnapman{qsnapman}
54 3063 : {
55 : utils::InitQuorumsCache(mapHasMinedCommitmentCache, m_chainstate.m_chainman.GetConsensus());
56 : LogPrintf("BLS verification uses %d additional threads\n", bls_threads);
57 : m_bls_queue.StartWorkerThreads(bls_threads);
58 3063 : }
59 :
60 6126 : CQuorumBlockProcessor::~CQuorumBlockProcessor()
61 3063 : {
62 3063 : m_bls_queue.StopWorkerThreads();
63 6126 : }
64 :
65 105757 : MessageProcessingResult CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view msg_type,
66 : CDataStream& vRecv)
67 : {
68 105757 : if (msg_type != NetMsgType::QFCOMMITMENT) {
69 103190 : return {};
70 : }
71 :
72 2567 : CFinalCommitment qc;
73 2567 : vRecv >> qc;
74 :
75 2567 : MessageProcessingResult ret;
76 2567 : ret.m_to_erase = CInv{MSG_QUORUM_FINAL_COMMITMENT, ::SerializeHash(qc)};
77 :
78 2567 : if (qc.IsNull()) {
79 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- null commitment from peer=%d\n", __func__, peer.GetId());
80 0 : ret.m_error = MisbehavingError{100};
81 0 : return ret;
82 : }
83 :
84 2567 : const auto& llmq_params_opt = Params().GetLLMQ(qc.llmqType);
85 2567 : if (!llmq_params_opt.has_value()) {
86 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- invalid commitment type %d from peer=%d\n", __func__,
87 : std23::to_underlying(qc.llmqType), peer.GetId());
88 0 : ret.m_error = MisbehavingError{100};
89 0 : return ret;
90 : }
91 2567 : auto type = qc.llmqType;
92 :
93 : // Verify that quorumHash is part of the active chain and that it's the first block in the DKG interval
94 : const CBlockIndex* pQuorumBaseBlockIndex;
95 : {
96 2567 : LOCK(::cs_main);
97 2567 : pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash);
98 2567 : if (pQuorumBaseBlockIndex == nullptr) {
99 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- unknown block %s in commitment, peer=%d\n", __func__,
100 : qc.quorumHash.ToString(), peer.GetId());
101 : // can't really punish the node here, as we might simply be the one that is on the wrong chain or not
102 : // fully synced
103 0 : return ret;
104 : }
105 2567 : if (m_chainstate.m_chain.Tip()->GetAncestor(pQuorumBaseBlockIndex->nHeight) != pQuorumBaseBlockIndex) {
106 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- block %s not in active chain, peer=%d\n", __func__,
107 : qc.quorumHash.ToString(), peer.GetId());
108 : // same, can't punish
109 0 : return ret;
110 : }
111 5134 : if (int quorumHeight = pQuorumBaseBlockIndex->nHeight - (pQuorumBaseBlockIndex->nHeight % llmq_params_opt->dkgInterval) + int(qc.quorumIndex);
112 2567 : quorumHeight != pQuorumBaseBlockIndex->nHeight) {
113 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- block %s is not the first block in the DKG interval, peer=%d\n", __func__,
114 : qc.quorumHash.ToString(), peer.GetId());
115 0 : ret.m_error = MisbehavingError{100};
116 0 : return ret;
117 : }
118 2567 : if (pQuorumBaseBlockIndex->nHeight < (m_chainstate.m_chain.Height() - llmq_params_opt->dkgInterval)) {
119 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- block %s is too old, peer=%d\n", __func__,
120 : qc.quorumHash.ToString(), peer.GetId());
121 0 : if (peer.GetCommonVersion() >= QFCOMMIT_STALE_REPROP_BAN_VERSION) {
122 0 : ret.m_error = MisbehavingError{100};
123 0 : }
124 0 : return ret;
125 : }
126 2567 : if (HasMinedCommitment(type, qc.quorumHash)) {
127 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- commitment for quorum hash[%s], type[%d], quorumIndex[%d] is already mined, peer=%d\n",
128 : __func__, qc.quorumHash.ToString(), std23::to_underlying(type), qc.quorumIndex, peer.GetId());
129 : // NOTE: do not punish here
130 0 : return ret;
131 : }
132 2567 : }
133 :
134 : {
135 : // Check if we already got a better one locally
136 : // We do this before verifying the commitment to avoid DoS
137 2567 : LOCK(minableCommitmentsCs);
138 2567 : auto k = std::make_pair(type, qc.quorumHash);
139 2567 : auto it = minableCommitmentsByQuorum.find(k);
140 2567 : if (it != minableCommitmentsByQuorum.end()) {
141 76 : auto jt = minableCommitments.find(it->second);
142 76 : if (jt != minableCommitments.end() && jt->second.CountSigners() <= qc.CountSigners()) {
143 62 : return ret;
144 : }
145 14 : }
146 2567 : }
147 :
148 2505 : if (!qc.Verify({m_dmnman, m_qsnapman, m_chainstate.m_chainman, pQuorumBaseBlockIndex}, /*checkSigs=*/true)) {
149 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- commitment for quorum %s:%d is not valid quorumIndex[%d] nversion[%d], peer=%d\n",
150 : __func__, qc.quorumHash.ToString(),
151 : std23::to_underlying(qc.llmqType), qc.quorumIndex, qc.nVersion, peer.GetId());
152 0 : ret.m_error = MisbehavingError{100};
153 0 : return ret;
154 : }
155 :
156 2505 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- received commitment for quorum %s:%d, validMembers=%d, signers=%d, peer=%d\n", __func__,
157 : qc.quorumHash.ToString(), std23::to_underlying(qc.llmqType), qc.CountValidMembers(), qc.CountSigners(), peer.GetId());
158 :
159 2505 : if (auto inv_opt = AddMineableCommitment(qc)) {
160 2451 : ret.m_inventory.emplace_back(inv_opt.value());
161 2451 : }
162 2505 : return ret;
163 105757 : }
164 :
165 341228 : bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex, BlockValidationState& state, bool fJustCheck, bool fBLSChecks)
166 : {
167 341228 : AssertLockHeld(::cs_main);
168 :
169 341228 : const auto blockHash = pindex->GetBlockHash();
170 :
171 341228 : if (!DeploymentActiveAt(*pindex, m_chainstate.m_chainman.GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) {
172 173715 : m_evoDb.Write(DB_BEST_BLOCK_UPGRADE, blockHash);
173 173715 : return true;
174 : }
175 :
176 167513 : PreComputeQuorumMembers(m_dmnman, m_qsnapman, m_chainstate.m_chainman, pindex, /*reset_cache=*/false);
177 :
178 167513 : std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;
179 167513 : if (!GetCommitmentsFromBlock(block, pindex, qcs, state)) {
180 0 : return false;
181 : }
182 :
183 : // The following checks make sure that there is always a (possibly null) commitment while in the mining phase
184 : // until the first non-null commitment has been mined. After the non-null commitment, no other commitments are
185 : // allowed, including null commitments.
186 : // Note: must only check quorums that were enabled at the _previous_ block height to match mining logic
187 717869 : for (const Consensus::LLMQParams& params : GetEnabledQuorumParams(m_chainstate.m_chainman, pindex->pprev)) {
188 : // skip these checks when replaying blocks after the crash
189 550356 : if (m_chainstate.m_chain.Tip() == nullptr) {
190 0 : break;
191 : }
192 :
193 550356 : const size_t numCommitmentsRequired = GetNumCommitmentsRequired(params, pindex->nHeight);
194 550356 : const auto numCommitmentsInNewBlock = qcs.count(params.type);
195 :
196 550356 : if (numCommitmentsRequired < numCommitmentsInNewBlock) {
197 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-not-allowed");
198 : }
199 :
200 550356 : if (numCommitmentsRequired > numCommitmentsInNewBlock) {
201 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-missing");
202 : }
203 550356 : if (IsQuorumRotationEnabled(params, pindex)) {
204 155043 : LogPrintf("[ProcessBlock] h[%d] numCommitmentsRequired[%d] numCommitmentsInNewBlock[%d]\n", pindex->nHeight, numCommitmentsRequired, numCommitmentsInNewBlock);
205 155043 : }
206 : }
207 :
208 167513 : if (fBLSChecks) {
209 167513 : CCheckQueueControl<utils::BlsCheck> queue_control(&m_bls_queue);
210 410748 : for (const auto& [_, qc] : qcs) {
211 233393 : if (qc.IsNull()) continue;
212 9842 : const auto* pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash);
213 4921 : if (pQuorumBaseBlockIndex == nullptr) {
214 0 : LogPrint(BCLog::LLMQ, "[ProcessBlock] h[%d] unexpectedly failed due to no known pindex for hash[%s]\n",
215 : pindex->nHeight, qc.quorumHash.ToString());
216 0 : return false;
217 : }
218 4921 : qc.VerifySignatureAsync({m_dmnman, m_qsnapman, m_chainstate.m_chainman, pQuorumBaseBlockIndex}, &queue_control);
219 : }
220 :
221 167513 : if (!queue_control.Wait()) {
222 : // at least one check failed
223 4 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-invalid");
224 : }
225 167513 : }
226 400894 : for (const auto& [_, qc] : qcs) {
227 233385 : if (!ProcessCommitment(pindex->nHeight, blockHash, qc, state, fJustCheck)) {
228 0 : LogPrintf("[ProcessBlock] failed h[%d] llmqType[%d] version[%d] quorumIndex[%d] quorumHash[%s]\n", pindex->nHeight, std23::to_underlying(qc.llmqType), qc.nVersion, qc.quorumIndex, qc.quorumHash.ToString());
229 0 : return false;
230 : }
231 : }
232 :
233 167509 : m_evoDb.Write(DB_BEST_BLOCK_UPGRADE, blockHash);
234 :
235 167509 : return true;
236 341228 : }
237 :
238 : // We store a mapping from minedHeight->quorumHeight in the DB
239 : // minedHeight is inversed so that entries are traversable in reversed order
240 2397967 : static std::tuple<std::string, Consensus::LLMQType, uint32_t> BuildInversedHeightKey(Consensus::LLMQType llmqType, int nMinedHeight)
241 : {
242 : // nMinedHeight must be converted to big endian to make it comparable when serialized
243 2397967 : return std::make_tuple(DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT, llmqType, htobe32_internal(std::numeric_limits<uint32_t>::max() - nMinedHeight));
244 : }
245 :
246 1424209 : static std::tuple<std::string, Consensus::LLMQType, int, uint32_t> BuildInversedHeightKeyIndexed(Consensus::LLMQType llmqType, int nMinedHeight, int quorumIndex)
247 : {
248 : // nMinedHeight must be converted to big endian to make it comparable when serialized
249 1424209 : return std::make_tuple(DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT_Q_INDEXED, llmqType, quorumIndex, htobe32_internal(std::numeric_limits<uint32_t>::max() - nMinedHeight));
250 : }
251 :
252 728981 : static bool IsMiningPhase(const Consensus::LLMQParams& llmqParams, const CChain& active_chain, int nHeight)
253 : EXCLUSIVE_LOCKS_REQUIRED(cs_main)
254 : {
255 728981 : AssertLockHeld(cs_main);
256 :
257 : // Note: This function can be called for new blocks
258 728981 : assert(nHeight <= active_chain.Height() + 1);
259 :
260 728981 : int quorumCycleStartHeight = nHeight - (nHeight % llmqParams.dkgInterval);
261 728981 : int quorumCycleMiningStartHeight = quorumCycleStartHeight + llmqParams.dkgMiningWindowStart;
262 728981 : int quorumCycleMiningEndHeight = quorumCycleStartHeight + llmqParams.dkgMiningWindowEnd;
263 :
264 728981 : return nHeight >= quorumCycleMiningStartHeight && nHeight <= quorumCycleMiningEndHeight;
265 : }
266 :
267 233385 : bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc,
268 : BlockValidationState& state, bool fJustCheck)
269 : {
270 233385 : AssertLockHeld(::cs_main);
271 :
272 233385 : const auto& llmq_params_opt = Params().GetLLMQ(qc.llmqType);
273 233385 : if (!llmq_params_opt.has_value()) {
274 0 : LogPrint(BCLog::LLMQ, "%s -- invalid commitment type %d\n", __func__, std23::to_underlying(qc.llmqType));
275 0 : return false;
276 : }
277 233385 : const auto& llmq_params = llmq_params_opt.value();
278 :
279 233385 : uint256 quorumHash = GetQuorumBlockHash(llmq_params, m_chainstate.m_chain, nHeight, qc.quorumIndex);
280 :
281 233385 : LogPrint(BCLog::LLMQ, /* Continued */
282 : "%s -- processing commitment for block height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, "
283 : "validMembers=%d, quorumPublicKey=%s "
284 : "fJustCheck[%d] processing commitment from block.\n",
285 : __func__, nHeight, std23::to_underlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(),
286 : qc.CountValidMembers(), qc.quorumPublicKey.ToString(), fJustCheck);
287 :
288 : // skip `bad-qc-block` checks below when replaying blocks after the crash
289 233385 : if (m_chainstate.m_chain.Tip() == nullptr) {
290 0 : quorumHash = qc.quorumHash;
291 0 : }
292 :
293 233385 : if (quorumHash.IsNull()) {
294 0 : LogPrint(BCLog::LLMQ, /* Continued */
295 : "%s -- height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, "
296 : "quorumPublicKey=%s quorumHash is null.\n",
297 : __func__, nHeight, std23::to_underlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(),
298 : qc.CountValidMembers(), qc.quorumPublicKey.ToString());
299 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-block");
300 : }
301 233385 : if (quorumHash != qc.quorumHash) {
302 0 : LogPrint(BCLog::LLMQ, /* Continued */
303 : "%s -- height=%d, type=%d, quorumIndex=%d, quorumHash=%s, qc.quorumHash=%s signers=%s, "
304 : "validMembers=%d, quorumPublicKey=%s non equal quorumHash.\n",
305 : __func__, nHeight, std23::to_underlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(),
306 : qc.quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString());
307 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-block");
308 : }
309 :
310 233385 : if (qc.IsNull()) {
311 228468 : if (!qc.VerifyNull()) {
312 0 : LogPrint(BCLog::LLMQ, /* Continued */
313 : "%s -- height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%dqc "
314 : "verifynull failed.\n",
315 : __func__, nHeight, std23::to_underlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(),
316 : qc.CountSigners(), qc.CountValidMembers());
317 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-invalid-null");
318 : }
319 228468 : return true;
320 : }
321 :
322 4917 : if (HasMinedCommitment(llmq_params.type, quorumHash)) {
323 : // should not happen as it's already handled in ProcessBlock
324 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-dup");
325 : }
326 :
327 4917 : if (!IsMiningPhase(llmq_params, m_chainstate.m_chain, nHeight)) {
328 : // should not happen as it's already handled in ProcessBlock
329 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-height");
330 : }
331 :
332 4917 : const auto* pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash);
333 4917 : if (pQuorumBaseBlockIndex == nullptr) {
334 0 : LogPrint(BCLog::LLMQ, "%s -- unexpectedly failed due to no known pindex for hash[%s]\n", __func__,
335 : qc.quorumHash.ToString());
336 0 : return false;
337 : }
338 :
339 : // we don't validate signatures here; they already validated on previous step
340 4917 : if (!qc.Verify({m_dmnman, m_qsnapman, m_chainstate.m_chainman, pQuorumBaseBlockIndex}, /*checksigs=*/false)) {
341 0 : LogPrint(BCLog::LLMQ, /* Continued */
342 : "%s -- height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, "
343 : "quorumPublicKey=%s qc verify failed.\n",
344 : __func__, nHeight, std23::to_underlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(),
345 : qc.CountValidMembers(), qc.quorumPublicKey.ToString());
346 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-invalid");
347 : }
348 :
349 4917 : if (fJustCheck) {
350 934 : return true;
351 : }
352 :
353 3983 : bool rotation_enabled = IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex);
354 :
355 3983 : if (rotation_enabled) {
356 1573 : LogPrint(BCLog::LLMQ, "%s -- height[%d] pQuorumBaseBlockIndex[%d] quorumIndex[%d] qversion[%d] Built\n",
357 : __func__, nHeight, pQuorumBaseBlockIndex->nHeight, qc.quorumIndex, qc.nVersion);
358 1573 : }
359 :
360 : // Store commitment in DB
361 3983 : auto cacheKey = std::make_pair(llmq_params.type, quorumHash);
362 3983 : m_evoDb.Write(std::make_pair(DB_MINED_COMMITMENT, cacheKey), std::make_pair(qc, blockHash));
363 :
364 3983 : if (rotation_enabled) {
365 1573 : m_evoDb.Write(BuildInversedHeightKeyIndexed(llmq_params.type, nHeight, int(qc.quorumIndex)), pQuorumBaseBlockIndex->nHeight);
366 1573 : } else {
367 2410 : m_evoDb.Write(BuildInversedHeightKey(llmq_params.type, nHeight), pQuorumBaseBlockIndex->nHeight);
368 : }
369 :
370 : {
371 3983 : LOCK(minableCommitmentsCs);
372 3983 : mapHasMinedCommitmentCache[qc.llmqType].erase(qc.quorumHash);
373 3983 : minableCommitmentsByQuorum.erase(cacheKey);
374 3983 : minableCommitments.erase(::SerializeHash(qc));
375 3983 : }
376 :
377 3983 : LogPrint(BCLog::LLMQ, "%s -- processed commitment from block. type=%d, quorumIndex=%d, quorumHash=%s\n", __func__,
378 : std23::to_underlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString());
379 :
380 3983 : return true;
381 233385 : }
382 :
383 26566 : bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex)
384 : {
385 26566 : AssertLockHeld(::cs_main);
386 :
387 26566 : PreComputeQuorumMembers(m_dmnman, m_qsnapman, m_chainstate.m_chainman, pindex, /*reset_cache=*/true);
388 :
389 26566 : std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;
390 26566 : if (BlockValidationState dummy; !GetCommitmentsFromBlock(block, pindex, qcs, dummy)) {
391 0 : return false;
392 : }
393 :
394 37292 : for (auto& [_, qc2] : qcs) {
395 10726 : auto& qc = qc2; // cannot capture structured binding into lambda
396 10726 : if (qc.IsNull()) {
397 10635 : continue;
398 : }
399 :
400 91 : m_evoDb.Erase(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash)));
401 :
402 91 : const auto& llmq_params_opt = Params().GetLLMQ(qc.llmqType);
403 91 : assert(llmq_params_opt.has_value());
404 :
405 91 : if (IsQuorumRotationEnabled(llmq_params_opt.value(), pindex)) {
406 16 : m_evoDb.Erase(BuildInversedHeightKeyIndexed(qc.llmqType, pindex->nHeight, int(qc.quorumIndex)));
407 16 : } else {
408 75 : m_evoDb.Erase(BuildInversedHeightKey(qc.llmqType, pindex->nHeight));
409 : }
410 :
411 182 : WITH_LOCK(minableCommitmentsCs, mapHasMinedCommitmentCache[qc.llmqType].erase(qc.quorumHash));
412 :
413 : // if a reorg happened, we should allow to mine this commitment later
414 91 : AddMineableCommitment(qc);
415 : }
416 :
417 26566 : m_evoDb.Write(DB_BEST_BLOCK_UPGRADE, pindex->pprev->GetBlockHash());
418 :
419 26566 : return true;
420 26566 : }
421 :
422 194079 : bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex, std::multimap<Consensus::LLMQType, CFinalCommitment>& ret, BlockValidationState& state)
423 : {
424 194079 : AssertLockHeld(::cs_main);
425 :
426 194079 : const auto& consensus = Params().GetConsensus();
427 :
428 194079 : ret.clear();
429 :
430 673680 : for (const auto& tx : block.vtx) {
431 479601 : if (tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
432 244119 : auto opt_qc = GetTxPayload<CFinalCommitmentTxPayload>(*tx);
433 244119 : if (!opt_qc) {
434 : // should not happen as it was verified before processing the block
435 0 : LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d GetTxPayload fails\n", __func__, pindex->nHeight);
436 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-payload");
437 : }
438 244119 : auto& qc = *opt_qc;
439 :
440 244119 : const auto& llmq_params_opt = Params().GetLLMQ(qc.commitment.llmqType);
441 244119 : if (!llmq_params_opt.has_value()) {
442 : // should not happen as it was verified before processing the block
443 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-commitment-type");
444 : }
445 :
446 : // only allow one commitment per type and per block (This was changed with rotation)
447 244119 : if (!IsQuorumRotationEnabled(llmq_params_opt.value(), pindex)) {
448 136916 : if (ret.count(qc.commitment.llmqType) != 0) {
449 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-dup");
450 : }
451 136916 : }
452 :
453 244119 : ret.emplace(qc.commitment.llmqType, std::move(qc.commitment));
454 244119 : }
455 : }
456 :
457 194079 : if (!DeploymentActiveAt(*pindex, consensus, Consensus::DEPLOYMENT_DIP0003) && !ret.empty()) {
458 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-premature");
459 : }
460 :
461 194079 : return true;
462 194079 : }
463 :
464 724064 : size_t CQuorumBlockProcessor::GetNumCommitmentsRequired(const Consensus::LLMQParams& llmqParams, int nHeight) const
465 : {
466 724064 : AssertLockHeld(::cs_main);
467 :
468 724064 : if (!IsMiningPhase(llmqParams, m_chainstate.m_chain, nHeight)) return 0;
469 :
470 : // Note: This function can be called for new blocks
471 261603 : assert(nHeight <= m_chainstate.m_chain.Height() + 1);
472 261603 : const auto *const pindex = m_chainstate.m_chain.Height() < nHeight ? m_chainstate.m_chain.Tip() : m_chainstate.m_chain.Tip()->GetAncestor(nHeight);
473 :
474 261603 : bool rotation_enabled = IsQuorumRotationEnabled(llmqParams, pindex);
475 261603 : size_t quorums_num = rotation_enabled ? llmqParams.signingActiveQuorumCount : 1;
476 261603 : size_t ret{0};
477 :
478 594228 : for (const auto quorumIndex : util::irange(quorums_num)) {
479 332625 : uint256 quorumHash = GetQuorumBlockHash(llmqParams, m_chainstate.m_chain, nHeight, quorumIndex);
480 332625 : if (!quorumHash.IsNull() && !HasMinedCommitment(llmqParams.type, quorumHash)) ++ret;
481 : }
482 :
483 261603 : return ret;
484 724064 : }
485 :
486 : // WARNING: This method returns uint256() on the first block of the DKG interval (because the block hash is not known yet)
487 628656 : uint256 CQuorumBlockProcessor::GetQuorumBlockHash(const Consensus::LLMQParams& llmqParams, const CChain& active_chain, int nHeight, int quorumIndex)
488 : {
489 628656 : AssertLockHeld(::cs_main);
490 :
491 628656 : int quorumStartHeight = nHeight - (nHeight % llmqParams.dkgInterval) + quorumIndex;
492 :
493 628656 : const CBlockIndex* pindex = active_chain[quorumStartHeight];
494 628656 : if (pindex == nullptr) {
495 0 : LogPrint(BCLog::LLMQ, "[GetQuorumBlockHash] llmqType[%d] h[%d] qi[%d] quorumStartHeight[%d] quorumHash[EMPTY]\n", std23::to_underlying(llmqParams.type), nHeight, quorumIndex, quorumStartHeight);
496 0 : return {};
497 : }
498 :
499 628656 : return pindex->GetBlockHash();
500 628656 : }
501 :
502 602919 : bool CQuorumBlockProcessor::HasMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash) const
503 : {
504 : bool fExists;
505 1176223 : if (LOCK(minableCommitmentsCs); mapHasMinedCommitmentCache[llmqType].get(quorumHash, fExists)) {
506 573304 : return fExists;
507 : }
508 :
509 29615 : fExists = m_evoDb.Exists(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(llmqType, quorumHash)));
510 :
511 29615 : LOCK(minableCommitmentsCs);
512 29615 : mapHasMinedCommitmentCache[llmqType].insert(quorumHash, fExists);
513 :
514 29615 : return fExists;
515 602919 : }
516 :
517 10450 : std::pair<CFinalCommitment, uint256> CQuorumBlockProcessor::GetMinedCommitment(Consensus::LLMQType llmqType,
518 : const uint256& quorumHash) const
519 : {
520 10450 : auto key = std::make_pair(DB_MINED_COMMITMENT, std::make_pair(llmqType, quorumHash));
521 10450 : std::pair<CFinalCommitment, uint256> ret;
522 10450 : if (!m_evoDb.Read(key, ret)) {
523 0 : return {CFinalCommitment{}, uint256::ZERO};
524 : }
525 10450 : return ret;
526 10450 : }
527 :
528 : // The returned quorums are in reversed order, so the most recent one is at index 0
529 1197741 : std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetMinedCommitmentsUntilBlock(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pindex, size_t maxCount) const
530 : {
531 1197741 : AssertLockNotHeld(m_evoDb.cs);
532 1197741 : LOCK(m_evoDb.cs);
533 :
534 1197741 : auto dbIt = m_evoDb.GetCurTransaction().NewIteratorUniquePtr();
535 :
536 1197741 : auto firstKey = BuildInversedHeightKey(llmqType, pindex->nHeight);
537 1197741 : auto lastKey = BuildInversedHeightKey(llmqType, 0);
538 :
539 1197741 : dbIt->Seek(firstKey);
540 :
541 1197741 : std::vector<const CBlockIndex*> ret;
542 1197741 : ret.reserve(maxCount);
543 :
544 1491887 : while (dbIt->Valid() && ret.size() < maxCount) {
545 1049591 : decltype(firstKey) curKey;
546 : int quorumHeight;
547 1049591 : if (!dbIt->GetKey(curKey) || curKey >= lastKey) {
548 115952 : break;
549 : }
550 933639 : if (std::get<0>(curKey) != DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT || std::get<1>(curKey) != llmqType) {
551 639493 : break;
552 : }
553 :
554 294146 : if (uint32_t nMinedHeight = std::numeric_limits<uint32_t>::max() - be32toh_internal(std::get<2>(curKey));
555 294146 : nMinedHeight > static_cast<uint32_t>(pindex->nHeight)) {
556 0 : break;
557 : }
558 :
559 294146 : if (!dbIt->GetValue(quorumHeight)) {
560 0 : break;
561 : }
562 :
563 294146 : const auto* pQuorumBaseBlockIndex = pindex->GetAncestor(quorumHeight);
564 294146 : assert(pQuorumBaseBlockIndex);
565 294146 : ret.emplace_back(pQuorumBaseBlockIndex);
566 :
567 294146 : dbIt->Next();
568 1049591 : }
569 :
570 1197741 : return ret;
571 1197741 : }
572 :
573 711310 : std::optional<const CBlockIndex*> CQuorumBlockProcessor::GetLastMinedCommitmentsByQuorumIndexUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, int quorumIndex, size_t cycle) const
574 : {
575 711310 : AssertLockNotHeld(m_evoDb.cs);
576 711310 : LOCK(m_evoDb.cs);
577 :
578 711310 : auto dbIt = m_evoDb.GetCurTransaction().NewIteratorUniquePtr();
579 :
580 711310 : auto firstKey = BuildInversedHeightKeyIndexed(llmqType, pindex->nHeight, quorumIndex);
581 711310 : auto lastKey = BuildInversedHeightKeyIndexed(llmqType, 0, quorumIndex);
582 :
583 711310 : size_t currentCycle = 0;
584 :
585 711310 : dbIt->Seek(firstKey);
586 :
587 736809 : while (dbIt->Valid()) {
588 142009 : decltype(firstKey) curKey;
589 : int quorumHeight;
590 142009 : if (!dbIt->GetKey(curKey) || curKey >= lastKey) {
591 25797 : return std::nullopt;
592 : }
593 116212 : if (std::get<0>(curKey) != DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT_Q_INDEXED || std::get<1>(curKey) != llmqType) {
594 0 : return std::nullopt;
595 : }
596 :
597 116212 : if (uint32_t nMinedHeight = std::numeric_limits<uint32_t>::max() - be32toh_internal(std::get<3>(curKey));
598 116212 : nMinedHeight > static_cast<uint32_t>(pindex->nHeight)) {
599 0 : return std::nullopt;
600 : }
601 :
602 116212 : if (!dbIt->GetValue(quorumHeight)) {
603 0 : return std::nullopt;
604 : }
605 :
606 116212 : const auto* pQuorumBaseBlockIndex = pindex->GetAncestor(quorumHeight);
607 116212 : assert(pQuorumBaseBlockIndex);
608 :
609 116212 : if (currentCycle == cycle) {
610 90713 : return std::make_optional(pQuorumBaseBlockIndex);
611 : }
612 :
613 25499 : currentCycle++;
614 :
615 25499 : dbIt->Next();
616 142009 : }
617 :
618 594800 : return std::nullopt;
619 711310 : }
620 :
621 355610 : std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetLastMinedCommitmentsPerQuorumIndexUntilBlock(
622 : Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t cycle) const
623 : {
624 355610 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
625 355610 : assert(llmq_params_opt.has_value());
626 355610 : std::vector<const CBlockIndex*> ret;
627 :
628 1066830 : for (const auto quorumIndex : util::irange(llmq_params_opt->signingActiveQuorumCount)) {
629 711220 : std::optional<const CBlockIndex*> q = GetLastMinedCommitmentsByQuorumIndexUntilBlock(llmqType, pindex, quorumIndex, cycle);
630 711220 : if (q.has_value()) {
631 90667 : ret.emplace_back(q.value());
632 90667 : }
633 : }
634 :
635 355610 : return ret;
636 355610 : }
637 :
638 144190 : std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetMinedCommitmentsIndexedUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t maxCount) const
639 : {
640 144190 : std::vector<const CBlockIndex*> ret;
641 :
642 144190 : size_t cycle = 0;
643 :
644 162064 : while (ret.size() < maxCount) {
645 157172 : std::vector<const CBlockIndex*> cycleRet = GetLastMinedCommitmentsPerQuorumIndexUntilBlock(llmqType, pindex, cycle);
646 :
647 157172 : if (cycleRet.empty()) {
648 139298 : return ret;
649 : }
650 :
651 17874 : size_t needToCopy = maxCount - ret.size();
652 17874 : std::copy_n(cycleRet.begin(), std::min(needToCopy, cycleRet.size()), std::back_inserter(ret));
653 17874 : cycle++;
654 157172 : }
655 :
656 4892 : return ret;
657 144190 : }
658 :
659 : // The returned quorums are in reversed order, so the most recent one is at index 0
660 206577 : std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> CQuorumBlockProcessor::GetMinedAndActiveCommitmentsUntilBlock(gsl::not_null<const CBlockIndex*> pindex) const
661 : {
662 206577 : std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> ret;
663 :
664 1239462 : for (const auto& params : Params().GetConsensus().llmqs) {
665 1032885 : auto& commitments = ret[params.type];
666 1032885 : if (IsQuorumRotationEnabled(params, pindex)) {
667 198434 : commitments = GetLastMinedCommitmentsPerQuorumIndexUntilBlock(params.type, pindex, 0);
668 198434 : } else {
669 834451 : commitments = GetMinedCommitmentsUntilBlock(params.type, pindex, params.signingActiveQuorumCount);
670 : }
671 : }
672 :
673 206577 : return ret;
674 206577 : }
675 :
676 15528 : bool CQuorumBlockProcessor::HasMineableCommitment(const uint256& hash) const
677 : {
678 15528 : LOCK(minableCommitmentsCs);
679 15528 : return minableCommitments.count(hash) != 0;
680 15528 : }
681 :
682 4480 : std::optional<CInv> CQuorumBlockProcessor::AddMineableCommitment(const CFinalCommitment& fqc)
683 : {
684 4480 : const uint256 commitmentHash = ::SerializeHash(fqc);
685 :
686 8960 : const bool relay = [&]() {
687 4480 : LOCK(minableCommitmentsCs);
688 :
689 4480 : auto k = std::make_pair(fqc.llmqType, fqc.quorumHash);
690 4480 : auto [itInserted, successfullyInserted] = minableCommitmentsByQuorum.try_emplace(k, commitmentHash);
691 4480 : if (successfullyInserted) {
692 3954 : minableCommitments.try_emplace(commitmentHash, fqc);
693 3954 : return true;
694 : } else {
695 526 : auto& insertedQuorumHash = itInserted->second;
696 526 : const auto& oldFqc = minableCommitments.at(insertedQuorumHash);
697 526 : if (fqc.CountSigners() > oldFqc.CountSigners()) {
698 : // new commitment has more signers, so override the known one
699 4 : insertedQuorumHash = commitmentHash;
700 4 : minableCommitments.erase(insertedQuorumHash);
701 4 : minableCommitments.try_emplace(commitmentHash, fqc);
702 4 : return true;
703 : }
704 : }
705 522 : return false;
706 4480 : }();
707 :
708 4480 : return relay ? std::make_optional(CInv{MSG_QUORUM_FINAL_COMMITMENT, commitmentHash}) : std::nullopt;
709 : }
710 :
711 2715 : bool CQuorumBlockProcessor::GetMineableCommitmentByHash(const uint256& commitmentHash, llmq::CFinalCommitment& ret) const
712 : {
713 2715 : LOCK(minableCommitmentsCs);
714 2715 : auto it = minableCommitments.find(commitmentHash);
715 2715 : if (it == minableCommitments.end()) {
716 2 : return false;
717 : }
718 2713 : ret = it->second;
719 2713 : return true;
720 2715 : }
721 :
722 : // Will return nullopt if no commitment should be mined
723 : // Will return a null commitment if no mineable commitment is known and none was mined yet
724 173708 : std::optional<std::vector<CFinalCommitment>> CQuorumBlockProcessor::GetMineableCommitments(const Consensus::LLMQParams& llmqParams, int nHeight) const
725 : {
726 173708 : AssertLockHeld(::cs_main);
727 :
728 173708 : std::vector<CFinalCommitment> ret;
729 :
730 173708 : if (GetNumCommitmentsRequired(llmqParams, nHeight) == 0) {
731 : // no commitment required
732 123389 : return std::nullopt;
733 : }
734 :
735 : // Note: This function can be called for new blocks
736 50319 : assert(nHeight <= m_chainstate.m_chain.Height() + 1);
737 50319 : const auto *const pindex = m_chainstate.m_chain.Height() < nHeight ? m_chainstate.m_chain.Tip() : m_chainstate.m_chain.Tip()->GetAncestor(nHeight);
738 :
739 50319 : bool rotation_enabled = IsQuorumRotationEnabled(llmqParams, pindex);
740 50319 : bool basic_bls_enabled{DeploymentActiveAfter(pindex, m_chainstate.m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V19)};
741 50319 : size_t quorums_num = rotation_enabled ? llmqParams.signingActiveQuorumCount : 1;
742 :
743 50319 : std::stringstream ss;
744 112965 : for (const auto quorumIndex : util::irange(quorums_num)) {
745 62646 : CFinalCommitment cf;
746 :
747 62646 : uint256 quorumHash = GetQuorumBlockHash(llmqParams, m_chainstate.m_chain, nHeight, quorumIndex);
748 62646 : if (quorumHash.IsNull()) {
749 0 : break;
750 : }
751 :
752 62646 : if (HasMinedCommitment(llmqParams.type, quorumHash)) continue;
753 :
754 62178 : LOCK(minableCommitmentsCs);
755 :
756 62178 : auto k = std::make_pair(llmqParams.type, quorumHash);
757 62178 : if (auto it = minableCommitmentsByQuorum.find(k); it == minableCommitmentsByQuorum.end()) {
758 : // null commitment required
759 58063 : cf = CFinalCommitment(llmqParams, quorumHash);
760 58063 : cf.quorumIndex = static_cast<int16_t>(quorumIndex);
761 58063 : cf.nVersion = CFinalCommitment::GetVersion(rotation_enabled, basic_bls_enabled);
762 58063 : ss << "{ created nversion[" << cf.nVersion << "] quorumIndex[" << cf.quorumIndex << "] }";
763 58063 : } else {
764 4115 : cf = minableCommitments.at(it->second);
765 4115 : ss << "{ cached nversion[" << cf.nVersion << "] quorumIndex[" << cf.quorumIndex << "] }";
766 : }
767 :
768 62178 : ret.push_back(std::move(cf));
769 62646 : }
770 :
771 50319 : LogPrint(BCLog::LLMQ, "GetMineableCommitments cf height[%d] content: %s\n", nHeight, ss.str());
772 :
773 50319 : if (ret.empty()) {
774 0 : return std::nullopt;
775 : }
776 50319 : return std::make_optional(ret);
777 173708 : }
778 :
779 125388 : bool CQuorumBlockProcessor::GetMineableCommitmentsTx(const Consensus::LLMQParams& llmqParams, int nHeight, std::vector<CTransactionRef>& ret) const
780 : {
781 125388 : AssertLockHeld(::cs_main);
782 125388 : std::optional<std::vector<CFinalCommitment>> qcs = GetMineableCommitments(llmqParams, nHeight);
783 125388 : if (!qcs.has_value()) {
784 81239 : return false;
785 : }
786 :
787 100157 : for (const auto& f : qcs.value()) {
788 56008 : CFinalCommitmentTxPayload qc;
789 56008 : qc.nHeight = nHeight;
790 56008 : qc.commitment = f;
791 56008 : CMutableTransaction tx;
792 56008 : tx.nVersion = 3;
793 56008 : tx.nType = TRANSACTION_QUORUM_COMMITMENT;
794 56008 : SetTxPayload(tx, qc);
795 56008 : ret.push_back(MakeTransactionRef(tx));
796 56008 : }
797 :
798 44149 : return true;
799 125388 : }
800 :
801 : } // namespace llmq
|