Skip to content

Commit

Permalink
Merge #2946: Dash LLMQ backports
Browse files Browse the repository at this point in the history
901976a Stop tracking interested/participating nodes and send/announce to MNAUTH peers (#2798) (Alexander Block)
59dfdc2 update bestChainLockWithKnownBlock in AcceptedBlockHeader (Alessandro Rezzi)
bd0a3f8 Introduce "qsendrecsigs" to indicate that plain recovered sigs should be sent (#2783) (Alexander Block)
846fd9a Make LLMQ/InstantSend/ChainLocks code less spammy (#2781) (Alexander Block)
1a52ad0 Fix LogPrintf call in ::DoInvalidateBlock (Alessandro Rezzi)
e3c5eef Multiple fixes/refactorings for ChainLocks (#2765) (Alexander Block)
9dda5d2 Implement LLMQ based InstantSend (#2735) (Alexander Block)
149e3e3 Various small cleanups (#2761) (UdjinM6)
cc7450d Use ReleaseNodeVector and CopyNodeVector in PIVX specific code (Alessandro Rezzi)
ed463a6 Do not hold cs_vNodes in CSigSharesManager::SendMessages() for too long (#2758) (UdjinM6)
cd1809f Implement persistence for LLMQ based InstantSend (#2756) (Alexander Block)
924ee8f Don't be too harsh for invalid CLSIGs (#2742) (Alexander Block)
f255314 Fix banning when local node doesn't have the vvec (#2739) (Alexander Block)

Pull request description:

  each commit backports a different PR. The PR number is in the commit message.

  There are also 3 extra (trivial) commits added by me

ACKs for top commit: 901976a
  Duddino:
    utACK 901976a
  Liquid369:
    uTACK 901976a

Tree-SHA512: 70b53ae3fc014c23964cae3452f6c8270d96b3b375c0f172e04039ba6f3b537d56c38677e5e4005c8b810a82b447a64fc978c2d63344b9e9736ceff7db162a18
  • Loading branch information
Fuzzbawls committed Nov 9, 2024
2 parents a32071f + 901976a commit a34f0d3
Show file tree
Hide file tree
Showing 20 changed files with 298 additions and 297 deletions.
12 changes: 6 additions & 6 deletions src/llmq/quorums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
}

cxxtimer::Timer t(true);
LogPrintf("CQuorum::StartCachePopulatorThread -- start\n");
LogPrint(BCLog::LLMQ, "CQuorum::StartCachePopulatorThread -- start\n");

// this thread will exit after some time
// when then later some other thread tries to get keys, it will be much faster
Expand All @@ -151,7 +151,7 @@ void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
_this->GetPubKeyShare(i);
}
}
LogPrintf("CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
LogPrint(BCLog::LLMQ, "CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
});
}

Expand Down Expand Up @@ -195,7 +195,7 @@ bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, const
quorum->WriteContributions(evoDb);
hasValidVvec = true;
} else {
LogPrintf("CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed\n", __func__, qc.quorumHash.ToString());
LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed\n", __func__, qc.quorumHash.ToString());
}
}

Expand Down Expand Up @@ -224,20 +224,20 @@ bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::
cxxtimer::Timer t2(true);
quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
if (quorumVvec == nullptr) {
LogPrintf("CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
// without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
// allows to use the quorum as a non-member (verification through the quorum pub key)
return false;
}
skShare = blsWorker.AggregateSecretKeys(skContributions);
if (!skShare.IsValid()) {
LogPrintf("CQuorumManager::%s -- failed to build skShare\n", __func__);
LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- failed to build skShare\n", __func__);
// We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
// have a valid quorum vvec at this point)
}
t2.stop();

LogPrintf("CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());

quorum->quorumVvec = quorumVvec;
quorum->skShare = skShare;
Expand Down
159 changes: 97 additions & 62 deletions src/llmq/quorums_chainlocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ CChainLocksHandler::~CChainLocksHandler()
void CChainLocksHandler::Start()
{
quorumSigningManager->RegisterRecoveredSigsListener(this);
scheduler->scheduleEvery([&]() {
EnforceBestChainLock();
// regularely retry signing the current chaintip
TrySignChainTip();
},
5000);
}

void CChainLocksHandler::Stop()
Expand Down Expand Up @@ -106,7 +112,7 @@ void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLock
LogPrintf("CChainLocksHandler::%s -- invalid CLSIG (%s), peer=%d\n", __func__, clsig.ToString(), from);
if (from != -1) {
LOCK(cs_main);
Misbehaving(from, 100);
Misbehaving(from, 10);
}
return;
}
Expand Down Expand Up @@ -147,88 +153,113 @@ void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLock
bestChainLockBlockIndex = pindex;
}

EnforceBestChainLock();
scheduler->scheduleFromNow([&]() {
EnforceBestChainLock();
},
0);

LogPrintf("CChainLocksHandler::%s -- processed new CLSIG (%s), peer=%d\n",
__func__, clsig.ToString(), from);
LogPrint(BCLog::LLMQ, "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());
LOCK2(cs_main, cs);

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;
}
if (pindexNew->GetBlockHash() == bestChainLock.blockHash) {
LogPrintf("CChainLocksHandler::%s -- block header %s came in late, updating and enforcing\n", __func__, pindexNew->GetBlockHash().ToString());

bestChainLockBlockIndex = pindexNew;
doEnforce = true;
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;
}
}
if (doEnforce) {
EnforceBestChainLock();

// when EnforceBestChainLock is called later, it might end up invalidating other chains but not activating the
// CLSIG locked chain. This happens when only the header is known but the block is still missing yet. The usual
// block processing logic will handle this when the block arrives
bestChainLockWithKnownBlock = bestChainLock;
bestChainLockBlockIndex = pindexNew;
}
}

void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork)
{
// don't call TrySignChainTip directly but instead let the scheduler call it. This way we ensure that cs_main is
// never locked and TrySignChainTip is not called twice in parallel. Also avoids recursive calls due to
// EnforceBestChainLock switching chains.
LOCK(cs);
if (tryLockChainTipScheduled) {
return;
}
tryLockChainTipScheduled = true;
scheduler->scheduleFromNow([&]() {
EnforceBestChainLock();
TrySignChainTip();
LOCK(cs);
tryLockChainTipScheduled = false;
},
0);
}

void CChainLocksHandler::TrySignChainTip()
{
Cleanup();

const CBlockIndex* pindex;
{
LOCK(cs_main);
pindex = chainActive.Tip();
}

if (!fMasterNode) {
return;
}
if (!pindexNew->pprev) {
if (!pindex->pprev) {
return;
}
if (!sporkManager.IsSporkActive(SPORK_23_CHAINLOCKS_ENFORCEMENT)) {
return;
}

Cleanup();

// 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 (bestChainLockBlockIndex == pindexNew) {
// we first got the CLSIG, then the header, and then the block was connected.
// In this case there is no need to continue here.
if (pindex->nHeight == lastSignedHeight) {
// already signed this one
return;
}

if (InternalHasConflictingChainLock(pindexNew->nHeight, pindexNew->GetBlockHash())) {
if (!inEnforceBestChainLock) {
// 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);
}
if (bestChainLock.nHeight >= pindex->nHeight) {
// already got the same CLSIG or a better one
return;
}

if (bestChainLock.nHeight >= pindexNew->nHeight) {
// already got the same CLSIG or a better one
if (InternalHasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
// don't sign if another conflicting CLSIG is already present. EnforceBestChainLock will later enforce
// the correct chain.
return;
}
}

if (pindexNew->nHeight == lastSignedHeight) {
// already signed this one
LogPrint(BCLog::LLMQ, "CChainLocksHandler::%s -- trying to sign %s, height=%d\n", __func__, pindex->GetBlockHash().ToString(), pindex->nHeight);

uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight));
uint256 msgHash = pindex->GetBlockHash();

{
LOCK(cs);
if (bestChainLock.nHeight >= pindex->nHeight) {
// might have happened while we didn't hold cs
return;
}
lastSignedHeight = pindexNew->nHeight;
lastSignedHeight = pindex->nHeight;
lastSignedRequestId = requestId;
lastSignedMsgHash = msgHash;
}
Expand All @@ -237,23 +268,30 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl
}

// WARNING: cs_main and cs should not be held!
// This should also not be called from validation signals, as this might result in recursive calls
void CChainLocksHandler::EnforceBestChainLock()
{
CChainLockSig clsig;
const CBlockIndex* pindex;
const CBlockIndex* currentBestChainLockBlockIndex;
{
LOCK(cs);
clsig = bestChainLockWithKnownBlock;
pindex = bestChainLockBlockIndex;
pindex = currentBestChainLockBlockIndex = this->bestChainLockBlockIndex;

if (!currentBestChainLockBlockIndex) {
// we don't have the header/block, so we can't do anything right now
return;
}
}

bool activateNeeded;
{
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.
inEnforceBestChainLock = 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());
Expand All @@ -262,20 +300,27 @@ void CChainLocksHandler::EnforceBestChainLock()
continue;
}
LogPrintf("CChainLocksHandler::%s -- CLSIG (%s) invalidates block %s\n",
__func__, bestChainLockWithKnownBlock.ToString(), jt->second->GetBlockHash().ToString());
__func__, clsig.ToString(), jt->second->GetBlockHash().ToString());
DoInvalidateBlock(jt->second, false);
}

pindex = pindex->pprev;
}
inEnforceBestChainLock = false;
// In case blocks from the correct chain are invalid at the moment, reconsider them. The only case where this
// can happen right now is when missing superblock triggers caused the main chain to be dismissed first. When
// the trigger later appears, this should bring us to the correct chain eventually. Please note that this does
// NOT enforce invalid blocks in any way, it just causes re-validation.
if (!currentBestChainLockBlockIndex->IsValid()) {
CValidationState state;
ReconsiderBlock(state, mapBlockIndex.at(currentBestChainLockBlockIndex->GetBlockHash()));
}

activateNeeded = chainActive.Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight) != currentBestChainLockBlockIndex;
}

CValidationState state;
if (!ActivateBestChain(state)) {
LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", state.GetRejectReason());
// This should not have happened and we are in a state were it's not safe to continue anymore
assert(false);
if (activateNeeded && !ActivateBestChain(state)) {
LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, state.GetRejectReason());
}
}

Expand Down Expand Up @@ -305,16 +350,6 @@ void CChainLocksHandler::HandleNewRecoveredSig(const llmq::CRecoveredSig& recove
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)
{
Expand All @@ -328,15 +363,15 @@ void CChainLocksHandler::DoInvalidateBlock(const CBlockIndex* pindex, bool activ

CValidationState state;
if (!InvalidateBlock(state, params, pindex2)) {
LogPrintf("CChainLocksHandler::UpdatedBlockTip -- InvalidateBlock failed: %s\n", state.GetRejectReason());
LogPrintf("CChainLocksHandler::%s -- InvalidateBlock failed: %s\n", __func__, state.GetRejectReason());
// 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)) {
LogPrintf("CChainLocksHandler::UpdatedBlockTip -- ActivateBestChain failed: %s\n", state.GetRejectReason());
LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, state.GetRejectReason());
// This should not have happened and we are in a state were it's not safe to continue anymore
assert(false);
}
Expand Down
4 changes: 2 additions & 2 deletions src/llmq/quorums_chainlocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class CChainLocksHandler : public CRecoveredSigsListener
private:
CScheduler* scheduler;
RecursiveMutex cs;
std::atomic<bool> inEnforceBestChainLock{false};
bool tryLockChainTipScheduled{false};

uint256 bestChainLockHash;
CChainLockSig bestChainLock;
Expand Down Expand Up @@ -75,6 +75,7 @@ class CChainLocksHandler : public CRecoveredSigsListener
void ProcessNewChainLock(NodeId from, const CChainLockSig& clsig, const uint256& hash);
void AcceptedBlockHeader(const CBlockIndex* pindexNew);
void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork);
void TrySignChainTip();
void EnforceBestChainLock();
virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);

Expand All @@ -86,7 +87,6 @@ class CChainLocksHandler : public CRecoveredSigsListener
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();
Expand Down
6 changes: 2 additions & 4 deletions src/llmq/quorums_dkgsession.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "bls/bls_worker.h"
#include "consensus/params.h"
#include "evo/deterministicmns.h"
#include "evo/evodb.h"
#include "net.h"
#include "llmq/quorums_utils.h"
#include "logging.h"
Expand Down Expand Up @@ -237,7 +236,6 @@ class CDKGSession
private:
const Consensus::LLMQParams& params;

CEvoDB& evoDb;
CBLSWorker& blsWorker;
CBLSWorkerCache cache;
CDKGSessionManager& dkgManager;
Expand Down Expand Up @@ -277,8 +275,8 @@ class CDKGSession
std::set<uint256> validCommitments;

public:
CDKGSession(const Consensus::LLMQParams& _params, CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) :
params(_params), evoDb(_evoDb), blsWorker(_blsWorker), cache(_blsWorker), dkgManager(_dkgManager) {}
CDKGSession(const Consensus::LLMQParams& _params, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) :
params(_params), blsWorker(_blsWorker), cache(_blsWorker), dkgManager(_dkgManager) {}

bool Init(const CBlockIndex* _pindexQuorum, const std::vector<CDeterministicMNCPtr>& mns, const uint256& _myProTxHash);

Expand Down
Loading

0 comments on commit a34f0d3

Please sign in to comment.