PIVX Core  5.6.99
P2P Digital Currency
quorums_chainlocks.cpp
Go to the documentation of this file.
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"
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 
27 {
28  return nHeight == -1 && blockHash == uint256();
29 }
30 
31 std::string CChainLockSig::ToString() const
32 {
33  return strprintf("CChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString());
34 }
35 
37  scheduler(_scheduler)
38 {
39 }
40 
42 {
43 }
44 
46 {
47  quorumSigningManager->RegisterRecoveredSigsListener(this);
48  scheduler->scheduleEvery([&]() {
50  // regularely retry signing the current chaintip
52  },
53  5000);
54 }
55 
57 {
58  quorumSigningManager->UnregisterRecoveredSigsListener(this);
59 }
60 
62 {
63  LOCK(cs);
64  return seenChainLocks.count(inv.hash) != 0;
65 }
66 
68 {
69  LOCK(cs);
70 
71  if (hash != bestChainLockHash) {
72  // we only propagate the best one and ditch all the old ones
73  return false;
74  }
75 
76  ret = bestChainLock;
77  return true;
78 }
79 
81 {
82  LOCK(cs);
83  return bestChainLock;
84 }
85 
86 void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
87 {
89  return;
90  }
91 
92  if (strCommand == NetMsgType::CLSIG) {
93  CChainLockSig clsig;
94  vRecv >> clsig;
95 
96  auto hash = ::SerializeHash(clsig);
97 
98  ProcessNewChainLock(pfrom->GetId(), clsig, hash);
99  }
100 }
101 
103 {
104  {
105  LOCK(cs_main);
106  g_connman->RemoveAskFor(hash, MSG_CLSIG);
107  }
108 
109  {
110  LOCK(cs);
111  if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) {
112  return;
113  }
114 
115  if (!bestChainLock.IsNull() && clsig.nHeight <= bestChainLock.nHeight) {
116  // no need to process/relay older CLSIGs
117  return;
118  }
119  }
120 
121  uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, clsig.nHeight));
122  uint256 msgHash = clsig.blockHash;
123  if (!quorumSigningManager->VerifyRecoveredSig(Params().GetConsensus().llmqTypeChainLocks, clsig.nHeight, requestId, msgHash, clsig.sig)) {
124  LogPrintf("CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d\n", __func__, clsig.ToString(), from);
125  if (from != -1) {
126  LOCK(cs_main);
127  Misbehaving(from, 10);
128  }
129  return;
130  }
131 
132  {
133  LOCK2(cs_main, cs);
134 
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  LogPrintf("CChainLocksHandler::%s -- new CLSIG (%s) tries to reorg previous CLSIG (%s), peer=%d\n",
139  __func__, clsig.ToString(), bestChainLock.ToString(), from);
140  return;
141  }
142 
143  bestChainLockHash = hash;
144  bestChainLock = clsig;
145 
146  CInv inv(MSG_CLSIG, hash);
147  g_connman->RelayInv(inv, LLMQS_PROTO_VERSION);
148 
149  auto blockIt = mapBlockIndex.find(clsig.blockHash);
150  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  if (blockIt->second->nHeight != clsig.nHeight) {
157  // Should not happen, same as the conflict check from above.
158  LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
159  __func__, clsig.ToString(), blockIt->second->nHeight);
160  return;
161  }
162 
163  const CBlockIndex* pindex = blockIt->second;
165  bestChainLockBlockIndex = pindex;
166  }
167 
168  scheduler->scheduleFromNow([&]() {
170  },
171  0);
172 
173  LogPrint(BCLog::LLMQ, "CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
174  __func__, clsig.ToString(), from);
175 }
176 
178 {
179  LOCK2(cs_main, cs);
180 
181  if (pindexNew->GetBlockHash() == bestChainLock.blockHash) {
182  LogPrintf("CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());
183 
184  if (bestChainLock.nHeight != pindexNew->nHeight) {
185  // Should not happen, same as the conflict check from ProcessNewChainLock.
186  LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
187  __func__, bestChainLock.ToString(), pindexNew->nHeight);
188  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
195  bestChainLockBlockIndex = pindexNew;
196  }
197 }
198 
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  LOCK(cs);
206  return;
207  }
209  scheduler->scheduleFromNow([&]() {
211  TrySignChainTip();
212  LOCK(cs);
213  tryLockChainTipScheduled = false;
214  },
215  0);
216 }
217 
219 {
220  Cleanup();
221 
222  if (!fMasterNode) {
223  return;
224  }
225 
227  return;
228  }
229 
230  const CBlockIndex* pindex;
231  {
232  LOCK(cs_main);
233  pindex = chainActive.Tip();
234  }
235 
236  if (!pindex->pprev) {
237  return;
238  }
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  {
249  LOCK(cs);
250 
251  if (pindex->nHeight == lastSignedHeight) {
252  // already signed this one
253  return;
254  }
255 
256  if (bestChainLock.nHeight >= pindex->nHeight) {
257  // already got the same CLSIG or a better one
258  return;
259  }
260 
261  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  LogPrint(BCLog::LLMQ, "CChainLocksHandler::%s -- trying to sign %s, height=%d\n", __func__, pindex->GetBlockHash().ToString(), pindex->nHeight);
269 
270  uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
271  uint256 msgHash = pindex->GetBlockHash();
272 
273  {
274  LOCK(cs);
275  if (bestChainLock.nHeight >= pindex->nHeight) {
276  // might have happened while we didn't hold cs
277  return;
278  }
279  lastSignedHeight = pindex->nHeight;
280  lastSignedRequestId = requestId;
281  lastSignedMsgHash = msgHash;
282  }
283 
284  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
290 {
293 
294  CChainLockSig clsig;
295  const CBlockIndex* pindex;
296  const CBlockIndex* currentBestChainLockBlockIndex;
297  {
298  LOCK(cs);
300  pindex = currentBestChainLockBlockIndex = this->bestChainLockBlockIndex;
301 
302  if (!currentBestChainLockBlockIndex) {
303  // we don't have the header/block, so we can't do anything right now
304  return;
305  }
306  }
307 
308  bool activateNeeded;
309  {
310  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  while (pindex && !chainActive.Contains(pindex)) {
316  // Invalidate all blocks that have the same prevBlockHash but are not equal to blockHash
317  auto itp = mapPrevBlockIndex.equal_range(pindex->pprev->GetBlockHash());
318  for (auto jt = itp.first; jt != itp.second; ++jt) {
319  if (jt->second == pindex) {
320  continue;
321  }
322  LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) invalidates block %s\n",
323  __func__, clsig.ToString(), jt->second->GetBlockHash().ToString());
324  DoInvalidateBlock(jt->second, false);
325  }
326 
327  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  if (!currentBestChainLockBlockIndex->IsValid()) {
334  CValidationState state;
335  ReconsiderBlock(state, mapBlockIndex.at(currentBestChainLockBlockIndex->GetBlockHash()));
336  }
337 
338  activateNeeded = chainActive.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != currentBestChainLockBlockIndex;
339  }
340 
341  CValidationState state;
342  if (activateNeeded && !ActivateBestChain(state)) {
343  LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, state.GetRejectReason());
344  }
345 }
346 
348 {
350  return;
351  }
352 
353  CChainLockSig clsig;
354  {
355  LOCK(cs);
356 
357  if (recoveredSig.id != lastSignedRequestId || recoveredSig.msgHash != lastSignedMsgHash) {
358  // this is not what we signed, so lets not create a CLSIG for it
359  return;
360  }
362  // already got the same or a better CLSIG through the CLSIG message
363  return;
364  }
365 
366  clsig.nHeight = lastSignedHeight;
368  clsig.sig = recoveredSig.sig.Get();
369  }
370  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 void CChainLocksHandler::DoInvalidateBlock(const CBlockIndex* pindex, bool activateBestChain)
375 {
376  auto& params = Params();
377 
378  {
379  LOCK(cs_main);
380 
381  // get the non-const pointer
382  CBlockIndex* pindex2 = mapBlockIndex[pindex->GetBlockHash()];
383 
384  CValidationState state;
385  if (!InvalidateBlock(state, params, pindex2)) {
386  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  assert(false);
389  }
390  }
391 
392  CValidationState state;
393  if (activateBestChain && !ActivateBestChain(state)) {
394  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  assert(false);
397  }
398 }
399 
400 bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash)
401 {
403  return false;
404  }
405 
406  LOCK(cs);
407  return InternalHasChainLock(nHeight, blockHash);
408 }
409 
410 bool CChainLocksHandler::InternalHasChainLock(int nHeight, const uint256& blockHash)
411 {
413 
415  return false;
416  }
417 
418  if (nHeight > bestChainLockBlockIndex->nHeight) {
419  return false;
420  }
421 
422  if (nHeight == bestChainLockBlockIndex->nHeight) {
423  return blockHash == bestChainLockBlockIndex->GetBlockHash();
424  }
425 
426  auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
427  return pAncestor && pAncestor->GetBlockHash() == blockHash;
428 }
429 
430 bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash)
431 {
433  return false;
434  }
435 
436  LOCK(cs);
437  return InternalHasConflictingChainLock(nHeight, blockHash);
438 }
439 
441 {
443 
445  return false;
446  }
447 
448  if (nHeight > bestChainLockBlockIndex->nHeight) {
449  return false;
450  }
451 
452  if (nHeight == bestChainLockBlockIndex->nHeight) {
453  return blockHash != bestChainLockBlockIndex->GetBlockHash();
454  }
455 
456  auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
457  assert(pAncestor);
458  return pAncestor->GetBlockHash() != blockHash;
459 }
460 
462 {
464  return;
465  }
466 
467  {
468  LOCK(cs);
470  return;
471  }
472  }
473 
474  LOCK2(cs_main, cs);
475 
476  for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
477  if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) {
478  it = seenChainLocks.erase(it);
479  } else {
480  ++it;
481  }
482  }
483 
485 }
486 
487 }
const CChainParams & Params()
Return the currently selected parameters.
const BLSObject & Get() const
Definition: bls_wrapper.h:350
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: chain.h:145
uint256 GetBlockHash() const
Definition: chain.h:215
bool IsValid(enum BlockStatus nUpTo=BLOCK_VALID_TRANSACTIONS) const
Check whether this block index entry is valid up to the passed validity level.
Definition: chain.cpp:313
CBlockIndex * GetAncestor(int height)
Efficiently find an ancestor of this block.
Definition: chain.cpp:113
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:151
CBlockIndex * Tip(bool fProofOfStake=false) const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:405
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:435
Definition: net.h:145
inv message data
Definition: protocol.h:466
uint256 hash
Definition: protocol.h:480
Information about a peer.
Definition: net.h:669
NodeId GetId() const
Definition: net.h:825
void scheduleFromNow(Function f, int64_t deltaMilliSeconds)
Definition: scheduler.cpp:94
void scheduleEvery(Function f, int64_t deltaMilliSeconds)
Definition: scheduler.cpp:105
bool IsSporkActive(SporkId nSporkID)
Definition: spork.cpp:220
Capture information about block/transaction validation.
Definition: validation.h:24
std::string GetRejectReason() const
Definition: validation.h:94
bool IsBlockchainSynced() const
std::string ToString() const
Definition: uint256.cpp:65
std::string ToString() const
virtual void HandleNewRecoveredSig(const CRecoveredSig &recoveredSig)
bool InternalHasConflictingChainLock(int nHeight, const uint256 &blockHash)
bool InternalHasChainLock(int nHeight, const uint256 &blockHash)
bool AlreadyHave(const CInv &inv)
bool GetChainLockByHash(const uint256 &hash, CChainLockSig &ret)
CChainLocksHandler(CScheduler *_scheduler)
CChainLockSig bestChainLockWithKnownBlock
bool HasChainLock(int nHeight, const uint256 &blockHash)
void DoInvalidateBlock(const CBlockIndex *pindex, bool activateBestChain)
std::map< uint256, int64_t > seenChainLocks
const CBlockIndex * bestChainLockBlockIndex
void ProcessMessage(CNode *pfrom, const std::string &strCommand, CDataStream &vRecv, CConnman &connman)
static const int64_t CLEANUP_SEEN_TIMEOUT
void AcceptedBlockHeader(const CBlockIndex *pindexNew)
void UpdatedBlockTip(const CBlockIndex *pindexNew)
static const int64_t CLEANUP_INTERVAL
void ProcessNewChainLock(NodeId from, const CChainLockSig &clsig, const uint256 &hash)
bool HasConflictingChainLock(int nHeight, const uint256 &blockHash)
CBLSLazySignature sig
256-bit opaque blob.
Definition: uint256.h:138
uint256 SerializeHash(const T &obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)
Compute the 256-bit hash of an object's serialization.
Definition: hash.h:289
std::unique_ptr< CConnman > g_connman
Definition: init.cpp:90
@ LOCK
Definition: lockunlock.h:16
#define LogPrint(category,...)
Definition: logging.h:163
@ LLMQ
Definition: logging.h:66
const char * CLSIG
Definition: protocol.cpp:69
Definition: quorums.cpp:26
std::unique_ptr< CSigningManager > quorumSigningManager
std::unique_ptr< CChainLocksHandler > chainLocksHandler
int NodeId
Definition: net.h:109
void Misbehaving(NodeId pnode, int howmuch, const std::string &message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Increase a node's misbehavior score.
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:80
@ MSG_CLSIG
Definition: protocol.h:460
CSporkManager sporkManager
Definition: spork.cpp:29
@ SPORK_23_CHAINLOCKS_ENFORCEMENT
Definition: sporkid.h:30
#define AssertLockNotHeld(cs)
Definition: sync.h:76
#define LOCK2(cs1, cs2)
Definition: sync.h:221
#define AssertLockHeld(cs)
Definition: sync.h:75
std::atomic< bool > fMasterNode
Definition: system.cpp:87
TierTwoSyncState g_tiertwo_sync_state
#define strprintf
Definition: tinyformat.h:1056
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: utiltime.cpp:61
bool ActivateBestChain(CValidationState &state, std::shared_ptr< const CBlock > pblock)
Make the best chain active, in multiple steps.
bool ReconsiderBlock(CValidationState &state, CBlockIndex *pindex)
Remove invalidity status from a block and its descendants.
BlockMap mapBlockIndex
Definition: validation.cpp:82
PrevBlockMap mapPrevBlockIndex
Definition: validation.cpp:83
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84
bool InvalidateBlock(CValidationState &state, const CChainParams &chainparams, CBlockIndex *pindex)
Mark a block as invalid.
CScheduler scheduler