From d6e7758519417aad9b6e311d521f905aa5f24176 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 5 Apr 2019 06:35:39 +0200 Subject: [PATCH 1/5] Keep track of when IS locks were mined --- src/llmq/quorums_instantsend.cpp | 28 ++++++++++++++++++++++++++-- src/llmq/quorums_instantsend.h | 3 +++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 9a323f57ea6c..623ee43e08db 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -85,6 +85,16 @@ void CInstantSendDb::RemoveInstantSendLock(const uint256& hash, CInstantSendLock } } +void CInstantSendDb::WriteInstantSendLockMined(const uint256& hash, int nHeight) +{ + db.Write(std::make_tuple(std::string("is_m"), std::numeric_limits::max() - nHeight, hash), true); +} + +void CInstantSendDb::RemoveInstantSendLockMined(const uint256& hash, int nHeight) +{ + db.Erase(std::make_tuple(std::string("is_m"), std::numeric_limits::max() - nHeight, hash)); +} + CInstantSendLockPtr CInstantSendDb::GetInstantSendLockByHash(const uint256& hash) { CInstantSendLockPtr ret; @@ -743,9 +753,23 @@ void CInstantSendManager::SyncTransaction(const CTransaction& tx, const CBlockIn return; } - bool locked = IsLocked(tx.GetHash()); + uint256 islockHash; + { + LOCK(cs); + islockHash = db.GetInstantSendLockHashByTxid(tx.GetHash()); + + // update DB about when an IS lock was mined + if (!islockHash.IsNull() && pindex) { + if (posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK) { + db.RemoveInstantSendLockMined(islockHash, pindex->nHeight); + } else { + db.WriteInstantSendLockMined(islockHash, pindex->nHeight); + } + } + } + bool chainlocked = pindex && chainLocksHandler->HasChainLock(pindex->nHeight, pindex->GetBlockHash()); - if (locked || chainlocked) { + if (!islockHash.IsNull() || chainlocked) { RetryLockTxs(tx.GetHash()); } else { ProcessTx(tx, Params().GetConsensus()); diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h index 3254595fdf65..d36b214e03dc 100644 --- a/src/llmq/quorums_instantsend.h +++ b/src/llmq/quorums_instantsend.h @@ -57,6 +57,9 @@ class CInstantSendDb void WriteNewInstantSendLock(const uint256& hash, const CInstantSendLock& islock); void RemoveInstantSendLock(const uint256& hash, CInstantSendLockPtr islock); + void WriteInstantSendLockMined(const uint256& hash, int nHeight); + void RemoveInstantSendLockMined(const uint256& hash, int nHeight); + CInstantSendLockPtr GetInstantSendLockByHash(const uint256& hash); uint256 GetInstantSendLockHashByTxid(const uint256& txid); CInstantSendLockPtr GetInstantSendLockByTxid(const uint256& txid); From 4577438e872b36c166e452396bb5c65f144dd206 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 5 Apr 2019 06:36:44 +0200 Subject: [PATCH 2/5] Implement RemoveConfirmedInstantSendLocks to prune confirmed IS locks from DB --- src/llmq/quorums_instantsend.cpp | 46 ++++++++++++++++++++++++++++++-- src/llmq/quorums_instantsend.h | 2 ++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 623ee43e08db..83ebc4e0fa99 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -62,6 +62,13 @@ void CInstantSendDb::WriteNewInstantSendLock(const uint256& hash, const CInstant } void CInstantSendDb::RemoveInstantSendLock(const uint256& hash, CInstantSendLockPtr islock) +{ + CDBBatch batch(db); + RemoveInstantSendLock(batch, hash, islock); + db.WriteBatch(batch); +} + +void CInstantSendDb::RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock) { if (!islock) { islock = GetInstantSendLockByHash(hash); @@ -70,13 +77,11 @@ void CInstantSendDb::RemoveInstantSendLock(const uint256& hash, CInstantSendLock } } - CDBBatch batch(db); batch.Erase(std::make_tuple(std::string("is_i"), hash)); batch.Erase(std::make_tuple(std::string("is_tx"), islock->txid)); for (auto& in : islock->inputs) { batch.Erase(std::make_tuple(std::string("is_in"), in)); } - db.WriteBatch(batch); islockCache.erase(hash); txidCache.erase(islock->txid); @@ -95,6 +100,43 @@ void CInstantSendDb::RemoveInstantSendLockMined(const uint256& hash, int nHeight db.Erase(std::make_tuple(std::string("is_m"), std::numeric_limits::max() - nHeight, hash)); } +std::unordered_map CInstantSendDb::RemoveConfirmedInstantSendLocks(int nUntilHeight) +{ + auto it = std::unique_ptr(db.NewIterator()); + + auto firstKey = std::make_tuple(std::string("is_m"), std::numeric_limits::max() - nUntilHeight, uint256()); + + it->Seek(firstKey); + + CDBBatch deleteBatch(db); + std::unordered_map ret; + while (it->Valid()) { + decltype(firstKey) curKey; + if (!it->GetKey(curKey) || std::get<0>(curKey) != "is_m") { + break; + } + int nHeight = std::numeric_limits::max() - std::get<1>(curKey); + if (nHeight > nUntilHeight) { + break; + } + + auto& islockHash = std::get<2>(curKey); + auto islock = GetInstantSendLockByHash(islockHash); + if (islock) { + RemoveInstantSendLock(deleteBatch, islockHash, islock); + ret.emplace(islockHash, islock); + } + + deleteBatch.Erase(curKey); + + it->Next(); + } + + db.WriteBatch(deleteBatch); + + return ret; +} + CInstantSendLockPtr CInstantSendDb::GetInstantSendLockByHash(const uint256& hash) { CInstantSendLockPtr ret; diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h index d36b214e03dc..d800ea61703c 100644 --- a/src/llmq/quorums_instantsend.h +++ b/src/llmq/quorums_instantsend.h @@ -56,9 +56,11 @@ class CInstantSendDb void WriteNewInstantSendLock(const uint256& hash, const CInstantSendLock& islock); void RemoveInstantSendLock(const uint256& hash, CInstantSendLockPtr islock); + void RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, CInstantSendLockPtr islock); void WriteInstantSendLockMined(const uint256& hash, int nHeight); void RemoveInstantSendLockMined(const uint256& hash, int nHeight); + std::unordered_map RemoveConfirmedInstantSendLocks(int nUntilHeight); CInstantSendLockPtr GetInstantSendLockByHash(const uint256& hash); uint256 GetInstantSendLockHashByTxid(const uint256& txid); From 8e7083cb89ed9f002f897a962580bff6e7816542 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 5 Apr 2019 06:37:42 +0200 Subject: [PATCH 3/5] Use db.RemoveConfirmedISLocks() in NotifyChainLock to remove confirmed locks Also move the actual logic into HandleFullyConfirmedBlock and call it from NotifyChainLock and UpdatedBlockTip. --- src/llmq/quorums_instantsend.cpp | 71 ++++++++++---------------------- src/llmq/quorums_instantsend.h | 3 +- 2 files changed, 24 insertions(+), 50 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 83ebc4e0fa99..172055e2248f 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -820,48 +820,12 @@ void CInstantSendManager::SyncTransaction(const CTransaction& tx, const CBlockIn void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindexChainLock) { - uint256 lastChainLockBlock; - { - LOCK(cs); - lastChainLockBlock = db.GetLastChainLockBlock(); - } - - // Let's find all islocks that correspond to TXs which are part of the freshly ChainLocked chain and then delete - // the islocks. We do this because the ChainLocks imply locking and thus it's not needed to further track - // or propagate the islocks - std::unordered_set toDelete; - auto pindex = pindexChainLock; - while (pindex && pindex->GetBlockHash() != lastChainLockBlock) { - CBlock block; - { - LOCK(cs_main); - if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { - pindex = pindex->pprev; - continue; - } - } - - LOCK(cs); - for (const auto& tx : block.vtx) { - auto islock = db.GetInstantSendLockByTxid(tx->GetHash()); - if (!islock) { - continue; - } - auto hash = ::SerializeHash(*islock); - LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: removing islock as it got ChainLocked in block %s\n", __func__, - islock->txid.ToString(), hash.ToString(), pindex->GetBlockHash().ToString()); - RemoveFinalISLock(hash, islock); - } - - pindex = pindex->pprev; - } - { LOCK(cs); db.WriteLastChainLockBlock(pindexChainLock->GetBlockHash()); } - RetryLockTxs(uint256()); + HandleFullyConfirmedBlock(pindexChainLock); } void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) @@ -871,26 +835,35 @@ void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) return; } - int nChainLockMinHeight = pindexNew->nHeight - Params().GetConsensus().nInstantSendKeepLock; - const CBlockIndex* pindex = pindexNew->GetAncestor(nChainLockMinHeight); + int nConfirmedHeight = pindexNew->nHeight - Params().GetConsensus().nInstantSendKeepLock; + const CBlockIndex* pindex = pindexNew->GetAncestor(nConfirmedHeight); if (pindex) { - // Pretend it was chainlocked at nChainLockMinHeight. - // This effectively drops all islocks below nChainLockMinHeight. - NotifyChainLock(pindex); + HandleFullyConfirmedBlock(pindex); } } -void CInstantSendManager::RemoveFinalISLock(const uint256& hash, const CInstantSendLockPtr& islock) +void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) { - AssertLockHeld(cs); + { + LOCK(cs); - db.RemoveInstantSendLock(hash, islock); - - for (auto& in : islock->inputs) { - auto inputRequestId = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in)); - inputRequestIds.erase(inputRequestId); + auto islocks = db.RemoveConfirmedInstantSendLocks(pindex->nHeight); + for (auto& p : islocks) { + auto& islockHash = p.first; + auto& islock = p.second; + LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", __func__, + islock->txid.ToString(), islockHash.ToString()); + + for (auto& in : islock->inputs) { + auto inputRequestId = ::SerializeHash(std::make_pair(INPUTLOCK_REQUESTID_PREFIX, in)); + inputRequestIds.erase(inputRequestId); + } + } } + + // Retry all not yet locked mempool TXs and TX which where mined after the fully confirmed block + RetryLockTxs(uint256()); } void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, const CInstantSendLock& islock) diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h index d800ea61703c..b4c2d009c1e4 100644 --- a/src/llmq/quorums_instantsend.h +++ b/src/llmq/quorums_instantsend.h @@ -128,7 +128,8 @@ class CInstantSendManager : public CRecoveredSigsListener void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, int posInBlock); void NotifyChainLock(const CBlockIndex* pindexChainLock); void UpdatedBlockTip(const CBlockIndex* pindexNew); - void RemoveFinalISLock(const uint256& hash, const CInstantSendLockPtr& islock); + + void HandleFullyConfirmedBlock(const CBlockIndex* pindex); void RemoveMempoolConflictsForLock(const uint256& hash, const CInstantSendLock& islock); void RetryLockTxs(const uint256& lockedParentTx); From b897505f8584c0dae8e18b9a2315c379ab990ebe Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 5 Apr 2019 06:59:50 +0200 Subject: [PATCH 4/5] Remove the need for maintaining the last ChainLocked block in the DB --- src/llmq/quorums_instantsend.cpp | 34 ++++++-------------------------- src/llmq/quorums_instantsend.h | 3 --- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 172055e2248f..7f9b05495877 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -201,18 +201,6 @@ CInstantSendLockPtr CInstantSendDb::GetInstantSendLockByInput(const COutPoint& o return GetInstantSendLockByHash(islockHash); } -void CInstantSendDb::WriteLastChainLockBlock(const uint256& hash) -{ - db.Write(std::make_tuple(std::string("is_lcb")), hash); -} - -uint256 CInstantSendDb::GetLastChainLockBlock() -{ - uint256 hashBlock; - db.Read(std::make_tuple(std::string("is_lcb")), hashBlock); - return hashBlock; -} - //////////////// CInstantSendManager::CInstantSendManager(CScheduler* _scheduler, CDBWrapper& _llmqDb) : @@ -820,11 +808,6 @@ void CInstantSendManager::SyncTransaction(const CTransaction& tx, const CBlockIn void CInstantSendManager::NotifyChainLock(const CBlockIndex* pindexChainLock) { - { - LOCK(cs); - db.WriteLastChainLockBlock(pindexChainLock->GetBlockHash()); - } - HandleFullyConfirmedBlock(pindexChainLock); } @@ -913,26 +896,21 @@ void CInstantSendManager::RetryLockTxs(const uint256& lockedParentTx) } } - uint256 lastChainLockBlock; - const CBlockIndex* pindexLastChainLockBlock = nullptr; const CBlockIndex* pindexWalk = nullptr; - { - LOCK(cs); - lastChainLockBlock = db.GetLastChainLockBlock(); - } { LOCK(cs_main); - if (!lastChainLockBlock.IsNull()) { - pindexLastChainLockBlock = mapBlockIndex.at(lastChainLockBlock); - pindexWalk = chainActive.Tip(); - } + pindexWalk = chainActive.Tip(); } // scan blocks until we hit the last chainlocked block we know of. Also stop scanning after a depth of 6 to avoid // signing thousands of TXs at once. Also, after a depth of 6, blocks get eligible for ChainLocking even if unsafe // TXs are included, so there is no need to retroactively sign these. int depth = 0; - while (pindexWalk && pindexWalk != pindexLastChainLockBlock && depth < 6) { + while (pindexWalk && depth < 6) { + if (chainLocksHandler->HasChainLock(pindexWalk->nHeight, pindexWalk->GetBlockHash())) { + break; + } + CBlock block; { LOCK(cs_main); diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h index b4c2d009c1e4..2355b984a9eb 100644 --- a/src/llmq/quorums_instantsend.h +++ b/src/llmq/quorums_instantsend.h @@ -66,9 +66,6 @@ class CInstantSendDb uint256 GetInstantSendLockHashByTxid(const uint256& txid); CInstantSendLockPtr GetInstantSendLockByTxid(const uint256& txid); CInstantSendLockPtr GetInstantSendLockByInput(const COutPoint& outpoint); - - void WriteLastChainLockBlock(const uint256& hashBlock); - uint256 GetLastChainLockBlock(); }; class CInstantSendManager : public CRecoveredSigsListener From d34ec78664bb00b6cea4d33fd8b27eb45454f280 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 5 Apr 2019 08:05:00 +0200 Subject: [PATCH 5/5] Update wallet transactions when confirmed IS locks are removed --- src/llmq/quorums_instantsend.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 7f9b05495877..2aff53cf5321 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -828,11 +828,12 @@ void CInstantSendManager::UpdatedBlockTip(const CBlockIndex* pindexNew) void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) { + std::unordered_map removeISLocks; { LOCK(cs); - auto islocks = db.RemoveConfirmedInstantSendLocks(pindex->nHeight); - for (auto& p : islocks) { + removeISLocks = db.RemoveConfirmedInstantSendLocks(pindex->nHeight); + for (auto& p : removeISLocks) { auto& islockHash = p.first; auto& islock = p.second; LogPrint("instantsend", "CInstantSendManager::%s -- txid=%s, islock=%s: removed islock as it got fully confirmed\n", __func__, @@ -845,6 +846,10 @@ void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) } } + for (auto& p : removeISLocks) { + UpdateWalletTransaction(p.second->txid, nullptr); + } + // Retry all not yet locked mempool TXs and TX which where mined after the fully confirmed block RetryLockTxs(uint256()); }