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 18625 : static void PreComputeQuorumMembers(CDeterministicMNManager& dmnman, llmq::CQuorumSnapshotManager& qsnapman,
31 : const ChainstateManager& chainman, const CBlockIndex* pindex, bool reset_cache)
32 : {
33 79439 : for (const Consensus::LLMQParams& params : llmq::GetEnabledQuorumParams(chainman, pindex->pprev)) {
34 60814 : if (llmq::IsQuorumRotationEnabled(params, pindex) && (pindex->nHeight % params.dkgInterval == 0)) {
35 792 : llmq::utils::GetAllQuorumMembers(params.type, {dmnman, qsnapman, chainman, pindex}, reset_cache);
36 792 : }
37 : }
38 18625 : }
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 360 : CQuorumBlockProcessor::CQuorumBlockProcessor(CChainState& chainstate, CDeterministicMNManager& dmnman, CEvoDB& evoDb,
49 : CQuorumSnapshotManager& qsnapman, int8_t bls_threads) :
50 180 : m_chainstate{chainstate},
51 180 : m_dmnman{dmnman},
52 180 : m_evoDb{evoDb},
53 180 : m_qsnapman{qsnapman}
54 180 : {
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 180 : }
59 :
60 360 : CQuorumBlockProcessor::~CQuorumBlockProcessor()
61 180 : {
62 180 : m_bls_queue.StopWorkerThreads();
63 360 : }
64 :
65 0 : MessageProcessingResult CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view msg_type,
66 : CDataStream& vRecv)
67 : {
68 0 : if (msg_type != NetMsgType::QFCOMMITMENT) {
69 0 : return {};
70 : }
71 :
72 0 : CFinalCommitment qc;
73 0 : vRecv >> qc;
74 :
75 0 : MessageProcessingResult ret;
76 0 : ret.m_to_erase = CInv{MSG_QUORUM_FINAL_COMMITMENT, ::SerializeHash(qc)};
77 :
78 0 : 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 0 : const auto& llmq_params_opt = Params().GetLLMQ(qc.llmqType);
85 0 : 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 0 : 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 0 : LOCK(::cs_main);
97 0 : pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash);
98 0 : 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 0 : 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 0 : if (int quorumHeight = pQuorumBaseBlockIndex->nHeight - (pQuorumBaseBlockIndex->nHeight % llmq_params_opt->dkgInterval) + int(qc.quorumIndex);
112 0 : 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 0 : 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 0 : 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 0 : }
133 :
134 : {
135 : // Check if we already got a better one locally
136 : // We do this before verifying the commitment to avoid DoS
137 0 : LOCK(minableCommitmentsCs);
138 0 : auto k = std::make_pair(type, qc.quorumHash);
139 0 : auto it = minableCommitmentsByQuorum.find(k);
140 0 : if (it != minableCommitmentsByQuorum.end()) {
141 0 : auto jt = minableCommitments.find(it->second);
142 0 : if (jt != minableCommitments.end() && jt->second.CountSigners() <= qc.CountSigners()) {
143 0 : return ret;
144 : }
145 0 : }
146 0 : }
147 :
148 0 : 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 0 : 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 0 : if (auto inv_opt = AddMineableCommitment(qc)) {
160 0 : ret.m_inventory.emplace_back(inv_opt.value());
161 0 : }
162 0 : return ret;
163 0 : }
164 :
165 49014 : bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex, BlockValidationState& state, bool fJustCheck, bool fBLSChecks)
166 : {
167 49014 : AssertLockHeld(::cs_main);
168 :
169 49014 : const auto blockHash = pindex->GetBlockHash();
170 :
171 49014 : if (!DeploymentActiveAt(*pindex, m_chainstate.m_chainman.GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) {
172 30778 : m_evoDb.Write(DB_BEST_BLOCK_UPGRADE, blockHash);
173 30778 : return true;
174 : }
175 :
176 18236 : PreComputeQuorumMembers(m_dmnman, m_qsnapman, m_chainstate.m_chainman, pindex, /*reset_cache=*/false);
177 :
178 18236 : std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;
179 18236 : 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 77880 : for (const Consensus::LLMQParams& params : GetEnabledQuorumParams(m_chainstate.m_chainman, pindex->pprev)) {
188 : // skip these checks when replaying blocks after the crash
189 59644 : if (m_chainstate.m_chain.Tip() == nullptr) {
190 0 : break;
191 : }
192 :
193 59644 : const size_t numCommitmentsRequired = GetNumCommitmentsRequired(params, pindex->nHeight);
194 59644 : const auto numCommitmentsInNewBlock = qcs.count(params.type);
195 :
196 59644 : if (numCommitmentsRequired < numCommitmentsInNewBlock) {
197 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-not-allowed");
198 : }
199 :
200 59644 : if (numCommitmentsRequired > numCommitmentsInNewBlock) {
201 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-missing");
202 : }
203 59644 : if (IsQuorumRotationEnabled(params, pindex)) {
204 18236 : LogPrintf("[ProcessBlock] h[%d] numCommitmentsRequired[%d] numCommitmentsInNewBlock[%d]\n", pindex->nHeight, numCommitmentsRequired, numCommitmentsInNewBlock);
205 18236 : }
206 : }
207 :
208 18236 : if (fBLSChecks) {
209 18236 : CCheckQueueControl<utils::BlsCheck> queue_control(&m_bls_queue);
210 47286 : for (const auto& [_, qc] : qcs) {
211 29050 : if (qc.IsNull()) continue;
212 0 : const auto* pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash);
213 0 : 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 0 : qc.VerifySignatureAsync({m_dmnman, m_qsnapman, m_chainstate.m_chainman, pQuorumBaseBlockIndex}, &queue_control);
219 : }
220 :
221 18236 : if (!queue_control.Wait()) {
222 : // at least one check failed
223 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-invalid");
224 : }
225 18236 : }
226 47286 : for (const auto& [_, qc] : qcs) {
227 29050 : 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 18236 : m_evoDb.Write(DB_BEST_BLOCK_UPGRADE, blockHash);
234 :
235 18236 : return true;
236 49014 : }
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 291760 : 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 291760 : return std::make_tuple(DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT, llmqType, htobe32_internal(std::numeric_limits<uint32_t>::max() - nMinedHeight));
244 : }
245 :
246 145880 : 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 145880 : 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 89606 : static bool IsMiningPhase(const Consensus::LLMQParams& llmqParams, const CChain& active_chain, int nHeight)
253 : EXCLUSIVE_LOCKS_REQUIRED(cs_main)
254 : {
255 89606 : AssertLockHeld(cs_main);
256 :
257 : // Note: This function can be called for new blocks
258 89606 : assert(nHeight <= active_chain.Height() + 1);
259 :
260 89606 : int quorumCycleStartHeight = nHeight - (nHeight % llmqParams.dkgInterval);
261 89606 : int quorumCycleMiningStartHeight = quorumCycleStartHeight + llmqParams.dkgMiningWindowStart;
262 89606 : int quorumCycleMiningEndHeight = quorumCycleStartHeight + llmqParams.dkgMiningWindowEnd;
263 :
264 89606 : return nHeight >= quorumCycleMiningStartHeight && nHeight <= quorumCycleMiningEndHeight;
265 : }
266 :
267 29050 : bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc,
268 : BlockValidationState& state, bool fJustCheck)
269 : {
270 29050 : AssertLockHeld(::cs_main);
271 :
272 29050 : const auto& llmq_params_opt = Params().GetLLMQ(qc.llmqType);
273 29050 : 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 29050 : const auto& llmq_params = llmq_params_opt.value();
278 :
279 29050 : uint256 quorumHash = GetQuorumBlockHash(llmq_params, m_chainstate.m_chain, nHeight, qc.quorumIndex);
280 :
281 29050 : 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 29050 : if (m_chainstate.m_chain.Tip() == nullptr) {
290 0 : quorumHash = qc.quorumHash;
291 0 : }
292 :
293 29050 : 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 29050 : 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 29050 : if (qc.IsNull()) {
311 29050 : 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 29050 : return true;
320 : }
321 :
322 0 : 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 0 : 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 0 : const auto* pQuorumBaseBlockIndex = m_chainstate.m_blockman.LookupBlockIndex(qc.quorumHash);
333 0 : 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 0 : 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 0 : if (fJustCheck) {
350 0 : return true;
351 : }
352 :
353 0 : bool rotation_enabled = IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex);
354 :
355 0 : if (rotation_enabled) {
356 0 : LogPrint(BCLog::LLMQ, "%s -- height[%d] pQuorumBaseBlockIndex[%d] quorumIndex[%d] qversion[%d] Built\n",
357 : __func__, nHeight, pQuorumBaseBlockIndex->nHeight, qc.quorumIndex, qc.nVersion);
358 0 : }
359 :
360 : // Store commitment in DB
361 0 : auto cacheKey = std::make_pair(llmq_params.type, quorumHash);
362 0 : m_evoDb.Write(std::make_pair(DB_MINED_COMMITMENT, cacheKey), std::make_pair(qc, blockHash));
363 :
364 0 : if (rotation_enabled) {
365 0 : m_evoDb.Write(BuildInversedHeightKeyIndexed(llmq_params.type, nHeight, int(qc.quorumIndex)), pQuorumBaseBlockIndex->nHeight);
366 0 : } else {
367 0 : m_evoDb.Write(BuildInversedHeightKey(llmq_params.type, nHeight), pQuorumBaseBlockIndex->nHeight);
368 : }
369 :
370 : {
371 0 : LOCK(minableCommitmentsCs);
372 0 : mapHasMinedCommitmentCache[qc.llmqType].erase(qc.quorumHash);
373 0 : minableCommitmentsByQuorum.erase(cacheKey);
374 0 : minableCommitments.erase(::SerializeHash(qc));
375 0 : }
376 :
377 0 : 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 0 : return true;
381 29050 : }
382 :
383 389 : bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex)
384 : {
385 389 : AssertLockHeld(::cs_main);
386 :
387 389 : PreComputeQuorumMembers(m_dmnman, m_qsnapman, m_chainstate.m_chainman, pindex, /*reset_cache=*/true);
388 :
389 389 : std::multimap<Consensus::LLMQType, CFinalCommitment> qcs;
390 389 : if (BlockValidationState dummy; !GetCommitmentsFromBlock(block, pindex, qcs, dummy)) {
391 0 : return false;
392 : }
393 :
394 389 : for (auto& [_, qc2] : qcs) {
395 0 : auto& qc = qc2; // cannot capture structured binding into lambda
396 0 : if (qc.IsNull()) {
397 0 : continue;
398 : }
399 :
400 0 : m_evoDb.Erase(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash)));
401 :
402 0 : const auto& llmq_params_opt = Params().GetLLMQ(qc.llmqType);
403 0 : assert(llmq_params_opt.has_value());
404 :
405 0 : if (IsQuorumRotationEnabled(llmq_params_opt.value(), pindex)) {
406 0 : m_evoDb.Erase(BuildInversedHeightKeyIndexed(qc.llmqType, pindex->nHeight, int(qc.quorumIndex)));
407 0 : } else {
408 0 : m_evoDb.Erase(BuildInversedHeightKey(qc.llmqType, pindex->nHeight));
409 : }
410 :
411 0 : WITH_LOCK(minableCommitmentsCs, mapHasMinedCommitmentCache[qc.llmqType].erase(qc.quorumHash));
412 :
413 : // if a reorg happened, we should allow to mine this commitment later
414 0 : AddMineableCommitment(qc);
415 : }
416 :
417 389 : m_evoDb.Write(DB_BEST_BLOCK_UPGRADE, pindex->pprev->GetBlockHash());
418 :
419 389 : return true;
420 389 : }
421 :
422 18625 : bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindex, std::multimap<Consensus::LLMQType, CFinalCommitment>& ret, BlockValidationState& state)
423 : {
424 18625 : AssertLockHeld(::cs_main);
425 :
426 18625 : const auto& consensus = Params().GetConsensus();
427 :
428 18625 : ret.clear();
429 :
430 66352 : for (const auto& tx : block.vtx) {
431 47727 : if (tx->nType == TRANSACTION_QUORUM_COMMITMENT) {
432 29050 : auto opt_qc = GetTxPayload<CFinalCommitmentTxPayload>(*tx);
433 29050 : 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 29050 : auto& qc = *opt_qc;
439 :
440 29050 : const auto& llmq_params_opt = Params().GetLLMQ(qc.commitment.llmqType);
441 29050 : 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 29050 : if (!IsQuorumRotationEnabled(llmq_params_opt.value(), pindex)) {
448 15466 : if (ret.count(qc.commitment.llmqType) != 0) {
449 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-dup");
450 : }
451 15466 : }
452 :
453 29050 : ret.emplace(qc.commitment.llmqType, std::move(qc.commitment));
454 29050 : }
455 : }
456 :
457 18625 : if (!DeploymentActiveAt(*pindex, consensus, Consensus::DEPLOYMENT_DIP0003) && !ret.empty()) {
458 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-premature");
459 : }
460 :
461 18625 : return true;
462 18625 : }
463 :
464 89606 : size_t CQuorumBlockProcessor::GetNumCommitmentsRequired(const Consensus::LLMQParams& llmqParams, int nHeight) const
465 : {
466 89606 : AssertLockHeld(::cs_main);
467 :
468 89606 : if (!IsMiningPhase(llmqParams, m_chainstate.m_chain, nHeight)) return 0;
469 :
470 : // Note: This function can be called for new blocks
471 33435 : assert(nHeight <= m_chainstate.m_chain.Height() + 1);
472 33435 : const auto *const pindex = m_chainstate.m_chain.Height() < nHeight ? m_chainstate.m_chain.Tip() : m_chainstate.m_chain.Tip()->GetAncestor(nHeight);
473 :
474 33435 : bool rotation_enabled = IsQuorumRotationEnabled(llmqParams, pindex);
475 33435 : size_t quorums_num = rotation_enabled ? llmqParams.signingActiveQuorumCount : 1;
476 33435 : size_t ret{0};
477 :
478 77070 : for (const auto quorumIndex : util::irange(quorums_num)) {
479 43635 : uint256 quorumHash = GetQuorumBlockHash(llmqParams, m_chainstate.m_chain, nHeight, quorumIndex);
480 43635 : if (!quorumHash.IsNull() && !HasMinedCommitment(llmqParams.type, quorumHash)) ++ret;
481 : }
482 :
483 33435 : return ret;
484 89606 : }
485 :
486 : // WARNING: This method returns uint256() on the first block of the DKG interval (because the block hash is not known yet)
487 87270 : uint256 CQuorumBlockProcessor::GetQuorumBlockHash(const Consensus::LLMQParams& llmqParams, const CChain& active_chain, int nHeight, int quorumIndex)
488 : {
489 87270 : AssertLockHeld(::cs_main);
490 :
491 87270 : int quorumStartHeight = nHeight - (nHeight % llmqParams.dkgInterval) + quorumIndex;
492 :
493 87270 : const CBlockIndex* pindex = active_chain[quorumStartHeight];
494 87270 : 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 87270 : return pindex->GetBlockHash();
500 87270 : }
501 :
502 58220 : bool CQuorumBlockProcessor::HasMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash) const
503 : {
504 : bool fExists;
505 114827 : if (LOCK(minableCommitmentsCs); mapHasMinedCommitmentCache[llmqType].get(quorumHash, fExists)) {
506 56607 : return fExists;
507 : }
508 :
509 1613 : fExists = m_evoDb.Exists(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(llmqType, quorumHash)));
510 :
511 1613 : LOCK(minableCommitmentsCs);
512 1613 : mapHasMinedCommitmentCache[llmqType].insert(quorumHash, fExists);
513 :
514 1613 : return fExists;
515 58220 : }
516 :
517 0 : std::pair<CFinalCommitment, uint256> CQuorumBlockProcessor::GetMinedCommitment(Consensus::LLMQType llmqType,
518 : const uint256& quorumHash) const
519 : {
520 0 : auto key = std::make_pair(DB_MINED_COMMITMENT, std::make_pair(llmqType, quorumHash));
521 0 : std::pair<CFinalCommitment, uint256> ret;
522 0 : if (!m_evoDb.Read(key, ret)) {
523 0 : return {CFinalCommitment{}, uint256::ZERO};
524 : }
525 0 : return ret;
526 0 : }
527 :
528 : // The returned quorums are in reversed order, so the most recent one is at index 0
529 145880 : std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetMinedCommitmentsUntilBlock(Consensus::LLMQType llmqType, gsl::not_null<const CBlockIndex*> pindex, size_t maxCount) const
530 : {
531 145880 : AssertLockNotHeld(m_evoDb.cs);
532 145880 : LOCK(m_evoDb.cs);
533 :
534 145880 : auto dbIt = m_evoDb.GetCurTransaction().NewIteratorUniquePtr();
535 :
536 145880 : auto firstKey = BuildInversedHeightKey(llmqType, pindex->nHeight);
537 145880 : auto lastKey = BuildInversedHeightKey(llmqType, 0);
538 :
539 145880 : dbIt->Seek(firstKey);
540 :
541 145880 : std::vector<const CBlockIndex*> ret;
542 145880 : ret.reserve(maxCount);
543 :
544 145880 : while (dbIt->Valid() && ret.size() < maxCount) {
545 121320 : decltype(firstKey) curKey;
546 : int quorumHeight;
547 121320 : if (!dbIt->GetKey(curKey) || curKey >= lastKey) {
548 0 : break;
549 : }
550 121320 : if (std::get<0>(curKey) != DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT || std::get<1>(curKey) != llmqType) {
551 121320 : break;
552 : }
553 :
554 0 : if (uint32_t nMinedHeight = std::numeric_limits<uint32_t>::max() - be32toh_internal(std::get<2>(curKey));
555 0 : nMinedHeight > static_cast<uint32_t>(pindex->nHeight)) {
556 0 : break;
557 : }
558 :
559 0 : if (!dbIt->GetValue(quorumHeight)) {
560 0 : break;
561 : }
562 :
563 0 : const auto* pQuorumBaseBlockIndex = pindex->GetAncestor(quorumHeight);
564 0 : assert(pQuorumBaseBlockIndex);
565 0 : ret.emplace_back(pQuorumBaseBlockIndex);
566 :
567 0 : dbIt->Next();
568 121320 : }
569 :
570 145880 : return ret;
571 145880 : }
572 :
573 72940 : std::optional<const CBlockIndex*> CQuorumBlockProcessor::GetLastMinedCommitmentsByQuorumIndexUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, int quorumIndex, size_t cycle) const
574 : {
575 72940 : AssertLockNotHeld(m_evoDb.cs);
576 72940 : LOCK(m_evoDb.cs);
577 :
578 72940 : auto dbIt = m_evoDb.GetCurTransaction().NewIteratorUniquePtr();
579 :
580 72940 : auto firstKey = BuildInversedHeightKeyIndexed(llmqType, pindex->nHeight, quorumIndex);
581 72940 : auto lastKey = BuildInversedHeightKeyIndexed(llmqType, 0, quorumIndex);
582 :
583 72940 : size_t currentCycle = 0;
584 :
585 72940 : dbIt->Seek(firstKey);
586 :
587 72940 : while (dbIt->Valid()) {
588 0 : decltype(firstKey) curKey;
589 : int quorumHeight;
590 0 : if (!dbIt->GetKey(curKey) || curKey >= lastKey) {
591 0 : return std::nullopt;
592 : }
593 0 : 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 0 : if (uint32_t nMinedHeight = std::numeric_limits<uint32_t>::max() - be32toh_internal(std::get<3>(curKey));
598 0 : nMinedHeight > static_cast<uint32_t>(pindex->nHeight)) {
599 0 : return std::nullopt;
600 : }
601 :
602 0 : if (!dbIt->GetValue(quorumHeight)) {
603 0 : return std::nullopt;
604 : }
605 :
606 0 : const auto* pQuorumBaseBlockIndex = pindex->GetAncestor(quorumHeight);
607 0 : assert(pQuorumBaseBlockIndex);
608 :
609 0 : if (currentCycle == cycle) {
610 0 : return std::make_optional(pQuorumBaseBlockIndex);
611 : }
612 :
613 0 : currentCycle++;
614 :
615 0 : dbIt->Next();
616 0 : }
617 :
618 72940 : return std::nullopt;
619 72940 : }
620 :
621 36470 : std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetLastMinedCommitmentsPerQuorumIndexUntilBlock(
622 : Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t cycle) const
623 : {
624 36470 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
625 36470 : assert(llmq_params_opt.has_value());
626 36470 : std::vector<const CBlockIndex*> ret;
627 :
628 109410 : for (const auto quorumIndex : util::irange(llmq_params_opt->signingActiveQuorumCount)) {
629 72940 : std::optional<const CBlockIndex*> q = GetLastMinedCommitmentsByQuorumIndexUntilBlock(llmqType, pindex, quorumIndex, cycle);
630 72940 : if (q.has_value()) {
631 0 : ret.emplace_back(q.value());
632 0 : }
633 : }
634 :
635 36470 : return ret;
636 36470 : }
637 :
638 0 : std::vector<const CBlockIndex*> CQuorumBlockProcessor::GetMinedCommitmentsIndexedUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t maxCount) const
639 : {
640 0 : std::vector<const CBlockIndex*> ret;
641 :
642 0 : size_t cycle = 0;
643 :
644 0 : while (ret.size() < maxCount) {
645 0 : std::vector<const CBlockIndex*> cycleRet = GetLastMinedCommitmentsPerQuorumIndexUntilBlock(llmqType, pindex, cycle);
646 :
647 0 : if (cycleRet.empty()) {
648 0 : return ret;
649 : }
650 :
651 0 : size_t needToCopy = maxCount - ret.size();
652 0 : std::copy_n(cycleRet.begin(), std::min(needToCopy, cycleRet.size()), std::back_inserter(ret));
653 0 : cycle++;
654 0 : }
655 :
656 0 : return ret;
657 0 : }
658 :
659 : // The returned quorums are in reversed order, so the most recent one is at index 0
660 36470 : std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> CQuorumBlockProcessor::GetMinedAndActiveCommitmentsUntilBlock(gsl::not_null<const CBlockIndex*> pindex) const
661 : {
662 36470 : std::map<Consensus::LLMQType, std::vector<const CBlockIndex*>> ret;
663 :
664 218820 : for (const auto& params : Params().GetConsensus().llmqs) {
665 182350 : auto& commitments = ret[params.type];
666 182350 : if (IsQuorumRotationEnabled(params, pindex)) {
667 36470 : commitments = GetLastMinedCommitmentsPerQuorumIndexUntilBlock(params.type, pindex, 0);
668 36470 : } else {
669 145880 : commitments = GetMinedCommitmentsUntilBlock(params.type, pindex, params.signingActiveQuorumCount);
670 : }
671 : }
672 :
673 36470 : return ret;
674 36470 : }
675 :
676 0 : bool CQuorumBlockProcessor::HasMineableCommitment(const uint256& hash) const
677 : {
678 0 : LOCK(minableCommitmentsCs);
679 0 : return minableCommitments.count(hash) != 0;
680 0 : }
681 :
682 0 : std::optional<CInv> CQuorumBlockProcessor::AddMineableCommitment(const CFinalCommitment& fqc)
683 : {
684 0 : const uint256 commitmentHash = ::SerializeHash(fqc);
685 :
686 0 : const bool relay = [&]() {
687 0 : LOCK(minableCommitmentsCs);
688 :
689 0 : auto k = std::make_pair(fqc.llmqType, fqc.quorumHash);
690 0 : auto [itInserted, successfullyInserted] = minableCommitmentsByQuorum.try_emplace(k, commitmentHash);
691 0 : if (successfullyInserted) {
692 0 : minableCommitments.try_emplace(commitmentHash, fqc);
693 0 : return true;
694 : } else {
695 0 : auto& insertedQuorumHash = itInserted->second;
696 0 : const auto& oldFqc = minableCommitments.at(insertedQuorumHash);
697 0 : if (fqc.CountSigners() > oldFqc.CountSigners()) {
698 : // new commitment has more signers, so override the known one
699 0 : insertedQuorumHash = commitmentHash;
700 0 : minableCommitments.erase(insertedQuorumHash);
701 0 : minableCommitments.try_emplace(commitmentHash, fqc);
702 0 : return true;
703 : }
704 : }
705 0 : return false;
706 0 : }();
707 :
708 0 : return relay ? std::make_optional(CInv{MSG_QUORUM_FINAL_COMMITMENT, commitmentHash}) : std::nullopt;
709 : }
710 :
711 0 : bool CQuorumBlockProcessor::GetMineableCommitmentByHash(const uint256& commitmentHash, llmq::CFinalCommitment& ret) const
712 : {
713 0 : LOCK(minableCommitmentsCs);
714 0 : auto it = minableCommitments.find(commitmentHash);
715 0 : if (it == minableCommitments.end()) {
716 0 : return false;
717 : }
718 0 : ret = it->second;
719 0 : return true;
720 0 : }
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 29962 : std::optional<std::vector<CFinalCommitment>> CQuorumBlockProcessor::GetMineableCommitments(const Consensus::LLMQParams& llmqParams, int nHeight) const
725 : {
726 29962 : AssertLockHeld(::cs_main);
727 :
728 29962 : std::vector<CFinalCommitment> ret;
729 :
730 29962 : if (GetNumCommitmentsRequired(llmqParams, nHeight) == 0) {
731 : // no commitment required
732 18785 : return std::nullopt;
733 : }
734 :
735 : // Note: This function can be called for new blocks
736 11177 : assert(nHeight <= m_chainstate.m_chain.Height() + 1);
737 11177 : const auto *const pindex = m_chainstate.m_chain.Height() < nHeight ? m_chainstate.m_chain.Tip() : m_chainstate.m_chain.Tip()->GetAncestor(nHeight);
738 :
739 11177 : bool rotation_enabled = IsQuorumRotationEnabled(llmqParams, pindex);
740 11177 : bool basic_bls_enabled{DeploymentActiveAfter(pindex, m_chainstate.m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V19)};
741 11177 : size_t quorums_num = rotation_enabled ? llmqParams.signingActiveQuorumCount : 1;
742 :
743 11177 : std::stringstream ss;
744 25762 : for (const auto quorumIndex : util::irange(quorums_num)) {
745 14585 : CFinalCommitment cf;
746 :
747 14585 : uint256 quorumHash = GetQuorumBlockHash(llmqParams, m_chainstate.m_chain, nHeight, quorumIndex);
748 14585 : if (quorumHash.IsNull()) {
749 0 : break;
750 : }
751 :
752 14585 : if (HasMinedCommitment(llmqParams.type, quorumHash)) continue;
753 :
754 14585 : LOCK(minableCommitmentsCs);
755 :
756 14585 : auto k = std::make_pair(llmqParams.type, quorumHash);
757 14585 : if (auto it = minableCommitmentsByQuorum.find(k); it == minableCommitmentsByQuorum.end()) {
758 : // null commitment required
759 14585 : cf = CFinalCommitment(llmqParams, quorumHash);
760 14585 : cf.quorumIndex = static_cast<int16_t>(quorumIndex);
761 14585 : cf.nVersion = CFinalCommitment::GetVersion(rotation_enabled, basic_bls_enabled);
762 14585 : ss << "{ created nversion[" << cf.nVersion << "] quorumIndex[" << cf.quorumIndex << "] }";
763 14585 : } else {
764 0 : cf = minableCommitments.at(it->second);
765 0 : ss << "{ cached nversion[" << cf.nVersion << "] quorumIndex[" << cf.quorumIndex << "] }";
766 : }
767 :
768 14585 : ret.push_back(std::move(cf));
769 14585 : }
770 :
771 11177 : LogPrint(BCLog::LLMQ, "GetMineableCommitments cf height[%d] content: %s\n", nHeight, ss.str());
772 :
773 11177 : if (ret.empty()) {
774 0 : return std::nullopt;
775 : }
776 11177 : return std::make_optional(ret);
777 29962 : }
778 :
779 29962 : bool CQuorumBlockProcessor::GetMineableCommitmentsTx(const Consensus::LLMQParams& llmqParams, int nHeight, std::vector<CTransactionRef>& ret) const
780 : {
781 29962 : AssertLockHeld(::cs_main);
782 29962 : std::optional<std::vector<CFinalCommitment>> qcs = GetMineableCommitments(llmqParams, nHeight);
783 29962 : if (!qcs.has_value()) {
784 18785 : return false;
785 : }
786 :
787 25762 : for (const auto& f : qcs.value()) {
788 14585 : CFinalCommitmentTxPayload qc;
789 14585 : qc.nHeight = nHeight;
790 14585 : qc.commitment = f;
791 14585 : CMutableTransaction tx;
792 14585 : tx.nVersion = 3;
793 14585 : tx.nType = TRANSACTION_QUORUM_COMMITMENT;
794 14585 : SetTxPayload(tx, qc);
795 14585 : ret.push_back(MakeTransactionRef(tx));
796 14585 : }
797 :
798 11177 : return true;
799 29962 : }
800 :
801 : } // namespace llmq
|