From 638bf88fa8344882b77cef477381ac8f2be1e6c3 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Mon, 15 Apr 2019 17:58:48 +0200 Subject: [PATCH] Archive islock hashes when removing confirmed islocks This allows AlreadyHave to check if an announced (via INV) islock was already known in the past. This avoids requesting islocks which got obsolete due to ChainLocks. --- src/llmq/quorums_instantsend.cpp | 61 ++++++++++++++++++++++++++------ src/llmq/quorums_instantsend.h | 2 ++ 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/llmq/quorums_instantsend.cpp b/src/llmq/quorums_instantsend.cpp index 44d9a31ae6de..03d472ca80ed 100644 --- a/src/llmq/quorums_instantsend.cpp +++ b/src/llmq/quorums_instantsend.cpp @@ -90,30 +90,30 @@ void CInstantSendDb::RemoveInstantSendLock(CDBBatch& batch, const uint256& hash, } } -static std::tuple BuildInversedISLockMinedKey(int nHeight, const uint256& islockHash) +static std::tuple BuildInversedISLockKey(const std::string& k, int nHeight, const uint256& islockHash) { - return std::make_tuple(std::string("is_m"), htobe32(std::numeric_limits::max() - nHeight), islockHash); + return std::make_tuple(k, htobe32(std::numeric_limits::max() - nHeight), islockHash); } void CInstantSendDb::WriteInstantSendLockMined(const uint256& hash, int nHeight) { - db.Write(BuildInversedISLockMinedKey(nHeight, hash), true); + db.Write(BuildInversedISLockKey("is_m", nHeight, hash), true); } void CInstantSendDb::RemoveInstantSendLockMined(const uint256& hash, int nHeight) { - db.Erase(BuildInversedISLockMinedKey(nHeight, hash)); + db.Erase(BuildInversedISLockKey("is_m", nHeight, hash)); } std::unordered_map CInstantSendDb::RemoveConfirmedInstantSendLocks(int nUntilHeight) { auto it = std::unique_ptr(db.NewIterator()); - auto firstKey = BuildInversedISLockMinedKey(nUntilHeight, uint256()); + auto firstKey = BuildInversedISLockKey("is_m", nUntilHeight, uint256()); it->Seek(firstKey); - CDBBatch deleteBatch(db); + CDBBatch batch(db); std::unordered_map ret; while (it->Valid()) { decltype(firstKey) curKey; @@ -128,20 +128,58 @@ std::unordered_map CInstantSendDb::RemoveConfirmed auto& islockHash = std::get<2>(curKey); auto islock = GetInstantSendLockByHash(islockHash); if (islock) { - RemoveInstantSendLock(deleteBatch, islockHash, islock); + RemoveInstantSendLock(batch, islockHash, islock); ret.emplace(islockHash, islock); } - deleteBatch.Erase(curKey); + // archive the islock hash, so that we're still able to check if we've seen the islock in the past + batch.Write(BuildInversedISLockKey("is_a1", nHeight, islockHash), true); + batch.Write(std::make_tuple(std::string("is_a2"), islockHash), true); + + batch.Erase(curKey); it->Next(); } - db.WriteBatch(deleteBatch); + db.WriteBatch(batch); return ret; } +void CInstantSendDb::RemoveArchivedInstantSendLocks(int nUntilHeight) +{ + auto it = std::unique_ptr(db.NewIterator()); + + auto firstKey = BuildInversedISLockKey("is_a1", nUntilHeight, uint256()); + + it->Seek(firstKey); + + CDBBatch batch(db); + while (it->Valid()) { + decltype(firstKey) curKey; + if (!it->GetKey(curKey) || std::get<0>(curKey) != "is_a1") { + break; + } + uint32_t nHeight = std::numeric_limits::max() - be32toh(std::get<1>(curKey)); + if (nHeight > nUntilHeight) { + break; + } + + auto& islockHash = std::get<2>(curKey); + batch.Erase(std::make_tuple(std::string("is_a2"), islockHash)); + batch.Erase(curKey); + + it->Next(); + } + + db.WriteBatch(batch); +} + +bool CInstantSendDb::HasArchivedInstantSendLock(const uint256& islockHash) +{ + return db.Exists(std::make_tuple(std::string("is_a2"), islockHash)); +} + CInstantSendLockPtr CInstantSendDb::GetInstantSendLockByHash(const uint256& hash) { CInstantSendLockPtr ret; @@ -875,6 +913,9 @@ void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex) LOCK(cs); removeISLocks = db.RemoveConfirmedInstantSendLocks(pindex->nHeight); + if (pindex->nHeight > 100) { + db.RemoveArchivedInstantSendLocks(pindex->nHeight - 100); + } for (auto& p : removeISLocks) { auto& islockHash = p.first; auto& islock = p.second; @@ -1049,7 +1090,7 @@ bool CInstantSendManager::AlreadyHave(const CInv& inv) } LOCK(cs); - return db.GetInstantSendLockByHash(inv.hash) != nullptr || pendingInstantSendLocks.count(inv.hash) != 0; + return db.GetInstantSendLockByHash(inv.hash) != nullptr || pendingInstantSendLocks.count(inv.hash) != 0 || db.HasArchivedInstantSendLock(inv.hash); } bool CInstantSendManager::GetInstantSendLockByHash(const uint256& hash, llmq::CInstantSendLock& ret) diff --git a/src/llmq/quorums_instantsend.h b/src/llmq/quorums_instantsend.h index 99a625073d6b..1cacf8891ff3 100644 --- a/src/llmq/quorums_instantsend.h +++ b/src/llmq/quorums_instantsend.h @@ -59,6 +59,8 @@ class CInstantSendDb void WriteInstantSendLockMined(const uint256& hash, int nHeight); void RemoveInstantSendLockMined(const uint256& hash, int nHeight); std::unordered_map RemoveConfirmedInstantSendLocks(int nUntilHeight); + void RemoveArchivedInstantSendLocks(int nUntilHeight); + bool HasArchivedInstantSendLock(const uint256& islockHash); CInstantSendLockPtr GetInstantSendLockByHash(const uint256& hash); uint256 GetInstantSendLockHashByTxid(const uint256& txid);