Implement and enforce ChainLocks
This commit is contained in:
parent
2bf6eb1c7c
commit
29532ba196
@ -144,6 +144,7 @@ BITCOIN_CORE_H = \
|
||||
llmq/quorums.h \
|
||||
llmq/quorums_blockprocessor.h \
|
||||
llmq/quorums_commitment.h \
|
||||
llmq/quorums_chainlocks.h \
|
||||
llmq/quorums_debug.h \
|
||||
llmq/quorums_dkgsessionhandler.h \
|
||||
llmq/quorums_dkgsessionmgr.h \
|
||||
@ -259,6 +260,7 @@ libdash_server_a_SOURCES = \
|
||||
llmq/quorums.cpp \
|
||||
llmq/quorums_blockprocessor.cpp \
|
||||
llmq/quorums_commitment.cpp \
|
||||
llmq/quorums_chainlocks.cpp \
|
||||
llmq/quorums_debug.cpp \
|
||||
llmq/quorums_dkgsessionhandler.cpp \
|
||||
llmq/quorums_dkgsessionmgr.cpp \
|
||||
|
@ -331,6 +331,7 @@ public:
|
||||
vSporkAddresses = {"Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"};
|
||||
nMinSporkKeys = 1;
|
||||
fBIP9CheckMasternodesUpgraded = true;
|
||||
consensus.llmqChainLocks = Consensus::LLMQ_400_60;
|
||||
|
||||
checkpointData = (CCheckpointData) {
|
||||
boost::assign::map_list_of
|
||||
@ -498,6 +499,7 @@ public:
|
||||
vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"};
|
||||
nMinSporkKeys = 1;
|
||||
fBIP9CheckMasternodesUpgraded = true;
|
||||
consensus.llmqChainLocks = Consensus::LLMQ_400_60;
|
||||
|
||||
checkpointData = (CCheckpointData) {
|
||||
boost::assign::map_list_of
|
||||
@ -644,6 +646,7 @@ public:
|
||||
nMinSporkKeys = 1;
|
||||
// devnets are started with no blocks and no MN, so we can't check for upgraded MN (as there are none)
|
||||
fBIP9CheckMasternodesUpgraded = false;
|
||||
consensus.llmqChainLocks = Consensus::LLMQ_400_60;
|
||||
|
||||
checkpointData = (CCheckpointData) {
|
||||
boost::assign::map_list_of
|
||||
@ -758,6 +761,7 @@ public:
|
||||
nMinSporkKeys = 1;
|
||||
// regtest usually has no masternodes in most tests, so don't check for upgraged MNs
|
||||
fBIP9CheckMasternodesUpgraded = false;
|
||||
consensus.llmqChainLocks = Consensus::LLMQ_10_60;
|
||||
|
||||
checkpointData = (CCheckpointData){
|
||||
boost::assign::map_list_of
|
||||
|
@ -181,6 +181,7 @@ struct Params {
|
||||
int nHighSubsidyFactor{1};
|
||||
|
||||
std::map<LLMQType, LLMQParams> llmqs;
|
||||
LLMQType llmqChainLocks;
|
||||
};
|
||||
} // namespace Consensus
|
||||
|
||||
|
@ -9,8 +9,6 @@
|
||||
#include "masternode-payments.h"
|
||||
#include "masternode-sync.h"
|
||||
#include "privatesend.h"
|
||||
#include "llmq/quorums.h"
|
||||
#include "llmq/quorums_dkgsessionmgr.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "privatesend-client.h"
|
||||
#endif // ENABLE_WALLET
|
||||
@ -18,6 +16,10 @@
|
||||
|
||||
#include "evo/deterministicmns.h"
|
||||
|
||||
#include "llmq/quorums.h"
|
||||
#include "llmq/quorums_chainlocks.h"
|
||||
#include "llmq/quorums_dkgsessionmgr.h"
|
||||
|
||||
void CDSNotificationInterface::InitializeCurrentBlockTip()
|
||||
{
|
||||
LOCK(cs_main);
|
||||
@ -26,6 +28,7 @@ void CDSNotificationInterface::InitializeCurrentBlockTip()
|
||||
|
||||
void CDSNotificationInterface::AcceptedBlockHeader(const CBlockIndex *pindexNew)
|
||||
{
|
||||
llmq::chainLocksHandler->AcceptedBlockHeader(pindexNew);
|
||||
masternodeSync.AcceptedBlockHeader(pindexNew);
|
||||
}
|
||||
|
||||
@ -55,6 +58,8 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
|
||||
if (fLiteMode)
|
||||
return;
|
||||
|
||||
llmq::chainLocksHandler->UpdatedBlockTip(pindexNew, pindexFork);
|
||||
|
||||
CPrivateSend::UpdatedBlockTip(pindexNew);
|
||||
#ifdef ENABLE_WALLET
|
||||
privateSendClient.UpdatedBlockTip(pindexNew);
|
||||
|
392
src/llmq/quorums_chainlocks.cpp
Normal file
392
src/llmq/quorums_chainlocks.cpp
Normal file
@ -0,0 +1,392 @@
|
||||
// Copyright (c) 2019 The Dash Core developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "quorums.h"
|
||||
#include "quorums_chainlocks.h"
|
||||
#include "quorums_signing.h"
|
||||
#include "quorums_utils.h"
|
||||
|
||||
#include "chain.h"
|
||||
#include "net_processing.h"
|
||||
#include "scheduler.h"
|
||||
#include "validation.h"
|
||||
|
||||
namespace llmq
|
||||
{
|
||||
|
||||
static const std::string CLSIG_REQUESTID_PREFIX = "clsig";
|
||||
|
||||
CChainLocksHandler* chainLocksHandler;
|
||||
|
||||
std::string CChainLockSig::ToString() const
|
||||
{
|
||||
return strprintf("CChainLockSig(nHeight=%d, blockHash=%s)", nHeight, blockHash.ToString());
|
||||
}
|
||||
|
||||
CChainLocksHandler::CChainLocksHandler(CScheduler* _scheduler) :
|
||||
scheduler(_scheduler)
|
||||
{
|
||||
quorumSigningManager->RegisterRecoveredSigsListener(this);
|
||||
}
|
||||
|
||||
CChainLocksHandler::~CChainLocksHandler()
|
||||
{
|
||||
quorumSigningManager->UnregisterRecoveredSigsListener(this);
|
||||
}
|
||||
|
||||
bool CChainLocksHandler::AlreadyHave(const CInv& inv)
|
||||
{
|
||||
LOCK(cs);
|
||||
return seenChainLocks.count(inv.hash) != 0;
|
||||
}
|
||||
|
||||
bool CChainLocksHandler::GetChainLockByHash(const uint256& hash, llmq::CChainLockSig& ret)
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
if (hash != bestChainLockHash) {
|
||||
// we only propagate the best one and ditch all the old ones
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = bestChainLock;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
|
||||
{
|
||||
if (strCommand == NetMsgType::CLSIG) {
|
||||
CChainLockSig clsig;
|
||||
vRecv >> clsig;
|
||||
|
||||
auto hash = ::SerializeHash(clsig);
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
connman.RemoveAskFor(hash);
|
||||
}
|
||||
|
||||
ProcessNewChainLock(pfrom->id, clsig, hash);
|
||||
}
|
||||
}
|
||||
|
||||
void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLockSig& clsig, const uint256& hash)
|
||||
{
|
||||
{
|
||||
LOCK(cs);
|
||||
if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bestChainLock.nHeight != -1 && clsig.nHeight <= bestChainLock.nHeight) {
|
||||
// no need to process/relay older CLSIGs
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, clsig.nHeight));
|
||||
uint256 msgHash = clsig.blockHash;
|
||||
if (!quorumSigningManager->VerifyRecoveredSig(Params().GetConsensus().llmqChainLocks, clsig.nHeight, requestId, msgHash, clsig.sig)) {
|
||||
LogPrintf("CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d\n", __func__, clsig.ToString(), from);
|
||||
if (from != -1) {
|
||||
LOCK(cs_main);
|
||||
Misbehaving(from, 100);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
LOCK2(cs_main, cs);
|
||||
|
||||
if (InternalHasConflictingChainLock(clsig.nHeight, clsig.blockHash)) {
|
||||
// This should not happen. If it happens, it means that a malicious entity controls a large part of the MN
|
||||
// network. In this case, we don't allow him to reorg older chainlocks.
|
||||
LogPrintf("CChainLocksHandler::%s -- new CLSIG (%s) tries to reorg previous CLSIG (%s), peer=%d\n",
|
||||
__func__, clsig.ToString(), bestChainLock.ToString(), from);
|
||||
return;
|
||||
}
|
||||
|
||||
bestChainLockHash = hash;
|
||||
bestChainLock = clsig;
|
||||
|
||||
CInv inv(MSG_CLSIG, hash);
|
||||
g_connman->RelayInv(inv);
|
||||
|
||||
auto blockIt = mapBlockIndex.find(clsig.blockHash);
|
||||
if (blockIt == mapBlockIndex.end()) {
|
||||
// we don't know the block/header for this CLSIG yet, so bail out for now
|
||||
// when the block or the header later comes in, we will enforce the correct chain
|
||||
return;
|
||||
}
|
||||
|
||||
if (blockIt->second->nHeight != clsig.nHeight) {
|
||||
// Should not happen, same as the conflict check from above.
|
||||
LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
|
||||
__func__, clsig.ToString(), blockIt->second->nHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
const CBlockIndex* pindex = blockIt->second;
|
||||
bestChainLockWithKnownBlock = bestChainLock;
|
||||
bestChainLockBlockIndex = pindex;
|
||||
}
|
||||
|
||||
EnforceBestChainLock();
|
||||
|
||||
LogPrintf("CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
|
||||
__func__, clsig.ToString(), from);
|
||||
}
|
||||
|
||||
void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* pindexNew)
|
||||
{
|
||||
bool doEnforce = false;
|
||||
{
|
||||
LOCK2(cs_main, cs);
|
||||
|
||||
if (pindexNew->GetBlockHash() == bestChainLock.blockHash) {
|
||||
LogPrintf("CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());
|
||||
|
||||
if (bestChainLock.nHeight != pindexNew->nHeight) {
|
||||
// Should not happen, same as the conflict check from ProcessNewChainLock.
|
||||
LogPrintf("CChainLocksHandler::%s -- height of CLSIG (%s) does not match the specified block's height (%d)\n",
|
||||
__func__, bestChainLock.ToString(), pindexNew->nHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
bestChainLockBlockIndex = pindexNew;
|
||||
doEnforce = true;
|
||||
}
|
||||
}
|
||||
if (doEnforce) {
|
||||
EnforceBestChainLock();
|
||||
}
|
||||
}
|
||||
|
||||
void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork)
|
||||
{
|
||||
if (!fMasternodeMode) {
|
||||
return;
|
||||
}
|
||||
if (!pindexNew->pprev) {
|
||||
return;
|
||||
}
|
||||
|
||||
// DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
|
||||
// To simplify the initial implementation, we skip this process and directly try to create a CLSIG
|
||||
// This will fail when multiple blocks compete, but we accept this for the initial implementation.
|
||||
// Later, we'll add the multiple attempts process.
|
||||
|
||||
uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindexNew->nHeight));
|
||||
uint256 msgHash = pindexNew->GetBlockHash();
|
||||
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
if (InternalHasConflictingChainLock(pindexNew->nHeight, pindexNew->GetBlockHash())) {
|
||||
if (!inInvalidate) {
|
||||
// we accepted this block when there was no lock yet, but now a conflicting lock appeared. Invalidate it.
|
||||
LogPrintf("CChainLocksHandler::%s -- conflicting lock after block was accepted, invalidating now\n",
|
||||
__func__);
|
||||
ScheduleInvalidateBlock(pindexNew);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (bestChainLock.nHeight >= pindexNew->nHeight) {
|
||||
// already got the same CLSIG or a better one
|
||||
return;
|
||||
}
|
||||
|
||||
if (pindexNew->nHeight == lastSignedHeight) {
|
||||
// already signed this one
|
||||
return;
|
||||
}
|
||||
lastSignedHeight = pindexNew->nHeight;
|
||||
lastSignedRequestId = requestId;
|
||||
lastSignedMsgHash = msgHash;
|
||||
}
|
||||
|
||||
quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqChainLocks, requestId, msgHash);
|
||||
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
// WARNING: cs_main and cs should not be held!
|
||||
void CChainLocksHandler::EnforceBestChainLock()
|
||||
{
|
||||
CChainLockSig clsig;
|
||||
const CBlockIndex* pindex;
|
||||
{
|
||||
LOCK(cs);
|
||||
clsig = bestChainLockWithKnownBlock;
|
||||
pindex = bestChainLockBlockIndex;
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
// Go backwards through the chain referenced by clsig until we find a block that is part of the main chain.
|
||||
// For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig
|
||||
// and invalidate each of them.
|
||||
inInvalidate = true; // avoid unnecessary ScheduleInvalidateBlock calls inside UpdatedBlockTip
|
||||
while (pindex && !chainActive.Contains(pindex)) {
|
||||
// Invalidate all blocks that have the same prevBlockHash but are not equal to blockHash
|
||||
auto itp = mapPrevBlockIndex.equal_range(pindex->pprev->GetBlockHash());
|
||||
for (auto jt = itp.first; jt != itp.second; ++jt) {
|
||||
if (jt->second == pindex) {
|
||||
continue;
|
||||
}
|
||||
LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) invalidates block %s\n",
|
||||
__func__, bestChainLockWithKnownBlock.ToString(), jt->second->GetBlockHash().ToString());
|
||||
DoInvalidateBlock(jt->second, false);
|
||||
}
|
||||
|
||||
pindex = pindex->pprev;
|
||||
}
|
||||
inInvalidate = false;
|
||||
}
|
||||
|
||||
CValidationState state;
|
||||
if (!ActivateBestChain(state, Params())) {
|
||||
LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", FormatStateMessage(state));
|
||||
// This should not have happened and we are in a state were it's not safe to continue anymore
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void CChainLocksHandler::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
|
||||
{
|
||||
CChainLockSig clsig;
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
if (recoveredSig.id != lastSignedRequestId || recoveredSig.msgHash != lastSignedMsgHash) {
|
||||
// this is not what we signed, so lets not create a CLSIG for it
|
||||
return;
|
||||
}
|
||||
if (bestChainLock.nHeight >= lastSignedHeight) {
|
||||
// already got the same or a better CLSIG through the CLSIG message
|
||||
return;
|
||||
}
|
||||
|
||||
clsig.nHeight = lastSignedHeight;
|
||||
clsig.blockHash = lastSignedMsgHash;
|
||||
clsig.sig = recoveredSig.sig;
|
||||
}
|
||||
ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
|
||||
}
|
||||
|
||||
void CChainLocksHandler::ScheduleInvalidateBlock(const CBlockIndex* pindex)
|
||||
{
|
||||
// Calls to InvalidateBlock and ActivateBestChain might result in re-invocation of the UpdatedBlockTip and other
|
||||
// signals, so we can't directly call it from signal handlers. We solve this by doing the call from the scheduler
|
||||
|
||||
scheduler->scheduleFromNow([this, pindex]() {
|
||||
DoInvalidateBlock(pindex, true);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// WARNING, do not hold cs while calling this method as we'll otherwise run into a deadlock
|
||||
void CChainLocksHandler::DoInvalidateBlock(const CBlockIndex* pindex, bool activateBestChain)
|
||||
{
|
||||
auto& params = Params();
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
||||
// get the non-const pointer
|
||||
CBlockIndex* pindex2 = mapBlockIndex[pindex->GetBlockHash()];
|
||||
|
||||
CValidationState state;
|
||||
if (!InvalidateBlock(state, params, pindex2)) {
|
||||
LogPrintf("CChainLocksHandler::UpdatedBlockTip -- InvalidateBlock failed: %s\n", FormatStateMessage(state));
|
||||
// This should not have happened and we are in a state were it's not safe to continue anymore
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
CValidationState state;
|
||||
if (activateBestChain && !ActivateBestChain(state, params)) {
|
||||
LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", FormatStateMessage(state));
|
||||
// This should not have happened and we are in a state were it's not safe to continue anymore
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool CChainLocksHandler::HasChainLock(int nHeight, const uint256& blockHash)
|
||||
{
|
||||
LOCK(cs);
|
||||
return InternalHasChainLock(nHeight, blockHash);
|
||||
}
|
||||
|
||||
bool CChainLocksHandler::InternalHasChainLock(int nHeight, const uint256& blockHash)
|
||||
{
|
||||
AssertLockHeld(cs);
|
||||
|
||||
if (!bestChainLockBlockIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nHeight > bestChainLockBlockIndex->nHeight) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nHeight == bestChainLockBlockIndex->nHeight) {
|
||||
return blockHash == bestChainLockBlockIndex->GetBlockHash();
|
||||
}
|
||||
|
||||
auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
|
||||
return pAncestor && pAncestor->GetBlockHash() == blockHash;
|
||||
}
|
||||
|
||||
bool CChainLocksHandler::HasConflictingChainLock(int nHeight, const uint256& blockHash)
|
||||
{
|
||||
LOCK(cs);
|
||||
return InternalHasConflictingChainLock(nHeight, blockHash);
|
||||
}
|
||||
|
||||
bool CChainLocksHandler::InternalHasConflictingChainLock(int nHeight, const uint256& blockHash)
|
||||
{
|
||||
AssertLockHeld(cs);
|
||||
|
||||
if (!bestChainLockBlockIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nHeight > bestChainLockBlockIndex->nHeight) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nHeight == bestChainLockBlockIndex->nHeight) {
|
||||
return blockHash != bestChainLockBlockIndex->GetBlockHash();
|
||||
}
|
||||
|
||||
auto pAncestor = bestChainLockBlockIndex->GetAncestor(nHeight);
|
||||
assert(pAncestor);
|
||||
return pAncestor->GetBlockHash() != blockHash;
|
||||
}
|
||||
|
||||
void CChainLocksHandler::Cleanup()
|
||||
{
|
||||
{
|
||||
LOCK(cs);
|
||||
if (GetTimeMillis() - lastCleanupTime < CLEANUP_INTERVAL) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOCK2(cs_main, cs);
|
||||
|
||||
for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) {
|
||||
if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) {
|
||||
it = seenChainLocks.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
lastCleanupTime = GetTimeMillis();
|
||||
}
|
||||
|
||||
}
|
99
src/llmq/quorums_chainlocks.h
Normal file
99
src/llmq/quorums_chainlocks.h
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2019 The Dash Core developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef DASH_QUORUMS_CHAINLOCKS_H
|
||||
#define DASH_QUORUMS_CHAINLOCKS_H
|
||||
|
||||
#include "llmq/quorums.h"
|
||||
#include "llmq/quorums_signing.h"
|
||||
|
||||
#include "net.h"
|
||||
#include "chainparams.h"
|
||||
|
||||
class CBlockIndex;
|
||||
class CScheduler;
|
||||
|
||||
namespace llmq
|
||||
{
|
||||
|
||||
class CChainLockSig
|
||||
{
|
||||
public:
|
||||
int32_t nHeight{-1};
|
||||
uint256 blockHash;
|
||||
CBLSSignature sig;
|
||||
|
||||
public:
|
||||
ADD_SERIALIZE_METHODS
|
||||
|
||||
template<typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action)
|
||||
{
|
||||
READWRITE(nHeight);
|
||||
READWRITE(blockHash);
|
||||
READWRITE(sig);
|
||||
}
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
class CChainLocksHandler : public CRecoveredSigsListener
|
||||
{
|
||||
static const int64_t CLEANUP_INTERVAL = 1000 * 30;
|
||||
static const int64_t CLEANUP_SEEN_TIMEOUT = 24 * 60 * 60 * 1000;
|
||||
|
||||
private:
|
||||
CScheduler* scheduler;
|
||||
CCriticalSection cs;
|
||||
bool inInvalidate{false};
|
||||
|
||||
uint256 bestChainLockHash;
|
||||
CChainLockSig bestChainLock;
|
||||
|
||||
CChainLockSig bestChainLockWithKnownBlock;
|
||||
const CBlockIndex* bestChainLockBlockIndex{nullptr};
|
||||
|
||||
int32_t lastSignedHeight{-1};
|
||||
uint256 lastSignedRequestId;
|
||||
uint256 lastSignedMsgHash;
|
||||
|
||||
std::map<uint256, int64_t> seenChainLocks;
|
||||
|
||||
int64_t lastCleanupTime{0};
|
||||
|
||||
public:
|
||||
CChainLocksHandler(CScheduler* _scheduler);
|
||||
~CChainLocksHandler();
|
||||
|
||||
public:
|
||||
bool AlreadyHave(const CInv& inv);
|
||||
bool GetChainLockByHash(const uint256& hash, CChainLockSig& ret);
|
||||
|
||||
void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
|
||||
void ProcessNewChainLock(NodeId from, const CChainLockSig& clsig, const uint256& hash);
|
||||
void AcceptedBlockHeader(const CBlockIndex* pindexNew);
|
||||
void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork);
|
||||
void EnforceBestChainLock();
|
||||
virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);
|
||||
|
||||
bool HasChainLock(int nHeight, const uint256& blockHash);
|
||||
bool HasConflictingChainLock(int nHeight, const uint256& blockHash);
|
||||
|
||||
private:
|
||||
// these require locks to be held already
|
||||
bool InternalHasChainLock(int nHeight, const uint256& blockHash);
|
||||
bool InternalHasConflictingChainLock(int nHeight, const uint256& blockHash);
|
||||
|
||||
void ScheduleInvalidateBlock(const CBlockIndex* pindex);
|
||||
void DoInvalidateBlock(const CBlockIndex* pindex, bool activateBestChain);
|
||||
|
||||
void Cleanup();
|
||||
};
|
||||
|
||||
extern CChainLocksHandler* chainLocksHandler;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //DASH_QUORUMS_CHAINLOCKS_H
|
@ -7,6 +7,7 @@
|
||||
#include "quorums.h"
|
||||
#include "quorums_blockprocessor.h"
|
||||
#include "quorums_commitment.h"
|
||||
#include "quorums_chainlocks.h"
|
||||
#include "quorums_debug.h"
|
||||
#include "quorums_dkgsessionmgr.h"
|
||||
#include "quorums_signing.h"
|
||||
@ -27,6 +28,7 @@ void InitLLMQSystem(CEvoDB& evoDb, CScheduler* scheduler, bool unitTests)
|
||||
quorumManager = new CQuorumManager(evoDb, blsWorker, *quorumDKGSessionManager);
|
||||
quorumSigSharesManager = new CSigSharesManager();
|
||||
quorumSigningManager = new CSigningManager(unitTests);
|
||||
chainLocksHandler = new CChainLocksHandler(scheduler);
|
||||
|
||||
quorumSigSharesManager->StartWorkerThread();
|
||||
}
|
||||
@ -37,6 +39,8 @@ void DestroyLLMQSystem()
|
||||
quorumSigSharesManager->StopWorkerThread();
|
||||
}
|
||||
|
||||
delete chainLocksHandler;
|
||||
chainLocksHandler = nullptr;
|
||||
delete quorumSigningManager;
|
||||
quorumSigningManager = nullptr;
|
||||
delete quorumSigSharesManager;
|
||||
|
@ -588,7 +588,7 @@ CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType
|
||||
|
||||
bool CSigningManager::VerifyRecoveredSig(Consensus::LLMQType llmqType, int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig)
|
||||
{
|
||||
auto& llmqParams = Params().GetConsensus().llmqs.at(Params().GetConsensus().llmqTypeForChainLocks);
|
||||
auto& llmqParams = Params().GetConsensus().llmqs.at(Params().GetConsensus().llmqChainLocks);
|
||||
|
||||
auto quorum = SelectQuorumForSigning(llmqParams.type, signedAtHeight, id);
|
||||
if (!quorum) {
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "evo/simplifiedmns.h"
|
||||
#include "llmq/quorums_blockprocessor.h"
|
||||
#include "llmq/quorums_commitment.h"
|
||||
#include "llmq/quorums_chainlocks.h"
|
||||
#include "llmq/quorums_debug.h"
|
||||
#include "llmq/quorums_dkgsessionmgr.h"
|
||||
#include "llmq/quorums_init.h"
|
||||
@ -973,6 +974,8 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||
return llmq::quorumDKGDebugManager->AlreadyHave(inv);
|
||||
case MSG_QUORUM_RECOVERED_SIG:
|
||||
return llmq::quorumSigningManager->AlreadyHave(inv);
|
||||
case MSG_CLSIG:
|
||||
return llmq::chainLocksHandler->AlreadyHave(inv);
|
||||
}
|
||||
|
||||
// Don't know what it is, just say we already got one
|
||||
@ -1285,6 +1288,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
|
||||
}
|
||||
}
|
||||
|
||||
if (!push && (inv.type == MSG_CLSIG)) {
|
||||
llmq::CChainLockSig o;
|
||||
if (llmq::chainLocksHandler->GetChainLockByHash(inv.hash, o)) {
|
||||
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::CLSIG, o));
|
||||
push = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!push)
|
||||
vNotFound.push_back(inv);
|
||||
}
|
||||
@ -1787,6 +1798,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
case MSG_QUORUM_RECOVERED_SIG:
|
||||
doubleRequestDelay = 5 * 1000000;
|
||||
break;
|
||||
case MSG_CLSIG:
|
||||
doubleRequestDelay = 5 * 1000000;
|
||||
break;
|
||||
}
|
||||
pfrom->AskFor(inv, doubleRequestDelay);
|
||||
}
|
||||
@ -2952,6 +2966,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
||||
llmq::quorumDKGDebugManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
|
||||
llmq::quorumSigSharesManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
|
||||
llmq::quorumSigningManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
|
||||
llmq::chainLocksHandler->ProcessMessage(pfrom, strCommand, vRecv, connman);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -69,6 +69,7 @@ const char *QSIGSHARESINV="qsigsinv";
|
||||
const char *QGETSIGSHARES="qgetsigs";
|
||||
const char *QBSIGSHARES="qbsigs";
|
||||
const char *QSIGREC="qsigrec";
|
||||
const char *CLSIG="clsig";
|
||||
};
|
||||
|
||||
static const char* ppszTypeName[] =
|
||||
@ -104,6 +105,7 @@ static const char* ppszTypeName[] =
|
||||
NetMsgType::QPCOMMITMENT,
|
||||
NetMsgType::QDEBUGSTATUS,
|
||||
NetMsgType::QSIGREC,
|
||||
NetMsgType::CLSIG,
|
||||
};
|
||||
|
||||
/** All known message types. Keep this in the same order as the list of
|
||||
@ -167,6 +169,7 @@ const static std::string allNetMessageTypes[] = {
|
||||
NetMsgType::QGETSIGSHARES,
|
||||
NetMsgType::QBSIGSHARES,
|
||||
NetMsgType::QSIGREC,
|
||||
NetMsgType::CLSIG,
|
||||
};
|
||||
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));
|
||||
|
||||
|
@ -275,6 +275,7 @@ extern const char *QSIGSHARESINV;
|
||||
extern const char *QGETSIGSHARES;
|
||||
extern const char *QBSIGSHARES;
|
||||
extern const char *QSIGREC;
|
||||
extern const char *CLSIG;
|
||||
};
|
||||
|
||||
/* Get a vector of all valid message types (see above) */
|
||||
@ -376,6 +377,7 @@ enum GetDataMsg {
|
||||
MSG_QUORUM_PREMATURE_COMMITMENT = 26,
|
||||
MSG_QUORUM_DEBUG_STATUS = 27,
|
||||
MSG_QUORUM_RECOVERED_SIG = 28,
|
||||
MSG_CLSIG = 29,
|
||||
};
|
||||
|
||||
/** inv message data */
|
||||
|
@ -46,6 +46,8 @@
|
||||
#include "evo/deterministicmns.h"
|
||||
#include "evo/cbtx.h"
|
||||
|
||||
#include "llmq/quorums_chainlocks.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
|
||||
@ -1897,6 +1899,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
|
||||
if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck))
|
||||
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
|
||||
|
||||
if (pindex->pprev && llmq::chainLocksHandler->HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
|
||||
return state.DoS(10, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
|
||||
}
|
||||
|
||||
// verify that the view's current state corresponds to the previous block
|
||||
uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash();
|
||||
assert(hashPrevBlock == view.GetBestBlock());
|
||||
@ -3442,6 +3448,10 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
|
||||
|
||||
if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime()))
|
||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state));
|
||||
|
||||
if (llmq::chainLocksHandler->HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) {
|
||||
return state.DoS(10, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
|
||||
}
|
||||
}
|
||||
if (pindex == NULL)
|
||||
pindex = AddToBlockIndex(block);
|
||||
@ -3593,13 +3603,20 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
assert(pindexPrev && pindexPrev == chainActive.Tip());
|
||||
if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, block.GetHash()))
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash))
|
||||
return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str());
|
||||
|
||||
if (llmq::chainLocksHandler->HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) {
|
||||
return state.DoS(10, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock");
|
||||
}
|
||||
|
||||
CCoinsViewCache viewNew(pcoinsTip);
|
||||
CBlockIndex indexDummy(block);
|
||||
indexDummy.pprev = pindexPrev;
|
||||
indexDummy.nHeight = pindexPrev->nHeight + 1;
|
||||
indexDummy.phashBlock = &hash;
|
||||
|
||||
// begin tx and let it rollback
|
||||
auto dbTx = evoDb->BeginTransaction();
|
||||
|
Loading…
Reference in New Issue
Block a user