From c42b200973f599227bf42d129d5bf95ec97ef686 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 31 Dec 2019 13:01:01 +0300 Subject: [PATCH] Try to avoid being marked as a bad quorum member when we sleep for too long in SleepBeforePhase (#3245) * Do not sleep at the last block of the phase, it's not safe * Refactor it a bit to make it clearer what's going on here * Stop sleeping if blocks came faster than we expected --- src/llmq/quorums_dkgsessionhandler.cpp | 40 ++++++++++++++++++++------ src/llmq/quorums_dkgsessionhandler.h | 1 + 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/llmq/quorums_dkgsessionhandler.cpp b/src/llmq/quorums_dkgsessionhandler.cpp index 905dbda98b..c5e7e83c45 100644 --- a/src/llmq/quorums_dkgsessionhandler.cpp +++ b/src/llmq/quorums_dkgsessionhandler.cpp @@ -115,6 +115,7 @@ void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) int quorumStageInt = pindexNew->nHeight % params.dkgInterval; const CBlockIndex* pindexQuorum = pindexNew->GetAncestor(pindexNew->nHeight - quorumStageInt); + currentHeight = pindexNew->nHeight; quorumHeight = pindexQuorum->nHeight; quorumHash = pindexQuorum->GetBlockHash(); @@ -236,22 +237,43 @@ void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, return; } - // expected time for a full phase - double phaseTime = params.dkgPhaseBlocks * Params().GetConsensus().nPowTargetSpacing * 1000; - // expected time per member - phaseTime = phaseTime / params.size; + // Two blocks can come very close to each other, this happens pretty regularly. We don't want to be + // left behind and marked as a bad member. This means that we should not count the last block of the + // phase as a safe one to keep sleeping, that's why we calculate the phase sleep time as a time of + // the full phase minus one block here. + double phaseSleepTime = (params.dkgPhaseBlocks - 1) * Params().GetConsensus().nPowTargetSpacing * 1000; + // Expected phase sleep time per member + double phaseSleepTimePerMember = phaseSleepTime / params.size; // Don't expect perfect block times and thus reduce the phase time to be on the secure side (caller chooses factor) - phaseTime *= randomSleepFactor; + double adjustedPhaseSleepTimePerMember = phaseSleepTimePerMember * randomSleepFactor; - int64_t sleepTime = (int64_t)(phaseTime * curSession->GetMyMemberIndex()); + int64_t sleepTime = (int64_t)(adjustedPhaseSleepTimePerMember * curSession->GetMyMemberIndex()); int64_t endTime = GetTimeMillis() + sleepTime; + int heightTmp{-1}; + int heightStart{-1}; + { + LOCK(cs); + heightTmp = heightStart = currentHeight; + } while (GetTimeMillis() < endTime) { if (stopRequested || ShutdownRequested()) { throw AbortPhaseException(); } - auto p = GetPhaseAndQuorumHash(); - if (p.first != curPhase || p.second != expectedQuorumHash) { - throw AbortPhaseException(); + { + LOCK(cs); + if (currentHeight > heightTmp) { + // New block(s) just came in + int64_t expectedBlockTime = (currentHeight - heightStart) * Params().GetConsensus().nPowTargetSpacing * 1000; + if (expectedBlockTime > sleepTime) { + // Blocks came faster than we expected, jump into the phase func asap + break; + } + heightTmp = currentHeight; + } + if (phase != curPhase || quorumHash != expectedQuorumHash) { + // Smth went wrong and/or we missed quite a few blocks and it's just too late now + throw AbortPhaseException(); + } } if (!runWhileWaiting()) { MilliSleep(100); diff --git a/src/llmq/quorums_dkgsessionhandler.h b/src/llmq/quorums_dkgsessionhandler.h index 7b3be79e94..b8e2a9604e 100644 --- a/src/llmq/quorums_dkgsessionhandler.h +++ b/src/llmq/quorums_dkgsessionhandler.h @@ -107,6 +107,7 @@ private: CDKGSessionManager& dkgManager; QuorumPhase phase{QuorumPhase_Idle}; + int currentHeight{-1}; int quorumHeight{-1}; uint256 quorumHash; std::shared_ptr curSession;