From 2352c263d7fee6b903d9954afd64772fd547c3e6 Mon Sep 17 00:00:00 2001 From: Peter John Bushnell Date: Mon, 15 Jul 2024 19:32:25 +0100 Subject: [PATCH] Snapshot on each block (#2723) * Add snapshot support * Generate snapshot on ConnectTip * Get snapshot on demand * Get changed map under lock * Update snapshot based on height * Revert "Update snapshot based on height" This reverts commit adc22c63b42b8d84200a9abe7d1620e217f2703b. * Do not Discard or Flush snapshot * Use snapshot in getblockcount * Snapshot on each block * Resolve issues post-merge * lint: circular deps * Only snapshot on each block near tip * Use IBD * Snapshot when current block near the current time * lint: circular deps * Add history snapshots * lint: circular deps * Add vault snapshots * Add GetVaultSnapshot wrapper * lint: circular deps * Pass vaultDB * Get all snapshots at once --------- Co-authored-by: Prasanna Loganathar --- src/Makefile.am | 2 + src/dbwrapper.h | 8 +- src/dfi/accountshistory.cpp | 4 + src/dfi/accountshistory.h | 8 +- src/dfi/masternodes.cpp | 10 +- src/dfi/masternodes.h | 4 +- src/dfi/snapshotmanager.cpp | 264 ++++++++++++++++++++++++ src/dfi/snapshotmanager.h | 142 +++++++++++++ src/dfi/vaulthistory.cpp | 4 + src/dfi/vaulthistory.h | 6 +- src/flushablestorage.h | 43 ++-- src/init.cpp | 5 + src/rpc/resultcache.cpp | 1 + src/rpc/resultcache.h | 11 +- src/test/setup_common.cpp | 5 + src/validation.cpp | 35 +++- test/lint/lint-circular-dependencies.sh | 6 +- 17 files changed, 496 insertions(+), 62 deletions(-) create mode 100644 src/dfi/snapshotmanager.cpp create mode 100644 src/dfi/snapshotmanager.h diff --git a/src/Makefile.am b/src/Makefile.am index 1d823f97df6..ca29ea9165f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -204,6 +204,7 @@ DEFI_CORE_H = \ dfi/oracles.h \ dfi/poolpairs.h \ dfi/proposals.h \ + dfi/snapshotmanager.h \ dfi/tokens.h \ dfi/threadpool.h \ dfi/coinselect.h \ @@ -460,6 +461,7 @@ libdefi_server_a_SOURCES = \ dfi/rpc_tokens.cpp \ dfi/rpc_vault.cpp \ dfi/skipped_txs.cpp \ + dfi/snapshotmanager.cpp \ dfi/tokens.cpp \ dfi/threadpool.cpp \ dfi/undos.cpp \ diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 23d1711dfeb..1d54891ae7d 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -362,8 +362,12 @@ class CDBWrapper return new CDBIterator(*this, pdb->NewIterator(readOptions)); } - [[nodiscard]] std::shared_ptr GetStorageSnapshot() const { - return std::make_shared(pdb); + [[nodiscard]] const leveldb::Snapshot* CreateLevelDBSnapshot() const { + return pdb->GetSnapshot(); + } + + void ReleaseSnapshot(const leveldb::Snapshot* snapshot) const { + pdb->ReleaseSnapshot(snapshot); } /** diff --git a/src/dfi/accountshistory.cpp b/src/dfi/accountshistory.cpp index 7c4fba9e4f8..40b3dfdfee7 100644 --- a/src/dfi/accountshistory.cpp +++ b/src/dfi/accountshistory.cpp @@ -89,6 +89,10 @@ Res CAccountsHistoryView::EraseAccountHistoryHeight(uint32_t height) { CAccountHistoryStorage::CAccountHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory, bool fWipe) : CStorageView(new CStorageLevelDB(dbName, cacheSize, fMemory, fWipe)) {} +CAccountHistoryStorage::CAccountHistoryStorage(std::shared_ptr &db, + std::unique_ptr &otherSnapshot) + : CStorageView(new CStorageLevelDB(db, otherSnapshot)) {} + CBurnHistoryStorage::CBurnHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory, bool fWipe) : CStorageView(new CStorageLevelDB(dbName, cacheSize, fMemory, fWipe)) {} diff --git a/src/dfi/accountshistory.h b/src/dfi/accountshistory.h index cd1671cb4ee..7f05eea524a 100644 --- a/src/dfi/accountshistory.h +++ b/src/dfi/accountshistory.h @@ -41,9 +41,12 @@ class CAccountsHistoryView : public virtual CStorageView { class CAccountHistoryStorage : public CAccountsHistoryView, public CAuctionHistoryView { public: - CAccountHistoryStorage(CAccountHistoryStorage &accountHistory) - : CStorageView(new CFlushableStorageKV(accountHistory.DB())) {} CAccountHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory = false, bool fWipe = false); + + explicit CAccountHistoryStorage(std::shared_ptr &db, + std::unique_ptr &otherSnapshot); + + CStorageLevelDB &GetStorage() { return static_cast(DB()); } }; class CBurnHistoryStorage : public CAccountsHistoryView { @@ -75,5 +78,6 @@ extern std::unique_ptr paccountHistoryDB; extern std::unique_ptr pburnHistoryDB; static constexpr bool DEFAULT_ACINDEX = true; +static constexpr bool DEFAULT_SNAPSHOT = false; #endif // DEFI_DFI_ACCOUNTSHISTORY_H diff --git a/src/dfi/masternodes.cpp b/src/dfi/masternodes.cpp index ef40993b3f0..5a6a10d6ed7 100644 --- a/src/dfi/masternodes.cpp +++ b/src/dfi/masternodes.cpp @@ -784,7 +784,7 @@ CCustomCSView::CCustomCSView(CStorageKV &st) CheckPrefixes(); } -CCustomCSView::CCustomCSView(std::unique_ptr &st, const MapKV &changed) +CCustomCSView::CCustomCSView(std::unique_ptr &st, MapKV &changed) : CStorageView(new CFlushableStorageKV(st, changed)) { CheckPrefixes(); } @@ -1413,11 +1413,3 @@ void CalcMissingRewardTempFix(CCustomCSView &mnview, const uint32_t targetHeight } } } - -std::unique_ptr GetViewSnapshot() { - // Get database snapshot and flushable storage changed map - auto [changed, snapshotDB] = pcustomcsview->GetStorage().GetSnapshotPair(); - - // Create new view using snapshot and change map - return std::make_unique(snapshotDB, changed); -} diff --git a/src/dfi/masternodes.h b/src/dfi/masternodes.h index 6d2b5733729..60a49777357 100644 --- a/src/dfi/masternodes.h +++ b/src/dfi/masternodes.h @@ -551,7 +551,7 @@ class CCustomCSView : public CMasternodesView, explicit CCustomCSView(CStorageKV &st); // Snapshot constructor - explicit CCustomCSView(std::unique_ptr &st, const MapKV &changed); + explicit CCustomCSView(std::unique_ptr &st, MapKV &changed); // Cache-upon-a-cache constructors CCustomCSView(CCustomCSView &other); @@ -628,8 +628,6 @@ class CCustomCSView : public CMasternodesView, std::map AmISignerNow(int height, const CAnchorData::CTeam &team); -std::unique_ptr GetViewSnapshot(); - /** Global DB and view that holds enhanced chainstate data (should be protected by cs_main) */ extern std::unique_ptr pcustomcsDB; extern std::unique_ptr pcustomcsview; diff --git a/src/dfi/snapshotmanager.cpp b/src/dfi/snapshotmanager.cpp new file mode 100644 index 00000000000..ddb636c162f --- /dev/null +++ b/src/dfi/snapshotmanager.cpp @@ -0,0 +1,264 @@ +#include + +#include +#include +#include + +template +static void CheckoutSnapshot(T &checkedOutMap, const CBlockSnapshot &snapshot) { + const auto checkOutKey = snapshot.GetKey(); + const auto dbSnapshot = snapshot.GetLevelDBSnapshot(); + if (checkedOutMap.count(checkOutKey)) { + ++checkedOutMap.at(checkOutKey).count; + } else { + checkedOutMap[checkOutKey] = {dbSnapshot, 1}; + } +} + +SnapshotCollection GetSnapshots() { + return psnapshotManager->GetSnapshots(); +} + +SnapshotCollection CSnapshotManager::GetSnapshots() { + if (auto currentSnapshots = GetCurrentSnapshots()) { + return std::move(*currentSnapshots); + } + return GetGlobalSnapshots(); +} + +std::optional CSnapshotManager::GetCurrentSnapshots() { + std::unique_lock lock(mtx); + + if (!currentViewSnapshot || (historyDB && !currentHistorySnapshot) || (vaultDB && !currentVaultSnapshot)) { + return {}; + } + + auto [changed, snapshotDB] = CheckoutViewSnapshot(); + auto viewSnapshot = std::make_unique(snapshotDB, changed); + + std::unique_ptr historySnapshot{}; + if (historyDB) { + auto snapshot = CheckoutHistorySnapshot(); + historySnapshot = std::make_unique(historyDB, snapshot); + } + + std::unique_ptr vaultSnapshot{}; + if (vaultDB) { + auto snapshot = CheckoutVaultSnapshot(); + vaultSnapshot = std::make_unique(vaultDB, snapshot); + } + + return std::make_tuple(std::move(viewSnapshot), std::move(historySnapshot), std::move(vaultSnapshot)); +} + +SnapshotCollection CSnapshotManager::GetGlobalSnapshots() { + // Same lock order as ConnectBlock + LOCK(cs_main); + std::unique_lock lock(mtx); + + auto [changed, snapshotDB] = GetGlobalViewSnapshot(); + auto viewSnapshot = std::make_unique(snapshotDB, changed); + + std::unique_ptr historySnapshot{}; + if (historyDB) { + auto snapshot = GetGlobalHistorySnapshot(); + historySnapshot = std::make_unique(historyDB, snapshot); + } + + std::unique_ptr vaultSnapshot{}; + if (vaultDB) { + auto snapshot = GetGlobalVaultSnapshot(); + vaultSnapshot = std::make_unique(vaultDB, snapshot); + } + + return std::make_tuple(std::move(viewSnapshot), std::move(historySnapshot), std::move(vaultSnapshot)); +} + +CCheckedOutSnapshot::~CCheckedOutSnapshot() { + // Check snapshot back in + psnapshotManager->ReturnSnapshot(key); +} + +CSnapshotManager::CSnapshotManager(std::unique_ptr &otherViewDB, + std::unique_ptr &otherHistoryDB, + std::unique_ptr &otherVaultDB) { + viewDB = otherViewDB->GetStorage().GetStorageLevelDB()->GetDB(); + + // acindex index might be disabled + if (otherHistoryDB) { + historyDB = otherHistoryDB->GetStorage().GetDB(); + } + + // vault index index might be disabled + if (otherVaultDB) { + vaultDB = otherVaultDB->GetStorage().GetDB(); + } +} + +template +static void ReturnSnapshot(T &db, U &snapshot, V &checkedMap) { + if (db && snapshot) { + if (snapshot->GetLevelDBSnapshot() && (!checkedMap.count(snapshot->GetKey()) || // Not in map + (checkedMap.count(snapshot->GetKey()) && // Is in map... + !checkedMap.at(snapshot->GetKey()).count))) { // ..but not in use + checkedMap.erase(snapshot->GetKey()); + db->ReleaseSnapshot(snapshot->GetLevelDBSnapshot()); + } + snapshot.reset(); + } +} + +template +static void SetCurrentSnapshot(T &db, U ¤tSnapshot, SnapshotType type, const CBlockIndex *block) { + if (db) { + // Get database snapshot + const auto snapshot = db->GetStorage().CreateLevelDBSnapshot(); + + // Set current snapshot + currentSnapshot = std::make_unique( + snapshot, MapKV{}, CBlockSnapshotKey{type, block->nHeight, block->GetBlockHash()}); + } +} + +void CSnapshotManager::SetBlockSnapshots(CFlushableStorageKV &viewStorge, + CAccountHistoryStorage *historyView, + CVaultHistoryStorage *vaultView, + const CBlockIndex *block, + const bool nearTip) { + std::unique_lock lock(mtx); + + // Return current snapshots + ::ReturnSnapshot(viewDB, currentViewSnapshot, checkedOutViewMap); + ::ReturnSnapshot(historyDB, currentHistorySnapshot, checkedOutHistoryMap); + ::ReturnSnapshot(vaultDB, currentVaultSnapshot, checkedOutVaultMap); + + // Do not create current snapshots if snapshots are disabled or not near tip + if (!gArgs.GetBoolArg("-enablesnapshots", DEFAULT_SNAPSHOT) || !nearTip) { + return; + } + + // Get view database snapshot and flushable storage changed map + auto [changedView, snapshotView] = viewStorge.CreateSnapshotData(); + + // Set current view snapshot + currentViewSnapshot = std::make_unique( + snapshotView, changedView, CBlockSnapshotKey{SnapshotType::VIEW, block->nHeight, block->GetBlockHash()}); + + // Set current snapshots + ::SetCurrentSnapshot(historyView, currentHistorySnapshot, SnapshotType::HISTORY, block); + ::SetCurrentSnapshot(vaultView, currentVaultSnapshot, SnapshotType::VAULT, block); +} + +std::pair> CSnapshotManager::GetGlobalViewSnapshot() { + // Get database snapshot and flushable storage changed map + auto [changedMap, snapshot] = pcustomcsview->GetStorage().CreateSnapshotData(); + + // Create checked out snapshot + const auto blockIndex = ::ChainActive().Tip(); + CBlockSnapshotKey key{SnapshotType::VIEW, blockIndex->nHeight, blockIndex->GetBlockHash()}; + auto globalSnapshot = std::make_unique(snapshot, key); + + // Set global as current snapshot + currentViewSnapshot = std::make_unique(globalSnapshot->GetLevelDBSnapshot(), changedMap, key); + + // Track checked out snapshot + ::CheckoutSnapshot(checkedOutViewMap, *currentViewSnapshot); + + return {changedMap, std::make_unique(viewDB, globalSnapshot)}; +} + +std::unique_ptr CSnapshotManager::GetGlobalHistorySnapshot() { + // Get database snapshot and flushable storage changed map + auto snapshot = paccountHistoryDB->GetStorage().CreateLevelDBSnapshot(); + + const auto blockIndex = ::ChainActive().Tip(); + CBlockSnapshotKey key{SnapshotType::HISTORY, blockIndex->nHeight, blockIndex->GetBlockHash()}; + auto globalSnapshot = std::make_unique(snapshot, key); + + // Set global as current snapshot + currentHistorySnapshot = std::make_unique(globalSnapshot->GetLevelDBSnapshot(), MapKV{}, key); + + // Track checked out snapshot + ::CheckoutSnapshot(checkedOutHistoryMap, *currentHistorySnapshot); + + // Create checked out snapshot + return globalSnapshot; +} + +std::unique_ptr CSnapshotManager::GetGlobalVaultSnapshot() { + // Get database snapshot and flushable storage changed map + auto snapshot = pvaultHistoryDB->GetStorage().CreateLevelDBSnapshot(); + + const auto blockIndex = ::ChainActive().Tip(); + CBlockSnapshotKey key{SnapshotType::VAULT, blockIndex->nHeight, blockIndex->GetBlockHash()}; + auto globalSnapshot = std::make_unique(snapshot, key); + + // Set global as current snapshot + currentVaultSnapshot = std::make_unique(globalSnapshot->GetLevelDBSnapshot(), MapKV{}, key); + + // Track checked out snapshot + ::CheckoutSnapshot(checkedOutVaultMap, *currentVaultSnapshot); + + // Create checked out snapshot + return globalSnapshot; +} + +std::pair> CSnapshotManager::CheckoutViewSnapshot() { + // Create checked out snapshot + auto snapshot = + std::make_unique(currentViewSnapshot->GetLevelDBSnapshot(), currentViewSnapshot->GetKey()); + + // Track checked out snapshot + ::CheckoutSnapshot(checkedOutViewMap, *currentViewSnapshot); + + return {currentViewSnapshot->GetChanged(), std::make_unique(viewDB, snapshot)}; +} + +std::unique_ptr CSnapshotManager::CheckoutHistorySnapshot() { + // Create checked out snapshot + auto snapshot = std::make_unique(currentHistorySnapshot->GetLevelDBSnapshot(), + currentHistorySnapshot->GetKey()); + + // Track checked out snapshot + ::CheckoutSnapshot(checkedOutHistoryMap, *currentHistorySnapshot); + + return snapshot; +} + +std::unique_ptr CSnapshotManager::CheckoutVaultSnapshot() { + // Create checked out snapshot + auto snapshot = std::make_unique(currentVaultSnapshot->GetLevelDBSnapshot(), + currentVaultSnapshot->GetKey()); + + // Track checked out snapshot + ::CheckoutSnapshot(checkedOutVaultMap, *currentVaultSnapshot); + + return snapshot; +} + +template +static void DestructSnapshot(const CBlockSnapshotKey &key, T &checkedOutMap, U ¤tSnapshot, V &db) { + if (checkedOutMap.count(key)) { + --checkedOutMap.at(key).count; + + bool isCurrentKey{}; + if (currentSnapshot) { + isCurrentKey = currentSnapshot->GetKey().hash == key.hash; + } + + // Release if not in use and not the current block + if (!checkedOutMap.at(key).count && !isCurrentKey) { + db->ReleaseSnapshot(checkedOutMap.at(key).snapshot); + checkedOutMap.erase(key); + } + } +} + +void CSnapshotManager::ReturnSnapshot(const CBlockSnapshotKey &key) { + std::unique_lock lock(mtx); + ::DestructSnapshot(key, checkedOutViewMap, currentViewSnapshot, viewDB); + ::DestructSnapshot(key, checkedOutHistoryMap, currentHistorySnapshot, historyDB); + ::DestructSnapshot(key, checkedOutVaultMap, currentVaultSnapshot, vaultDB); +} + +std::unique_ptr psnapshotManager; diff --git a/src/dfi/snapshotmanager.h b/src/dfi/snapshotmanager.h new file mode 100644 index 00000000000..9d20440e036 --- /dev/null +++ b/src/dfi/snapshotmanager.h @@ -0,0 +1,142 @@ +#ifndef DEFI_DFI_SNAPSHOTMANAGER_H +#define DEFI_DFI_SNAPSHOTMANAGER_H + +#include + +#include +#include +#include +#include +#include + +class CAccountHistoryStorage; +class CBlockIndex; +class CCustomCSView; +class CDBWrapper; +class CFlushableStorageKV; +class CSnapshotManager; +class CStorageLevelDB; +class CVaultHistoryStorage; + +namespace leveldb { + class Snapshot; +} + +using TBytes = std::vector; +using MapKV = std::map>; + +using SnapshotCollection = std::tuple, + std::unique_ptr, + std::unique_ptr>; + +SnapshotCollection GetSnapshots(); + +enum class SnapshotType : uint8_t { VIEW, HISTORY, VAULT }; + +struct CBlockSnapshotKey { + SnapshotType type{}; + int64_t height{}; + uint256 hash{}; + + struct Comparator { + bool operator()(const CBlockSnapshotKey &a, const CBlockSnapshotKey &b) const { + if (a.type < b.type) { + return true; + } + if (a.type > b.type) { + return false; + } + if (a.height < b.height) { + return true; + } + if (a.height > b.height) { + return false; + } + return a.hash < b.hash; + } + }; +}; + +struct CBlockSnapshotValue { + const leveldb::Snapshot *snapshot; + int64_t count; +}; + +class CBlockSnapshot { + const leveldb::Snapshot *snapshot{}; + MapKV changed; + CBlockSnapshotKey key; + +public: + CBlockSnapshot(const leveldb::Snapshot *otherSnapshot, const MapKV &otherChanged, const CBlockSnapshotKey &otherKey) + : snapshot(otherSnapshot), + changed(otherChanged), + key(otherKey) {} + + [[nodiscard]] const leveldb::Snapshot *GetLevelDBSnapshot() const { return snapshot; } + [[nodiscard]] const CBlockSnapshotKey &GetKey() const { return key; } + [[nodiscard]] const MapKV &GetChanged() const { return changed; } +}; + +class CCheckedOutSnapshot { + const leveldb::Snapshot *snapshot; + CBlockSnapshotKey key; + +public: + explicit CCheckedOutSnapshot(const leveldb::Snapshot *otherSnapshot, const CBlockSnapshotKey &otherKey) + : snapshot(otherSnapshot), + key(otherKey) {} + CCheckedOutSnapshot(const CCheckedOutSnapshot &) = delete; + CCheckedOutSnapshot &operator=(const CCheckedOutSnapshot &) = delete; + + ~CCheckedOutSnapshot(); + + [[nodiscard]] const leveldb::Snapshot *GetLevelDBSnapshot() const { return snapshot; } +}; + +class CSnapshotManager { + std::unique_ptr currentViewSnapshot; + std::unique_ptr currentHistorySnapshot; + std::unique_ptr currentVaultSnapshot; + + std::mutex mtx; + std::shared_ptr viewDB; + std::shared_ptr historyDB; + std::shared_ptr vaultDB; + + using CheckoutOutMap = std::map; + CheckoutOutMap checkedOutViewMap; + CheckoutOutMap checkedOutHistoryMap; + CheckoutOutMap checkedOutVaultMap; + +public: + CSnapshotManager() = delete; + CSnapshotManager(std::unique_ptr &otherViewDB, + std::unique_ptr &otherHistoryDB, + std::unique_ptr &otherVaultDB); + + CSnapshotManager(const CSnapshotManager &other) = delete; + CSnapshotManager &operator=(const CSnapshotManager &other) = delete; + + SnapshotCollection GetSnapshots(); + void SetBlockSnapshots(CFlushableStorageKV &viewStorge, + CAccountHistoryStorage *historyView, + CVaultHistoryStorage *vaultView, + const CBlockIndex *block, + const bool nearTip); + void ReturnSnapshot(const CBlockSnapshotKey &key); + +private: + std::optional GetCurrentSnapshots(); + SnapshotCollection GetGlobalSnapshots(); + std::pair> CheckoutViewSnapshot(); + std::unique_ptr CheckoutHistorySnapshot(); + std::unique_ptr CheckoutVaultSnapshot(); + std::pair> GetGlobalViewSnapshot(); + std::unique_ptr GetGlobalHistorySnapshot(); + std::unique_ptr GetGlobalVaultSnapshot(); +}; + +extern std::unique_ptr psnapshotManager; + +#endif // DEFI_DFI_SNAPSHOTMANAGER_H diff --git a/src/dfi/vaulthistory.cpp b/src/dfi/vaulthistory.cpp index 50dfb1be50c..d87e30453f2 100644 --- a/src/dfi/vaulthistory.cpp +++ b/src/dfi/vaulthistory.cpp @@ -102,4 +102,8 @@ void CVaultHistoryView::EraseGlobalScheme(const VaultGlobalSchemeKey &key) { CVaultHistoryStorage::CVaultHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory, bool fWipe) : CStorageView(new CStorageLevelDB(dbName, cacheSize, fMemory, fWipe)) {} +CVaultHistoryStorage::CVaultHistoryStorage(std::shared_ptr &db, + std::unique_ptr &otherSnapshot) + : CStorageView(new CStorageLevelDB(db, otherSnapshot)) {} + std::unique_ptr pvaultHistoryDB; diff --git a/src/dfi/vaulthistory.h b/src/dfi/vaulthistory.h index 9065ca98d1b..dcb355ebfdb 100644 --- a/src/dfi/vaulthistory.h +++ b/src/dfi/vaulthistory.h @@ -195,9 +195,11 @@ class CVaultHistoryView : public virtual CStorageView { class CVaultHistoryStorage : public CVaultHistoryView { public: - CVaultHistoryStorage(CVaultHistoryStorage &vaultHistory) - : CStorageView(new CFlushableStorageKV(vaultHistory.DB())) {} CVaultHistoryStorage(const fs::path &dbName, std::size_t cacheSize, bool fMemory = false, bool fWipe = false); + + explicit CVaultHistoryStorage(std::shared_ptr &db, std::unique_ptr &otherSnapshot); + + CStorageLevelDB &GetStorage() { return static_cast(DB()); } }; extern std::unique_ptr pvaultHistoryDB; diff --git a/src/flushablestorage.h b/src/flushablestorage.h index a51f6366b16..ebca7e24511 100644 --- a/src/flushablestorage.h +++ b/src/flushablestorage.h @@ -5,6 +5,8 @@ #ifndef DEFI_FLUSHABLESTORAGE_H #define DEFI_FLUSHABLESTORAGE_H +#include + #include #include @@ -142,8 +144,8 @@ class CStorageLevelDB : public CStorageKV { : db{std::make_shared(dbName, cacheSize, fMemory, fWipe)}, batch(*db) {} // Snapshot constructor - CStorageLevelDB(std::shared_ptr &db, std::shared_ptr &otherSnapshot) - : db(db), batch(*db), snapshot(otherSnapshot) { + CStorageLevelDB(std::shared_ptr &db, std::unique_ptr &otherSnapshot) + : db(db), batch(*db), snapshot(std::move(otherSnapshot)) { options.snapshot = snapshot->GetLevelDBSnapshot(); } @@ -201,9 +203,10 @@ class CStorageLevelDB : public CStorageKV { return db->IsEmpty(); } - [[nodiscard]] std::shared_ptr GetStorageSnapshot() const { - return db->GetStorageSnapshot(); + [[nodiscard]] const leveldb::Snapshot* CreateLevelDBSnapshot() const { + return db->CreateLevelDBSnapshot(); } + [[nodiscard]] std::shared_ptr& GetDB() { return db; } @@ -215,7 +218,7 @@ class CStorageLevelDB : public CStorageKV { // If this snapshot is set it will be used when // reading from the DB. - std::shared_ptr snapshot; + std::unique_ptr snapshot; }; // Flushable storage @@ -367,26 +370,14 @@ class CFlushableStorageKV : public CStorageKV { return changed; } - std::pair> GetSnapshotPair() { + [[nodiscard]] CStorageLevelDB* GetStorageLevelDB() const { const auto storageLevelDB = dynamic_cast(&db); assert(storageLevelDB); - if (blockTipChanged.load()) { - // Lock cs_main when updating from tipSnapshot to avoid - // race with Dis/ConnectTip. - LOCK(cs_main); - // Double check bool for safety now we are under lock. - if (blockTipChanged.load()) { - tipSnapshot = storageLevelDB->GetStorageSnapshot(); - changedCopy = changed; - blockTipChanged.store(false); - } - } - - return {changedCopy, std::make_unique(storageLevelDB->GetDB(), tipSnapshot)}; + return storageLevelDB; } - void BlockTipChanged() { - blockTipChanged.store(true); + std::pair CreateSnapshotData() { + return {changed, GetStorageLevelDB()->CreateLevelDBSnapshot()}; } private: @@ -394,16 +385,6 @@ class CFlushableStorageKV : public CStorageKV { CStorageKV& db; MapKV changed; - // Used to create a CStorageLevelDB object. - std::shared_ptr tipSnapshot; - - // Copy of the changed map at the point of snapshot creation. - // Can be used lock free. - MapKV changedCopy; - - // Used to determine whether the block tip has changed. - std::atomic blockTipChanged{true}; - // Whether this view is using a snapshot bool snapshot{}; }; diff --git a/src/init.cpp b/src/init.cpp index 7607ec1af20..6c746d6c830 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -539,6 +539,7 @@ void SetupServerArgs() gArgs.AddArg("-blocktimeordering", strprintf("(Deprecated) Whether to order transactions by time, otherwise ordered by fee (default: %u)", false), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-txordering", strprintf("Whether to order transactions by entry time, fee or both randomly (0: mixed, 1: fee based, 2: entry time) (default: %u)", DEFAULT_TX_ORDERING), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-ethstartstate", strprintf("Initialise Ethereum state trie using JSON input"), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + gArgs.AddArg("-enablesnapshots", strprintf("Whether to enable snapshot on each block (default: %u)", DEFAULT_SNAPSHOT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-ascendingstaketime", strprintf("Test staking forward in time from the current block"), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #ifdef USE_UPNP #if USE_UPNP @@ -2260,6 +2261,10 @@ bool AppInitMain(InitInterfaces& interfaces) block_notify_genesis_wait_connection.disconnect(); } + // Set snapshot now chain has loaded + psnapshotManager = std::make_unique(pcustomcsview, paccountHistoryDB, pvaultHistoryDB); + + if (ShutdownRequested()) { return false; } diff --git a/src/rpc/resultcache.cpp b/src/rpc/resultcache.cpp index ed9f84532e6..46d77aa8549 100644 --- a/src/rpc/resultcache.cpp +++ b/src/rpc/resultcache.cpp @@ -1,6 +1,7 @@ #include #include #include +#include void RPCResultCache::Init(RPCCacheMode mode) { std::unique_lock l{aMutex}; diff --git a/src/rpc/resultcache.h b/src/rpc/resultcache.h index b51e610b4f9..747e3ef54ae 100644 --- a/src/rpc/resultcache.h +++ b/src/rpc/resultcache.h @@ -2,14 +2,15 @@ #define DEFI_RPC_RESULTCACHE_H #include -#include +#include #include -#include -#include #include -#include -#include +#include #include +#include +#include +#include +#include struct CGetBurnInfoResult { CAmount burntDFI{}; diff --git a/src/test/setup_common.cpp b/src/test/setup_common.cpp index 32a945ad7ba..ac50c59448d 100644 --- a/src/test/setup_common.cpp +++ b/src/test/setup_common.cpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -137,6 +139,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha pcustomcsDB.reset(); pcustomcsDB = std::make_unique(GetDataDir() / "enhancedcs", nMinDbCache << 20, true, true); pcustomcsview = std::make_unique(*pcustomcsDB.get()); + paccountHistoryDB = std::make_unique(GetDataDir() / "history", nMinDbCache << 20, true, true); + pvaultHistoryDB = std::make_unique(GetDataDir() / "vault", nMinDbCache << 20, true, true); panchorauths.reset(); panchorauths = std::make_unique(); @@ -145,6 +149,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha panchors.reset(); panchors = std::make_unique(nMinDbCache << 20, true, true); panchors->Load(); + psnapshotManager = std::make_unique(pcustomcsview, paccountHistoryDB, pvaultHistoryDB); } if (!LoadGenesisBlock(chainparams)) { diff --git a/src/validation.cpp b/src/validation.cpp index fcd50e9e3d9..31b5a797eff 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2185,7 +2185,6 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock &block, auto prevHeight = pindex->pprev->nHeight; mnview.SetLastHeight(prevHeight); - SetLastValidatedHeight(prevHeight); return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } @@ -3412,7 +3411,6 @@ bool CChainState::ConnectBlock(const CBlock &block, } } mnview.SetLastHeight(pindex->nHeight); - SetLastValidatedHeight(pindex->nHeight); auto &checkpoints = chainparams.Checkpoints().mapCheckpoints; auto it = checkpoints.lower_bound(pindex->nHeight); @@ -3723,6 +3721,10 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams &chainPar } } +static bool BlockchainNearTip(const int64_t blockTime) { + return blockTime > (GetTime() - nMaxTipAge); +} + /** Disconnect m_chain's tip. * After calling, the mempool will be in an inconsistent state, with * transactions from disconnected blocks being added to disconnectpool. You @@ -3766,8 +3768,6 @@ bool CChainState::DisconnectTip(CValidationState &state, assert(flushed); mnview.GetHistoryWriters().FlushDB(); - pcustomcsview->GetStorage().BlockTipChanged(); - if (!disconnectedConfirms.empty()) { for (const auto &confirm : disconnectedConfirms) { panchorAwaitingConfirms->Add(confirm); @@ -3802,6 +3802,19 @@ bool CChainState::DisconnectTip(CValidationState &state, m_chain.SetTip(pindexDelete->pprev); UpdateTip(pindexDelete->pprev, chainparams); + + SetLastValidatedHeight(pindexDelete->pprev->nHeight); + + // DisconnectTip might be called before psnapshotManager has been initialised + // as part of start-up so check psnapshotManager before using it. + if (psnapshotManager) { + psnapshotManager->SetBlockSnapshots(pcustomcsview->GetStorage(), + paccountHistoryDB.get(), + pvaultHistoryDB.get(), + pindexDelete->pprev, + BlockchainNearTip(pindexDelete->pprev->GetBlockTime())); + } + // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: GetMainSignals().BlockDisconnected(pblock); @@ -3944,8 +3957,6 @@ bool CChainState::ConnectTip(CValidationState &state, assert(flushed); mnview.GetHistoryWriters().FlushDB(); - pcustomcsview->GetStorage().BlockTipChanged(); - // Delete all other confirms from memory if (rewardedAnchors) { std::vector oldConfirms; @@ -3998,6 +4009,18 @@ bool CChainState::ConnectTip(CValidationState &state, } } + SetLastValidatedHeight(pindexNew->nHeight); + + // ConnectTip might be called before psnapshotManager has been initialised + // as part of start-up so check psnapshotManager before using it. + if (psnapshotManager) { + psnapshotManager->SetBlockSnapshots(pcustomcsview->GetStorage(), + paccountHistoryDB.get(), + pvaultHistoryDB.get(), + pindexNew, + BlockchainNearTip(pindexNew->GetBlockTime())); + } + int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 66c0d6de432..68a5c14f088 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -31,6 +31,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "dfi/accountshistory -> dfi/historywriter -> dfi/mn_checks -> dfi/accountshistory" "dfi/accountshistory -> dfi/masternodes -> dfi/accountshistory" "dfi/accountshistory -> dfi/masternodes -> validation -> dfi/accountshistory" + "dfi/accountshistory -> flushablestorage -> dfi/snapshotmanager -> dfi/accountshistory" "dfi/anchors -> dfi/masternodes -> dfi/anchors" "dfi/anchors -> dfi/masternodes -> net_processing -> dfi/anchors" "dfi/anchors -> spv/spv_wrapper -> dfi/anchors" @@ -74,21 +75,21 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "dfi/masternodes -> dfi/oracles -> dfi/masternodes" "dfi/masternodes -> dfi/proposals -> dfi/masternodes" "dfi/masternodes -> dfi/vaulthistory -> dfi/masternodes" + "dfi/masternodes -> flushablestorage -> dfi/snapshotmanager -> dfi/masternodes" "dfi/masternodes -> net_processing -> dfi/masternodes" "dfi/masternodes -> wallet/wallet -> dfi/masternodes" "dfi/mn_checks -> txmempool -> dfi/mn_checks" "dfi/mn_checks -> validation -> dfi/mn_checks" "dfi/mn_checks -> validation -> wallet/wallet -> dfi/mn_checks" - "dfi/mn_rpc -> rpc/resultcache -> dfi/mn_rpc" "dfi/mn_rpc -> wallet/rpcwallet -> init -> ffi/ffiexports -> dfi/mn_rpc" "dfi/govvariables/attributes -> dfi/mn_rpc -> wallet/rpcwallet -> init -> miner -> dfi/govvariables/attributes" "dfi/govvariables/attributes -> dfi/mn_rpc -> wallet/rpcwallet -> init -> rpc/blockchain -> dfi/govvariables/attributes" "dfi/mn_rpc -> wallet/rpcwallet -> init -> miner -> dfi/validation -> dfi/mn_rpc" + "dfi/snapshotmanager -> dfi/vaulthistory -> flushablestorage -> dfi/snapshotmanager" "dfi/validation -> validation -> dfi/validation" "dfi/validation -> ffi/ffiexports -> dfi/validation" "logging -> util/system -> logging" "logging -> util/system -> sync -> logging" - "node/transaction -> validation -> rpc/resultcache -> rpc/util -> node/transaction" "miner -> wallet/wallet -> policy/fees -> miner" "net_processing -> validation -> net_processing" "policy/fees -> txmempool -> policy/fees" @@ -102,6 +103,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "pos_kernel -> validation -> txdb -> pos_kernel" "pubkey -> script/standard -> pubkey" "pubkey -> script/standard -> script/interpreter -> pubkey" + "rpc/resultcache -> validation -> rpc/resultcache" "spv/support/BRAddress -> spv/support/BRBech32 -> spv/support/BRAddress" "spv/bitcoin/BRChainParams -> spv/bitcoin/BRMerkleBlock -> spv/support/BRAddress -> spv/bitcoin/BRChainParams" "spv/bitcoin/BRChainParams -> spv/bitcoin/BRPeer -> spv/bitcoin/BRTransaction -> spv/support/BRKey -> spv/bitcoin/BRChainParams"