Line data Source code
1 : // Copyright (c) 2019 The Dash Core developers
2 : // Copyright (c) 2023 The PIVX Core developers
3 : // Distributed under the MIT/X11 software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "quorums.h"
7 : #include "quorums_chainlocks.h"
8 : #include "quorums_signing.h"
9 : #include "quorums_utils.h"
10 :
11 : #include "chain.h"
12 : #include "net_processing.h"
13 : #include "scheduler.h"
14 : #include "spork.h"
15 : #include "sporkid.h"
16 : #include "tiertwo/tiertwo_sync_state.h"
17 : #include "validation.h"
18 :
19 : namespace llmq
20 : {
21 :
22 : static const std::string CLSIG_REQUESTID_PREFIX = "clsig";
23 :
24 : std::unique_ptr<CChainLocksHandler> chainLocksHandler{nullptr};
25 :
26 836 : bool CChainLockSig::IsNull() const
27 : {
28 869 : return nHeight == -1 && blockHash == uint256();
29 : }
30 :
31 837 : std::string CChainLockSig::ToString() const
32 : {
33 1674 : return strprintf("CChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString());
34 : }
35 :
36 475 : CChainLocksHandler::CChainLocksHandler(CScheduler* _scheduler) :
37 1900 : scheduler(_scheduler)
38 : {
39 475 : }
40 :
41 950 : CChainLocksHandler::~CChainLocksHandler()
42 : {
43 950 : }
44 :
45 347 : void CChainLocksHandler::Start()
46 : {
47 347 : quorumSigningManager->RegisterRecoveredSigsListener(this);
48 347 : scheduler->scheduleEvery([&]() {
49 3525 : EnforceBestChainLock();
50 : // regularely retry signing the current chaintip
51 3525 : TrySignChainTip();
52 : },
53 : 5000);
54 347 : }
55 :
56 357 : void CChainLocksHandler::Stop()
57 : {
58 357 : quorumSigningManager->UnregisterRecoveredSigsListener(this);
59 357 : }
60 :
61 9843 : bool CChainLocksHandler::AlreadyHave(const CInv& inv)
62 : {
63 9843 : LOCK(cs);
64 19686 : return seenChainLocks.count(inv.hash) != 0;
65 : }
66 :
67 727 : bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, llmq::CChainLockSig& ret)
68 : {
69 727 : LOCK(cs);
70 :
71 727 : if (hash != bestChainLockHash) {
72 : // we only propagate the best one and ditch all the old ones
73 : return false;
74 : }
75 :
76 727 : ret = bestChainLock;
77 727 : return true;
78 : }
79 :
80 0 : CChainLockSig CChainLocksHandler::GetBestChainLock()
81 : {
82 0 : LOCK(cs);
83 0 : return bestChainLock;
84 : }
85 :
86 725 : void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
87 : {
88 725 : if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
89 : return;
90 : }
91 :
92 725 : if (strCommand == NetMsgType::CLSIG) {
93 725 : CChainLockSig clsig;
94 725 : vRecv >> clsig;
95 :
96 725 : auto hash = ::SerializeHash(clsig);
97 :
98 725 : ProcessNewChainLock(pfrom->GetId(), clsig, hash);
99 : }
100 : }
101 :
102 842 : void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLockSig& clsig, const uint256& hash)
103 : {
104 842 : {
105 842 : LOCK(cs_main);
106 842 : g_connman->RemoveAskFor(hash, MSG_CLSIG);
107 : }
108 :
109 842 : {
110 842 : LOCK(cs);
111 842 : if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) {
112 7 : return;
113 : }
114 :
115 836 : if (!bestChainLock.IsNull() && clsig.nHeight <= bestChainLock.nHeight) {
116 : // no need to process/relay older CLSIGs
117 : return;
118 : }
119 : }
120 :
121 835 : uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, clsig.nHeight));
122 835 : uint256 msgHash = clsig.blockHash;
123 835 : if (!quorumSigningManager->VerifyRecoveredSig(Params().GetConsensus().llmqTypeChainLocks, clsig.nHeight, requestId, msgHash, clsig.sig)) {
124 0 : LogPrintf("CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d\n", __func__, clsig.ToString(), from);
125 0 : if (from != -1) {
126 0 : LOCK(cs_main);
127 0 : Misbehaving(from, 10);
128 : }
129 0 : return;
130 : }
131 :
132 835 : {
133 1670 : LOCK2(cs_main, cs);
134 :
135 835 : if (InternalHasConflictingChainLock(clsig.nHeight, clsig.blockHash)) {
136 : // This should not happen. If it happens, it means that a malicious entity controls a large part of the MN
137 : // network. In this case, we don't allow him to reorg older chainlocks.
138 0 : LogPrintf("CChainLocksHandler::%s -- new CLSIG (%s) tries to reorg previous CLSIG (%s), peer=%d\n",
139 0 : __func__, clsig.ToString(), bestChainLock.ToString(), from);
140 0 : return;
141 : }
142 :
143 835 : bestChainLockHash = hash;
144 835 : bestChainLock = clsig;
145 :
146 835 : CInv inv(MSG_CLSIG, hash);
147 835 : g_connman->RelayInv(inv, LLMQS_PROTO_VERSION);
148 :
149 835 : auto blockIt = mapBlockIndex.find(clsig.blockHash);
150 835 : if (blockIt == mapBlockIndex.end()) {
151 : // we don't know the block/header for this CLSIG yet, so bail out for now
152 : // when the block or the header later comes in, we will enforce the correct chain
153 : return;
154 : }
155 :
156 835 : if (blockIt->second->nHeight != clsig.nHeight) {
157 : // Should not happen, same as the conflict check from above.
158 0 : LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
159 0 : __func__, clsig.ToString(), blockIt->second->nHeight);
160 0 : return;
161 : }
162 :
163 835 : const CBlockIndex* pindex = blockIt->second;
164 835 : bestChainLockWithKnownBlock = bestChainLock;
165 835 : bestChainLockBlockIndex = pindex;
166 : }
167 :
168 835 : scheduler->scheduleFromNow([&]() {
169 835 : EnforceBestChainLock();
170 : },
171 : 0);
172 :
173 1670 : LogPrint(BCLog::LLMQ, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
174 : __func__, clsig.ToString(), from);
175 : }
176 :
177 41186 : void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* pindexNew)
178 : {
179 123558 : LOCK2(cs_main, cs);
180 :
181 41186 : if (pindexNew->GetBlockHash() == bestChainLock.blockHash) {
182 0 : LogPrintf("CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());
183 :
184 0 : if (bestChainLock.nHeight != pindexNew->nHeight) {
185 : // Should not happen, same as the conflict check from ProcessNewChainLock.
186 0 : LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
187 0 : __func__, bestChainLock.ToString(), pindexNew->nHeight);
188 0 : return;
189 : }
190 :
191 : // when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the
192 : // CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual
193 : // block processing logic will handle this when the block arrives
194 0 : bestChainLockWithKnownBlock = bestChainLock;
195 0 : bestChainLockBlockIndex = pindexNew;
196 : }
197 : }
198 :
199 41471 : void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew)
200 : {
201 : // don't call TrySignChainTip directly but instead let the scheduler call it. This way we ensure that cs_main is
202 : // never locked and TrySignChainTip is not called twice in parallel. Also avoids recursive calls due to
203 : // EnforceBestChainLock switching chains.
204 82835 : LOCK(cs);
205 41471 : if (tryLockChainTipScheduled) {
206 214 : return;
207 : }
208 41364 : tryLockChainTipScheduled = true;
209 82728 : scheduler->scheduleFromNow([&]() {
210 41357 : EnforceBestChainLock();
211 41357 : TrySignChainTip();
212 41357 : LOCK(cs);
213 41357 : tryLockChainTipScheduled = false;
214 41357 : },
215 : 0);
216 : }
217 :
218 44882 : void CChainLocksHandler::TrySignChainTip()
219 : {
220 44882 : Cleanup();
221 :
222 44882 : if (!fMasterNode) {
223 41625 : return;
224 : }
225 :
226 5979 : if (!g_tiertwo_sync_state.IsBlockchainSynced()) {
227 : return;
228 : }
229 :
230 5979 : const CBlockIndex* pindex;
231 5979 : {
232 5979 : LOCK(cs_main);
233 11958 : pindex = chainActive.Tip();
234 : }
235 :
236 5979 : if (!pindex->pprev) {
237 : return;
238 : }
239 5979 : if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
240 : return;
241 : }
242 :
243 : // DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
244 : // To simplify the initial implementation, we skip this process and directly try to create a CLSIG
245 : // This will fail when multiple blocks compete, but we accept this for the initial implementation.
246 : // Later, we'll add the multiple attempts process.
247 :
248 4142 : {
249 4142 : LOCK(cs);
250 :
251 4142 : if (pindex->nHeight == lastSignedHeight) {
252 : // already signed this one
253 885 : return;
254 : }
255 :
256 3257 : if (bestChainLock.nHeight >= pindex->nHeight) {
257 : // already got the same CLSIG or a better one
258 : return;
259 : }
260 :
261 3257 : if (InternalHasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
262 : // don't sign if another conflicting CLSIG is already present. EnforceBestChainLock will later enforce
263 : // the correct chain.
264 : return;
265 : }
266 : }
267 :
268 6514 : LogPrint(BCLog::LLMQ, "CChainLocksHandler::%s -- trying to sign %s, height=%d\n", __func__, pindex->GetBlockHash().ToString(), pindex->nHeight);
269 :
270 3257 : uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
271 3257 : uint256 msgHash = pindex->GetBlockHash();
272 :
273 3257 : {
274 3257 : LOCK(cs);
275 3257 : if (bestChainLock.nHeight >= pindex->nHeight) {
276 : // might have happened while we didn't hold cs
277 0 : return;
278 : }
279 3257 : lastSignedHeight = pindex->nHeight;
280 3257 : lastSignedRequestId = requestId;
281 3257 : lastSignedMsgHash = msgHash;
282 : }
283 :
284 3257 : quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqTypeChainLocks, requestId, msgHash);
285 : }
286 :
287 : // WARNING: cs_main and cs should not be held!
288 : // This should also not be called from validation signals, as this might result in recursive calls
289 45717 : void CChainLocksHandler::EnforceBestChainLock()
290 : {
291 45717 : AssertLockNotHeld(cs);
292 45717 : AssertLockNotHeld(cs_main);
293 :
294 45717 : CChainLockSig clsig;
295 45717 : const CBlockIndex* pindex;
296 45717 : const CBlockIndex* currentBestChainLockBlockIndex;
297 45717 : {
298 45717 : LOCK(cs);
299 45717 : clsig = bestChainLockWithKnownBlock;
300 45717 : pindex = currentBestChainLockBlockIndex = this->bestChainLockBlockIndex;
301 :
302 45717 : if (!currentBestChainLockBlockIndex) {
303 : // we don't have the header/block, so we can't do anything right now
304 81748 : return;
305 : }
306 : }
307 :
308 4843 : bool activateNeeded;
309 4843 : {
310 4843 : LOCK(cs_main);
311 :
312 : // Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
313 : // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
314 : // and invalidate each of them.
315 9694 : while (pindex && !chainActive.Contains(pindex)) {
316 : // Invalidate all blocks that have the same prevBlockHash but are not equal to blockHash
317 4 : auto itp = mapPrevBlockIndex.equal_range(pindex->pprev->GetBlockHash());
318 10 : for (auto jt = itp.first; jt != itp.second; ++jt) {
319 6 : if (jt->second == pindex) {
320 4 : continue;
321 : }
322 4 : LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) invalidates block %s\n",
323 4 : __func__, clsig.ToString(), jt->second->GetBlockHash().ToString());
324 2 : DoInvalidateBlock(jt->second, false);
325 : }
326 :
327 4 : pindex = pindex->pprev;
328 : }
329 : // In case blocks from the correct chain are invalid at the moment, reconsider them. The only case where this
330 : // can happen right now is when missing superblock triggers caused the main chain to be dismissed first. When
331 : // the trigger later appears, this should bring us to the correct chain eventually. Please note that this does
332 : // NOT enforce invalid blocks in any way, it just causes re-validation.
333 4843 : if (!currentBestChainLockBlockIndex->IsValid()) {
334 0 : CValidationState state;
335 0 : ReconsiderBlock(state, mapBlockIndex.at(currentBestChainLockBlockIndex->GetBlockHash()));
336 : }
337 :
338 9686 : activateNeeded = chainActive.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != currentBestChainLockBlockIndex;
339 : }
340 :
341 9686 : CValidationState state;
342 4845 : if (activateNeeded && !ActivateBestChain(state)) {
343 0 : LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, state.GetRejectReason());
344 : }
345 : }
346 :
347 2110 : void CChainLocksHandler::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
348 : {
349 2110 : if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
350 1993 : return;
351 : }
352 :
353 2110 : CChainLockSig clsig;
354 2110 : {
355 2110 : LOCK(cs);
356 :
357 2110 : if (recoveredSig.id != lastSignedRequestId || recoveredSig.msgHash != lastSignedMsgHash) {
358 : // this is not what we signed, so lets not create a CLSIG for it
359 1993 : return;
360 : }
361 540 : if (bestChainLock.nHeight >= lastSignedHeight) {
362 : // already got the same or a better CLSIG through the CLSIG message
363 : return;
364 : }
365 :
366 117 : clsig.nHeight = lastSignedHeight;
367 117 : clsig.blockHash = lastSignedMsgHash;
368 117 : clsig.sig = recoveredSig.sig.Get();
369 : }
370 117 : ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
371 : }
372 :
373 : // WARNING, do not hold cs while calling this method as we'll otherwise run into a deadlock
374 2 : void CChainLocksHandler::DoInvalidateBlock(const CBlockIndex* pindex, bool activateBestChain)
375 : {
376 2 : auto& params = Params();
377 :
378 2 : {
379 2 : LOCK(cs_main);
380 :
381 : // get the non-const pointer
382 2 : CBlockIndex* pindex2 = mapBlockIndex[pindex->GetBlockHash()];
383 :
384 4 : CValidationState state;
385 2 : if (!InvalidateBlock(state, params, pindex2)) {
386 0 : LogPrintf("CChainLocksHandler::%s -- InvalidateBlock failed: %s\n", __func__, state.GetRejectReason());
387 : // This should not have happened and we are in a state were it's not safe to continue anymore
388 0 : assert(false);
389 : }
390 : }
391 :
392 4 : CValidationState state;
393 2 : if (activateBestChain && !ActivateBestChain(state)) {
394 0 : LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, state.GetRejectReason());
395 : // This should not have happened and we are in a state were it's not safe to continue anymore
396 0 : assert(false);
397 : }
398 2 : }
399 :
400 1246 : bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash)
401 : {
402 1246 : if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
403 : return false;
404 : }
405 :
406 1573 : LOCK(cs);
407 327 : return InternalHasChainLock(nHeight, blockHash);
408 : }
409 :
410 327 : bool CChainLocksHandler::InternalHasChainLock(int nHeight, const uint256& blockHash)
411 : {
412 327 : AssertLockHeld(cs);
413 :
414 327 : if (!bestChainLockBlockIndex) {
415 : return false;
416 : }
417 :
418 261 : if (nHeight > bestChainLockBlockIndex->nHeight) {
419 : return false;
420 : }
421 :
422 237 : if (nHeight == bestChainLockBlockIndex->nHeight) {
423 36 : return blockHash == bestChainLockBlockIndex->GetBlockHash();
424 : }
425 :
426 201 : auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
427 201 : return pAncestor && pAncestor->GetBlockHash() == blockHash;
428 : }
429 :
430 97639 : bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash)
431 : {
432 97639 : if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
433 : return false;
434 : }
435 :
436 120437 : LOCK(cs);
437 22798 : return InternalHasConflictingChainLock(nHeight, blockHash);
438 : }
439 :
440 26890 : bool CChainLocksHandler::InternalHasConflictingChainLock(int nHeight, const uint256& blockHash)
441 : {
442 26890 : AssertLockHeld(cs);
443 :
444 26890 : if (!bestChainLockBlockIndex) {
445 : return false;
446 : }
447 :
448 10849 : if (nHeight > bestChainLockBlockIndex->nHeight) {
449 : return false;
450 : }
451 :
452 17 : if (nHeight == bestChainLockBlockIndex->nHeight) {
453 15 : return blockHash != bestChainLockBlockIndex->GetBlockHash();
454 : }
455 :
456 2 : auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
457 2 : assert(pAncestor);
458 2 : return pAncestor->GetBlockHash() != blockHash;
459 : }
460 :
461 44882 : void CChainLocksHandler::Cleanup()
462 : {
463 44882 : if (!g_tiertwo_sync_state.IsBlockchainSynced()) {
464 44234 : return;
465 : }
466 :
467 30556 : {
468 30556 : LOCK(cs);
469 30556 : if (GetTimeMillis() - lastCleanupTime < CLEANUP_INTERVAL) {
470 59816 : return;
471 : }
472 : }
473 :
474 1944 : LOCK2(cs_main, cs);
475 :
476 648 : for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
477 1134 : if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) {
478 0 : it = seenChainLocks.erase(it);
479 : } else {
480 2916 : ++it;
481 : }
482 : }
483 :
484 648 : lastCleanupTime = GetTimeMillis();
485 : }
486 :
487 : }
|