From 0a5e8eb862025d609164405f38f65a6e95e91557 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Thu, 28 Feb 2019 15:02:20 +0100 Subject: [PATCH] Move ChainLock signing into TrySignChainTip and call it periodically Later commits will introduce checks for "safe TXs" which might abort the signing on first try, but succeed a few seconds later, so we periodically retry to sign the tip. --- src/llmq/quorums_chainlocks.cpp | 52 ++++++++++++++++++++++++--------- src/llmq/quorums_chainlocks.h | 6 ++-- src/llmq/quorums_init.cpp | 4 +-- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp index 67c9ddbeffeed..eed337bfd861d 100644 --- a/src/llmq/quorums_chainlocks.cpp +++ b/src/llmq/quorums_chainlocks.cpp @@ -35,12 +35,16 @@ CChainLocksHandler::~CChainLocksHandler() { } -void CChainLocksHandler::RegisterAsRecoveredSigsListener() +void CChainLocksHandler::Start() { quorumSigningManager->RegisterRecoveredSigsListener(this); + scheduler->scheduleEvery([&]() { + // regularely retry signing the current chaintip as it might have failed before due to missing ixlocks + TrySignChainTip(); + }, 5000); } -void CChainLocksHandler::UnregisterAsRecoveredSigsListener() +void CChainLocksHandler::Stop() { quorumSigningManager->UnregisterRecoveredSigsListener(this); } @@ -184,30 +188,52 @@ void CChainLocksHandler::AcceptedBlockHeader(const CBlockIndex* 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 + LOCK(cs); + if (tryLockChainTipScheduled) { + return; + } + tryLockChainTipScheduled = true; + scheduler->scheduleFromNow([&]() { + TrySignChainTip(); + LOCK(cs); + tryLockChainTipScheduled = false; + }, 0); +} + +void CChainLocksHandler::TrySignChainTip() +{ + Cleanup(); + + const CBlockIndex* pindex; + { + LOCK(cs_main); + pindex = chainActive.Tip(); + } + if (!fMasternodeMode) { return; } - if (!pindexNew->pprev) { + if (!pindex->pprev) { return; } if (!sporkManager.IsSporkActive(SPORK_19_CHAINLOCKS_ENABLED)) { 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(); + uint256 requestId = ::SerializeHash(std::make_pair(CLSIG_REQUESTID_PREFIX, pindex->nHeight)); + uint256 msgHash = pindex->GetBlockHash(); { LOCK(cs); - if (bestChainLockBlockIndex == pindexNew) { + if (bestChainLockBlockIndex == pindex) { // we first got the CLSIG, then the header, and then the block was connected. // In this case there is no need to continue here. // However, NotifyChainLock might not have been called yet, so call it now if needed @@ -218,26 +244,26 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl return; } - if (InternalHasConflictingChainLock(pindexNew->nHeight, pindexNew->GetBlockHash())) { + if (InternalHasConflictingChainLock(pindex->nHeight, pindex->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); + ScheduleInvalidateBlock(pindex); } return; } - if (bestChainLock.nHeight >= pindexNew->nHeight) { + if (bestChainLock.nHeight >= pindex->nHeight) { // already got the same CLSIG or a better one return; } - if (pindexNew->nHeight == lastSignedHeight) { + if (pindex->nHeight == lastSignedHeight) { // already signed this one return; } - lastSignedHeight = pindexNew->nHeight; + lastSignedHeight = pindex->nHeight; lastSignedRequestId = requestId; lastSignedMsgHash = msgHash; } diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h index 410f2bf80bcb0..93ed32404f888 100644 --- a/src/llmq/quorums_chainlocks.h +++ b/src/llmq/quorums_chainlocks.h @@ -49,6 +49,7 @@ class CChainLocksHandler : public CRecoveredSigsListener private: CScheduler* scheduler; CCriticalSection cs; + bool tryLockChainTipScheduled{false}; std::atomic inEnforceBestChainLock{false}; uint256 bestChainLockHash; @@ -74,8 +75,8 @@ class CChainLocksHandler : public CRecoveredSigsListener CChainLocksHandler(CScheduler* _scheduler); ~CChainLocksHandler(); - void RegisterAsRecoveredSigsListener(); - void UnregisterAsRecoveredSigsListener(); + void Start(); + void Stop(); bool AlreadyHave(const CInv& inv); bool GetChainLockByHash(const uint256& hash, CChainLockSig& ret); @@ -86,6 +87,7 @@ class CChainLocksHandler : public CRecoveredSigsListener void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork); void NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr& block); void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock); + void TrySignChainTip(); void EnforceBestChainLock(); virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig); diff --git a/src/llmq/quorums_init.cpp b/src/llmq/quorums_init.cpp index a2620683b6fc8..578d9783e4bbc 100644 --- a/src/llmq/quorums_init.cpp +++ b/src/llmq/quorums_init.cpp @@ -66,7 +66,7 @@ void StartLLMQSystem() quorumSigSharesManager->StartWorkerThread(); } if (chainLocksHandler) { - chainLocksHandler->RegisterAsRecoveredSigsListener(); + chainLocksHandler->Start(); } if (quorumInstantSendManager) { quorumInstantSendManager->RegisterAsRecoveredSigsListener(); @@ -79,7 +79,7 @@ void StopLLMQSystem() quorumInstantSendManager->UnregisterAsRecoveredSigsListener(); } if (chainLocksHandler) { - chainLocksHandler->UnregisterAsRecoveredSigsListener(); + chainLocksHandler->Stop(); } if (quorumSigSharesManager) { quorumSigSharesManager->StopWorkerThread();