Implement and enforce ChainLocks

This commit is contained in:
Alexander Block 2019-01-22 14:20:32 +01:00
parent 2bf6eb1c7c
commit 29532ba196
12 changed files with 548 additions and 4 deletions

View File

@ -144,6 +144,7 @@ BITCOIN_CORE_H = \
llmq/quorums.h \ llmq/quorums.h \
llmq/quorums_blockprocessor.h \ llmq/quorums_blockprocessor.h \
llmq/quorums_commitment.h \ llmq/quorums_commitment.h \
llmq/quorums_chainlocks.h \
llmq/quorums_debug.h \ llmq/quorums_debug.h \
llmq/quorums_dkgsessionhandler.h \ llmq/quorums_dkgsessionhandler.h \
llmq/quorums_dkgsessionmgr.h \ llmq/quorums_dkgsessionmgr.h \
@ -259,6 +260,7 @@ libdash_server_a_SOURCES = \
llmq/quorums.cpp \ llmq/quorums.cpp \
llmq/quorums_blockprocessor.cpp \ llmq/quorums_blockprocessor.cpp \
llmq/quorums_commitment.cpp \ llmq/quorums_commitment.cpp \
llmq/quorums_chainlocks.cpp \
llmq/quorums_debug.cpp \ llmq/quorums_debug.cpp \
llmq/quorums_dkgsessionhandler.cpp \ llmq/quorums_dkgsessionhandler.cpp \
llmq/quorums_dkgsessionmgr.cpp \ llmq/quorums_dkgsessionmgr.cpp \

View File

@ -331,6 +331,7 @@ public:
vSporkAddresses = {"Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"}; vSporkAddresses = {"Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"};
nMinSporkKeys = 1; nMinSporkKeys = 1;
fBIP9CheckMasternodesUpgraded = true; fBIP9CheckMasternodesUpgraded = true;
consensus.llmqChainLocks = Consensus::LLMQ_400_60;
checkpointData = (CCheckpointData) { checkpointData = (CCheckpointData) {
boost::assign::map_list_of boost::assign::map_list_of
@ -498,6 +499,7 @@ public:
vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"}; vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"};
nMinSporkKeys = 1; nMinSporkKeys = 1;
fBIP9CheckMasternodesUpgraded = true; fBIP9CheckMasternodesUpgraded = true;
consensus.llmqChainLocks = Consensus::LLMQ_400_60;
checkpointData = (CCheckpointData) { checkpointData = (CCheckpointData) {
boost::assign::map_list_of boost::assign::map_list_of
@ -644,6 +646,7 @@ public:
nMinSporkKeys = 1; nMinSporkKeys = 1;
// devnets are started with no blocks and no MN, so we can't check for upgraded MN (as there are none) // devnets are started with no blocks and no MN, so we can't check for upgraded MN (as there are none)
fBIP9CheckMasternodesUpgraded = false; fBIP9CheckMasternodesUpgraded = false;
consensus.llmqChainLocks = Consensus::LLMQ_400_60;
checkpointData = (CCheckpointData) { checkpointData = (CCheckpointData) {
boost::assign::map_list_of boost::assign::map_list_of
@ -758,6 +761,7 @@ public:
nMinSporkKeys = 1; nMinSporkKeys = 1;
// regtest usually has no masternodes in most tests, so don't check for upgraged MNs // regtest usually has no masternodes in most tests, so don't check for upgraged MNs
fBIP9CheckMasternodesUpgraded = false; fBIP9CheckMasternodesUpgraded = false;
consensus.llmqChainLocks = Consensus::LLMQ_10_60;
checkpointData = (CCheckpointData){ checkpointData = (CCheckpointData){
boost::assign::map_list_of boost::assign::map_list_of

View File

@ -181,6 +181,7 @@ struct Params {
int nHighSubsidyFactor{1}; int nHighSubsidyFactor{1};
std::map<LLMQType, LLMQParams> llmqs; std::map<LLMQType, LLMQParams> llmqs;
LLMQType llmqChainLocks;
}; };
} // namespace Consensus } // namespace Consensus

View File

@ -9,8 +9,6 @@
#include "masternode-payments.h" #include "masternode-payments.h"
#include "masternode-sync.h" #include "masternode-sync.h"
#include "privatesend.h" #include "privatesend.h"
#include "llmq/quorums.h"
#include "llmq/quorums_dkgsessionmgr.h"
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
#include "privatesend-client.h" #include "privatesend-client.h"
#endif // ENABLE_WALLET #endif // ENABLE_WALLET
@ -18,6 +16,10 @@
#include "evo/deterministicmns.h" #include "evo/deterministicmns.h"
#include "llmq/quorums.h"
#include "llmq/quorums_chainlocks.h"
#include "llmq/quorums_dkgsessionmgr.h"
void CDSNotificationInterface::InitializeCurrentBlockTip() void CDSNotificationInterface::InitializeCurrentBlockTip()
{ {
LOCK(cs_main); LOCK(cs_main);
@ -26,6 +28,7 @@ void CDSNotificationInterface::InitializeCurrentBlockTip()
void CDSNotificationInterface::AcceptedBlockHeader(const CBlockIndex *pindexNew) void CDSNotificationInterface::AcceptedBlockHeader(const CBlockIndex *pindexNew)
{ {
llmq::chainLocksHandler->AcceptedBlockHeader(pindexNew);
masternodeSync.AcceptedBlockHeader(pindexNew); masternodeSync.AcceptedBlockHeader(pindexNew);
} }
@ -55,6 +58,8 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
if (fLiteMode) if (fLiteMode)
return; return;
llmq::chainLocksHandler->UpdatedBlockTip(pindexNew, pindexFork);
CPrivateSend::UpdatedBlockTip(pindexNew); CPrivateSend::UpdatedBlockTip(pindexNew);
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
privateSendClient.UpdatedBlockTip(pindexNew); privateSendClient.UpdatedBlockTip(pindexNew);

View 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();
}
}

View 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

View File

@ -7,6 +7,7 @@
#include "quorums.h" #include "quorums.h"
#include "quorums_blockprocessor.h" #include "quorums_blockprocessor.h"
#include "quorums_commitment.h" #include "quorums_commitment.h"
#include "quorums_chainlocks.h"
#include "quorums_debug.h" #include "quorums_debug.h"
#include "quorums_dkgsessionmgr.h" #include "quorums_dkgsessionmgr.h"
#include "quorums_signing.h" #include "quorums_signing.h"
@ -27,6 +28,7 @@ void InitLLMQSystem(CEvoDB& evoDb, CScheduler* scheduler, bool unitTests)
quorumManager = new CQuorumManager(evoDb, blsWorker, *quorumDKGSessionManager); quorumManager = new CQuorumManager(evoDb, blsWorker, *quorumDKGSessionManager);
quorumSigSharesManager = new CSigSharesManager(); quorumSigSharesManager = new CSigSharesManager();
quorumSigningManager = new CSigningManager(unitTests); quorumSigningManager = new CSigningManager(unitTests);
chainLocksHandler = new CChainLocksHandler(scheduler);
quorumSigSharesManager->StartWorkerThread(); quorumSigSharesManager->StartWorkerThread();
} }
@ -37,6 +39,8 @@ void DestroyLLMQSystem()
quorumSigSharesManager->StopWorkerThread(); quorumSigSharesManager->StopWorkerThread();
} }
delete chainLocksHandler;
chainLocksHandler = nullptr;
delete quorumSigningManager; delete quorumSigningManager;
quorumSigningManager = nullptr; quorumSigningManager = nullptr;
delete quorumSigSharesManager; delete quorumSigSharesManager;

View File

@ -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) 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); auto quorum = SelectQuorumForSigning(llmqParams.type, signedAtHeight, id);
if (!quorum) { if (!quorum) {

View File

@ -46,6 +46,7 @@
#include "evo/simplifiedmns.h" #include "evo/simplifiedmns.h"
#include "llmq/quorums_blockprocessor.h" #include "llmq/quorums_blockprocessor.h"
#include "llmq/quorums_commitment.h" #include "llmq/quorums_commitment.h"
#include "llmq/quorums_chainlocks.h"
#include "llmq/quorums_debug.h" #include "llmq/quorums_debug.h"
#include "llmq/quorums_dkgsessionmgr.h" #include "llmq/quorums_dkgsessionmgr.h"
#include "llmq/quorums_init.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); return llmq::quorumDKGDebugManager->AlreadyHave(inv);
case MSG_QUORUM_RECOVERED_SIG: case MSG_QUORUM_RECOVERED_SIG:
return llmq::quorumSigningManager->AlreadyHave(inv); 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 // 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) if (!push)
vNotFound.push_back(inv); vNotFound.push_back(inv);
} }
@ -1787,6 +1798,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
case MSG_QUORUM_RECOVERED_SIG: case MSG_QUORUM_RECOVERED_SIG:
doubleRequestDelay = 5 * 1000000; doubleRequestDelay = 5 * 1000000;
break; break;
case MSG_CLSIG:
doubleRequestDelay = 5 * 1000000;
break;
} }
pfrom->AskFor(inv, doubleRequestDelay); 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::quorumDKGDebugManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
llmq::quorumSigSharesManager->ProcessMessage(pfrom, strCommand, vRecv, connman); llmq::quorumSigSharesManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
llmq::quorumSigningManager->ProcessMessage(pfrom, strCommand, vRecv, connman); llmq::quorumSigningManager->ProcessMessage(pfrom, strCommand, vRecv, connman);
llmq::chainLocksHandler->ProcessMessage(pfrom, strCommand, vRecv, connman);
} }
else else
{ {

View File

@ -69,6 +69,7 @@ const char *QSIGSHARESINV="qsigsinv";
const char *QGETSIGSHARES="qgetsigs"; const char *QGETSIGSHARES="qgetsigs";
const char *QBSIGSHARES="qbsigs"; const char *QBSIGSHARES="qbsigs";
const char *QSIGREC="qsigrec"; const char *QSIGREC="qsigrec";
const char *CLSIG="clsig";
}; };
static const char* ppszTypeName[] = static const char* ppszTypeName[] =
@ -104,6 +105,7 @@ static const char* ppszTypeName[] =
NetMsgType::QPCOMMITMENT, NetMsgType::QPCOMMITMENT,
NetMsgType::QDEBUGSTATUS, NetMsgType::QDEBUGSTATUS,
NetMsgType::QSIGREC, NetMsgType::QSIGREC,
NetMsgType::CLSIG,
}; };
/** All known message types. Keep this in the same order as the list of /** 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::QGETSIGSHARES,
NetMsgType::QBSIGSHARES, NetMsgType::QBSIGSHARES,
NetMsgType::QSIGREC, NetMsgType::QSIGREC,
NetMsgType::CLSIG,
}; };
const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes));

View File

@ -275,6 +275,7 @@ extern const char *QSIGSHARESINV;
extern const char *QGETSIGSHARES; extern const char *QGETSIGSHARES;
extern const char *QBSIGSHARES; extern const char *QBSIGSHARES;
extern const char *QSIGREC; extern const char *QSIGREC;
extern const char *CLSIG;
}; };
/* Get a vector of all valid message types (see above) */ /* Get a vector of all valid message types (see above) */
@ -376,6 +377,7 @@ enum GetDataMsg {
MSG_QUORUM_PREMATURE_COMMITMENT = 26, MSG_QUORUM_PREMATURE_COMMITMENT = 26,
MSG_QUORUM_DEBUG_STATUS = 27, MSG_QUORUM_DEBUG_STATUS = 27,
MSG_QUORUM_RECOVERED_SIG = 28, MSG_QUORUM_RECOVERED_SIG = 28,
MSG_CLSIG = 29,
}; };
/** inv message data */ /** inv message data */

View File

@ -46,6 +46,8 @@
#include "evo/deterministicmns.h" #include "evo/deterministicmns.h"
#include "evo/cbtx.h" #include "evo/cbtx.h"
#include "llmq/quorums_chainlocks.h"
#include <atomic> #include <atomic>
#include <sstream> #include <sstream>
@ -1897,6 +1899,10 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck)) if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !fJustCheck))
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); 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 // verify that the view's current state corresponds to the previous block
uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash();
assert(hashPrevBlock == view.GetBestBlock()); assert(hashPrevBlock == view.GetBestBlock());
@ -3442,6 +3448,10 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime())) if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev, GetAdjustedTime()))
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); 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) if (pindex == NULL)
pindex = AddToBlockIndex(block); pindex = AddToBlockIndex(block);
@ -3593,13 +3603,20 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
assert(pindexPrev && pindexPrev == chainActive.Tip()); 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()); 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); CCoinsViewCache viewNew(pcoinsTip);
CBlockIndex indexDummy(block); CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev; indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1; indexDummy.nHeight = pindexPrev->nHeight + 1;
indexDummy.phashBlock = &hash;
// begin tx and let it rollback // begin tx and let it rollback
auto dbTx = evoDb->BeginTransaction(); auto dbTx = evoDb->BeginTransaction();