diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index 954de50d0..418f1f9e2 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -71,9 +71,15 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con llmq::quorumDKGSessionManager->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); } +void CDSNotificationInterface::NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr& block) +{ + llmq::chainLocksHandler->NewPoWValidBlock(pindex, block); +} + void CDSNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) { llmq::quorumInstantSendManager->SyncTransaction(tx, pindex, posInBlock); + llmq::chainLocksHandler->SyncTransaction(tx, pindex, posInBlock); instantsend.SyncTransaction(tx, pindex, posInBlock); CPrivateSend::SyncTransaction(tx, pindex, posInBlock); } diff --git a/src/dsnotificationinterface.h b/src/dsnotificationinterface.h index f4e97d7ed..60c1e5bd3 100644 --- a/src/dsnotificationinterface.h +++ b/src/dsnotificationinterface.h @@ -21,6 +21,7 @@ protected: void AcceptedBlockHeader(const CBlockIndex *pindexNew) override; void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; + void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr& block) override; void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock) override; void NotifyMasternodeListChanged(const CDeterministicMNList& newList) override; void NotifyChainLock(const CBlockIndex* pindex); diff --git a/src/llmq/quorums_chainlocks.cpp b/src/llmq/quorums_chainlocks.cpp index 5a51f98e9..67c9ddbef 100644 --- a/src/llmq/quorums_chainlocks.cpp +++ b/src/llmq/quorums_chainlocks.cpp @@ -11,6 +11,7 @@ #include "net_processing.h" #include "scheduler.h" #include "spork.h" +#include "txmempool.h" #include "validation.h" namespace llmq @@ -244,6 +245,51 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqChainLocks, requestId, msgHash); } +void CChainLocksHandler::NewPoWValidBlock(const CBlockIndex* pindex, const std::shared_ptr& block) +{ + LOCK(cs); + if (blockTxs.count(pindex->GetBlockHash())) { + // should actually not happen (blocks are only written once to disk and this is when NewPoWValidBlock is called) + // but be extra safe here in case this behaviour changes. + return; + } + + // We listen for NewPoWValidBlock so that we can collect all TX ids of all included TXs of newly received blocks + // We need this information later when we try to sign a new tip, so that we can determine if all included TXs are + // safe. + + auto txs = std::make_shared>(); + for (const auto& tx : block->vtx) { + if (tx->nVersion == 3) { + if (tx->nType == TRANSACTION_COINBASE || + tx->nType == TRANSACTION_QUORUM_COMMITMENT) { + continue; + } + } + txs->emplace(tx->GetHash()); + } + blockTxs[pindex->GetBlockHash()] = txs; + + int64_t curTime = GetAdjustedTime(); + for (auto& tx : block->vtx) { + txFirstSeenTime.emplace(tx->GetHash(), curTime); + } +} + +void CChainLocksHandler::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock) +{ + if (tx.nVersion == 3) { + if (tx.nType == TRANSACTION_COINBASE || + tx.nType == TRANSACTION_QUORUM_COMMITMENT) { + return; + } + } + + LOCK(cs); + int64_t curTime = GetAdjustedTime(); + txFirstSeenTime.emplace(tx.GetHash(), curTime); +} + // WARNING: cs_main and cs should not be held! void CChainLocksHandler::EnforceBestChainLock() { @@ -420,7 +466,9 @@ void CChainLocksHandler::Cleanup() } } - LOCK2(cs_main, cs); + // need mempool.cs due to GetTransaction calls + LOCK2(cs_main, mempool.cs); + LOCK(cs); for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) { if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) { @@ -430,6 +478,38 @@ void CChainLocksHandler::Cleanup() } } + for (auto it = blockTxs.begin(); it != blockTxs.end(); ) { + auto pindex = mapBlockIndex.at(it->first); + if (InternalHasChainLock(pindex->nHeight, pindex->GetBlockHash())) { + for (auto& txid : *it->second) { + txFirstSeenTime.erase(txid); + } + it = blockTxs.erase(it); + } else if (InternalHasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) { + it = blockTxs.erase(it); + } else { + ++it; + } + } + for (auto it = txFirstSeenTime.begin(); it != txFirstSeenTime.end(); ) { + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(it->first, tx, Params().GetConsensus(), hashBlock)) { + // tx has vanished, probably due to conflicts + it = txFirstSeenTime.erase(it); + } else if (!hashBlock.IsNull()) { + auto pindex = mapBlockIndex.at(hashBlock); + if (chainActive.Tip()->GetAncestor(pindex->nHeight) == pindex && chainActive.Height() - pindex->nHeight >= 6) { + // tx got confirmed >= 6 times, so we can stop keeping track of it + it = txFirstSeenTime.erase(it); + } else { + ++it; + } + } else { + ++it; + } + } + lastCleanupTime = GetTimeMillis(); } diff --git a/src/llmq/quorums_chainlocks.h b/src/llmq/quorums_chainlocks.h index b7cd441e0..410f2bf80 100644 --- a/src/llmq/quorums_chainlocks.h +++ b/src/llmq/quorums_chainlocks.h @@ -12,6 +12,7 @@ #include "chainparams.h" #include +#include class CBlockIndex; class CScheduler; @@ -61,6 +62,10 @@ private: uint256 lastSignedRequestId; uint256 lastSignedMsgHash; + // We keep track of txids from recently received blocks so that we can check if all TXs got ixlocked + std::unordered_map>> blockTxs; + std::unordered_map txFirstSeenTime; + std::map seenChainLocks; int64_t lastCleanupTime{0}; @@ -79,6 +84,8 @@ public: void ProcessNewChainLock(NodeId from, const CChainLockSig& clsig, const uint256& hash); void AcceptedBlockHeader(const CBlockIndex* pindexNew); 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 EnforceBestChainLock(); virtual void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);