From a0dd9e22d430ace7c33bb7266c4944b5e1b18d2b Mon Sep 17 00:00:00 2001 From: Harrm Date: Wed, 30 Apr 2025 16:31:01 +0300 Subject: [PATCH 01/11] Direct storage prototype --- .../impl/app_configuration_impl.cpp | 51 +++++++------ core/injector/application_injector.cpp | 12 ++-- core/injector/calculate_genesis_state.hpp | 32 ++++++--- core/network/impl/state_sync_request_flow.cpp | 2 +- core/network/impl/synchronizer_impl.cpp | 30 +++++++- core/network/impl/synchronizer_impl.hpp | 10 ++- core/runtime/common/module_instance.cpp | 5 +- .../runtime/common/module_repository_impl.cpp | 5 +- .../common/trie_storage_provider_impl.cpp | 2 +- .../runtime/heap_alloc_strategy_heappages.hpp | 4 +- core/runtime/wavm/module_instance.cpp | 2 +- .../impl/storage_changes_tracker_impl.cpp | 1 + core/storage/face/batch_writeable.hpp | 4 +- core/storage/face/writeable.hpp | 5 -- core/storage/rocksdb/rocksdb.cpp | 33 ++++++--- core/storage/rocksdb/rocksdb.hpp | 8 ++- core/storage/rocksdb/rocksdb_spaces.cpp | 3 +- core/storage/spaces.hpp | 1 + .../trie/impl/ephemeral_trie_batch_impl.cpp | 16 +++++ .../trie/impl/ephemeral_trie_batch_impl.hpp | 8 +++ .../trie/impl/persistent_trie_batch_impl.cpp | 48 ++++++++++++- .../trie/impl/persistent_trie_batch_impl.hpp | 14 +++- .../trie/impl/topper_trie_batch_impl.cpp | 6 +- .../trie/impl/topper_trie_batch_impl.hpp | 2 +- core/storage/trie/impl/trie_batch_base.cpp | 43 ++++++++++- core/storage/trie/impl/trie_batch_base.hpp | 12 ++++ core/storage/trie/impl/trie_storage_impl.cpp | 71 ++++++++++++++++--- core/storage/trie/impl/trie_storage_impl.hpp | 13 +++- .../trie/polkadot_trie/polkadot_trie.hpp | 4 +- .../trie/polkadot_trie/polkadot_trie_impl.cpp | 5 -- core/storage/trie/trie_batches.hpp | 4 +- core/storage/trie/types.hpp | 4 ++ core/utils/kagome_db_editor.cpp | 5 +- 33 files changed, 365 insertions(+), 100 deletions(-) diff --git a/core/application/impl/app_configuration_impl.cpp b/core/application/impl/app_configuration_impl.cpp index ccbd343e60..73c4ac21f8 100644 --- a/core/application/impl/app_configuration_impl.cpp +++ b/core/application/impl/app_configuration_impl.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -133,30 +134,40 @@ namespace kagome::application { return name; } - std::optional str_to_sync_method( + using namespace std::literals; + + static constexpr std:: + array, 6> + kSyncMethods{ + std::pair{"Full"sv, kagome::application::SyncMethod::Full}, + {"Fast"sv, kagome::application::SyncMethod::Fast}, + {"FastWithoutState"sv, + kagome::application::SyncMethod::FastWithoutState}, + {"Warp"sv, kagome::application::SyncMethod::Warp}, + {"Unsafe"sv, kagome::application::SyncMethod::Unsafe}, + {"Auto"sv, kagome::application::SyncMethod::Auto}, + }; + + std::optional str_to_sync_method( std::string_view str) { - using SM = application::SyncMethod; - if (str == "Full") { - return SM::Full; - } - if (str == "Fast") { - return SM::Fast; - } - if (str == "FastWithoutState") { - return SM::FastWithoutState; - } - if (str == "Warp") { - return SM::Warp; - } - if (str == "Unsafe") { - return SM::Unsafe; - } - if (str == "Auto") { - return SM::Auto; + for (auto &[method_name, method] : kSyncMethods) { + if (str == method_name) { + return method; + } } return std::nullopt; } + std::array get_sync_methods() { + std::array sync_methods; + auto it = sync_methods.begin(); + for (auto &[name, _] : kSyncMethods) { + *it = name; + ++it; + } + return sync_methods; + } + std::optional parseAllowUnsafeRpc( std::string_view str) { if (str == "unsafe") { @@ -868,7 +879,7 @@ namespace kagome::application { ("dev", "if node run in development mode") ("dev-with-wipe", "if needed to wipe base path (only for dev mode)") ("sync", po::value()->default_value(def_full_sync), - "choose the desired sync method (Full, Fast). Full is used by default.") + fmt::format("choose the desired sync method ({}). Full is used by default.", get_sync_methods()).c_str()) ("wasm-execution", po::value()->default_value(def_wasm_execution), fmt::format("choose the desired wasm execution method ({})", execution_methods_str).c_str()) ("wasm-interpreter", po::value()->default_value(def_wasm_interpreter), diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index e214a20d93..ea7d842728 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -731,6 +731,8 @@ namespace { return get_rocks_db(config, chain_spec); }), bind_by_lambda([](const auto &injector) { + const auto &storage = + injector.template create>(); auto root_res = injector::calculate_genesis_state( injector @@ -740,6 +742,7 @@ namespace { injector.template create(), injector .template create(), + *storage->getSpace(storage::Space::kTrieDirectKV), injector.template create< sptr>()); if (root_res.has_error()) { @@ -747,8 +750,6 @@ namespace { } const auto &hasher = injector.template create>(); - const auto &storage = - injector.template create>(); return blockchain::BlockStorageImpl::create(root_res.value(), storage, hasher) .value(); }), @@ -855,14 +856,17 @@ namespace { }), bind_by_lambda([](const auto &injector) { - return storage::trie::TrieStorageImpl::createEmpty( + auto storage = injector.template create>(); + + return storage::trie::TrieStorageImpl::createEmpty( injector.template create< sptr>(), injector.template create>(), injector.template create< sptr>(), injector.template create< - sptr>()) + sptr>(), + storage->getSpace(storage::Space::kTrieDirectKV)) .value(); }), di::bind.template to(), diff --git a/core/injector/calculate_genesis_state.hpp b/core/injector/calculate_genesis_state.hpp index 36c0263998..9e580ebbfe 100644 --- a/core/injector/calculate_genesis_state.hpp +++ b/core/injector/calculate_genesis_state.hpp @@ -24,15 +24,20 @@ namespace kagome::injector { const crypto::Hasher &hasher, runtime::RuntimeInstancesPool &module_factory, storage::trie::TrieSerializer &trie_serializer, + storage::BufferStorage &direct_trie_storage, std::shared_ptr runtime_cache) { - auto trie_from = [](const application::GenesisRawData &kv) { + auto trie_from = [&direct_trie_storage](BufferView prefix, + const application::GenesisRawData &kv) + -> outcome::result> { auto trie = storage::trie::PolkadotTrieImpl::createEmpty(); - for (auto &[k, v] : kv) { - trie->put(k, common::BufferView{v}).value(); + for (const auto &[key, val] : kv) { + OUTCOME_TRY(trie->put(Buffer{prefix}.put(key), val.view())); + OUTCOME_TRY( + direct_trie_storage.put(Buffer{prefix}.put(key), val.view())); } return trie; }; - auto top_trie = trie_from(chain_spec.getGenesisTopSection()); + OUTCOME_TRY(top_trie, trie_from({}, chain_spec.getGenesisTopSection())); OUTCOME_TRY(code, top_trie->get(storage::kRuntimeCodeKey)); auto code_hash = hasher.blake2b_256(code); @@ -59,21 +64,26 @@ namespace kagome::injector { } auto version = storage::trie::StateVersion{runtime_version->state_version}; std::vector> child_tries; - for (auto &[child, kv] : chain_spec.getGenesisChildrenDefaultSection()) { - child_tries.emplace_back(trie_from(kv)); + for (const auto &[child, kv] : + chain_spec.getGenesisChildrenDefaultSection()) { + common::Buffer child_prefix; + child_prefix += storage::kChildStorageDefaultPrefix; + child_prefix += child; + + OUTCOME_TRY(child_trie, trie_from(child_prefix, kv)); + child_tries.emplace_back(child_trie); OUTCOME_TRY(root_and_batch, trie_serializer.storeTrie(*child_tries.back(), version)); OUTCOME_TRY(root_and_batch.second->commit()); - common::Buffer child2; - child2 += storage::kChildStorageDefaultPrefix; - child2 += child; - OUTCOME_TRY( - top_trie->put(child2, common::BufferView{root_and_batch.first})); + OUTCOME_TRY(top_trie->put(child_prefix, + common::BufferView{root_and_batch.first})); } OUTCOME_TRY(root_and_batch, trie_serializer.storeTrie(*top_trie, version)); OUTCOME_TRY(root_and_batch.second->commit()); + OUTCOME_TRY(direct_trie_storage.put(storage::trie::kLastCommittedHashKey, + root_and_batch.first)); return root_and_batch.first; } } // namespace kagome::injector diff --git a/core/network/impl/state_sync_request_flow.cpp b/core/network/impl/state_sync_request_flow.cpp index 1a62d750c2..c7dd748222 100644 --- a/core/network/impl/state_sync_request_flow.cpp +++ b/core/network/impl/state_sync_request_flow.cpp @@ -105,7 +105,7 @@ namespace kagome::network { pop_level = false; break; } - if (level.value_hash and not isKnown(*level.value_hash)) { + if ((level.value_hash != nullptr) and not isKnown(*level.value_hash)) { auto it = nodes.find(*level.value_hash); if (it == nodes.end()) { return outcome::success(); diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index 5616a7a88e..090d6130f3 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -26,6 +26,7 @@ #include "network/warp/protocol.hpp" #include "primitives/common.hpp" #include "storage/predefined_keys.hpp" +#include "storage/rocksdb/rocksdb.hpp" #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie/trie_batches.hpp" #include "storage/trie/trie_storage.hpp" @@ -112,7 +113,8 @@ namespace kagome::network { std::shared_ptr beefy, std::shared_ptr grandpa_environment, common::MainThreadPool &main_thread_pool, - std::shared_ptr block_storage) + std::shared_ptr block_storage, + std::shared_ptr db) : log_(log::createLogger("Synchronizer", "synchronizer")), block_tree_(std::move(block_tree)), block_appender_(std::move(block_appender)), @@ -132,7 +134,10 @@ namespace kagome::network { poolHandlerReadyMake(app_state_manager, main_thread_pool)}, block_storage_{std::move(block_storage)}, max_parallel_downloads_{app_config.maxParallelDownloads()}, - random_gen_{std::random_device{}()} { + random_gen_{std::random_device{}()}, + trie_direct_storage_{ + reinterpret_cast(*db).getRocksSpace( + storage::Space::kTrieDirectKV)} { BOOST_ASSERT(block_tree_); BOOST_ASSERT(block_executor_); BOOST_ASSERT(trie_node_db_); @@ -145,6 +150,7 @@ namespace kagome::network { BOOST_ASSERT(chain_sub_engine_); BOOST_ASSERT(main_pool_handler_); BOOST_ASSERT(block_storage_); + BOOST_ASSERT(trie_direct_storage_); sync_method_ = app_config.syncMethod(); @@ -959,6 +965,21 @@ namespace kagome::network { state_sync_->peer, std::move(request), std::move(response_handler)); } + outcome::result updateDirectStorage( + const storage::trie::RootHash &root, + storage::trie::TrieBatch &trie, + storage::RocksDbSpace &direct_storage) { + OUTCOME_TRY(direct_storage.clear()); + auto batch = direct_storage.batch(); + for (auto cursor = trie.trieCursor(); cursor->isValid();) { + OUTCOME_TRY(batch->put(cursor->key().value(), cursor->value().value())); + OUTCOME_TRY(cursor->next()); + } + OUTCOME_TRY(batch->put(storage::trie::kLastCommittedHashKey, root)); + OUTCOME_TRY(batch->commit()); + return outcome::success(); + } + outcome::result SynchronizerImpl::syncState( std::unique_lock &lock, outcome::result &&_res) { @@ -971,6 +992,9 @@ namespace kagome::network { } OUTCOME_TRY(trie_pruner_->addNewState(state_sync_flow_->root(), storage::trie::StateVersion::V0)); + OUTCOME_TRY(trie, storage_->getEphemeralBatchAt(state_sync_flow_->root())); + OUTCOME_TRY(updateDirectStorage( + state_sync_flow_->root(), *trie, *trie_direct_storage_)); auto block = state_sync_flow_->blockInfo(); state_sync_flow_.reset(); SL_INFO(log_, "State syncing block {} has finished.", block); @@ -1674,7 +1698,7 @@ namespace kagome::network { }; fetch(peer_id, std::move(request), "unsafe", std::move(cb2)); } - + void SynchronizerImpl::setHangTimer() { hang_timer_ = scheduler_->scheduleWithHandle( [WEAK_SELF] { diff --git a/core/network/impl/synchronizer_impl.hpp b/core/network/impl/synchronizer_impl.hpp index 2b2c12dc31..d4f950cef0 100644 --- a/core/network/impl/synchronizer_impl.hpp +++ b/core/network/impl/synchronizer_impl.hpp @@ -28,7 +28,6 @@ #include "network/types/blocks_request.hpp" #include "network/types/blocks_response.hpp" #include "primitives/event_types.hpp" -#include "storage/spaced_storage.hpp" #include "telemetry/service.hpp" #include "utils/safe_object.hpp" @@ -64,6 +63,11 @@ namespace kagome::network { class PeerManager; } // namespace kagome::network +namespace kagome::storage { + class SpacedStorage; + class RocksDbSpace; +} + namespace kagome::storage::trie { class TrieSerializer; class TrieStorage; @@ -131,7 +135,8 @@ namespace kagome::network { std::shared_ptr beefy, std::shared_ptr grandpa_environment, common::MainThreadPool &main_thread_pool, - std::shared_ptr block_storage); + std::shared_ptr block_storage, + std::shared_ptr db); /** @see AppStateManager::takeControl */ bool start(); @@ -275,6 +280,7 @@ namespace kagome::network { std::shared_ptr block_storage_; uint32_t max_parallel_downloads_; std::mt19937 random_gen_; + std::shared_ptr trie_direct_storage_; application::SyncMethod sync_method_; diff --git a/core/runtime/common/module_instance.cpp b/core/runtime/common/module_instance.cpp index 833ddc0638..a66f139a28 100644 --- a/core/runtime/common/module_instance.cpp +++ b/core/runtime/common/module_instance.cpp @@ -69,9 +69,8 @@ namespace kagome::runtime { } outcome::result ModuleInstance::stateless() { - getEnvironment() - .storage_provider->setToEphemeralAt(storage::trie::kEmptyRootHash) - .value(); + OUTCOME_TRY(getEnvironment().storage_provider->setToEphemeralAt( + storage::trie::kEmptyRootHash)); OUTCOME_TRY(resetMemory()); return outcome::success(); } diff --git a/core/runtime/common/module_repository_impl.cpp b/core/runtime/common/module_repository_impl.cpp index 9896933084..6283344fd1 100644 --- a/core/runtime/common/module_repository_impl.cpp +++ b/core/runtime/common/module_repository_impl.cpp @@ -74,7 +74,7 @@ namespace kagome::runtime { KAGOME_PROFILE_START(module_retrieval) Item item; - OUTCOME_TRY(SAFE_UNIQUE(cache_)->outcome::result { + auto res = SAFE_UNIQUE(cache_)->outcome::result { if (auto r = cache_.get(state)) { item = r->get(); } else { @@ -96,7 +96,8 @@ namespace kagome::runtime { cache_.put(state, item); } return outcome::success(); - }); + }; + OUTCOME_TRY(res); return item; } } // namespace kagome::runtime diff --git a/core/runtime/common/trie_storage_provider_impl.cpp b/core/runtime/common/trie_storage_provider_impl.cpp index 7546f5324a..a372d8de72 100644 --- a/core/runtime/common/trie_storage_provider_impl.cpp +++ b/core/runtime/common/trie_storage_provider_impl.cpp @@ -158,7 +158,7 @@ namespace kagome::runtime { // TODO(turuslan): #2067, clone batch or implement delta_trie_root auto child_apply = [&](BufferView child, - storage::BufferStorage &map) -> outcome::result { + auto &map) -> outcome::result { for (auto &transaction : transaction_stack_) { auto it = transaction.child_batches.find(child); if (it == transaction.child_batches.end()) { diff --git a/core/runtime/heap_alloc_strategy_heappages.hpp b/core/runtime/heap_alloc_strategy_heappages.hpp index cbaf9c4cfd..9e682bd876 100644 --- a/core/runtime/heap_alloc_strategy_heappages.hpp +++ b/core/runtime/heap_alloc_strategy_heappages.hpp @@ -16,7 +16,7 @@ namespace kagome { /// Convert ":heappages" from state trie to `HeapAllocStrategy`. inline outcome::result> - heapAllocStrategyHeappages(const storage::BufferStorage &trie) { + heapAllocStrategyHeappages(const storage::face::Readable &trie) { OUTCOME_TRY(raw, trie.tryGet(storage::kRuntimeHeappagesKey)); if (raw) { if (auto r = scale::decode(*raw)) { @@ -27,7 +27,7 @@ namespace kagome { } inline outcome::result heapAllocStrategyHeappagesDefault( - const storage::BufferStorage &trie) { + const storage::face::Readable &trie) { OUTCOME_TRY(config, heapAllocStrategyHeappages(trie)); return config.value_or(HeapAllocStrategyStatic{kDefaultHeapAllocPages}); } diff --git a/core/runtime/wavm/module_instance.cpp b/core/runtime/wavm/module_instance.cpp index bdb4589394..cbea05220e 100644 --- a/core/runtime/wavm/module_instance.cpp +++ b/core/runtime/wavm/module_instance.cpp @@ -115,7 +115,7 @@ namespace kagome::runtime::wavm { &, // not used, but has to have been created before the call std::string_view name, common::BufferView encoded_args) const { - auto memory = env_.memory_provider->getCurrentMemory().value(); + OUTCOME_TRY(memory, env_.memory_provider->getCurrentMemory()); PtrSize args_span{memory.get().storeBuffer(encoded_args)}; diff --git a/core/storage/changes_trie/impl/storage_changes_tracker_impl.cpp b/core/storage/changes_trie/impl/storage_changes_tracker_impl.cpp index 9d18be020d..fa12b23ab2 100644 --- a/core/storage/changes_trie/impl/storage_changes_tracker_impl.cpp +++ b/core/storage/changes_trie/impl/storage_changes_tracker_impl.cpp @@ -52,4 +52,5 @@ namespace kagome::storage::changes_trie { } } } + } // namespace kagome::storage::changes_trie diff --git a/core/storage/face/batch_writeable.hpp b/core/storage/face/batch_writeable.hpp index 6077980e53..e0a8138ef4 100644 --- a/core/storage/face/batch_writeable.hpp +++ b/core/storage/face/batch_writeable.hpp @@ -27,9 +27,7 @@ namespace kagome::storage::face { * @brief Creates new Write Batch - an object, which can be used to * efficiently write bulk data. */ - virtual std::unique_ptr> batch() { - throw std::logic_error{"BatchWriteable::batch not implemented"}; - } + virtual std::unique_ptr> batch() = 0; }; } // namespace kagome::storage::face diff --git a/core/storage/face/writeable.hpp b/core/storage/face/writeable.hpp index 4c967c1750..8463baa69c 100644 --- a/core/storage/face/writeable.hpp +++ b/core/storage/face/writeable.hpp @@ -24,17 +24,12 @@ namespace kagome::storage::face { /** * @brief Store value by key - * @param key key - * @param value value - * @return result containing void if put successful, error otherwise */ virtual outcome::result put(const View &key, OwnedOrView &&value) = 0; /** * @brief Remove value by key - * @param key K - * @return error code if error happened */ virtual outcome::result remove(const View &key) = 0; }; diff --git a/core/storage/rocksdb/rocksdb.cpp b/core/storage/rocksdb/rocksdb.cpp index 259d5c36b9..7ab434ad37 100644 --- a/core/storage/rocksdb/rocksdb.cpp +++ b/core/storage/rocksdb/rocksdb.cpp @@ -347,7 +347,7 @@ namespace kagome::storage { return outcome::success(); } - std::shared_ptr RocksDb::getSpace(Space space) { + std::shared_ptr RocksDb::getRocksSpace(Space space) { if (spaces_.contains(space)) { return spaces_[space]; } @@ -366,7 +366,11 @@ namespace kagome::storage { return space_ptr; } - void RocksDb::dropColumn(kagome::storage::Space space) { + std::shared_ptr RocksDb::getSpace(Space space) { + return getRocksSpace(space); + } + + outcome::result RocksDb::dropColumn(kagome::storage::Space space) { auto space_name = spaceName(space); auto column_it = std::ranges::find_if( column_family_handles_, @@ -374,18 +378,22 @@ namespace kagome::storage { return handle->GetName() == space_name; }); if (column_family_handles_.end() == column_it) { - throw DatabaseError::INVALID_ARGUMENT; + return DatabaseError::INVALID_ARGUMENT; } auto &handle = *column_it; - auto e = [this](const rocksdb::Status &status) { + auto status_to_result = + [this](const rocksdb::Status &status) -> outcome::result { if (!status.ok()) { logger_->error("DB operation failed: {}", status.ToString()); - throw status_as_error(status); + return status_as_error(status); } + return outcome::success(); }; - e(db_->DropColumnFamily(handle)); - e(db_->DestroyColumnFamilyHandle(handle)); - e(db_->CreateColumnFamily({}, space_name, &handle)); + OUTCOME_TRY(status_to_result(db_->DropColumnFamily(handle))); + OUTCOME_TRY(status_to_result(db_->DestroyColumnFamilyHandle(handle))); + OUTCOME_TRY( + status_to_result(db_->CreateColumnFamily({}, space_name, &handle))); + return outcome::success(); } rocksdb::BlockBasedTableOptions RocksDb::tableOptionsConfiguration( @@ -560,6 +568,15 @@ namespace kagome::storage { return status_as_error(status); } + outcome::result RocksDbSpace::clear() { + auto rocks = storage_.lock(); + if (!rocks) { + return DatabaseError::STORAGE_GONE; + } + OUTCOME_TRY(rocks->dropColumn(space_)); + return outcome::success(); + } + void RocksDbSpace::compact(const Buffer &first, const Buffer &last) { auto rocks = storage_.lock(); if (!rocks) { diff --git a/core/storage/rocksdb/rocksdb.hpp b/core/storage/rocksdb/rocksdb.hpp index 6d2e66369c..442152029f 100644 --- a/core/storage/rocksdb/rocksdb.hpp +++ b/core/storage/rocksdb/rocksdb.hpp @@ -54,13 +54,14 @@ namespace kagome::storage { bool enable_migration = true); std::shared_ptr getSpace(Space space) override; + std::shared_ptr getRocksSpace(Space space); /** * Implementation specific way to erase the whole space data. * Not exposed at SpacedStorage level as only used in pruner. * @param space - storage space identifier to clear */ - void dropColumn(Space space); + outcome::result dropColumn(Space space); /** * Prepare configuration structure @@ -123,7 +124,7 @@ namespace kagome::storage { rocksdb::DBWithTTL *db_{}; std::vector column_family_handles_; - boost::container::flat_map> spaces_; + boost::container::flat_map> spaces_; rocksdb::ReadOptions ro_; rocksdb::WriteOptions wo_; log::Logger logger_; @@ -155,6 +156,8 @@ namespace kagome::storage { outcome::result remove(const BufferView &key) override; + outcome::result clear(); + void compact(const Buffer &first, const Buffer &last); friend class RocksDbBatch; @@ -166,6 +169,7 @@ namespace kagome::storage { std::weak_ptr storage_; // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) const RocksDb::ColumnFamilyHandlePtr &column_; + Space space_; log::Logger logger_; }; } // namespace kagome::storage diff --git a/core/storage/rocksdb/rocksdb_spaces.cpp b/core/storage/rocksdb/rocksdb_spaces.cpp index aef0ba0fba..b22b2450e3 100644 --- a/core/storage/rocksdb/rocksdb_spaces.cpp +++ b/core/storage/rocksdb/rocksdb_spaces.cpp @@ -22,7 +22,8 @@ namespace kagome::storage { "dispute_data", "beefy_justification", "avaliability_storage", - "audi_peers"}; + "audi_peers", + "trie_direct_kv"}; static_assert(kNames.size() == Space::kTotal - 1); std::string spaceName(Space space) { diff --git a/core/storage/spaces.hpp b/core/storage/spaces.hpp index 45cce8598e..f39680882a 100644 --- a/core/storage/spaces.hpp +++ b/core/storage/spaces.hpp @@ -24,6 +24,7 @@ namespace kagome::storage { kBeefyJustification, kAvaliabilityStorage, kAudiPeers, + kTrieDirectKV, kTotal }; diff --git a/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp b/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp index e619e19a80..bc7e4ba131 100644 --- a/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp +++ b/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp @@ -20,6 +20,22 @@ namespace kagome::storage::trie { // on_child_node_loaded_ can be zero } + EphemeralTrieBatchImpl::EphemeralTrieBatchImpl( + std::shared_ptr codec, + std::shared_ptr trie, + std::shared_ptr serializer, + TrieSerializer::OnNodeLoaded on_child_node_loaded, + std::shared_ptr direct_kv_storage, + Fresh) + : TrieBatchBase{std::move(codec), + std::move(serializer), + std::move(trie), + direct_kv_storage, + Fresh{}}, + on_child_node_loaded_{std::move(on_child_node_loaded)} { + // on_child_node_loaded_ can be zero + } + outcome::result> EphemeralTrieBatchImpl::clearPrefix(const BufferView &prefix, std::optional limit) { diff --git a/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp b/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp index 745ae06cd9..b4a802337f 100644 --- a/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp +++ b/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp @@ -21,6 +21,14 @@ namespace kagome::storage::trie { std::shared_ptr trie, std::shared_ptr serializer, TrieSerializer::OnNodeLoaded on_child_node_loaded); + + EphemeralTrieBatchImpl(std::shared_ptr codec, + std::shared_ptr trie, + std::shared_ptr serializer, + TrieSerializer::OnNodeLoaded on_child_node_loaded, + std::shared_ptr direct_kv_storage, + Fresh); + ~EphemeralTrieBatchImpl() override = default; outcome::result> clearPrefix( diff --git a/core/storage/trie/impl/persistent_trie_batch_impl.cpp b/core/storage/trie/impl/persistent_trie_batch_impl.cpp index 6d30a57fb4..a66e6aed97 100644 --- a/core/storage/trie/impl/persistent_trie_batch_impl.cpp +++ b/core/storage/trie/impl/persistent_trie_batch_impl.cpp @@ -43,6 +43,26 @@ namespace kagome::storage::trie { BOOST_ASSERT(state_pruner_ != nullptr); } + PersistentTrieBatchImpl::PersistentTrieBatchImpl( + std::shared_ptr codec, + std::shared_ptr serializer, + TrieChangesTrackerOpt changes, + std::shared_ptr trie, + std::shared_ptr state_pruner, + std::shared_ptr direct_kv_storage, + Fresh) + : TrieBatchBase{std::move(codec), + std::move(serializer), + std::move(trie), + direct_kv_storage, + Fresh{}}, + changes_{std::move(changes)}, + state_pruner_{std::move(state_pruner)} { + BOOST_ASSERT((changes_.has_value() && changes_.value() != nullptr) + or not changes_.has_value()); + BOOST_ASSERT(state_pruner_ != nullptr); + } + outcome::result PersistentTrieBatchImpl::commit( StateVersion version) { OUTCOME_TRY(commitChildren(version)); @@ -54,6 +74,19 @@ namespace kagome::storage::trie { // batch must not be committed before pruner addNewState or pruner breaks // probably should enforce this more strictly in the API OUTCOME_TRY(batch->commit()); + if (direct_kv_) { + auto batch = direct_kv_->storage->batch(); + OUTCOME_TRY(batch->put(kLastCommittedHashKey, root)); + for (const auto &[key, value] : direct_kv_->batch) { + if (value) { + OUTCOME_TRY(batch->put(key, value->view())); + } else { + OUTCOME_TRY(batch->remove(key)); + } + } + OUTCOME_TRY(batch->commit()); + SL_DEBUG(logger_, "Update latest state: {}", root); + } SL_TRACE_FUNC_CALL(logger_, root); return root; } @@ -63,10 +96,17 @@ namespace kagome::storage::trie { std::optional limit) { SL_TRACE_VOID_FUNC_CALL(logger_, prefix); return trie_->clearPrefix( - prefix, limit, [&](const auto &key, auto &&) -> outcome::result { + prefix, + limit, + [&](const auto &key, + std::optional &&value) -> outcome::result { if (changes_.has_value()) { changes_.value()->onRemove(key); } + if (direct_kv_) { + direct_kv_->batch[key] = + std::forward>(value); + } return outcome::success(); }); } @@ -82,6 +122,9 @@ namespace kagome::storage::trie { changes_.value()->onPut(key, value_copy, is_new_entry); } + if (res and direct_kv_) { + direct_kv_->batch[key] = value_copy; + } return res; } @@ -91,6 +134,9 @@ namespace kagome::storage::trie { SL_TRACE_VOID_FUNC_CALL(logger_, key); changes_.value()->onRemove(key); } + if (direct_kv_) { + direct_kv_->batch[key].reset(); + } return outcome::success(); } diff --git a/core/storage/trie/impl/persistent_trie_batch_impl.hpp b/core/storage/trie/impl/persistent_trie_batch_impl.hpp index a0d64ac754..6af5757a66 100644 --- a/core/storage/trie/impl/persistent_trie_batch_impl.hpp +++ b/core/storage/trie/impl/persistent_trie_batch_impl.hpp @@ -29,12 +29,22 @@ namespace kagome::storage::trie { NO_TRIE = 1, }; + PersistentTrieBatchImpl( + std::shared_ptr codec, + std::shared_ptr serializer, + TrieChangesTrackerOpt changes, + std::shared_ptr trie, + std::shared_ptr state_pruner); + PersistentTrieBatchImpl( std::shared_ptr codec, std::shared_ptr serializer, TrieChangesTrackerOpt changes, std::shared_ptr trie, - std::shared_ptr state_pruner); + std::shared_ptr state_pruner, + std::shared_ptr direct_kv_storage, + Fresh); + ~PersistentTrieBatchImpl() override = default; outcome::result commit(StateVersion version) override; @@ -45,7 +55,7 @@ namespace kagome::storage::trie { outcome::result put(const BufferView &key, BufferOrView &&value) override; outcome::result remove(const BufferView &key) override; - + protected: outcome::result> createFromTrieHash( const RootHash &trie_hash) override; diff --git a/core/storage/trie/impl/topper_trie_batch_impl.cpp b/core/storage/trie/impl/topper_trie_batch_impl.cpp index 072639e7af..1225ccb65f 100644 --- a/core/storage/trie/impl/topper_trie_batch_impl.cpp +++ b/core/storage/trie/impl/topper_trie_batch_impl.cpp @@ -109,14 +109,14 @@ namespace kagome::storage::trie { } outcome::result TopperTrieBatchImpl::writeBack() { - if (auto p = parent_.lock()) { - return apply(*p); + if (auto parent = parent_.lock()) { + return apply(*parent); } return Error::PARENT_EXPIRED; } outcome::result TopperTrieBatchImpl::apply( - storage::BufferStorage &map) { + face::Writeable &map) { for (auto &[k, v] : cache_) { if (v) { OUTCOME_TRY(map.put(k, BufferView{*v})); diff --git a/core/storage/trie/impl/topper_trie_batch_impl.hpp b/core/storage/trie/impl/topper_trie_batch_impl.hpp index 535d3c22cd..4ee054a50a 100644 --- a/core/storage/trie/impl/topper_trie_batch_impl.hpp +++ b/core/storage/trie/impl/topper_trie_batch_impl.hpp @@ -48,7 +48,7 @@ namespace kagome::storage::trie { outcome::result>> createChildBatch( common::BufferView path) override; - outcome::result apply(storage::BufferStorage &map); + outcome::result apply(face::Writeable &map); private: std::map> cache_; diff --git a/core/storage/trie/impl/trie_batch_base.cpp b/core/storage/trie/impl/trie_batch_base.cpp index b7ee2a649a..7e1674268d 100644 --- a/core/storage/trie/impl/trie_batch_base.cpp +++ b/core/storage/trie/impl/trie_batch_base.cpp @@ -6,11 +6,10 @@ #include "storage/trie/impl/trie_batch_base.hpp" +#include "storage/database_error.hpp" #include "storage/trie/polkadot_trie/polkadot_trie.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_cursor_impl.hpp" -#include - namespace kagome::storage::trie { TrieBatchBase::TrieBatchBase(std::shared_ptr codec, @@ -24,13 +23,43 @@ namespace kagome::storage::trie { BOOST_ASSERT(trie_ != nullptr); } + TrieBatchBase::TrieBatchBase(std::shared_ptr codec, + std::shared_ptr serializer, + std::shared_ptr trie, + std::shared_ptr direct_kv_storage, + Fresh) + : TrieBatchBase(codec, serializer, trie) { + BOOST_ASSERT(direct_kv_storage != nullptr); + direct_kv_ = {.storage = direct_kv_storage}; + } + outcome::result TrieBatchBase::get( const BufferView &key) const { + if (direct_kv_) { + if (auto it = direct_kv_->batch.find(key); + it != direct_kv_->batch.end()) { + if (it->second) { + return BufferOrView{it->second->view()}; + } + return DatabaseError::NOT_FOUND; + } + return direct_kv_->storage->get(key); + } return trie_->get(key); } outcome::result> TrieBatchBase::tryGet( const BufferView &key) const { + if (direct_kv_) { + if (auto it = direct_kv_->batch.find(key); + it != direct_kv_->batch.end()) { + if (it->second) { + return BufferOrView{it->second->view()}; + } + return std::nullopt; + } + return direct_kv_->storage->tryGet(key); + } return trie_->tryGet(key); } @@ -39,6 +68,16 @@ namespace kagome::storage::trie { } outcome::result TrieBatchBase::contains(const BufferView &key) const { + if (direct_kv_) { + if (auto it = direct_kv_->batch.find(key); + it != direct_kv_->batch.end()) { + if (it->second) { + return true; + } + return false; + } + return direct_kv_->storage->contains(key); + } return trie_->contains(key); } diff --git a/core/storage/trie/impl/trie_batch_base.hpp b/core/storage/trie/impl/trie_batch_base.hpp index 13ff6a2855..e1d07d46df 100644 --- a/core/storage/trie/impl/trie_batch_base.hpp +++ b/core/storage/trie/impl/trie_batch_base.hpp @@ -23,6 +23,13 @@ namespace kagome::storage::trie { TrieBatchBase(std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr trie); + + struct Fresh {}; + TrieBatchBase(std::shared_ptr codec, + std::shared_ptr serializer, + std::shared_ptr trie, + std::shared_ptr direct_kv_storage, + Fresh); TrieBatchBase(const TrieBatchBase &) = delete; TrieBatchBase(TrieBatchBase &&) noexcept = default; @@ -52,6 +59,11 @@ namespace kagome::storage::trie { std::shared_ptr codec_; std::shared_ptr serializer_; std::shared_ptr trie_; + struct DirectStorage { + std::shared_ptr storage; + std::unordered_map> batch; + }; + std::optional direct_kv_; private: std::unordered_map> diff --git a/core/storage/trie/impl/trie_storage_impl.cpp b/core/storage/trie/impl/trie_storage_impl.cpp index 4cfa22e623..111e92fcd8 100644 --- a/core/storage/trie/impl/trie_storage_impl.cpp +++ b/core/storage/trie/impl/trie_storage_impl.cpp @@ -19,44 +19,70 @@ namespace kagome::storage::trie { const std::shared_ptr &trie_factory, std::shared_ptr codec, std::shared_ptr serializer, - std::shared_ptr state_pruner) { + std::shared_ptr state_pruner, + std::shared_ptr direct_kv) { // will never be used, so content of the callback doesn't matter auto empty_trie = trie_factory->createEmpty(); // ensure retrieval of empty trie succeeds OUTCOME_TRY(serializer->storeTrie(*empty_trie, StateVersion::V0)); - return std::unique_ptr(new TrieStorageImpl( - std::move(codec), std::move(serializer), std::move(state_pruner))); + return std::unique_ptr( + new TrieStorageImpl(std::move(codec), + std::move(serializer), + std::move(state_pruner), + std::move(direct_kv))); } outcome::result> TrieStorageImpl::createFromStorage( std::shared_ptr codec, std::shared_ptr serializer, - std::shared_ptr state_pruner) { - return std::unique_ptr(new TrieStorageImpl( - std::move(codec), std::move(serializer), std::move(state_pruner))); + std::shared_ptr state_pruner, + std::shared_ptr direct_kv) { + return std::unique_ptr( + new TrieStorageImpl(std::move(codec), + std::move(serializer), + std::move(state_pruner), + std::move(direct_kv))); } TrieStorageImpl::TrieStorageImpl( std::shared_ptr codec, std::shared_ptr serializer, - std::shared_ptr state_pruner) + std::shared_ptr state_pruner, + std::shared_ptr direct_kv) : codec_{std::move(codec)}, serializer_{std::move(serializer)}, state_pruner_{std::move(state_pruner)}, + direct_kv_{std::move(direct_kv)}, logger_{log::createLogger("TrieStorage", "storage")} { BOOST_ASSERT(codec_ != nullptr); BOOST_ASSERT(state_pruner_ != nullptr); BOOST_ASSERT(serializer_ != nullptr); + BOOST_ASSERT(direct_kv_ != nullptr); } outcome::result> TrieStorageImpl::getPersistentBatchAt(const RootHash &root, TrieChangesTrackerOpt changes_tracker) { - SL_DEBUG(logger_, - "Initialize persistent trie batch with root: {}", - root.toHex()); OUTCOME_TRY(trie, serializer_->retrieveTrie(root, nullptr)); + OUTCOME_TRY(last_hash, getLastCommittedHash()); + if (root == last_hash) { + SL_DEBUG(logger_, + "Initialize persistent trie batch with root: {} at latest state " + "(direct storage is enabled)", + root.toHex()); + return std::make_unique(codec_, + serializer_, + changes_tracker, + std::move(trie), + state_pruner_, + direct_kv_, + TrieBatchBase::Fresh{}); + } + SL_DEBUG(logger_, + "Initialize persistent trie batch with root: {} at an old state " + "(direct storage is disabled, the latest state is {})", + root, last_hash); return std::make_unique( codec_, serializer_, changes_tracker, std::move(trie), state_pruner_); } @@ -65,6 +91,23 @@ namespace kagome::storage::trie { TrieStorageImpl::getEphemeralBatchAt(const RootHash &root) const { SL_DEBUG(logger_, "Initialize ephemeral trie batch with root: {}", root); OUTCOME_TRY(trie, serializer_->retrieveTrie(root, nullptr)); + OUTCOME_TRY(last_hash, getLastCommittedHash()); + if (root == last_hash) { + SL_DEBUG(logger_, + "Initialize ephemeral trie batch with root: {} at latest state " + "(direct storage is enabled)", + root); + return std::make_unique(codec_, + std::move(trie), + serializer_, + nullptr, + direct_kv_, + TrieBatchBase::Fresh{}); + } + SL_DEBUG(logger_, + "Initialize ephemeral trie batch with root: {} at an old state " + "(direct storage is disabled, the latest state is {})", + root, last_hash); return std::make_unique( codec_, std::move(trie), serializer_, nullptr); } @@ -79,4 +122,12 @@ namespace kagome::storage::trie { codec_, std::move(trie), serializer_, on_node_loaded); } + outcome::result TrieStorageImpl::getLastCommittedHash() const { + OUTCOME_TRY(last_hash, direct_kv_->tryGet(kLastCommittedHashKey)); + if (!last_hash) { + return kEmptyRootHash; + } + return RootHash::fromSpan(*last_hash); + } + } // namespace kagome::storage::trie diff --git a/core/storage/trie/impl/trie_storage_impl.hpp b/core/storage/trie/impl/trie_storage_impl.hpp index 29a631a11a..8d71907bac 100644 --- a/core/storage/trie/impl/trie_storage_impl.hpp +++ b/core/storage/trie/impl/trie_storage_impl.hpp @@ -10,6 +10,7 @@ #include "log/logger.hpp" #include "primitives/event_types.hpp" +#include "storage/spaced_storage.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_factory.hpp" #include "storage/trie/serialization/codec.hpp" #include "storage/trie/serialization/trie_serializer.hpp" @@ -26,12 +27,14 @@ namespace kagome::storage::trie { const std::shared_ptr &trie_factory, std::shared_ptr codec, std::shared_ptr serializer, - std::shared_ptr state_pruner); + std::shared_ptr state_pruner, + std::shared_ptr direct_kv); static outcome::result> createFromStorage( std::shared_ptr codec, std::shared_ptr serializer, - std::shared_ptr state_pruner); + std::shared_ptr state_pruner, + std::shared_ptr direct_kv); TrieStorageImpl(const TrieStorageImpl &) = delete; void operator=(const TrieStorageImpl &) = delete; @@ -52,12 +55,16 @@ namespace kagome::storage::trie { TrieStorageImpl( std::shared_ptr codec, std::shared_ptr serializer, - std::shared_ptr state_pruner); + std::shared_ptr state_pruner, + std::shared_ptr direct_kv); private: + outcome::result getLastCommittedHash() const; + std::shared_ptr codec_; std::shared_ptr serializer_; std::shared_ptr state_pruner_; + std::shared_ptr direct_kv_; log::Logger logger_; }; diff --git a/core/storage/trie/polkadot_trie/polkadot_trie.hpp b/core/storage/trie/polkadot_trie/polkadot_trie.hpp index d2d4c188ff..14e946bf0d 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie.hpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie.hpp @@ -18,7 +18,9 @@ namespace kagome::storage::trie { * For specification see Polkadot Runtime Environment Protocol Specification * '2.1.2 The General Tree Structure' and further */ - class PolkadotTrie : public BufferStorage, + class PolkadotTrie : public face::Readable, + public face::Iterable, + public face::Writeable, public std::enable_shared_from_this { public: using NodePtr = std::shared_ptr; diff --git a/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp b/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp index 70837f048d..fd8159ea7d 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp @@ -315,9 +315,6 @@ namespace { } // namespace namespace kagome::storage::trie { - // PolkadotTrieImpl::PolkadotTrieImpl(PolkadotTrieImpl &&) noexcept = - // default; PolkadotTrieImpl &PolkadotTrieImpl::operator=(PolkadotTrieImpl - // &&) = default; std::shared_ptr PolkadotTrieImpl::createEmpty( RetrieveFunctions retrieve_functions) { @@ -346,8 +343,6 @@ namespace kagome::storage::trie { std::move(root))}, logger_{log::createLogger("PolkadotTrie", "trie")} {} - // PolkadotTrieImpl::~PolkadotTrieImpl() {} - PolkadotTrie::ConstNodePtr PolkadotTrieImpl::getRoot() const { return nodes_->getRoot(); } diff --git a/core/storage/trie/trie_batches.hpp b/core/storage/trie/trie_batches.hpp index 6c6b177066..f680420517 100644 --- a/core/storage/trie/trie_batches.hpp +++ b/core/storage/trie/trie_batches.hpp @@ -12,7 +12,9 @@ namespace kagome::storage::trie { - class TrieBatch : public BufferStorage { + class TrieBatch : public face::Readable, + public face::Iterable, + public face::Writeable { public: std::unique_ptr cursor() final { return trieCursor(); diff --git a/core/storage/trie/types.hpp b/core/storage/trie/types.hpp index 85e960123e..6697a0579e 100644 --- a/core/storage/trie/types.hpp +++ b/core/storage/trie/types.hpp @@ -7,6 +7,7 @@ #pragma once #include "common/blob.hpp" +#include "common/buffer.hpp" namespace kagome::storage::trie { @@ -22,4 +23,7 @@ namespace kagome::storage::trie { }}; constexpr uint8_t kEscapeCompactHeader = 1; + + const Buffer kLastCommittedHashKey = Buffer::fromString("kagome_last_committed_hash"); + } // namespace kagome::storage::trie diff --git a/core/utils/kagome_db_editor.cpp b/core/utils/kagome_db_editor.cpp index ec5cd26639..a474ce5eb0 100644 --- a/core/utils/kagome_db_editor.cpp +++ b/core/utils/kagome_db_editor.cpp @@ -259,7 +259,7 @@ Kagome DB Editor - a storage pruner. Allows to reduce occupied disk space. try { storage = storage::RocksDb::create(args[DB_PATH], rocksdb::Options()).value(); - storage->dropColumn(storage::Space::kBlockBody); + storage->dropColumn(storage::Space::kBlockBody).value(); buffer_storage = storage->getSpace(storage::Space::kDefault); } catch (std::system_error &e) { log->error("{}", e.what()); @@ -388,7 +388,8 @@ Kagome DB Editor - a storage pruner. Allows to reduce occupied disk space. injector.template create>(), injector.template create>(), injector - .template create>()) + .template create>(), + storage->getSpace(storage::Space::kTrieDirectKV)) .value(); if (COMPACT == cmd) { From 8252e39b3fee38219c26b628da2e7c2814dad4ab Mon Sep 17 00:00:00 2001 From: Harrm Date: Wed, 30 Apr 2025 16:31:05 +0300 Subject: [PATCH 02/11] Direct storage prototype --- core/injector/calculate_genesis_state.hpp | 5 +++-- core/network/impl/synchronizer_impl.hpp | 2 +- core/runtime/common/trie_storage_provider_impl.cpp | 5 ++--- core/runtime/heap_alloc_strategy_heappages.hpp | 3 ++- core/storage/rocksdb/rocksdb.hpp | 5 +++-- .../trie/impl/persistent_trie_batch_impl.hpp | 14 +++++++------- core/storage/trie/impl/trie_batch_base.hpp | 2 +- core/storage/trie/impl/trie_storage_impl.cpp | 6 ++++-- .../trie/polkadot_trie/polkadot_trie_impl.cpp | 12 ++++++------ core/storage/trie/types.hpp | 3 ++- 10 files changed, 31 insertions(+), 26 deletions(-) diff --git a/core/injector/calculate_genesis_state.hpp b/core/injector/calculate_genesis_state.hpp index 9e580ebbfe..261c35683b 100644 --- a/core/injector/calculate_genesis_state.hpp +++ b/core/injector/calculate_genesis_state.hpp @@ -26,8 +26,9 @@ namespace kagome::injector { storage::trie::TrieSerializer &trie_serializer, storage::BufferStorage &direct_trie_storage, std::shared_ptr runtime_cache) { - auto trie_from = [&direct_trie_storage](BufferView prefix, - const application::GenesisRawData &kv) + auto trie_from = [&direct_trie_storage]( + BufferView prefix, + const application::GenesisRawData &kv) -> outcome::result> { auto trie = storage::trie::PolkadotTrieImpl::createEmpty(); for (const auto &[key, val] : kv) { diff --git a/core/network/impl/synchronizer_impl.hpp b/core/network/impl/synchronizer_impl.hpp index d4f950cef0..0620513efe 100644 --- a/core/network/impl/synchronizer_impl.hpp +++ b/core/network/impl/synchronizer_impl.hpp @@ -66,7 +66,7 @@ namespace kagome::network { namespace kagome::storage { class SpacedStorage; class RocksDbSpace; -} +} // namespace kagome::storage namespace kagome::storage::trie { class TrieSerializer; diff --git a/core/runtime/common/trie_storage_provider_impl.cpp b/core/runtime/common/trie_storage_provider_impl.cpp index a372d8de72..31108646ec 100644 --- a/core/runtime/common/trie_storage_provider_impl.cpp +++ b/core/runtime/common/trie_storage_provider_impl.cpp @@ -156,9 +156,8 @@ namespace kagome::runtime { outcome::result TrieStorageProviderImpl::commit( const std::optional &child, StateVersion version) { // TODO(turuslan): #2067, clone batch or implement delta_trie_root - auto child_apply = - [&](BufferView child, - auto &map) -> outcome::result { + auto child_apply = [&](BufferView child, + auto &map) -> outcome::result { for (auto &transaction : transaction_stack_) { auto it = transaction.child_batches.find(child); if (it == transaction.child_batches.end()) { diff --git a/core/runtime/heap_alloc_strategy_heappages.hpp b/core/runtime/heap_alloc_strategy_heappages.hpp index 9e682bd876..95a87a58a1 100644 --- a/core/runtime/heap_alloc_strategy_heappages.hpp +++ b/core/runtime/heap_alloc_strategy_heappages.hpp @@ -16,7 +16,8 @@ namespace kagome { /// Convert ":heappages" from state trie to `HeapAllocStrategy`. inline outcome::result> - heapAllocStrategyHeappages(const storage::face::Readable &trie) { + heapAllocStrategyHeappages( + const storage::face::Readable &trie) { OUTCOME_TRY(raw, trie.tryGet(storage::kRuntimeHeappagesKey)); if (raw) { if (auto r = scale::decode(*raw)) { diff --git a/core/storage/rocksdb/rocksdb.hpp b/core/storage/rocksdb/rocksdb.hpp index 442152029f..1df6048e23 100644 --- a/core/storage/rocksdb/rocksdb.hpp +++ b/core/storage/rocksdb/rocksdb.hpp @@ -61,7 +61,7 @@ namespace kagome::storage { * Not exposed at SpacedStorage level as only used in pruner. * @param space - storage space identifier to clear */ - outcome::result dropColumn(Space space); + outcome::result dropColumn(Space space); /** * Prepare configuration structure @@ -124,7 +124,8 @@ namespace kagome::storage { rocksdb::DBWithTTL *db_{}; std::vector column_family_handles_; - boost::container::flat_map> spaces_; + boost::container::flat_map> + spaces_; rocksdb::ReadOptions ro_; rocksdb::WriteOptions wo_; log::Logger logger_; diff --git a/core/storage/trie/impl/persistent_trie_batch_impl.hpp b/core/storage/trie/impl/persistent_trie_batch_impl.hpp index 6af5757a66..43ad3d8cab 100644 --- a/core/storage/trie/impl/persistent_trie_batch_impl.hpp +++ b/core/storage/trie/impl/persistent_trie_batch_impl.hpp @@ -30,12 +30,12 @@ namespace kagome::storage::trie { }; PersistentTrieBatchImpl( - std::shared_ptr codec, - std::shared_ptr serializer, - TrieChangesTrackerOpt changes, - std::shared_ptr trie, - std::shared_ptr state_pruner); - + std::shared_ptr codec, + std::shared_ptr serializer, + TrieChangesTrackerOpt changes, + std::shared_ptr trie, + std::shared_ptr state_pruner); + PersistentTrieBatchImpl( std::shared_ptr codec, std::shared_ptr serializer, @@ -55,7 +55,7 @@ namespace kagome::storage::trie { outcome::result put(const BufferView &key, BufferOrView &&value) override; outcome::result remove(const BufferView &key) override; - + protected: outcome::result> createFromTrieHash( const RootHash &trie_hash) override; diff --git a/core/storage/trie/impl/trie_batch_base.hpp b/core/storage/trie/impl/trie_batch_base.hpp index e1d07d46df..7746431631 100644 --- a/core/storage/trie/impl/trie_batch_base.hpp +++ b/core/storage/trie/impl/trie_batch_base.hpp @@ -23,7 +23,7 @@ namespace kagome::storage::trie { TrieBatchBase(std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr trie); - + struct Fresh {}; TrieBatchBase(std::shared_ptr codec, std::shared_ptr serializer, diff --git a/core/storage/trie/impl/trie_storage_impl.cpp b/core/storage/trie/impl/trie_storage_impl.cpp index 111e92fcd8..f6513f618b 100644 --- a/core/storage/trie/impl/trie_storage_impl.cpp +++ b/core/storage/trie/impl/trie_storage_impl.cpp @@ -82,7 +82,8 @@ namespace kagome::storage::trie { SL_DEBUG(logger_, "Initialize persistent trie batch with root: {} at an old state " "(direct storage is disabled, the latest state is {})", - root, last_hash); + root, + last_hash); return std::make_unique( codec_, serializer_, changes_tracker, std::move(trie), state_pruner_); } @@ -107,7 +108,8 @@ namespace kagome::storage::trie { SL_DEBUG(logger_, "Initialize ephemeral trie batch with root: {} at an old state " "(direct storage is disabled, the latest state is {})", - root, last_hash); + root, + last_hash); return std::make_unique( codec_, std::move(trie), serializer_, nullptr); } diff --git a/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp b/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp index fd8159ea7d..70d2af6639 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie_impl.cpp @@ -330,17 +330,17 @@ namespace kagome::storage::trie { PolkadotTrieImpl::PolkadotTrieImpl(RetrieveFunctions retrieve_functions) : nodes_{std::make_unique( - std::move(retrieve_functions.retrieve_node), - std::move(retrieve_functions.retrieve_value), - nullptr)}, + std::move(retrieve_functions.retrieve_node), + std::move(retrieve_functions.retrieve_value), + nullptr)}, logger_{log::createLogger("PolkadotTrie", "trie")} {} PolkadotTrieImpl::PolkadotTrieImpl(NodePtr root, RetrieveFunctions retrieve_functions) : nodes_{std::make_unique( - std::move(retrieve_functions.retrieve_node), - std::move(retrieve_functions.retrieve_value), - std::move(root))}, + std::move(retrieve_functions.retrieve_node), + std::move(retrieve_functions.retrieve_value), + std::move(root))}, logger_{log::createLogger("PolkadotTrie", "trie")} {} PolkadotTrie::ConstNodePtr PolkadotTrieImpl::getRoot() const { diff --git a/core/storage/trie/types.hpp b/core/storage/trie/types.hpp index 6697a0579e..bad76c15cb 100644 --- a/core/storage/trie/types.hpp +++ b/core/storage/trie/types.hpp @@ -24,6 +24,7 @@ namespace kagome::storage::trie { constexpr uint8_t kEscapeCompactHeader = 1; - const Buffer kLastCommittedHashKey = Buffer::fromString("kagome_last_committed_hash"); + const Buffer kLastCommittedHashKey = + Buffer::fromString("kagome_last_committed_hash"); } // namespace kagome::storage::trie From 0b0703cc010bb5cd08d75dfa5858327c0ce2e2c6 Mon Sep 17 00:00:00 2001 From: Harrm Date: Fri, 16 May 2025 13:07:57 +0300 Subject: [PATCH 03/11] In progress --- CMakeLists.txt | 5 ++- .../impl/app_configuration_impl.cpp | 16 +++---- core/blockchain/impl/block_tree_impl.cpp | 37 ++++++++++----- core/blockchain/impl/block_tree_impl.hpp | 3 ++ core/injector/application_injector.cpp | 3 +- core/network/impl/peer_view.cpp | 13 +++--- core/network/impl/state_sync_request_flow.cpp | 13 ++++++ core/network/impl/synchronizer_impl.cpp | 21 ++------- .../pvf/kagome_pvf_worker_injector.hpp | 4 ++ .../runtime/common/module_repository_impl.cpp | 3 ++ core/runtime/common/storage_code_provider.cpp | 1 + core/storage/rocksdb/rocksdb.cpp | 6 ++- core/storage/rocksdb/rocksdb.hpp | 1 + core/storage/trie/impl/trie_storage_impl.hpp | 4 ++ core/storage/trie/trie_storage.hpp | 2 + core/storage/trie/update_direct_storage.hpp | 45 +++++++++++++++++++ .../core/storage/trie/trie_storage_mock.hpp | 2 + 17 files changed, 134 insertions(+), 45 deletions(-) create mode 100644 core/storage/trie/update_direct_storage.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 43a9028995..d79635ee6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,10 @@ set(WASMEDGE_ID 3fbf6c6a39a5fd065e8964a979d5edcddf877cca) include("cmake/Hunter/init.cmake") -add_compile_options(-gdwarf-4) +if (${CMAKE_BUILD_TYPE} MATCHES "^(Debug|RelWithDebInfo)$") + add_compile_options(-gdwarf-4 -fno-omit-frame-pointer) +endif() + project(kagome VERSION 0.9.3 LANGUAGES C CXX diff --git a/core/application/impl/app_configuration_impl.cpp b/core/application/impl/app_configuration_impl.cpp index 73c4ac21f8..140be74fef 100644 --- a/core/application/impl/app_configuration_impl.cpp +++ b/core/application/impl/app_configuration_impl.cpp @@ -137,18 +137,18 @@ namespace kagome::application { using namespace std::literals; static constexpr std:: - array, 6> + array, 6> kSyncMethods{ - std::pair{"Full"sv, kagome::application::SyncMethod::Full}, - {"Fast"sv, kagome::application::SyncMethod::Fast}, + std::pair{"Full"sv, application::SyncMethod::Full}, + {"Fast"sv, application::SyncMethod::Fast}, {"FastWithoutState"sv, - kagome::application::SyncMethod::FastWithoutState}, - {"Warp"sv, kagome::application::SyncMethod::Warp}, - {"Unsafe"sv, kagome::application::SyncMethod::Unsafe}, - {"Auto"sv, kagome::application::SyncMethod::Auto}, + application::SyncMethod::FastWithoutState}, + {"Warp"sv, application::SyncMethod::Warp}, + {"Unsafe"sv, application::SyncMethod::Unsafe}, + {"Auto"sv, application::SyncMethod::Auto}, }; - std::optional str_to_sync_method( + std::optional str_to_sync_method( std::string_view str) { for (auto &[method_name, method] : kSyncMethods) { if (str == method_name) { diff --git a/core/blockchain/impl/block_tree_impl.cpp b/core/blockchain/impl/block_tree_impl.cpp index b472c3306e..d53fc025ec 100644 --- a/core/blockchain/impl/block_tree_impl.cpp +++ b/core/blockchain/impl/block_tree_impl.cpp @@ -21,6 +21,7 @@ #include "crypto/blake2/blake2b.h" #include "log/profiling_logger.hpp" #include "storage/database_error.hpp" +#include "storage/trie/update_direct_storage.hpp" #include "storage/trie_pruner/trie_pruner.hpp" #include "utils/pool_handler.hpp" @@ -124,6 +125,7 @@ namespace kagome::blockchain { extrinsic_event_key_repo, std::shared_ptr justification_storage_policy, + std::shared_ptr trie_storage, std::shared_ptr state_pruner, common::MainThreadPool &main_thread_pool) { BOOST_ASSERT(storage != nullptr); @@ -154,6 +156,17 @@ namespace kagome::blockchain { highest_leaf, last_finalized_block_info); + OUTCOME_TRY(highest_leaf_header, + storage->getBlockHeader(highest_leaf.hash)); + // this is temporary debug code + // OUTCOME_TRY( + // trie, + // trie_storage->getEphemeralBatchAt(highest_leaf_header.state_root)); + // auto &direct_storage = + // dynamic_cast(trie_storage->DEBUG_getDirectStorage()); + // OUTCOME_TRY(storage::trie::updateDirectStorage( + // highest_leaf_header.state_root, *trie, direct_storage, log)); + // Load non-finalized block from block storage std::map collected; @@ -269,6 +282,7 @@ namespace kagome::blockchain { std::move(extrinsic_events_engine), std::move(extrinsic_event_key_repo), std::move(justification_storage_policy), + trie_storage, state_pruner, main_thread_pool)); // Add non-finalized block to the block tree @@ -393,19 +407,21 @@ namespace kagome::blockchain { extrinsic_event_key_repo, std::shared_ptr justification_storage_policy, + std::shared_ptr trie_storage, std::shared_ptr state_pruner, common::MainThreadPool &main_thread_pool) : block_tree_data_{BlockTreeData{ - .storage_ = std::move(storage), - .state_pruner_ = std::move(state_pruner), - .tree_ = std::make_unique(finalized), - .hasher_ = std::move(hasher), - .extrinsic_event_key_repo_ = std::move(extrinsic_event_key_repo), - .justification_storage_policy_ = - std::move(justification_storage_policy), - .genesis_block_hash_ = {}, - .blocks_pruning_ = {app_config.blocksPruning(), finalized.number}, - }}, + .storage_ = std::move(storage), + .trie_storage_ = std::move(trie_storage), + .state_pruner_ = std::move(state_pruner), + .tree_ = std::make_unique(finalized), + .hasher_ = std::move(hasher), + .extrinsic_event_key_repo_ = std::move(extrinsic_event_key_repo), + .justification_storage_policy_ = + std::move(justification_storage_policy), + .genesis_block_hash_ = {}, + .blocks_pruning_ = {app_config.blocksPruning(), finalized.number}, + }}, chain_events_engine_{std::move(chain_events_engine)}, main_pool_handler_{main_thread_pool.handlerStarted()}, extrinsic_events_engine_{std::move(extrinsic_events_engine)} { @@ -415,6 +431,7 @@ namespace kagome::blockchain { BOOST_ASSERT(p.hasher_ != nullptr); BOOST_ASSERT(p.extrinsic_event_key_repo_ != nullptr); BOOST_ASSERT(p.justification_storage_policy_ != nullptr); + BOOST_ASSERT(p.trie_storage_ != nullptr); BOOST_ASSERT(p.state_pruner_ != nullptr); // Register metrics diff --git a/core/blockchain/impl/block_tree_impl.hpp b/core/blockchain/impl/block_tree_impl.hpp index 7ad1b9e78e..538be417d8 100644 --- a/core/blockchain/impl/block_tree_impl.hpp +++ b/core/blockchain/impl/block_tree_impl.hpp @@ -66,6 +66,7 @@ namespace kagome::blockchain { extrinsic_event_key_repo, std::shared_ptr justification_storage_policy, + std::shared_ptr trie_storage, std::shared_ptr state_pruner, common::MainThreadPool &main_thread_pool); @@ -177,6 +178,7 @@ namespace kagome::blockchain { struct BlockTreeData { std::shared_ptr storage_; + std::shared_ptr trie_storage_; std::shared_ptr state_pruner_; std::unique_ptr tree_; std::shared_ptr hasher_; @@ -204,6 +206,7 @@ namespace kagome::blockchain { extrinsic_event_key_repo, std::shared_ptr justification_storage_policy, + std::shared_ptr trie_storage, std::shared_ptr state_pruner, common::MainThreadPool &main_thread_pool); diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index ea7d842728..13e3dcf350 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -369,6 +369,7 @@ namespace { injector.template create(), injector.template create>(), injector.template create>(), + injector.template create>(), injector.template create>(), injector.template create()); // clang-format on @@ -857,7 +858,7 @@ namespace { bind_by_lambda([](const auto &injector) { auto storage = injector.template create>(); - + return storage::trie::TrieStorageImpl::createEmpty( injector.template create< sptr>(), diff --git a/core/network/impl/peer_view.cpp b/core/network/impl/peer_view.cpp index de34815758..9c4b8919e0 100644 --- a/core/network/impl/peer_view.cpp +++ b/core/network/impl/peer_view.cpp @@ -15,20 +15,21 @@ namespace kagome::network { /** * @returns `View` with all leaves, `View` with `MAX_VIEW_HEAD` leaves */ - inline std::pair makeViews( - const LazySPtr &block_tree) { + std::pair makeViews( + const LazySPtr &lazy_block_tree) { std::pair result; auto &[view, stripped_view] = result; auto &heads_ = stripped_view.heads_; - auto last_finalized = block_tree.get()->getLastFinalized().number; + BOOST_ASSERT(lazy_block_tree.get() != nullptr); + auto &block_tree = *lazy_block_tree.get(); + auto last_finalized = block_tree.getLastFinalized().number; view.finalized_number_ = last_finalized; stripped_view.finalized_number_ = last_finalized; - auto heads = block_tree.get()->getLeavesInfo(); + auto heads = block_tree.getLeavesInfo(); - std::ranges::sort(heads, - [](const auto &l, const auto &r) { return l < r; }); + std::ranges::sort(heads); heads_.reserve(std::min(MAX_VIEW_HEADS, heads.size())); for (const auto &head : diff --git a/core/network/impl/state_sync_request_flow.cpp b/core/network/impl/state_sync_request_flow.cpp index c7dd748222..55fe959e39 100644 --- a/core/network/impl/state_sync_request_flow.cpp +++ b/core/network/impl/state_sync_request_flow.cpp @@ -5,6 +5,7 @@ */ #include +#include #include "crypto/blake2/blake2b.h" #include "network/impl/state_sync_request_flow.hpp" @@ -97,6 +98,16 @@ namespace kagome::network { OUTCOME_TRY(push(it)); } auto pop_level = true; + size_t count{}; + auto start = std::chrono::steady_clock::now(); + libp2p::common::FinalAction print_count = [this, &count, start] { + auto now = std::chrono::steady_clock::now(); + log_->debug( + "Inserted {} entries to node DB as part of state sync in {} ms", + count, + std::chrono::duration_cast(now - start) + .count()); + }; while (not level.stack.empty()) { auto child = level.value_child; if (child and not isKnown(*child)) { @@ -111,6 +122,7 @@ namespace kagome::network { return outcome::success(); } OUTCOME_TRY(node_db_->put(it->first, std::move(it->second.first))); + ++count; known_.emplace(it->first); } for (level.branchInit(); not level.branch_end; level.branchNext()) { @@ -127,6 +139,7 @@ namespace kagome::network { if (level.branch_end) { auto &t = level.stack.back().t; OUTCOME_TRY(node_db_->put(t.hash, std::move(t.encoded))); + ++count; known_.emplace(t.hash); level.pop(); if (not level.stack.empty()) { diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index 090d6130f3..d4a9fe8252 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -30,6 +30,7 @@ #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie/trie_batches.hpp" #include "storage/trie/trie_storage.hpp" +#include "storage/trie/update_direct_storage.hpp" #include "storage/trie_pruner/trie_pruner.hpp" #include "utils/pool_handler_ready_make.hpp" #include "utils/sptr.hpp" @@ -965,21 +966,6 @@ namespace kagome::network { state_sync_->peer, std::move(request), std::move(response_handler)); } - outcome::result updateDirectStorage( - const storage::trie::RootHash &root, - storage::trie::TrieBatch &trie, - storage::RocksDbSpace &direct_storage) { - OUTCOME_TRY(direct_storage.clear()); - auto batch = direct_storage.batch(); - for (auto cursor = trie.trieCursor(); cursor->isValid();) { - OUTCOME_TRY(batch->put(cursor->key().value(), cursor->value().value())); - OUTCOME_TRY(cursor->next()); - } - OUTCOME_TRY(batch->put(storage::trie::kLastCommittedHashKey, root)); - OUTCOME_TRY(batch->commit()); - return outcome::success(); - } - outcome::result SynchronizerImpl::syncState( std::unique_lock &lock, outcome::result &&_res) { @@ -993,8 +979,9 @@ namespace kagome::network { OUTCOME_TRY(trie_pruner_->addNewState(state_sync_flow_->root(), storage::trie::StateVersion::V0)); OUTCOME_TRY(trie, storage_->getEphemeralBatchAt(state_sync_flow_->root())); - OUTCOME_TRY(updateDirectStorage( - state_sync_flow_->root(), *trie, *trie_direct_storage_)); + SL_DEBUG(log_, "New direct storage root: {}", state_sync_flow_->root()); + OUTCOME_TRY(storage::trie::updateDirectStorage( + state_sync_flow_->root(), *trie, *trie_direct_storage_, log_)); auto block = state_sync_flow_->blockInfo(); state_sync_flow_.reset(); SL_INFO(log_, "State syncing block {} has finished.", block); diff --git a/core/parachain/pvf/kagome_pvf_worker_injector.hpp b/core/parachain/pvf/kagome_pvf_worker_injector.hpp index 7dd6517e84..d0a01c16ba 100644 --- a/core/parachain/pvf/kagome_pvf_worker_injector.hpp +++ b/core/parachain/pvf/kagome_pvf_worker_injector.hpp @@ -70,6 +70,10 @@ namespace kagome::parachain { const OnNodeLoaded &on_node_loaded) const override { return nullptr; } + + virtual storage::BufferStorage &DEBUG_getDirectStorage() { + std::abort(); + } }; template diff --git a/core/runtime/common/module_repository_impl.cpp b/core/runtime/common/module_repository_impl.cpp index 6283344fd1..6e872d9d1d 100644 --- a/core/runtime/common/module_repository_impl.cpp +++ b/core/runtime/common/module_repository_impl.cpp @@ -82,6 +82,9 @@ namespace kagome::runtime { if (not code_res) { code_res = code_provider_->getCodeAt(storage_state); } + BOOST_ASSERT_MSG( + code_res.has_value(), + "A :code entry should always be present in the storage"); auto &code_zstd = *code_res.value(); item.hash = hasher_->blake2b_256(code_zstd); OUTCOME_TRY(code, uncompressCodeIfNeeded(code_zstd)); diff --git a/core/runtime/common/storage_code_provider.cpp b/core/runtime/common/storage_code_provider.cpp index 092ec8e603..2bcf44c8e5 100644 --- a/core/runtime/common/storage_code_provider.cpp +++ b/core/runtime/common/storage_code_provider.cpp @@ -45,6 +45,7 @@ namespace kagome::runtime { return std::make_shared(std::move(code)); } } + SL_DEBUG(logger_, "Reading code from storage at {}", state); OUTCOME_TRY(batch, storage_->getEphemeralBatchAt(state)); OUTCOME_TRY(code, batch->get(storage::kRuntimeCodeKey)); cached_code_ = std::make_shared(std::move(code)); diff --git a/core/storage/rocksdb/rocksdb.cpp b/core/storage/rocksdb/rocksdb.cpp index 7ab434ad37..b6d7cbcb43 100644 --- a/core/storage/rocksdb/rocksdb.cpp +++ b/core/storage/rocksdb/rocksdb.cpp @@ -360,8 +360,8 @@ namespace kagome::storage { if (column_family_handles_.end() == column) { throw DatabaseError::INVALID_ARGUMENT; } - auto space_ptr = - std::make_shared(weak_from_this(), *column, logger_); + auto space_ptr = std::make_shared( + weak_from_this(), *column, space, logger_); spaces_[space] = space_ptr; return space_ptr; } @@ -457,9 +457,11 @@ namespace kagome::storage { RocksDbSpace::RocksDbSpace(std::weak_ptr storage, const RocksDb::ColumnFamilyHandlePtr &column, + Space space, log::Logger logger) : storage_{std::move(storage)}, column_{column}, + space_{space}, logger_{std::move(logger)} {} std::unique_ptr RocksDbSpace::batch() { diff --git a/core/storage/rocksdb/rocksdb.hpp b/core/storage/rocksdb/rocksdb.hpp index 1df6048e23..186b14e49b 100644 --- a/core/storage/rocksdb/rocksdb.hpp +++ b/core/storage/rocksdb/rocksdb.hpp @@ -137,6 +137,7 @@ namespace kagome::storage { RocksDbSpace(std::weak_ptr storage, const RocksDb::ColumnFamilyHandlePtr &column, + Space space, log::Logger logger); std::unique_ptr batch() override; diff --git a/core/storage/trie/impl/trie_storage_impl.hpp b/core/storage/trie/impl/trie_storage_impl.hpp index 8d71907bac..5754baa354 100644 --- a/core/storage/trie/impl/trie_storage_impl.hpp +++ b/core/storage/trie/impl/trie_storage_impl.hpp @@ -51,6 +51,10 @@ namespace kagome::storage::trie { const RootHash &root, const OnNodeLoaded &on_node_loaded) const override; + BufferStorage &DEBUG_getDirectStorage() override { + return *direct_kv_; + } + protected: TrieStorageImpl( std::shared_ptr codec, diff --git a/core/storage/trie/trie_storage.hpp b/core/storage/trie/trie_storage.hpp index a4449b41de..975754a053 100644 --- a/core/storage/trie/trie_storage.hpp +++ b/core/storage/trie/trie_storage.hpp @@ -41,6 +41,8 @@ namespace kagome::storage::trie { virtual outcome::result> getProofReaderBatchAt( const RootHash &root, const OnNodeLoaded &on_node_loaded) const = 0; + + virtual BufferStorage &DEBUG_getDirectStorage() = 0; }; } // namespace kagome::storage::trie diff --git a/core/storage/trie/update_direct_storage.hpp b/core/storage/trie/update_direct_storage.hpp new file mode 100644 index 0000000000..fa8fe192ce --- /dev/null +++ b/core/storage/trie/update_direct_storage.hpp @@ -0,0 +1,45 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "log/logger.hpp" +#include "outcome/outcome.hpp" +#include "storage/rocksdb/rocksdb.hpp" +#include "storage/trie/trie_batches.hpp" + +namespace kagome::storage::trie { + + inline outcome::result updateDirectStorage(const trie::RootHash &root, + trie::TrieBatch &trie, + RocksDbSpace &direct_storage, + log::Logger &log) { + OUTCOME_TRY(direct_storage.clear()); + auto batch = direct_storage.batch(); + size_t count{}; + auto checkpoint = std::chrono::steady_clock::now(); + auto cursor = trie.trieCursor(); + OUTCOME_TRY(cursor->seekFirst()); + BOOST_ASSERT(cursor->isValid()); + while (cursor->isValid()) { + OUTCOME_TRY(batch->put(cursor->key().value(), cursor->value().value())); + OUTCOME_TRY(cursor->next()); + ++count; + auto now = std::chrono::steady_clock::now(); + if (now - checkpoint > std::chrono::seconds(1)) { + log->debug( + "Inserted {} keys into direct storage with root {}", count, root); + checkpoint = now; + } + } + log->debug("Inserted total of {} keys into direct storage with root {}", + count, + root); + OUTCOME_TRY(batch->put(storage::trie::kLastCommittedHashKey, root)); + OUTCOME_TRY(batch->commit()); + return outcome::success(); + } +} // namespace kagome::storage::trie diff --git a/test/mock/core/storage/trie/trie_storage_mock.hpp b/test/mock/core/storage/trie/trie_storage_mock.hpp index 00565c9680..a342ffb12b 100644 --- a/test/mock/core/storage/trie/trie_storage_mock.hpp +++ b/test/mock/core/storage/trie/trie_storage_mock.hpp @@ -28,6 +28,8 @@ namespace kagome::storage::trie { getProofReaderBatchAt, (const RootHash &root, const OnNodeLoaded &on_node_loaded), (const, override)); + + MOCK_METHOD(BufferStorage &, DEBUG_getDirectStorage, (), (override)); }; } // namespace kagome::storage::trie From 6bd2a52c62072d06117bc22af6df57a45fd42cf3 Mon Sep 17 00:00:00 2001 From: Harrm Date: Mon, 19 May 2025 18:46:02 +0300 Subject: [PATCH 04/11] in progress --- cmake/toolchain/compiler/gcc-14.cmake | 34 +++++++++++++++++++ cmake/toolchain/gcc-14_cxx20.cmake | 2 ++ cmake/toolchain/gcc-14_mold_cxx20.cmake | 3 ++ core/network/impl/state_sync_request_flow.cpp | 2 ++ core/network/impl/state_sync_request_flow.hpp | 5 +++ core/network/impl/synchronizer_impl.cpp | 18 +++++++++- .../parachain/peer_relay_parent_knowledge.hpp | 16 ++++++--- .../trie/polkadot_trie/polkadot_trie.hpp | 4 +-- core/storage/trie/update_direct_storage.hpp | 4 +-- 9 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 cmake/toolchain/compiler/gcc-14.cmake create mode 100644 cmake/toolchain/gcc-14_cxx20.cmake create mode 100644 cmake/toolchain/gcc-14_mold_cxx20.cmake diff --git a/cmake/toolchain/compiler/gcc-14.cmake b/cmake/toolchain/compiler/gcc-14.cmake new file mode 100644 index 0000000000..c9ac6f7af7 --- /dev/null +++ b/cmake/toolchain/compiler/gcc-14.cmake @@ -0,0 +1,34 @@ +if(DEFINED POLLY_COMPILER_GCC_13_CMAKE_) + return() +else() + set(POLLY_COMPILER_GCC_13_CMAKE_ 1) +endif() + +find_program(CMAKE_C_COMPILER gcc-14) +find_program(CMAKE_CXX_COMPILER g++-14) + +if(NOT CMAKE_C_COMPILER) + message(FATAL_ERROR "gcc-14 not found") +endif() + +if(NOT CMAKE_CXX_COMPILER) +message(FATAL_ERROR "g++-14 not found") +endif() + +set( + CMAKE_C_COMPILER + "${CMAKE_C_COMPILER}" + CACHE + STRING + "C compiler" + FORCE +) + +set( + CMAKE_CXX_COMPILER + "${CMAKE_CXX_COMPILER}" + CACHE + STRING + "C++ compiler" + FORCE +) diff --git a/cmake/toolchain/gcc-14_cxx20.cmake b/cmake/toolchain/gcc-14_cxx20.cmake new file mode 100644 index 0000000000..327991dfe1 --- /dev/null +++ b/cmake/toolchain/gcc-14_cxx20.cmake @@ -0,0 +1,2 @@ +include(${CMAKE_CURRENT_LIST_DIR}/compiler/gcc-14.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cxx20.cmake) diff --git a/cmake/toolchain/gcc-14_mold_cxx20.cmake b/cmake/toolchain/gcc-14_mold_cxx20.cmake new file mode 100644 index 0000000000..95e3b51e7a --- /dev/null +++ b/cmake/toolchain/gcc-14_mold_cxx20.cmake @@ -0,0 +1,3 @@ +include(${CMAKE_CURRENT_LIST_DIR}/compiler/gcc-14.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cxx20.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/linker/mold.cmake) diff --git a/core/network/impl/state_sync_request_flow.cpp b/core/network/impl/state_sync_request_flow.cpp index 55fe959e39..2e47560aef 100644 --- a/core/network/impl/state_sync_request_flow.cpp +++ b/core/network/impl/state_sync_request_flow.cpp @@ -121,6 +121,7 @@ namespace kagome::network { if (it == nodes.end()) { return outcome::success(); } + nodes_.emplace(std::pair{it->first, it->second.first}); OUTCOME_TRY(node_db_->put(it->first, std::move(it->second.first))); ++count; known_.emplace(it->first); @@ -138,6 +139,7 @@ namespace kagome::network { } if (level.branch_end) { auto &t = level.stack.back().t; + nodes_.emplace(std::pair{t.hash, t.encoded}); OUTCOME_TRY(node_db_->put(t.hash, std::move(t.encoded))); ++count; known_.emplace(t.hash); diff --git a/core/network/impl/state_sync_request_flow.hpp b/core/network/impl/state_sync_request_flow.hpp index 2c7cc0a2fc..3f4ae14838 100644 --- a/core/network/impl/state_sync_request_flow.hpp +++ b/core/network/impl/state_sync_request_flow.hpp @@ -46,6 +46,10 @@ namespace kagome::network { return block_.state_root; } + auto &nodes() const { + return nodes_; + } + bool complete() const { return done_; } @@ -58,6 +62,7 @@ namespace kagome::network { bool isKnown(const common::Hash256 &hash); std::shared_ptr node_db_; + std::unordered_map nodes_; primitives::BlockInfo block_info_; primitives::BlockHeader block_; diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index d4a9fe8252..c1499a48fd 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -27,6 +27,7 @@ #include "primitives/common.hpp" #include "storage/predefined_keys.hpp" #include "storage/rocksdb/rocksdb.hpp" +#include "storage/trie/polkadot_trie/polkadot_trie_impl.hpp" #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie/trie_batches.hpp" #include "storage/trie/trie_storage.hpp" @@ -978,7 +979,22 @@ namespace kagome::network { } OUTCOME_TRY(trie_pruner_->addNewState(state_sync_flow_->root(), storage::trie::StateVersion::V0)); - OUTCOME_TRY(trie, storage_->getEphemeralBatchAt(state_sync_flow_->root())); + auto root = state_sync_flow_->nodes().at(Buffer{state_sync_flow_->root()}); + storage::trie::PolkadotCodec codec{crypto::blake2b}; + OUTCOME_TRY(root_node, codec.decodeNode(root)); + auto trie = storage::trie::PolkadotTrieImpl::create( + root_node, + storage::trie::PolkadotTrieImpl::RetrieveFunctions{ + [this, &codec](const storage::trie::DummyNode &node) { + if (auto hash = node.db_key.asHash()) { + auto enc = state_sync_flow_->nodes().at(node.db_key.asBuffer()); + return codec.decodeNode(enc); + } + return codec.decodeNode(node.db_key.asBuffer()); + }, + [this](const common::Hash256 &value_hash) { + return state_sync_flow_->nodes().at(Buffer{value_hash}); + }}); SL_DEBUG(log_, "New direct storage root: {}", state_sync_flow_->root()); OUTCOME_TRY(storage::trie::updateDirectStorage( state_sync_flow_->root(), *trie, *trie_direct_storage_, log_)); diff --git a/core/parachain/peer_relay_parent_knowledge.hpp b/core/parachain/peer_relay_parent_knowledge.hpp index f64ca9bb9a..1b3e243d8f 100644 --- a/core/parachain/peer_relay_parent_knowledge.hpp +++ b/core/parachain/peer_relay_parent_knowledge.hpp @@ -12,16 +12,22 @@ #include "network/types/collator_messages.hpp" #include "parachain/candidate_view.hpp" +namespace kagome::parachain { + + struct PeerStatement { + network::CompactStatement compact_statement; + network::ValidatorIndex validator_index; + }; + +} // namespace kagome::parachain + +// SCALE_TIE_HASH_STD(kagome::parachain::PeerStatement); + namespace kagome::parachain { /// knowledge that a peer has about goings-on in a relay parent. struct PeerRelayParentKnowledge { using CandidateHash = network::CandidateHash; - struct PeerStatement { - network::CompactStatement compact_statement; - network::ValidatorIndex validator_index; - }; - std::unordered_set sent_candidates; /// candidates that the peer is aware of because we /// sent statements to it. This indicates that we can diff --git a/core/storage/trie/polkadot_trie/polkadot_trie.hpp b/core/storage/trie/polkadot_trie/polkadot_trie.hpp index 14e946bf0d..3880e40653 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie.hpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie.hpp @@ -44,12 +44,12 @@ namespace kagome::storage::trie { : retrieve_node{std::move(retrieve_node)}, retrieve_value{std::move(retrieve_value)} {} - inline static outcome::result defaultNodeRetrieve( + static outcome::result defaultNodeRetrieve( const DummyNode &node) { return nullptr; } - inline static outcome::result> + static outcome::result> defaultValueRetrieve(const common::Hash256 &) { return TrieError::VALUE_RETRIEVE_NOT_PROVIDED; } diff --git a/core/storage/trie/update_direct_storage.hpp b/core/storage/trie/update_direct_storage.hpp index fa8fe192ce..6299b19a4b 100644 --- a/core/storage/trie/update_direct_storage.hpp +++ b/core/storage/trie/update_direct_storage.hpp @@ -9,12 +9,12 @@ #include "log/logger.hpp" #include "outcome/outcome.hpp" #include "storage/rocksdb/rocksdb.hpp" -#include "storage/trie/trie_batches.hpp" +#include "storage/trie/polkadot_trie/polkadot_trie.hpp" namespace kagome::storage::trie { inline outcome::result updateDirectStorage(const trie::RootHash &root, - trie::TrieBatch &trie, + trie::PolkadotTrie &trie, RocksDbSpace &direct_storage, log::Logger &log) { OUTCOME_TRY(direct_storage.clear()); From ec15230494bb68363fd86aa5a216e633fa95c8ed Mon Sep 17 00:00:00 2001 From: Harrm Date: Tue, 20 May 2025 14:49:43 +0300 Subject: [PATCH 05/11] 'Fix' tests --- cmake/Hunter/config.cmake | 3 ++- test/core/blockchain/block_tree_test.cpp | 6 ++++++ .../network/state_protocol_observer_test.cpp | 7 ++++++- test/core/network/synchronizer_test.cpp | 5 ++++- .../runtime/trie_storage_provider_test.cpp | 7 ++++++- .../trie/trie_storage/trie_batch_test.cpp | 21 +++++++++++++------ .../trie/trie_storage/trie_storage_test.cpp | 20 ++++++++++++------ 7 files changed, 53 insertions(+), 16 deletions(-) diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index 4aa9e79329..9404dc74b3 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -145,9 +145,10 @@ hunter_config( hunter_config( binaryen - VERSION 1.38.28-patch.3 + VERSION 1.38.28-patch.4 CMAKE_ARGS CMAKE_POLICY_VERSION_MINIMUM=3.5 + BUILD_STATIC_LIB=ON KEEP_PACKAGE_SOURCES ) diff --git a/test/core/blockchain/block_tree_test.cpp b/test/core/blockchain/block_tree_test.cpp index daad120345..6f1ca3b21c 100644 --- a/test/core/blockchain/block_tree_test.cpp +++ b/test/core/blockchain/block_tree_test.cpp @@ -22,6 +22,7 @@ #include "mock/core/blockchain/block_storage_mock.hpp" #include "mock/core/blockchain/justification_storage_policy.hpp" #include "mock/core/consensus/babe/babe_config_repository_mock.hpp" +#include "mock/core/storage/trie/trie_storage_mock.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "mock/core/transaction_pool/transaction_pool_mock.hpp" #include "network/impl/extrinsic_observer_impl.hpp" @@ -57,6 +58,7 @@ using primitives::Consensus; using primitives::Digest; using primitives::Justification; using primitives::PreRuntime; +using storage::trie::TrieStorageMock; using storage::trie_pruner::TriePrunerMock; using transaction_pool::TransactionPoolMock; using BabeSeal = consensus::babe::Seal; @@ -170,6 +172,7 @@ struct BlockTreeTest : public testing::Test { ext_events_engine, extrinsic_event_key_repo, justification_storage_policy_, + trie_storage_, state_pruner_, *main_thread_pool_) .value(); @@ -275,6 +278,9 @@ struct BlockTreeTest : public testing::Test { justification_storage_policy_ = std::make_shared>(); + std::shared_ptr trie_storage_ = + std::make_shared(); + std::shared_ptr state_pruner_ = std::make_shared(); diff --git a/test/core/network/state_protocol_observer_test.cpp b/test/core/network/state_protocol_observer_test.cpp index 14889cd5cb..8e45b1f17b 100644 --- a/test/core/network/state_protocol_observer_test.cpp +++ b/test/core/network/state_protocol_observer_test.cpp @@ -13,6 +13,7 @@ #include #include "mock/core/blockchain/block_header_repository_mock.hpp" +#include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "network/types/state_request.hpp" #include "storage/in_memory/in_memory_spaced_storage.hpp" @@ -56,7 +57,11 @@ std::shared_ptr makeEmptyInMemoryTrie() { .WillByDefault(Return(outcome::success())); return kagome::storage::trie::TrieStorageImpl::createEmpty( - trie_factory, codec, serializer, state_pruner) + trie_factory, + codec, + serializer, + state_pruner, + std::make_shared()) .value(); } diff --git a/test/core/network/synchronizer_test.cpp b/test/core/network/synchronizer_test.cpp index 8be1082a50..a02415b467 100644 --- a/test/core/network/synchronizer_test.cpp +++ b/test/core/network/synchronizer_test.cpp @@ -111,7 +111,8 @@ class SynchronizerTest nullptr, grandpa_environment, *main_thread_pool, - block_storage); + block_storage, + spaced_storage); } void TearDown() override { @@ -148,6 +149,8 @@ class SynchronizerTest std::make_shared(); std::shared_ptr block_storage = std::make_shared(); + std::shared_ptr spaced_storage = + std::make_shared(); std::shared_ptr watchdog = std::make_shared(std::chrono::milliseconds(1)); diff --git a/test/core/runtime/trie_storage_provider_test.cpp b/test/core/runtime/trie_storage_provider_test.cpp index b229540e3a..8a56359d27 100644 --- a/test/core/runtime/trie_storage_provider_test.cpp +++ b/test/core/runtime/trie_storage_provider_test.cpp @@ -11,6 +11,7 @@ #include #include "common/buffer.hpp" +#include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "runtime/common/runtime_execution_error.hpp" #include "storage/in_memory/in_memory_spaced_storage.hpp" @@ -51,7 +52,11 @@ class TrieStorageProviderTest : public ::testing::Test { std::make_shared(); auto trieDb = kagome::storage::trie::TrieStorageImpl::createEmpty( - trie_factory, codec, serializer, state_pruner) + trie_factory, + codec, + serializer, + state_pruner, + std::make_shared()) .value(); storage_provider_ = diff --git a/test/core/storage/trie/trie_storage/trie_batch_test.cpp b/test/core/storage/trie/trie_storage/trie_batch_test.cpp index 089da4091b..c23b54c49a 100644 --- a/test/core/storage/trie/trie_storage/trie_batch_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_batch_test.cpp @@ -9,6 +9,7 @@ #include +#include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/spaced_storage_mock.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "storage/changes_trie/impl/storage_changes_tracker_impl.hpp" @@ -62,9 +63,13 @@ class TrieBatchTest : public test::BaseRocksDB_Test { testing::A(), _)) .WillByDefault(Return(outcome::success())); - trie = - TrieStorageImpl::createEmpty(factory, codec, serializer, state_pruner) - .value(); + trie = TrieStorageImpl::createEmpty( + factory, + codec, + serializer, + state_pruner, + std::make_shared()) + .value(); } static const std::vector> data; @@ -212,9 +217,13 @@ TEST_F(TrieBatchTest, ConsistentOnFailure) { addNewState(testing::A(), _)) .WillByDefault(Return(outcome::success())); - auto trie = - TrieStorageImpl::createEmpty(factory, codec, serializer, state_pruner) - .value(); + auto trie = TrieStorageImpl::createEmpty( + factory, + codec, + serializer, + state_pruner, + std::make_shared()) + .value(); auto batch = trie->getPersistentBatchAt(empty_hash, std::nullopt).value(); ASSERT_OUTCOME_SUCCESS(batch->put("123"_buf, "111"_buf)); diff --git a/test/core/storage/trie/trie_storage/trie_storage_test.cpp b/test/core/storage/trie/trie_storage/trie_storage_test.cpp index 1227332102..4cc4947be6 100644 --- a/test/core/storage/trie/trie_storage/trie_storage_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_storage_test.cpp @@ -11,6 +11,7 @@ #include #include "filesystem/common.hpp" +#include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "storage/rocksdb/rocksdb.hpp" #include "storage/trie/impl/trie_storage_backend_impl.hpp" @@ -65,9 +66,13 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { testing::A(), _)) .WillByDefault(Return(outcome::success())); - auto storage = - TrieStorageImpl::createEmpty(factory, codec, serializer, state_pruner) - .value(); + auto storage = TrieStorageImpl::createEmpty( + factory, + codec, + serializer, + state_pruner, + std::make_shared()) + .value(); auto batch = storage @@ -84,9 +89,12 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { auto serializer = std::make_shared( factory, codec, std::make_shared(new_rocks_db)); auto state_pruner = std::make_shared(); - auto storage = - TrieStorageImpl::createFromStorage(codec, serializer, state_pruner) - .value(); + auto storage = TrieStorageImpl::createFromStorage( + codec, + serializer, + state_pruner, + std::make_shared()) + .value(); auto batch = storage->getPersistentBatchAt(root, std::nullopt).value(); ASSERT_OUTCOME_SUCCESS(v1, batch->get("123"_buf)); ASSERT_EQ(v1, "abc"_buf); From 7bda3270b9f124d698b7cc4a14059dc0cc95ac88 Mon Sep 17 00:00:00 2001 From: Harrm Date: Tue, 20 May 2025 15:41:22 +0300 Subject: [PATCH 06/11] Fix binaryen? --- cmake/Hunter/config.cmake | 3 +-- core/runtime/binaryen/CMakeLists.txt | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index 9404dc74b3..4aa9e79329 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -145,10 +145,9 @@ hunter_config( hunter_config( binaryen - VERSION 1.38.28-patch.4 + VERSION 1.38.28-patch.3 CMAKE_ARGS CMAKE_POLICY_VERSION_MINIMUM=3.5 - BUILD_STATIC_LIB=ON KEEP_PACKAGE_SOURCES ) diff --git a/core/runtime/binaryen/CMakeLists.txt b/core/runtime/binaryen/CMakeLists.txt index 0f4e5fcab3..5eb0ba5bed 100644 --- a/core/runtime/binaryen/CMakeLists.txt +++ b/core/runtime/binaryen/CMakeLists.txt @@ -11,6 +11,7 @@ target_link_libraries(binaryen_wasm_memory logger runtime_common binaryen::binaryen + binaryen::ir memory_allocator ) kagome_install(binaryen_wasm_memory) @@ -27,6 +28,7 @@ add_library(binaryen_runtime_external_interface ) target_link_libraries(binaryen_runtime_external_interface binaryen::binaryen + binaryen::ir binaryen_wasm_memory logger ) @@ -45,6 +47,7 @@ add_library(binaryen_wasm_module ) target_link_libraries(binaryen_wasm_module binaryen::binaryen + binaryen::ir logger binaryen_runtime_external_interface trie_storage_provider @@ -61,6 +64,7 @@ target_link_libraries(binaryen_instance_environment_factory core_api_factory binaryen_memory_provider binaryen::binaryen + binaryen::ir ) kagome_install(binaryen_instance_environment_factory) From 6a99390aab8fbed055ec3775419f816a9ab9e390 Mon Sep 17 00:00:00 2001 From: Harrm Date: Tue, 20 May 2025 19:11:16 +0300 Subject: [PATCH 07/11] Almost working, forks are left --- core/consensus/beefy/impl/beefy_impl.cpp | 2 +- core/network/impl/state_sync_request_flow.cpp | 17 +++++++++++------ core/network/impl/synchronizer_impl.cpp | 19 +++++++++++++++++-- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/core/consensus/beefy/impl/beefy_impl.cpp b/core/consensus/beefy/impl/beefy_impl.cpp index 99f9eae875..fa29125ccc 100644 --- a/core/consensus/beefy/impl/beefy_impl.cpp +++ b/core/consensus/beefy/impl/beefy_impl.cpp @@ -162,7 +162,7 @@ namespace kagome::network { return; } if (block_number >= next_digest_) { - SL_VERBOSE(log_, "ignoring vote for unindexed block {}", block_number); + SL_TRACE(log_, "ignoring vote for unindexed block {}", block_number); return; } auto next_session = sessions_.upper_bound(block_number); diff --git a/core/network/impl/state_sync_request_flow.cpp b/core/network/impl/state_sync_request_flow.cpp index 2e47560aef..0bffc09516 100644 --- a/core/network/impl/state_sync_request_flow.cpp +++ b/core/network/impl/state_sync_request_flow.cpp @@ -116,12 +116,14 @@ namespace kagome::network { pop_level = false; break; } - if ((level.value_hash != nullptr) and not isKnown(*level.value_hash)) { + if (level.value_hash != nullptr and not isKnown(*level.value_hash)) { auto it = nodes.find(*level.value_hash); if (it == nodes.end()) { return outcome::success(); } - nodes_.emplace(std::pair{it->first, it->second.first}); + auto [_, inserted] = + nodes_.emplace(std::pair{it->first, it->second.first}); + BOOST_VERIFY(inserted); OUTCOME_TRY(node_db_->put(it->first, std::move(it->second.first))); ++count; known_.emplace(it->first); @@ -139,7 +141,8 @@ namespace kagome::network { } if (level.branch_end) { auto &t = level.stack.back().t; - nodes_.emplace(std::pair{t.hash, t.encoded}); + auto [_, inserted] = nodes_.emplace(std::pair{t.hash, t.encoded}); + BOOST_VERIFY(inserted); OUTCOME_TRY(node_db_->put(t.hash, std::move(t.encoded))); ++count; known_.emplace(t.hash); @@ -164,9 +167,11 @@ namespace kagome::network { if (known_.find(hash) != known_.end()) { return true; } - if (auto node_res = node_db_->contains(hash), - value_res = node_db_->contains(hash); - (node_res and node_res.value()) or (value_res and value_res.value())) { + auto node_res = node_db_->contains(hash); + auto in_mem_node = nodes_.contains(common::BufferView{hash}); + // if something's in the database, we still need to put into in memory + // storage + if (node_res.value() and in_mem_node) { known_.emplace(hash); return true; } diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index c1499a48fd..d68794773d 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -31,6 +31,7 @@ #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie/trie_batches.hpp" #include "storage/trie/trie_storage.hpp" +#include "storage/trie/trie_storage_backend.hpp" #include "storage/trie/update_direct_storage.hpp" #include "storage/trie_pruner/trie_pruner.hpp" #include "utils/pool_handler_ready_make.hpp" @@ -987,8 +988,22 @@ namespace kagome::network { storage::trie::PolkadotTrieImpl::RetrieveFunctions{ [this, &codec](const storage::trie::DummyNode &node) { if (auto hash = node.db_key.asHash()) { - auto enc = state_sync_flow_->nodes().at(node.db_key.asBuffer()); - return codec.decodeNode(enc); + if (auto it = + state_sync_flow_->nodes().find(node.db_key.asBuffer()); + it != state_sync_flow_->nodes().end()) { + auto enc = it->second; + return codec.decodeNode(enc); + } else { + if (trie_node_db_->contains(node.db_key.asBuffer())) { + throw std::runtime_error{ + std::format("Node DB has {}, in memory db doesn't", + node.db_key.asBuffer().toHex())}; + } else { + throw std::runtime_error{std::format( + "Node DB doesn't {}, neither does in memory db", + node.db_key.asBuffer().toHex())}; + } + } } return codec.decodeNode(node.db_key.asBuffer()); }, From eb3b44d6f14570d03a4d467920e83a0e8d222d03 Mon Sep 17 00:00:00 2001 From: Harrm Date: Wed, 28 May 2025 15:24:27 +0300 Subject: [PATCH 08/11] Format --- core/blockchain/impl/block_tree_impl.cpp | 22 +- core/blockchain/impl/block_tree_impl.hpp | 3 + core/common/monadic_utils.hpp | 12 +- .../consensus/timeline/impl/timeline_impl.cpp | 5 +- core/injector/application_injector.cpp | 19 +- core/injector/calculate_genesis_state.hpp | 18 +- core/network/impl/synchronizer_impl.cpp | 32 +- core/network/impl/synchronizer_impl.hpp | 5 +- .../pvf/kagome_pvf_worker_injector.hpp | 2 +- core/storage/CMakeLists.txt | 1 + core/storage/face/generic_maps.hpp | 3 + core/storage/face/map_cursor.hpp | 7 + core/storage/face/readable.hpp | 2 + core/storage/in_memory/cursor.hpp | 24 +- core/storage/in_memory/in_memory_storage.cpp | 36 +- core/storage/in_memory/in_memory_storage.hpp | 5 +- core/storage/map_prefix/prefix.cpp | 34 +- core/storage/map_prefix/prefix.hpp | 9 +- core/storage/rocksdb/rocksdb.cpp | 25 ++ core/storage/rocksdb/rocksdb.hpp | 3 +- core/storage/rocksdb/rocksdb_cursor.cpp | 26 ++ core/storage/rocksdb/rocksdb_cursor.hpp | 2 + core/storage/rocksdb/rocksdb_spaces.cpp | 3 +- core/storage/spaces.hpp | 1 + core/storage/trie/impl/direct_storage.cpp | 377 ++++++++++++++++++ core/storage/trie/impl/direct_storage.hpp | 106 +++++ .../trie/impl/ephemeral_trie_batch_impl.cpp | 6 +- .../trie/impl/ephemeral_trie_batch_impl.hpp | 13 +- .../trie/impl/persistent_trie_batch_impl.cpp | 51 +-- .../trie/impl/persistent_trie_batch_impl.hpp | 11 +- .../trie/impl/topper_trie_batch_impl.cpp | 2 +- .../trie/impl/topper_trie_batch_impl.hpp | 2 +- core/storage/trie/impl/trie_batch_base.cpp | 34 +- core/storage/trie/impl/trie_batch_base.hpp | 19 +- .../trie/impl/trie_storage_backend_impl.cpp | 10 + .../trie/impl/trie_storage_backend_impl.hpp | 3 + core/storage/trie/impl/trie_storage_impl.cpp | 72 +--- core/storage/trie/impl/trie_storage_impl.hpp | 14 +- .../polkadot_trie/polkadot_trie_cursor.hpp | 2 +- .../polkadot_trie_cursor_impl.cpp | 16 +- .../polkadot_trie_cursor_impl.hpp | 4 +- core/storage/trie/trie_storage.hpp | 4 +- core/storage/trie/types.hpp | 3 - core/storage/trie/update_direct_storage.hpp | 45 --- core/utils/kagome_db_editor.cpp | 20 +- scripts/apply_clang_format.sh | 34 ++ 46 files changed, 860 insertions(+), 287 deletions(-) create mode 100644 core/storage/trie/impl/direct_storage.cpp create mode 100644 core/storage/trie/impl/direct_storage.hpp delete mode 100644 core/storage/trie/update_direct_storage.hpp create mode 100755 scripts/apply_clang_format.sh diff --git a/core/blockchain/impl/block_tree_impl.cpp b/core/blockchain/impl/block_tree_impl.cpp index d53fc025ec..f56887a6a9 100644 --- a/core/blockchain/impl/block_tree_impl.cpp +++ b/core/blockchain/impl/block_tree_impl.cpp @@ -21,7 +21,7 @@ #include "crypto/blake2/blake2b.h" #include "log/profiling_logger.hpp" #include "storage/database_error.hpp" -#include "storage/trie/update_direct_storage.hpp" +#include "storage/trie/impl/direct_storage.hpp" #include "storage/trie_pruner/trie_pruner.hpp" #include "utils/pool_handler.hpp" @@ -127,6 +127,7 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, + std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool) { BOOST_ASSERT(storage != nullptr); @@ -156,17 +157,6 @@ namespace kagome::blockchain { highest_leaf, last_finalized_block_info); - OUTCOME_TRY(highest_leaf_header, - storage->getBlockHeader(highest_leaf.hash)); - // this is temporary debug code - // OUTCOME_TRY( - // trie, - // trie_storage->getEphemeralBatchAt(highest_leaf_header.state_root)); - // auto &direct_storage = - // dynamic_cast(trie_storage->DEBUG_getDirectStorage()); - // OUTCOME_TRY(storage::trie::updateDirectStorage( - // highest_leaf_header.state_root, *trie, direct_storage, log)); - // Load non-finalized block from block storage std::map collected; @@ -284,6 +274,7 @@ namespace kagome::blockchain { std::move(justification_storage_policy), trie_storage, state_pruner, + trie_direct_storage, main_thread_pool)); // Add non-finalized block to the block tree for (auto &item : collected) { @@ -409,11 +400,13 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, + std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool) : block_tree_data_{BlockTreeData{ .storage_ = std::move(storage), .trie_storage_ = std::move(trie_storage), .state_pruner_ = std::move(state_pruner), + .trie_direct_storage_ = std::move(trie_direct_storage), .tree_ = std::make_unique(finalized), .hasher_ = std::move(hasher), .extrinsic_event_key_repo_ = std::move(extrinsic_event_key_repo), @@ -433,6 +426,7 @@ namespace kagome::blockchain { BOOST_ASSERT(p.justification_storage_policy_ != nullptr); BOOST_ASSERT(p.trie_storage_ != nullptr); BOOST_ASSERT(p.state_pruner_ != nullptr); + BOOST_ASSERT(p.trie_direct_storage_ != nullptr); // Register metrics BOOST_ASSERT(telemetry_ != nullptr); @@ -800,6 +794,8 @@ namespace kagome::blockchain { auto changes = p.tree_->finalize(node); OUTCOME_TRY(reorgAndPrune(p, changes)); + OUTCOME_TRY( + p.trie_direct_storage_->updateDirectState(header.state_root)); OUTCOME_TRY(pruneTrie(p, node->info.number)); notifyChainEventsEngine( @@ -1328,6 +1324,8 @@ namespace kagome::blockchain { } extrinsics.emplace_back(std::move(ext)); } + OUTCOME_TRY( + p.trie_direct_storage_->discardDiff(block_header.state_root)); p.state_pruner_->schedulePrune( block_header.state_root, block_header.blockInfo(), diff --git a/core/blockchain/impl/block_tree_impl.hpp b/core/blockchain/impl/block_tree_impl.hpp index 538be417d8..73214b3141 100644 --- a/core/blockchain/impl/block_tree_impl.hpp +++ b/core/blockchain/impl/block_tree_impl.hpp @@ -68,6 +68,7 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, + std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool); /// Recover block tree state at provided block @@ -180,6 +181,7 @@ namespace kagome::blockchain { std::shared_ptr storage_; std::shared_ptr trie_storage_; std::shared_ptr state_pruner_; + std::shared_ptr trie_direct_storage_; std::unique_ptr tree_; std::shared_ptr hasher_; std::shared_ptr @@ -208,6 +210,7 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, + std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool); outcome::result reorgAndPrune(BlockTreeData &p, diff --git a/core/common/monadic_utils.hpp b/core/common/monadic_utils.hpp index d93a4505a1..7a92a146ae 100644 --- a/core/common/monadic_utils.hpp +++ b/core/common/monadic_utils.hpp @@ -24,7 +24,7 @@ namespace kagome::common { typename R = std::invoke_result_t> std::optional map_optional(const std::optional &opt, const F &f) { if (opt.has_value()) { - return std::optional{f(opt.value())}; + return std::optional{std::invoke(f, opt.value())}; } return std::nullopt; } @@ -37,7 +37,7 @@ namespace kagome::common { template > std::optional map_optional(std::optional &&opt, const F &f) { if (opt.has_value()) { - return std::optional{f(std::move(opt.value()))}; + return std::optional{std::invoke(f, std::move(opt.value()))}; } return std::nullopt; } @@ -54,7 +54,7 @@ namespace kagome::common { typename R = std::invoke_result_t> CustomOutcome map_result(const CustomOutcome &res, const F &f) { if (res.has_value()) { - return CustomOutcome{f(res.value())}; + return CustomOutcome{std::invoke(f, res.value())}; } return res.as_failure(); } @@ -70,7 +70,7 @@ namespace kagome::common { typename R = std::invoke_result_t> outcome::result map_result(outcome::result &&res, const F &f) { if (res.has_value()) { - return outcome::result{f(std::move(res.value()))}; + return outcome::result{std::invoke(f, std::move(res.value()))}; } return res.as_failure(); } @@ -88,7 +88,7 @@ namespace kagome::common { outcome::result> map_result_optional( const outcome::result> &res_opt, const F &f) { return map_result(res_opt, [&f](auto &opt) { - return map_optional(opt, [&f](auto &v) { return f(v); }); + return map_optional(opt, [&f](auto &v) { return std::invoke(f, v); }); }); } @@ -104,7 +104,7 @@ namespace kagome::common { outcome::result> &&res_opt, const F &f) { return map_result(std::move(res_opt), [&f](std::optional &&opt) { return map_optional(std::move(opt), - [&f](T &&v) { return f(std::move(v)); }); + [&f](T &&v) { return std::invoke(f, std::move(v)); }); }); } diff --git a/core/consensus/timeline/impl/timeline_impl.cpp b/core/consensus/timeline/impl/timeline_impl.cpp index 2dfd627444..9c35ab585e 100644 --- a/core/consensus/timeline/impl/timeline_impl.cpp +++ b/core/consensus/timeline/impl/timeline_impl.cpp @@ -185,9 +185,10 @@ namespace kagome::consensus { if (auto res = trie_storage_->getEphemeralBatchAt(state_root); not res.has_value()) { if (sync_method_ == SyncMethod::Full) { - SL_WARN(log_, "Can't get state of best block: {}", res.error()); SL_CRITICAL(log_, - "Try restart at least once with `--sync Fast' CLI arg"); + "Can't get state of best block: {}. Try restart at least " + "once with `--sync Fast' CLI arg", + res.error()); return false; } full_sync_available = false; diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 13e3dcf350..f74d5aeea2 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -371,6 +371,7 @@ namespace { injector.template create>(), injector.template create>(), injector.template create>(), + injector.template create>(), injector.template create()); // clang-format on @@ -743,7 +744,8 @@ namespace { injector.template create(), injector .template create(), - *storage->getSpace(storage::Space::kTrieDirectKV), + *injector + .template create>(), injector.template create< sptr>()); if (root_res.has_error()) { @@ -856,9 +858,7 @@ namespace { ); }), bind_by_lambda([](const auto - &injector) { - auto storage = injector.template create>(); - + &injector) { return storage::trie::TrieStorageImpl::createEmpty( injector.template create< sptr>(), @@ -867,9 +867,18 @@ namespace { sptr>(), injector.template create< sptr>(), - storage->getSpace(storage::Space::kTrieDirectKV)) + injector.template create< + sptr>()) .value(); }), + bind_by_lambda([](const auto + &injector) { + auto storage = injector.template create>(); + auto& rocksdb = dynamic_cast(*storage); + return storage::trie::DirectStorage::create( + rocksdb.getRocksSpace(storage::Space::kTrieDirectKV), + rocksdb.getRocksSpace(storage::Space::kTrieDiff)).value(); + }), di::bind.template to(), bind_by_lambda([](const auto&) { return std::make_shared(crypto::blake2b<32>); diff --git a/core/injector/calculate_genesis_state.hpp b/core/injector/calculate_genesis_state.hpp index 261c35683b..6943af8679 100644 --- a/core/injector/calculate_genesis_state.hpp +++ b/core/injector/calculate_genesis_state.hpp @@ -13,6 +13,7 @@ #include "runtime/runtime_instances_pool.hpp" #include "runtime/wabt/version.hpp" #include "storage/predefined_keys.hpp" +#include "storage/trie/impl/direct_storage.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_impl.hpp" #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie_pruner/trie_pruner.hpp" @@ -24,17 +25,14 @@ namespace kagome::injector { const crypto::Hasher &hasher, runtime::RuntimeInstancesPool &module_factory, storage::trie::TrieSerializer &trie_serializer, - storage::BufferStorage &direct_trie_storage, + storage::trie::DirectStorage &direct_trie_storage, std::shared_ptr runtime_cache) { - auto trie_from = [&direct_trie_storage]( - BufferView prefix, - const application::GenesisRawData &kv) + auto trie_from = [](BufferView prefix, + const application::GenesisRawData &kv) -> outcome::result> { auto trie = storage::trie::PolkadotTrieImpl::createEmpty(); for (const auto &[key, val] : kv) { OUTCOME_TRY(trie->put(Buffer{prefix}.put(key), val.view())); - OUTCOME_TRY( - direct_trie_storage.put(Buffer{prefix}.put(key), val.view())); } return trie; }; @@ -83,8 +81,12 @@ namespace kagome::injector { OUTCOME_TRY(root_and_batch, trie_serializer.storeTrie(*top_trie, version)); OUTCOME_TRY(root_and_batch.second->commit()); - OUTCOME_TRY(direct_trie_storage.put(storage::trie::kLastCommittedHashKey, - root_and_batch.first)); + + if (direct_trie_storage.getDirectStateRoot() + == storage::trie::kEmptyRootHash) { + OUTCOME_TRY(direct_trie_storage.resetDirectState(root_and_batch.first, + *top_trie)); + } return root_and_batch.first; } } // namespace kagome::injector diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index d68794773d..123fdc7ba2 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -27,12 +27,12 @@ #include "primitives/common.hpp" #include "storage/predefined_keys.hpp" #include "storage/rocksdb/rocksdb.hpp" +#include "storage/trie/impl/direct_storage.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_impl.hpp" #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie/trie_batches.hpp" #include "storage/trie/trie_storage.hpp" #include "storage/trie/trie_storage_backend.hpp" -#include "storage/trie/update_direct_storage.hpp" #include "storage/trie_pruner/trie_pruner.hpp" #include "utils/pool_handler_ready_make.hpp" #include "utils/sptr.hpp" @@ -117,7 +117,7 @@ namespace kagome::network { std::shared_ptr grandpa_environment, common::MainThreadPool &main_thread_pool, std::shared_ptr block_storage, - std::shared_ptr db) + std::shared_ptr direct_storage) : log_(log::createLogger("Synchronizer", "synchronizer")), block_tree_(std::move(block_tree)), block_appender_(std::move(block_appender)), @@ -138,9 +138,7 @@ namespace kagome::network { block_storage_{std::move(block_storage)}, max_parallel_downloads_{app_config.maxParallelDownloads()}, random_gen_{std::random_device{}()}, - trie_direct_storage_{ - reinterpret_cast(*db).getRocksSpace( - storage::Space::kTrieDirectKV)} { + trie_direct_storage_{direct_storage} { BOOST_ASSERT(block_tree_); BOOST_ASSERT(block_executor_); BOOST_ASSERT(trie_node_db_); @@ -993,17 +991,15 @@ namespace kagome::network { it != state_sync_flow_->nodes().end()) { auto enc = it->second; return codec.decodeNode(enc); - } else { - if (trie_node_db_->contains(node.db_key.asBuffer())) { - throw std::runtime_error{ - std::format("Node DB has {}, in memory db doesn't", - node.db_key.asBuffer().toHex())}; - } else { - throw std::runtime_error{std::format( - "Node DB doesn't {}, neither does in memory db", - node.db_key.asBuffer().toHex())}; - } } + if (trie_node_db_->contains(node.db_key.asBuffer())) { + throw std::runtime_error{ + std::format("Node DB has {}, in memory db doesn't", + node.db_key.asBuffer().toHex())}; + } + throw std::runtime_error{ + std::format("Node DB doesn't {}, neither does in memory db", + node.db_key.asBuffer().toHex())}; } return codec.decodeNode(node.db_key.asBuffer()); }, @@ -1011,8 +1007,10 @@ namespace kagome::network { return state_sync_flow_->nodes().at(Buffer{value_hash}); }}); SL_DEBUG(log_, "New direct storage root: {}", state_sync_flow_->root()); - OUTCOME_TRY(storage::trie::updateDirectStorage( - state_sync_flow_->root(), *trie, *trie_direct_storage_, log_)); + + OUTCOME_TRY(trie_direct_storage_->resetDirectState(state_sync_flow_->root(), + *trie)); + auto block = state_sync_flow_->blockInfo(); state_sync_flow_.reset(); SL_INFO(log_, "State syncing block {} has finished.", block); diff --git a/core/network/impl/synchronizer_impl.hpp b/core/network/impl/synchronizer_impl.hpp index 0620513efe..32b8cd6515 100644 --- a/core/network/impl/synchronizer_impl.hpp +++ b/core/network/impl/synchronizer_impl.hpp @@ -71,6 +71,7 @@ namespace kagome::storage { namespace kagome::storage::trie { class TrieSerializer; class TrieStorage; + class DirectStorage; } // namespace kagome::storage::trie namespace kagome::storage::trie_pruner { @@ -136,7 +137,7 @@ namespace kagome::network { std::shared_ptr grandpa_environment, common::MainThreadPool &main_thread_pool, std::shared_ptr block_storage, - std::shared_ptr db); + std::shared_ptr direct_storage); /** @see AppStateManager::takeControl */ bool start(); @@ -280,7 +281,7 @@ namespace kagome::network { std::shared_ptr block_storage_; uint32_t max_parallel_downloads_; std::mt19937 random_gen_; - std::shared_ptr trie_direct_storage_; + std::shared_ptr trie_direct_storage_; application::SyncMethod sync_method_; diff --git a/core/parachain/pvf/kagome_pvf_worker_injector.hpp b/core/parachain/pvf/kagome_pvf_worker_injector.hpp index a2f95c5997..539593b3e9 100644 --- a/core/parachain/pvf/kagome_pvf_worker_injector.hpp +++ b/core/parachain/pvf/kagome_pvf_worker_injector.hpp @@ -71,7 +71,7 @@ namespace kagome::parachain { return nullptr; } - virtual storage::BufferStorage &DEBUG_getDirectStorage() { + virtual storage::trie::DirectStorage &DEBUG_getDirectStorage() { std::abort(); } }; diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt index 2c69d8bb5b..1a579593af 100644 --- a/core/storage/CMakeLists.txt +++ b/core/storage/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(storage trie/impl/trie_storage_backend_impl.cpp trie/impl/persistent_trie_batch_impl.cpp trie/impl/topper_trie_batch_impl.cpp + trie/impl/direct_storage.cpp trie/polkadot_trie/trie_node.cpp trie/polkadot_trie/polkadot_trie_impl.cpp trie/polkadot_trie/polkadot_trie_factory_impl.cpp diff --git a/core/storage/face/generic_maps.hpp b/core/storage/face/generic_maps.hpp index 8787d141b0..96f10ce60a 100644 --- a/core/storage/face/generic_maps.hpp +++ b/core/storage/face/generic_maps.hpp @@ -22,6 +22,9 @@ namespace kagome::storage::face { Iterable, Writeable, BatchWriteable { + virtual outcome::result clear() = 0; + virtual outcome::result removePrefix(const View &prefix) = 0; + /** * Reports RAM state size * @return size in bytes diff --git a/core/storage/face/map_cursor.hpp b/core/storage/face/map_cursor.hpp index 69d0b7c15c..79197e0f1b 100644 --- a/core/storage/face/map_cursor.hpp +++ b/core/storage/face/map_cursor.hpp @@ -29,6 +29,13 @@ namespace kagome::storage::face { */ virtual outcome::result seekFirst() = 0; + /** + * @brief Find a key equal or greater than given key and seek iterator to + * this key. + * @return error if any, true if \arg key found, false otherwise + */ + virtual outcome::result seekLowerBound(const View &key) = 0; + /** * @brief Find given key and seek iterator to this key. * @return error if any, true if \arg key found, false otherwise diff --git a/core/storage/face/readable.hpp b/core/storage/face/readable.hpp index 843e15c0fb..3b7e2bfc41 100644 --- a/core/storage/face/readable.hpp +++ b/core/storage/face/readable.hpp @@ -6,6 +6,8 @@ #pragma once +#include + #include #include "storage/face/owned_or_view.hpp" diff --git a/core/storage/in_memory/cursor.hpp b/core/storage/in_memory/cursor.hpp index 52b7b79ae9..7d347b2b14 100644 --- a/core/storage/in_memory/cursor.hpp +++ b/core/storage/in_memory/cursor.hpp @@ -15,16 +15,20 @@ namespace kagome::storage { explicit InMemoryCursor(InMemoryStorage &db) : db{db} {} outcome::result seekFirst() override { - return seek(db.storage.begin()); + return seek(db.storage_.begin()); } outcome::result seek(const BufferView &key) override { - return seek(db.storage.lower_bound(key.toHex())); + return seek(db.storage_.find(key)); + } + + outcome::result seekLowerBound(const BufferView &key) override { + return seek(db.storage_.lower_bound(key)); } outcome::result seekLast() override { - return seek(db.storage.empty() ? db.storage.end() - : std::prev(db.storage.end())); + return seek(db.storage_.empty() ? db.storage_.end() + : std::prev(db.storage_.end())); } bool isValid() const override { @@ -32,13 +36,13 @@ namespace kagome::storage { } outcome::result next() override { - seek(db.storage.upper_bound(kv->first.toHex())); + seek(db.storage_.upper_bound(kv->first)); return outcome::success(); } outcome::result prev() override { - auto it = db.storage.lower_bound(kv->first.toHex()); - seek(it == db.storage.begin() ? db.storage.end() : std::prev(it)); + auto it = db.storage_.lower_bound(kv->first); + seek(it == db.storage_.begin() ? db.storage_.end() : std::prev(it)); return outcome::success(); } @@ -57,11 +61,11 @@ namespace kagome::storage { } private: - bool seek(decltype(InMemoryStorage::storage)::iterator it) { - if (it == db.storage.end()) { + bool seek(decltype(InMemoryStorage::storage_)::iterator it) { + if (it == db.storage_.end()) { kv.reset(); } else { - kv.emplace(Buffer::fromHex(it->first).value(), it->second); + kv.emplace(it->first, it->second); } return isValid(); } diff --git a/core/storage/in_memory/in_memory_storage.cpp b/core/storage/in_memory/in_memory_storage.cpp index e1862a3a65..6a61159260 100644 --- a/core/storage/in_memory/in_memory_storage.cpp +++ b/core/storage/in_memory/in_memory_storage.cpp @@ -16,8 +16,8 @@ namespace kagome::storage { outcome::result InMemoryStorage::get( const BufferView &key) const { - if (storage.find(key.toHex()) != storage.end()) { - return BufferView{storage.at(key.toHex())}; + if (storage_.find(key) != storage_.end()) { + return BufferView{storage_.at(key)}; } return DatabaseError::NOT_FOUND; @@ -25,8 +25,8 @@ namespace kagome::storage { outcome::result> InMemoryStorage::tryGet( const common::BufferView &key) const { - if (storage.find(key.toHex()) != storage.end()) { - return BufferView{storage.at(key.toHex())}; + if (storage_.find(key) != storage_.end()) { + return BufferView{storage_.at(key)}; } return std::nullopt; @@ -34,30 +34,44 @@ namespace kagome::storage { outcome::result InMemoryStorage::put(const BufferView &key, BufferOrView &&value) { - auto it = storage.find(key.toHex()); - if (it != storage.end()) { + auto it = storage_.find(key); + if (it != storage_.end()) { size_t old_value_size = it->second.size(); BOOST_ASSERT(size_ >= old_value_size); size_ -= old_value_size; } size_ += value.size(); - storage[key.toHex()] = std::move(value).intoBuffer(); + storage_[key] = std::move(value).intoBuffer(); return outcome::success(); } outcome::result InMemoryStorage::contains(const BufferView &key) const { - return storage.find(key.toHex()) != storage.end(); + return storage_.find(key) != storage_.end(); } outcome::result InMemoryStorage::remove(const BufferView &key) { - auto it = storage.find(key.toHex()); - if (it != storage.end()) { + auto it = storage_.find(key); + if (it != storage_.end()) { size_ -= it->second.size(); - storage.erase(it); + storage_.erase(it); } return outcome::success(); } + outcome::result InMemoryStorage::removePrefix( + const common::BufferView &prefix) { + auto start = storage_.lower_bound(prefix); + auto end = storage_.upper_bound(prefix); + storage_.erase(start, end); + return outcome::success(); + } + + outcome::result InMemoryStorage::clear() { + storage_.clear(); + size_ = 0; + return outcome::success(); + } + std::unique_ptr InMemoryStorage::batch() { return std::make_unique(*this); } diff --git a/core/storage/in_memory/in_memory_storage.hpp b/core/storage/in_memory/in_memory_storage.hpp index dc777c1c59..c6169e0a76 100644 --- a/core/storage/in_memory/in_memory_storage.hpp +++ b/core/storage/in_memory/in_memory_storage.hpp @@ -36,6 +36,9 @@ namespace kagome::storage { const common::BufferView &key) const override; outcome::result remove(const common::BufferView &key) override; + outcome::result removePrefix( + const common::BufferView &prefix) override; + outcome::result clear() override; std::unique_ptr batch() override; @@ -44,7 +47,7 @@ namespace kagome::storage { std::optional byteSizeHint() const override; private: - std::map storage; + std::map> storage_; size_t size_ = 0; friend class InMemoryCursor; diff --git a/core/storage/map_prefix/prefix.cpp b/core/storage/map_prefix/prefix.cpp index 8ad19f3280..bf004ef3d5 100644 --- a/core/storage/map_prefix/prefix.cpp +++ b/core/storage/map_prefix/prefix.cpp @@ -32,7 +32,13 @@ namespace kagome::storage { } outcome::result MapPrefix::Cursor::seek(const BufferView &key) { - OUTCOME_TRY(cursor->seek(map._key(key))); + OUTCOME_TRY(cursor->seek(map.prefix_key(key))); + return isValid(); + } + + outcome::result MapPrefix::Cursor::seekLowerBound( + const BufferView &key) { + OUTCOME_TRY(cursor->seekLowerBound(map.prefix_key(key))); return isValid(); } @@ -84,11 +90,11 @@ namespace kagome::storage { outcome::result MapPrefix::Batch::put(const BufferView &key, BufferOrView &&value) { - return batch->put(map._key(key), std::move(value)); + return batch->put(map.prefix_key(key), std::move(value)); } outcome::result MapPrefix::Batch::remove(const BufferView &key) { - return batch->remove(map._key(key)); + return batch->remove(map.prefix_key(key)); } outcome::result MapPrefix::Batch::commit() { @@ -104,30 +110,35 @@ namespace kagome::storage { after_prefix{afterPrefix(this->prefix)}, map{std::move(map)} {} - Buffer MapPrefix::_key(BufferView key) const { + Buffer MapPrefix::prefix_key(BufferView key) const { return Buffer{prefix}.put(key); } outcome::result MapPrefix::get(const BufferView &key) const { - return map->get(_key(key)); + return map->get(prefix_key(key)); } outcome::result> MapPrefix::tryGet( const BufferView &key) const { - return map->tryGet(_key(key)); + return map->tryGet(prefix_key(key)); } outcome::result MapPrefix::contains(const BufferView &key) const { - return map->contains(_key(key)); + return map->contains(prefix_key(key)); } outcome::result MapPrefix::put(const BufferView &key, BufferOrView &&value) { - return map->put(_key(key), std::move(value)); + return map->put(prefix_key(key), std::move(value)); } outcome::result MapPrefix::remove(const BufferView &key) { - return map->remove(_key(key)); + return map->remove(prefix_key(key)); + } + + outcome::result MapPrefix::removePrefix(const BufferView &prefix) { + // it makes a prefixed prefix, yes + return map->remove(prefix_key(prefix)); } std::unique_ptr MapPrefix::batch() { @@ -137,4 +148,9 @@ namespace kagome::storage { std::unique_ptr MapPrefix::cursor() { return std::make_unique(*this, map->cursor()); } + + outcome::result MapPrefix::clear() { + return map->removePrefix(prefix); + } + } // namespace kagome::storage diff --git a/core/storage/map_prefix/prefix.hpp b/core/storage/map_prefix/prefix.hpp index 48d42b5b5e..5c6c60c590 100644 --- a/core/storage/map_prefix/prefix.hpp +++ b/core/storage/map_prefix/prefix.hpp @@ -11,14 +11,15 @@ namespace kagome::storage { /** * Map wrapper to use keys under prefix. - * Cursor removes key prefix and can seeks first/last. + * Cursor removes key prefix and can seek first/last. */ - struct MapPrefix : BufferStorage { + struct MapPrefix final : BufferStorage { struct Cursor : BufferStorageCursor { Cursor(MapPrefix &map, std::unique_ptr cursor); outcome::result seekFirst() override; outcome::result seek(const BufferView &key) override; + outcome::result seekLowerBound(const BufferView &key) override; outcome::result seekLast() override; bool isValid() const override; outcome::result next() override; @@ -46,7 +47,7 @@ namespace kagome::storage { }; MapPrefix(BufferView prefix, std::shared_ptr map); - Buffer _key(BufferView key) const; + Buffer prefix_key(BufferView key) const; outcome::result contains(const BufferView &key) const override; outcome::result get(const BufferView &key) const override; @@ -55,8 +56,10 @@ namespace kagome::storage { outcome::result put(const BufferView &key, BufferOrView &&value) override; outcome::result remove(const BufferView &key) override; + outcome::result removePrefix(const BufferView &prefix) override; std::unique_ptr batch() override; std::unique_ptr cursor() override; + outcome::result clear() override; Buffer prefix; std::optional after_prefix; diff --git a/core/storage/rocksdb/rocksdb.cpp b/core/storage/rocksdb/rocksdb.cpp index b6d7cbcb43..89305ebcbd 100644 --- a/core/storage/rocksdb/rocksdb.cpp +++ b/core/storage/rocksdb/rocksdb.cpp @@ -570,6 +570,31 @@ namespace kagome::storage { return status_as_error(status); } + outcome::result RocksDbSpace::removePrefix(const BufferView &prefix) { + OUTCOME_TRY(rocks, use()); + std::unique_ptr iter{ + rocks->db_->NewIterator(rocksdb::ReadOptions{}, column_)}; + + iter->Seek(make_slice(prefix)); + if (!iter->status().ok()) { + return status_as_error(iter->status()); + } + auto start = iter->key(); + Buffer next{prefix}; + next[next.size() - 1]++; + iter->Seek(make_slice(next)); + auto end = iter->key(); + if (!iter->status().ok()) { + return status_as_error(iter->status()); + } + auto status = + rocks->db_->DeleteRange(rocksdb::WriteOptions{}, column_, start, end); + if (!status.ok()) { + return status_as_error(status); + } + return outcome::success(); + } + outcome::result RocksDbSpace::clear() { auto rocks = storage_.lock(); if (!rocks) { diff --git a/core/storage/rocksdb/rocksdb.hpp b/core/storage/rocksdb/rocksdb.hpp index 186b14e49b..dc089601a9 100644 --- a/core/storage/rocksdb/rocksdb.hpp +++ b/core/storage/rocksdb/rocksdb.hpp @@ -157,8 +157,9 @@ namespace kagome::storage { BufferOrView &&value) override; outcome::result remove(const BufferView &key) override; + outcome::result removePrefix(const BufferView &prefix) override; - outcome::result clear(); + outcome::result clear() override; void compact(const Buffer &first, const Buffer &last); diff --git a/core/storage/rocksdb/rocksdb_cursor.cpp b/core/storage/rocksdb/rocksdb_cursor.cpp index c03ba6f87e..797dc996bb 100644 --- a/core/storage/rocksdb/rocksdb_cursor.cpp +++ b/core/storage/rocksdb/rocksdb_cursor.cpp @@ -15,16 +15,36 @@ namespace kagome::storage { outcome::result RocksDBCursor::seekFirst() { i_->SeekToFirst(); + if (!i_->status().ok()) { + return status_as_error(i_->status()); + } return isValid(); } outcome::result RocksDBCursor::seek(const BufferView &key) { i_->Seek(make_slice(key)); + if (!i_->status().ok()) { + return status_as_error(i_->status()); + } + if (make_slice(key) == i_->key()) { + return isValid(); + } + return DatabaseError::NOT_FOUND; + } + + outcome::result RocksDBCursor::seekLowerBound(const BufferView &key) { + i_->Seek(make_slice(key)); + if (!i_->status().ok()) { + return status_as_error(i_->status()); + } return isValid(); } outcome::result RocksDBCursor::seekLast() { i_->SeekToLast(); + if (!i_->status().ok()) { + return status_as_error(i_->status()); + } return isValid(); } @@ -34,11 +54,17 @@ namespace kagome::storage { outcome::result RocksDBCursor::next() { i_->Next(); + if (!i_->status().ok()) { + return status_as_error(i_->status()); + } return outcome::success(); } outcome::result RocksDBCursor::prev() { i_->Prev(); + if (!i_->status().ok()) { + return status_as_error(i_->status()); + } return outcome::success(); } diff --git a/core/storage/rocksdb/rocksdb_cursor.hpp b/core/storage/rocksdb/rocksdb_cursor.hpp index e6db84b89b..0d64dc0c1b 100644 --- a/core/storage/rocksdb/rocksdb_cursor.hpp +++ b/core/storage/rocksdb/rocksdb_cursor.hpp @@ -21,6 +21,8 @@ namespace kagome::storage { outcome::result seek(const BufferView &key) override; + outcome::result seekLowerBound(const BufferView &key) override; + outcome::result seekLast() override; bool isValid() const override; diff --git a/core/storage/rocksdb/rocksdb_spaces.cpp b/core/storage/rocksdb/rocksdb_spaces.cpp index b22b2450e3..f9397d7d8f 100644 --- a/core/storage/rocksdb/rocksdb_spaces.cpp +++ b/core/storage/rocksdb/rocksdb_spaces.cpp @@ -23,7 +23,8 @@ namespace kagome::storage { "beefy_justification", "avaliability_storage", "audi_peers", - "trie_direct_kv"}; + "trie_direct_kv", + "trie_diff"}; static_assert(kNames.size() == Space::kTotal - 1); std::string spaceName(Space space) { diff --git a/core/storage/spaces.hpp b/core/storage/spaces.hpp index f39680882a..6ba6c969bc 100644 --- a/core/storage/spaces.hpp +++ b/core/storage/spaces.hpp @@ -25,6 +25,7 @@ namespace kagome::storage { kAvaliabilityStorage, kAudiPeers, kTrieDirectKV, + kTrieDiff, kTotal }; diff --git a/core/storage/trie/impl/direct_storage.cpp b/core/storage/trie/impl/direct_storage.cpp new file mode 100644 index 0000000000..522ec1f8e8 --- /dev/null +++ b/core/storage/trie/impl/direct_storage.cpp @@ -0,0 +1,377 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "storage/trie/impl/direct_storage.hpp" +#include "common/monadic_utils.hpp" +#include "storage/database_error.hpp" +#include "storage/map_prefix/prefix.hpp" +#include "storage/rocksdb/rocksdb.hpp" +#include "storage/trie/polkadot_trie/polkadot_trie.hpp" + +OUTCOME_CPP_DEFINE_CATEGORY(kagome::storage::trie, DirectStorageError, e) { + switch (e) { + case DirectStorageError::DISCONNECTED_UPDATE: + return "Direct state updated to a state, that is not a descendant of the " + "current direct state"; + + case DirectStorageError::DISCONNECTED_DIFF: + return "A state diff added that is not a descendant of a registered " + "state"; + + case DirectStorageError::DISCONNECTING_DISCARD: + return "A state diff discarded that has non-discarded descendants"; + + case DirectStorageError::ORPHANED_VIEW: + return "Direct storage view references a state which diff is not present " + "in the direct storage"; + case DirectStorageError::DIFF_TO_THIS_STATE_ALREADY_STORED: + return "Diff with the same state root already stored. Attempt to store " + "another one with the same root is suspicious"; + case DirectStorageError::DISCARD_UNKNOWN_DIFF: + return "Discard requested of a diff that was not added to direct storage"; + case DirectStorageError::APPLY_UNKNOWN_DIFF: + return "Apply requested for a diff that was not added to direct storage"; + case DirectStorageError::EMPTY_DIFF: + return "Detected an empty diff. Diffs are not supposed to be empty"; + } + __builtin_unreachable(); +} + +// TODO(Harrm): probably should cook up a normal function that returns an +// optional or a result instead +#define FIND_OR_RETURN(result, key, map, error) \ + [[maybe_unused]] auto result##_it = (map).find((key)); \ + if (result##_it == (map).end()) { \ + return (error); \ + } \ + [[maybe_unused]] auto &result = (result##_it)->second; + +namespace kagome::storage::trie { + + const Buffer kLatestFinalizedStateKey = + Buffer::fromString("kagome_latest_finalized_state"); + // the distance between the last finalized and last produced block is + // typically pretty small, if we accumulate a lot of diffs it means that + // something likely is wrong + + constexpr size_t kExpectedMaxDiffNum = 16; + + DirectStorageView::DirectStorageView( + std::shared_ptr storage, RootHash state_root) + : storage_{std::move(storage)}, state_root_{state_root} { + BOOST_ASSERT(storage_ != nullptr); + } + + outcome::result DirectStorageView::get( + const BufferView &key) const { + RootHash current_state = state_root_; + for (;;) { + if (storage_->state_root_ == current_state) { + return storage_->direct_state_db_->get(key); + } + + OUTCOME_TRY(diff_present, storage_->diff_db_->contains(current_state)); + if (!diff_present) { + return DirectStorageError::ORPHANED_VIEW; + } + + OUTCOME_TRY(value_opt, storage_->getAt(current_state, key)); + if (value_opt.has_value()) { + auto &value = *value_opt; + if (std::holds_alternative(value)) { + return BufferOrView{std::move(std::get(value))}; + } + return DatabaseError::NOT_FOUND; + } + OUTCOME_TRY(parent_opt, storage_->getStateParent(current_state)); + if (!parent_opt) { + return DirectStorageError::ORPHANED_VIEW; + } + + current_state = *parent_opt; + } + } + + outcome::result> DirectStorageView::tryGet( + const BufferView &key) const { + auto res = get(key); + if (res.has_error()) { + if (res.error() == DatabaseError::NOT_FOUND) { + return std::nullopt; + } + return res.error(); + } + return std::optional{std::move(res.value())}; + } + + outcome::result DirectStorageView::contains( + const BufferView &key) const { + RootHash current_state = state_root_; + + for (;;) { + if (storage_->state_root_ == current_state) { + return storage_->direct_state_db_->contains(key); + } + OUTCOME_TRY(diff_present, storage_->diff_db_->contains(current_state)); + if (!diff_present) { + return DirectStorageError::ORPHANED_VIEW; + } + + OUTCOME_TRY(value_opt, storage_->getAt(current_state, key)); + if (value_opt.has_value()) { + auto &value = *value_opt; + return std::holds_alternative(value); + } + OUTCOME_TRY(parent_opt, storage_->getStateParent(current_state)); + if (!parent_opt) { + return DirectStorageError::ORPHANED_VIEW; + } + current_state = *parent_opt; + } + } + + outcome::result> DirectStorage::create( + std::shared_ptr direct_db, + std::shared_ptr diff_db) { + std::unique_ptr storage{new DirectStorage{}}; + storage->direct_state_db_ = direct_db; + storage->diff_db_ = diff_db; + OUTCOME_TRY(state_root, direct_db->tryGet(kLatestFinalizedStateKey)); + BOOST_OUTCOME_TRY( + storage->state_root_, + RootHash::fromSpan(common::map_optional(state_root, &BufferOrView::view) + .value_or(storage::trie::kEmptyRootHash))); + SL_VERBOSE(storage->logger_, + "Initialize direct storage at state {}", + common::map_optional( + common::map_optional(state_root, &BufferOrView::view), + &BufferView::toHex) + .value_or("")); + return storage; + } + + const RootHash &DirectStorage::getDirectStateRoot() const { + return state_root_; + } + + outcome::result DirectStorage::resetDirectState( + const RootHash &new_state_root, const PolkadotTrie &new_state) { + SL_VERBOSE(logger_, + "Start resetting direct storage to new state ", + new_state_root); + OUTCOME_TRY(diff_db_->clear()); + OUTCOME_TRY(direct_state_db_->clear()); + + auto batch = direct_state_db_->batch(); + size_t count{}; + auto checkpoint = std::chrono::steady_clock::now(); + auto cursor = new_state.trieCursor(); + OUTCOME_TRY(cursor->seekFirst()); + BOOST_ASSERT(cursor->isValid()); + while (cursor->isValid()) { + OUTCOME_TRY(batch->put(cursor->key().value(), cursor->value().value())); + OUTCOME_TRY(cursor->next()); + ++count; + auto now = std::chrono::steady_clock::now(); + if (now - checkpoint > std::chrono::seconds(1)) { + SL_DEBUG(logger_, + "Inserted {} keys into direct storage with root {}", + count, + new_state_root); + checkpoint = now; + } + } + SL_VERBOSE(logger_, + "Inserted total of {} keys into direct storage with root {}", + count, + new_state_root); + OUTCOME_TRY(batch->commit()); + OUTCOME_TRY(batch->put(kLatestFinalizedStateKey, new_state_root)); + state_root_ = new_state_root; + return outcome::success(); + } + + outcome::result DirectStorage::updateDirectState( + const RootHash &target_state) { + std::vector diffs; + RootHash current_state = target_state; + size_t idx = 0; + while (current_state != state_root_) { + OUTCOME_TRY(diff_known, diff_db_->contains(current_state)); + if (!diff_known) { + return DirectStorageError::DISCONNECTED_UPDATE; + } + + diffs.emplace_back(current_state); + OUTCOME_TRY(parent_opt, getStateParent(current_state)); + if (!parent_opt) { + return DirectStorageError::DISCONNECTED_UPDATE; + } + // TOOD(Harrm) should probably be trace + SL_DEBUG(logger_, + "#{}, Accumulating diffs for update, {} -> {}", + idx++, + *parent_opt, + current_state); + + current_state = *parent_opt; + } + + for (auto root : diffs | std::views::reverse) { + OUTCOME_TRY(applyDiff(root)); + OUTCOME_TRY(discardDiff(root)); + state_root_ = root; + } + + // TODO(Harrm): maybe cleanup now orphaned diffs, although they should be + // discarded with their blocks anyway + SL_VERBOSE(logger_, "Update direct storage to new state {}", target_state); + + return outcome::success(); + } + + outcome::result DirectStorage::storeDiff(DiffRoots roots, + StateDiff &&diff) { + if (diff.empty()) { + return DirectStorageError::EMPTY_DIFF; + } + OUTCOME_TRY(from_diff_known, diff_db_->contains(roots.from)); + if (roots.from != state_root_ && !from_diff_known) { + return DirectStorageError::DISCONNECTED_DIFF; + } + OUTCOME_TRY(to_diff_known, diff_db_->contains(roots.to)); + if (to_diff_known) { + return DirectStorageError::DIFF_TO_THIS_STATE_ALREADY_STORED; + } + + auto diff_batch = diff_db_->batch(); + OUTCOME_TRY(diff_batch->put(roots.to, Buffer{1})); + for (auto &[key, val] : diff) { + Buffer full_key; + full_key.put(roots.to); + full_key.put(key); + if (val.has_value()) { + val->putUint8(1); + OUTCOME_TRY(diff_batch->put(full_key, std::move(*val))); + + } else { + OUTCOME_TRY(diff_batch->put(full_key, Buffer{0})); + } + } + // remember parent state + OUTCOME_TRY(diff_batch->put(roots.to, roots.from)); + OUTCOME_TRY(diff_batch->commit()); + + SL_DEBUG(logger_, + "Store new diff for state transition from {} to {}", + roots.from, + roots.to); + return outcome::success(); + } + + outcome::result DirectStorage::discardDiff(const RootHash &to_state) { + OUTCOME_TRY(diff_known, diff_db_->contains(to_state)); + if (!diff_known) { + return DirectStorageError::DISCARD_UNKNOWN_DIFF; + } + OUTCOME_TRY(parent_opt, getStateParent(to_state)); + if (!parent_opt) { + return DirectStorageError::DISCARD_UNKNOWN_DIFF; + } + + OUTCOME_TRY(diff_db_->remove(to_state)); + SL_WARN(logger_, "TODO(Harrm): IMPORTANT remove all diff entries"); + + SL_DEBUG(logger_, + "Discard diff for state transition from {} to {}", + *parent_opt, + to_state); + return outcome::success(); + } + + outcome::result> DirectStorage::getViewAt( + const RootHash &state_root) const { + OUTCOME_TRY(diff_known, diff_db_->contains(state_root)); + if (state_root != state_root_ && !diff_known) { + SL_DEBUG(logger_, + "Failed to get direct storage view at state {}: no such state " + "stored in direct storage", + state_root); + return DirectStorageError::ORPHANED_VIEW; + } + SL_DEBUG(logger_, "Get direct storage view at state {}", state_root); + return std::make_unique(shared_from_this(), state_root); + } + + outcome::result< + std::optional>> + DirectStorage::getAt(const RootHash &state, common::BufferView key) const { + Buffer full_key; + full_key.put(state); + full_key.put(key); + OUTCOME_TRY(value_opt, diff_db_->tryGet(full_key)); + if (!value_opt) { + return std::nullopt; + } + auto value = value_opt->intoBuffer(); + if (value[value.size() - 1] == 1) { + value.pop_back(); + return std::variant(std::move(value)); + } + return std::variant(ValueDeleted{}); + } + + outcome::result> DirectStorage::getStateParent( + const RootHash &state) const { + OUTCOME_TRY(parent_opt, diff_db_->tryGet(state)); + if (parent_opt) { + OUTCOME_TRY(root, RootHash::fromSpan(parent_opt->view())); + return root; + } + return std::nullopt; + } + + outcome::result DirectStorage::applyDiff(const RootHash &new_root) { + SL_DEBUG(logger_, "Start applying diff to state {}", new_root); + + OUTCOME_TRY(diff_known, diff_db_->contains(new_root)); + if (!diff_known) { + SL_DEBUG( + logger_, "Apply failed, diff for state {} was not stored", new_root); + return DirectStorageError::APPLY_UNKNOWN_DIFF; + } + + MapPrefix diff_prefix{new_root, direct_state_db_}; + + auto batch = direct_state_db_->batch(); + size_t num = 0; + auto iter = diff_prefix.cursor(); + OUTCOME_TRY(iter->seekFirst()); + if (!iter->isValid()) { + return DirectStorageError::EMPTY_DIFF; + } + while (iter->isValid()) { + auto key = *iter->key(); + auto value = *iter->value(); + if (value.view() == std::array{0}) { + OUTCOME_TRY(batch->remove(key)); + } else { + auto value_buf = std::move(value).intoBuffer(); + value_buf.pop_back(); + OUTCOME_TRY(batch->put(key, std::move(value_buf))); + } + ++num; + } + + OUTCOME_TRY(batch->put(kLatestFinalizedStateKey, new_root)); + + OUTCOME_TRY(batch->commit()); + SL_DEBUG(logger_, "Applied diff to state {} with {} writes", new_root, num); + + return outcome::success(); + } + +} // namespace kagome::storage::trie diff --git a/core/storage/trie/impl/direct_storage.hpp b/core/storage/trie/impl/direct_storage.hpp new file mode 100644 index 0000000000..77099a32a6 --- /dev/null +++ b/core/storage/trie/impl/direct_storage.hpp @@ -0,0 +1,106 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "common/buffer.hpp" +#include "log/logger.hpp" +#include "storage/buffer_map_types.hpp" +#include "storage/trie/types.hpp" + +namespace kagome::storage { + class RocksDbSpace; +} + +namespace kagome::storage::trie { + + class PolkadotTrie; + + enum class DirectStorageError : uint8_t { + DISCONNECTED_UPDATE = 1, + DISCONNECTED_DIFF, + DISCONNECTING_DISCARD, + DISCARD_UNKNOWN_DIFF, + ORPHANED_VIEW, + DIFF_TO_THIS_STATE_ALREADY_STORED, + APPLY_UNKNOWN_DIFF, + EMPTY_DIFF, + }; + + using StateDiff = std::unordered_map>; + + class DirectStorage; + + class DirectStorageView final : public face::Readable { + public: + DirectStorageView(std::shared_ptr storage, + RootHash state_root); + + outcome::result get(const BufferView &key) const override; + outcome::result> tryGet( + const BufferView &key) const override; + + outcome::result contains(const BufferView &key) const override; + + const RootHash &getStateRoot() const { + return state_root_; + } + + private: + outcome::result>> + findLatestSource(BufferView key); + + std::shared_ptr storage_; + RootHash state_root_; + }; + + // TODO(Harrm) store large values separately? + class DirectStorage final + : public std::enable_shared_from_this { + public: + friend class DirectStorageView; + + static outcome::result> create( + std::shared_ptr direct_db, + std::shared_ptr diff_db); + + const RootHash &getDirectStateRoot() const; + + outcome::result resetDirectState(const RootHash &new_state_root, + const PolkadotTrie &new_state); + + outcome::result updateDirectState(const RootHash &target_state); + + struct DiffRoots { + const RootHash &from; + const RootHash &to; + }; + outcome::result storeDiff(DiffRoots roots, StateDiff &&diff); + outcome::result discardDiff(const RootHash &to_state); + + outcome::result> getViewAt( + const RootHash &state_root) const; + + private: + struct ValueDeleted {}; + outcome::result>> + getAt(const RootHash &state, common::BufferView key) const; + outcome::result> getStateParent( + const RootHash &state) const; + + outcome::result applyDiff(const RootHash &new_root); + + RootHash state_root_; + std::shared_ptr direct_state_db_; + std::shared_ptr diff_db_; + log::Logger logger_ = log::createLogger("DirectStorage", "storage"); + }; + +} // namespace kagome::storage::trie + +OUTCOME_HPP_DECLARE_ERROR(kagome::storage::trie, DirectStorageError) diff --git a/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp b/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp index bc7e4ba131..7e23ad0aa5 100644 --- a/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp +++ b/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp @@ -25,13 +25,13 @@ namespace kagome::storage::trie { std::shared_ptr trie, std::shared_ptr serializer, TrieSerializer::OnNodeLoaded on_child_node_loaded, - std::shared_ptr direct_kv_storage, - Fresh) + std::shared_ptr direct_kv_storage, + std::shared_ptr direct_storage_view) : TrieBatchBase{std::move(codec), std::move(serializer), std::move(trie), direct_kv_storage, - Fresh{}}, + direct_storage_view}, on_child_node_loaded_{std::move(on_child_node_loaded)} { // on_child_node_loaded_ can be zero } diff --git a/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp b/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp index b4a802337f..0a2de2dd30 100644 --- a/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp +++ b/core/storage/trie/impl/ephemeral_trie_batch_impl.hpp @@ -22,12 +22,13 @@ namespace kagome::storage::trie { std::shared_ptr serializer, TrieSerializer::OnNodeLoaded on_child_node_loaded); - EphemeralTrieBatchImpl(std::shared_ptr codec, - std::shared_ptr trie, - std::shared_ptr serializer, - TrieSerializer::OnNodeLoaded on_child_node_loaded, - std::shared_ptr direct_kv_storage, - Fresh); + EphemeralTrieBatchImpl( + std::shared_ptr codec, + std::shared_ptr trie, + std::shared_ptr serializer, + TrieSerializer::OnNodeLoaded on_child_node_loaded, + std::shared_ptr direct_kv_storage, + std::shared_ptr direct_storage_view); ~EphemeralTrieBatchImpl() override = default; diff --git a/core/storage/trie/impl/persistent_trie_batch_impl.cpp b/core/storage/trie/impl/persistent_trie_batch_impl.cpp index a66e6aed97..de859e018a 100644 --- a/core/storage/trie/impl/persistent_trie_batch_impl.cpp +++ b/core/storage/trie/impl/persistent_trie_batch_impl.cpp @@ -16,17 +16,6 @@ #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie_pruner/trie_pruner.hpp" -OUTCOME_CPP_DEFINE_CATEGORY(kagome::storage::trie, - PersistentTrieBatchImpl::Error, - e) { - using E = kagome::storage::trie::PersistentTrieBatchImpl::Error; - switch (e) { - case E::NO_TRIE: - return "Trie was not created or already was destructed."; - } - return "Unknown error"; -} - namespace kagome::storage::trie { PersistentTrieBatchImpl::PersistentTrieBatchImpl( @@ -49,13 +38,13 @@ namespace kagome::storage::trie { TrieChangesTrackerOpt changes, std::shared_ptr trie, std::shared_ptr state_pruner, - std::shared_ptr direct_kv_storage, - Fresh) + std::shared_ptr direct_kv_storage, + std::shared_ptr direct_storage_view) : TrieBatchBase{std::move(codec), std::move(serializer), std::move(trie), direct_kv_storage, - Fresh{}}, + direct_storage_view}, changes_{std::move(changes)}, state_pruner_{std::move(state_pruner)} { BOOST_ASSERT((changes_.has_value() && changes_.value() != nullptr) @@ -74,18 +63,11 @@ namespace kagome::storage::trie { // batch must not be committed before pruner addNewState or pruner breaks // probably should enforce this more strictly in the API OUTCOME_TRY(batch->commit()); - if (direct_kv_) { - auto batch = direct_kv_->storage->batch(); - OUTCOME_TRY(batch->put(kLastCommittedHashKey, root)); - for (const auto &[key, value] : direct_kv_->batch) { - if (value) { - OUTCOME_TRY(batch->put(key, value->view())); - } else { - OUTCOME_TRY(batch->remove(key)); - } - } - OUTCOME_TRY(batch->commit()); - SL_DEBUG(logger_, "Update latest state: {}", root); + if (direct_) { + OUTCOME_TRY(direct_->storage->storeDiff( + {.from = direct_->view->getStateRoot(), .to = root}, + std::exchange(direct_->diff, StateDiff{}))); + SL_DEBUG(logger_, "New diff for state: {}", root); } SL_TRACE_FUNC_CALL(logger_, root); return root; @@ -103,8 +85,8 @@ namespace kagome::storage::trie { if (changes_.has_value()) { changes_.value()->onRemove(key); } - if (direct_kv_) { - direct_kv_->batch[key] = + if (direct_) { + direct_->diff[key] = std::forward>(value); } return outcome::success(); @@ -113,17 +95,16 @@ namespace kagome::storage::trie { outcome::result PersistentTrieBatchImpl::put(const BufferView &key, BufferOrView &&value) { - OUTCOME_TRY(contains, trie_->contains(key)); - bool is_new_entry = not contains; + OUTCOME_TRY(is_key_already_contained, trie_->contains(key)); auto value_copy = value.mut(); auto res = trie_->put(key, std::move(value)); if (res and changes_.has_value()) { SL_TRACE_VOID_FUNC_CALL(logger_, key, value_copy); - changes_.value()->onPut(key, value_copy, is_new_entry); + changes_.value()->onPut(key, value_copy, !is_key_already_contained); } - if (res and direct_kv_) { - direct_kv_->batch[key] = value_copy; + if (res and direct_) { + direct_->diff[key] = value_copy; } return res; } @@ -134,8 +115,8 @@ namespace kagome::storage::trie { SL_TRACE_VOID_FUNC_CALL(logger_, key); changes_.value()->onRemove(key); } - if (direct_kv_) { - direct_kv_->batch[key].reset(); + if (direct_) { + direct_->diff[key].reset(); } return outcome::success(); } diff --git a/core/storage/trie/impl/persistent_trie_batch_impl.hpp b/core/storage/trie/impl/persistent_trie_batch_impl.hpp index 43ad3d8cab..ef56e92a58 100644 --- a/core/storage/trie/impl/persistent_trie_batch_impl.hpp +++ b/core/storage/trie/impl/persistent_trie_batch_impl.hpp @@ -25,10 +25,6 @@ namespace kagome::storage::trie { class PersistentTrieBatchImpl final : public TrieBatchBase { public: - enum class Error : uint8_t { - NO_TRIE = 1, - }; - PersistentTrieBatchImpl( std::shared_ptr codec, std::shared_ptr serializer, @@ -42,8 +38,8 @@ namespace kagome::storage::trie { TrieChangesTrackerOpt changes, std::shared_ptr trie, std::shared_ptr state_pruner, - std::shared_ptr direct_kv_storage, - Fresh); + std::shared_ptr direct_kv_storage, + std::shared_ptr direct_storage_view); ~PersistentTrieBatchImpl() override = default; @@ -66,6 +62,3 @@ namespace kagome::storage::trie { }; } // namespace kagome::storage::trie - -OUTCOME_HPP_DECLARE_ERROR(kagome::storage::trie, - PersistentTrieBatchImpl::Error); diff --git a/core/storage/trie/impl/topper_trie_batch_impl.cpp b/core/storage/trie/impl/topper_trie_batch_impl.cpp index 1225ccb65f..02e56b0b8d 100644 --- a/core/storage/trie/impl/topper_trie_batch_impl.cpp +++ b/core/storage/trie/impl/topper_trie_batch_impl.cpp @@ -183,7 +183,7 @@ namespace kagome::storage::trie { : parent_cursor_->value(); } - outcome::result TopperTrieCursor::seekLowerBound( + outcome::result TopperTrieCursor::seekLowerBound( const BufferView &key) { OUTCOME_TRY(parent_cursor_->seekLowerBound(key)); cached_parent_key_ = parent_cursor_->key(); diff --git a/core/storage/trie/impl/topper_trie_batch_impl.hpp b/core/storage/trie/impl/topper_trie_batch_impl.hpp index 4ee054a50a..ce378a29af 100644 --- a/core/storage/trie/impl/topper_trie_batch_impl.hpp +++ b/core/storage/trie/impl/topper_trie_batch_impl.hpp @@ -76,7 +76,7 @@ namespace kagome::storage::trie { std::optional key() const override; std::optional value() const override; - outcome::result seekLowerBound(const BufferView &key) override; + outcome::result seekLowerBound(const BufferView &key) override; outcome::result seekUpperBound(const BufferView &key) override; private: diff --git a/core/storage/trie/impl/trie_batch_base.cpp b/core/storage/trie/impl/trie_batch_base.cpp index 7e1674268d..6a6a71c496 100644 --- a/core/storage/trie/impl/trie_batch_base.cpp +++ b/core/storage/trie/impl/trie_batch_base.cpp @@ -23,42 +23,41 @@ namespace kagome::storage::trie { BOOST_ASSERT(trie_ != nullptr); } - TrieBatchBase::TrieBatchBase(std::shared_ptr codec, - std::shared_ptr serializer, - std::shared_ptr trie, - std::shared_ptr direct_kv_storage, - Fresh) + TrieBatchBase::TrieBatchBase( + std::shared_ptr codec, + std::shared_ptr serializer, + std::shared_ptr trie, + std::shared_ptr direct_kv_storage, + std::shared_ptr direct_storage_view) : TrieBatchBase(codec, serializer, trie) { BOOST_ASSERT(direct_kv_storage != nullptr); - direct_kv_ = {.storage = direct_kv_storage}; + direct_.emplace(direct_kv_storage, direct_storage_view, StateDiff{}); } outcome::result TrieBatchBase::get( const BufferView &key) const { - if (direct_kv_) { - if (auto it = direct_kv_->batch.find(key); - it != direct_kv_->batch.end()) { + if (direct_) { + if (auto it = direct_->diff.find(key); it != direct_->diff.end()) { if (it->second) { return BufferOrView{it->second->view()}; } return DatabaseError::NOT_FOUND; } - return direct_kv_->storage->get(key); + return direct_->view->get(key); } return trie_->get(key); } outcome::result> TrieBatchBase::tryGet( const BufferView &key) const { - if (direct_kv_) { - if (auto it = direct_kv_->batch.find(key); - it != direct_kv_->batch.end()) { + if (direct_) { + if (auto it = direct_->diff.find(key); it != direct_->diff.end()) { if (it->second) { return BufferOrView{it->second->view()}; } return std::nullopt; } - return direct_kv_->storage->tryGet(key); + return direct_->view->tryGet(key); } return trie_->tryGet(key); } @@ -68,15 +67,14 @@ namespace kagome::storage::trie { } outcome::result TrieBatchBase::contains(const BufferView &key) const { - if (direct_kv_) { - if (auto it = direct_kv_->batch.find(key); - it != direct_kv_->batch.end()) { + if (direct_) { + if (auto it = direct_->diff.find(key); it != direct_->diff.end()) { if (it->second) { return true; } return false; } - return direct_kv_->storage->contains(key); + return direct_->view->contains(key); } return trie_->contains(key); } diff --git a/core/storage/trie/impl/trie_batch_base.hpp b/core/storage/trie/impl/trie_batch_base.hpp index 7746431631..c6d0e2a0f5 100644 --- a/core/storage/trie/impl/trie_batch_base.hpp +++ b/core/storage/trie/impl/trie_batch_base.hpp @@ -12,6 +12,7 @@ #include #include "log/logger.hpp" +#include "storage/trie/impl/direct_storage.hpp" #include "storage/trie/serialization/trie_serializer.hpp" namespace kagome::storage::trie { @@ -24,12 +25,11 @@ namespace kagome::storage::trie { std::shared_ptr serializer, std::shared_ptr trie); - struct Fresh {}; TrieBatchBase(std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr trie, - std::shared_ptr direct_kv_storage, - Fresh); + std::shared_ptr direct_kv_storage, + std::shared_ptr direct_storage_view); TrieBatchBase(const TrieBatchBase &) = delete; TrieBatchBase(TrieBatchBase &&) noexcept = default; @@ -59,11 +59,16 @@ namespace kagome::storage::trie { std::shared_ptr codec_; std::shared_ptr serializer_; std::shared_ptr trie_; - struct DirectStorage { - std::shared_ptr storage; - std::unordered_map> batch; + + struct DirectStorageStuff { + // direct storage manages overall logic + std::shared_ptr storage; + // view belongs to a particular state + std::shared_ptr view; + // diff accumulates current changes + StateDiff diff; }; - std::optional direct_kv_; + std::optional direct_; private: std::unordered_map> diff --git a/core/storage/trie/impl/trie_storage_backend_impl.cpp b/core/storage/trie/impl/trie_storage_backend_impl.cpp index c8b60e5e73..3e155db20c 100644 --- a/core/storage/trie/impl/trie_storage_backend_impl.cpp +++ b/core/storage/trie/impl/trie_storage_backend_impl.cpp @@ -51,4 +51,14 @@ namespace kagome::storage::trie { outcome::result TrieStorageBackendImpl::remove(const BufferView &key) { return storage_->remove(key); } + + outcome::result TrieStorageBackendImpl::removePrefix( + const common::BufferView &prefix) { + return storage_->removePrefix(prefix); + } + + outcome::result TrieStorageBackendImpl::clear() { + return storage_->clear(); + } + } // namespace kagome::storage::trie diff --git a/core/storage/trie/impl/trie_storage_backend_impl.hpp b/core/storage/trie/impl/trie_storage_backend_impl.hpp index 95786ad952..034a7f3732 100644 --- a/core/storage/trie/impl/trie_storage_backend_impl.hpp +++ b/core/storage/trie/impl/trie_storage_backend_impl.hpp @@ -30,6 +30,9 @@ namespace kagome::storage::trie { outcome::result put(const BufferView &key, BufferOrView &&value) override; outcome::result remove(const common::BufferView &key) override; + outcome::result removePrefix( + const common::BufferView &prefix) override; + outcome::result clear() override; private: std::shared_ptr storage_; diff --git a/core/storage/trie/impl/trie_storage_impl.cpp b/core/storage/trie/impl/trie_storage_impl.cpp index f6513f618b..7104fa08a4 100644 --- a/core/storage/trie/impl/trie_storage_impl.cpp +++ b/core/storage/trie/impl/trie_storage_impl.cpp @@ -20,7 +20,7 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv) { + std::shared_ptr direct_kv) { // will never be used, so content of the callback doesn't matter auto empty_trie = trie_factory->createEmpty(); // ensure retrieval of empty trie succeeds @@ -37,7 +37,7 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv) { + std::shared_ptr direct_kv) { return std::unique_ptr( new TrieStorageImpl(std::move(codec), std::move(serializer), @@ -49,7 +49,7 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv) + std::shared_ptr direct_kv) : codec_{std::move(codec)}, serializer_{std::move(serializer)}, state_pruner_{std::move(state_pruner)}, @@ -65,53 +65,31 @@ namespace kagome::storage::trie { TrieStorageImpl::getPersistentBatchAt(const RootHash &root, TrieChangesTrackerOpt changes_tracker) { OUTCOME_TRY(trie, serializer_->retrieveTrie(root, nullptr)); - OUTCOME_TRY(last_hash, getLastCommittedHash()); - if (root == last_hash) { - SL_DEBUG(logger_, - "Initialize persistent trie batch with root: {} at latest state " - "(direct storage is enabled)", - root.toHex()); - return std::make_unique(codec_, - serializer_, - changes_tracker, - std::move(trie), - state_pruner_, - direct_kv_, - TrieBatchBase::Fresh{}); - } SL_DEBUG(logger_, - "Initialize persistent trie batch with root: {} at an old state " - "(direct storage is disabled, the latest state is {})", - root, - last_hash); - return std::make_unique( - codec_, serializer_, changes_tracker, std::move(trie), state_pruner_); + "Initialize persistent trie batch with root: {}", + root.toHex()); + OUTCOME_TRY(direct_view, direct_kv_->getViewAt(root)); + return std::make_unique(codec_, + serializer_, + changes_tracker, + std::move(trie), + state_pruner_, + direct_kv_, + std::move(direct_view)); } outcome::result> TrieStorageImpl::getEphemeralBatchAt(const RootHash &root) const { SL_DEBUG(logger_, "Initialize ephemeral trie batch with root: {}", root); OUTCOME_TRY(trie, serializer_->retrieveTrie(root, nullptr)); - OUTCOME_TRY(last_hash, getLastCommittedHash()); - if (root == last_hash) { - SL_DEBUG(logger_, - "Initialize ephemeral trie batch with root: {} at latest state " - "(direct storage is enabled)", - root); - return std::make_unique(codec_, - std::move(trie), - serializer_, - nullptr, - direct_kv_, - TrieBatchBase::Fresh{}); - } - SL_DEBUG(logger_, - "Initialize ephemeral trie batch with root: {} at an old state " - "(direct storage is disabled, the latest state is {})", - root, - last_hash); - return std::make_unique( - codec_, std::move(trie), serializer_, nullptr); + OUTCOME_TRY(direct_view, direct_kv_->getViewAt(root)); + + return std::make_unique(codec_, + std::move(trie), + serializer_, + nullptr, + direct_kv_, + std::move(direct_view)); } outcome::result> @@ -124,12 +102,4 @@ namespace kagome::storage::trie { codec_, std::move(trie), serializer_, on_node_loaded); } - outcome::result TrieStorageImpl::getLastCommittedHash() const { - OUTCOME_TRY(last_hash, direct_kv_->tryGet(kLastCommittedHashKey)); - if (!last_hash) { - return kEmptyRootHash; - } - return RootHash::fromSpan(*last_hash); - } - } // namespace kagome::storage::trie diff --git a/core/storage/trie/impl/trie_storage_impl.hpp b/core/storage/trie/impl/trie_storage_impl.hpp index 5754baa354..3fbc27968c 100644 --- a/core/storage/trie/impl/trie_storage_impl.hpp +++ b/core/storage/trie/impl/trie_storage_impl.hpp @@ -21,6 +21,8 @@ namespace kagome::storage::trie_pruner { namespace kagome::storage::trie { + class DirectStorage; + class TrieStorageImpl : public TrieStorage { public: static outcome::result> createEmpty( @@ -28,13 +30,13 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv); + std::shared_ptr direct_kv); static outcome::result> createFromStorage( std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv); + std::shared_ptr direct_kv); TrieStorageImpl(const TrieStorageImpl &) = delete; void operator=(const TrieStorageImpl &) = delete; @@ -51,7 +53,7 @@ namespace kagome::storage::trie { const RootHash &root, const OnNodeLoaded &on_node_loaded) const override; - BufferStorage &DEBUG_getDirectStorage() override { + DirectStorage &DEBUG_getDirectStorage() override { return *direct_kv_; } @@ -60,15 +62,13 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv); + std::shared_ptr direct_kv); private: - outcome::result getLastCommittedHash() const; - std::shared_ptr codec_; std::shared_ptr serializer_; std::shared_ptr state_pruner_; - std::shared_ptr direct_kv_; + std::shared_ptr direct_kv_; log::Logger logger_; }; diff --git a/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp b/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp index 6f7674caf8..4f46f4eb37 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp @@ -19,7 +19,7 @@ namespace kagome::storage::trie { * Seek the first element with key not less than \arg key * @return true if the trie is not empty */ - virtual outcome::result seekLowerBound( + virtual outcome::result seekLowerBound( const common::BufferView &key) = 0; /** diff --git a/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.cpp b/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.cpp index b94415d299..b52c65fa19 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.cpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.cpp @@ -98,7 +98,7 @@ namespace kagome::storage::trie { return true; } - outcome::result PolkadotTrieCursorImpl::seekLowerBoundInternal( + outcome::result PolkadotTrieCursorImpl::seekLowerBoundInternal( const TrieNode ¤t, BufferView sought_nibbles) { BOOST_ASSERT(isValid()); auto [sought_nibbles_mismatch, current_mismatch] = @@ -148,7 +148,8 @@ namespace kagome::storage::trie { "We're in a branch and proceed to child {:x}", (int)child_idx); if (child_idx > sought_nibbles[mismatch_pos]) { - return nextNodeWithValueInSubTree(*child); + OUTCOME_TRY(nextNodeWithValueInSubTree(*child)); + return true; } return seekLowerBoundInternal( *child, sought_nibbles.subspan(mismatch_pos + 1)); @@ -164,16 +165,17 @@ namespace kagome::storage::trie { if (longer_or_greater) { SL_TRACE(log_, "We're looking for next node with value in outer tree"); SAFE_CALL(found, nextNodeWithValueInOuterTree()) + SL_TRACE(log_, "Done at {}", key().value()); if (!found) { state_ = ReachedEndState{}; + return false; } - SL_TRACE(log_, "Done at {}", key().value()); - return outcome::success(); + return true; } UNREACHABLE } - outcome::result PolkadotTrieCursorImpl::seekLowerBound( + outcome::result PolkadotTrieCursorImpl::seekLowerBound( const common::BufferView &key) { if (trie_->getRoot() == nullptr) { SL_TRACE(log_, "Seek lower bound for {} -> null root", key); @@ -182,8 +184,8 @@ namespace kagome::storage::trie { } state_ = SearchState{*trie_->getRoot()}; auto nibbles = KeyNibbles::fromByteBuffer(key); - SAFE_VOID_CALL(seekLowerBoundInternal(*trie_->getRoot(), nibbles)) - return outcome::success(); + SAFE_CALL(found, seekLowerBoundInternal(*trie_->getRoot(), nibbles)) + return found; } outcome::result PolkadotTrieCursorImpl::nextNodeWithValueInOuterTree() { diff --git a/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.hpp b/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.hpp index 458568da5f..7c3b91edef 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.hpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie_cursor_impl.hpp @@ -47,7 +47,7 @@ namespace kagome::storage::trie { /** * Seek the first element with key not less than \arg key */ - [[nodiscard]] outcome::result seekLowerBound( + [[nodiscard]] outcome::result seekLowerBound( const common::BufferView &key) override; /** @@ -67,7 +67,7 @@ namespace kagome::storage::trie { [[nodiscard]] std::optional value() const override; private: - outcome::result seekLowerBoundInternal(const TrieNode ¤t, + outcome::result seekLowerBoundInternal(const TrieNode ¤t, BufferView left_nibbles); outcome::result nextNodeWithValueInOuterTree(); outcome::result nextNodeWithValueInSubTree( diff --git a/core/storage/trie/trie_storage.hpp b/core/storage/trie/trie_storage.hpp index 975754a053..89f90f467c 100644 --- a/core/storage/trie/trie_storage.hpp +++ b/core/storage/trie/trie_storage.hpp @@ -13,6 +13,8 @@ namespace kagome::storage::trie { + class DirectStorage; + /** * Grants access to the storage in two ways: * - persistent batch that will @@ -42,7 +44,7 @@ namespace kagome::storage::trie { virtual outcome::result> getProofReaderBatchAt( const RootHash &root, const OnNodeLoaded &on_node_loaded) const = 0; - virtual BufferStorage &DEBUG_getDirectStorage() = 0; + virtual DirectStorage &DEBUG_getDirectStorage() = 0; }; } // namespace kagome::storage::trie diff --git a/core/storage/trie/types.hpp b/core/storage/trie/types.hpp index bad76c15cb..8140dca96d 100644 --- a/core/storage/trie/types.hpp +++ b/core/storage/trie/types.hpp @@ -24,7 +24,4 @@ namespace kagome::storage::trie { constexpr uint8_t kEscapeCompactHeader = 1; - const Buffer kLastCommittedHashKey = - Buffer::fromString("kagome_last_committed_hash"); - } // namespace kagome::storage::trie diff --git a/core/storage/trie/update_direct_storage.hpp b/core/storage/trie/update_direct_storage.hpp deleted file mode 100644 index 6299b19a4b..0000000000 --- a/core/storage/trie/update_direct_storage.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "log/logger.hpp" -#include "outcome/outcome.hpp" -#include "storage/rocksdb/rocksdb.hpp" -#include "storage/trie/polkadot_trie/polkadot_trie.hpp" - -namespace kagome::storage::trie { - - inline outcome::result updateDirectStorage(const trie::RootHash &root, - trie::PolkadotTrie &trie, - RocksDbSpace &direct_storage, - log::Logger &log) { - OUTCOME_TRY(direct_storage.clear()); - auto batch = direct_storage.batch(); - size_t count{}; - auto checkpoint = std::chrono::steady_clock::now(); - auto cursor = trie.trieCursor(); - OUTCOME_TRY(cursor->seekFirst()); - BOOST_ASSERT(cursor->isValid()); - while (cursor->isValid()) { - OUTCOME_TRY(batch->put(cursor->key().value(), cursor->value().value())); - OUTCOME_TRY(cursor->next()); - ++count; - auto now = std::chrono::steady_clock::now(); - if (now - checkpoint > std::chrono::seconds(1)) { - log->debug( - "Inserted {} keys into direct storage with root {}", count, root); - checkpoint = now; - } - } - log->debug("Inserted total of {} keys into direct storage with root {}", - count, - root); - OUTCOME_TRY(batch->put(storage::trie::kLastCommittedHashKey, root)); - OUTCOME_TRY(batch->commit()); - return outcome::success(); - } -} // namespace kagome::storage::trie diff --git a/core/utils/kagome_db_editor.cpp b/core/utils/kagome_db_editor.cpp index a474ce5eb0..6f360dfe83 100644 --- a/core/utils/kagome_db_editor.cpp +++ b/core/utils/kagome_db_editor.cpp @@ -5,6 +5,7 @@ */ #include "injector/idle_trie_pruner.hpp" +#include "storage/trie/impl/direct_storage.hpp" #include "storage/trie/trie_batches.hpp" #include "storage/trie/trie_storage_backend.hpp" #if defined(BACKWARD_HAS_BACKTRACE) @@ -83,13 +84,24 @@ namespace kagome { BufferOrView &&value) override { abort(); } + outcome::result remove(const common::BufferView &key) override { abort(); } + outcome::result removePrefix( + const common::BufferView &prefix) override { + abort(); + } + + outcome::result clear() override { + abort(); + } + void track(BufferView key) const { keys.emplace(common::Hash256::fromSpan(key).value()); } + bool tracked(BufferView key) const { return keys.contains(common::Hash256::fromSpan(key).value()); } @@ -269,6 +281,11 @@ Kagome DB Editor - a storage pruner. Allows to reduce occupied disk space. auto trie_node_tracker = std::make_shared( std::make_shared(storage)); + std::shared_ptr direct_storage = + storage::trie::DirectStorage::create( + storage->getRocksSpace(storage::Space::kTrieDirectKV), + storage->getRocksSpace(storage::Space::kTrieDiff)) + .value(); auto injector = di::make_injector( di::bind.to([](const auto &injector) { @@ -278,6 +295,7 @@ Kagome DB Editor - a storage pruner. Allows to reduce occupied disk space. injector.template create>()); }), di::bind.to(trie_node_tracker), + di::bind.to(direct_storage), di::bind.to( std::make_shared()), di::bind.to([](const auto &injector) { @@ -389,7 +407,7 @@ Kagome DB Editor - a storage pruner. Allows to reduce occupied disk space. injector.template create>(), injector .template create>(), - storage->getSpace(storage::Space::kTrieDirectKV)) + injector.template create>()) .value(); if (COMPACT == cmd) { diff --git a/scripts/apply_clang_format.sh b/scripts/apply_clang_format.sh new file mode 100755 index 0000000000..528634479d --- /dev/null +++ b/scripts/apply_clang_format.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Copyright Quadrivium LLC +# All Rights Reserved +# SPDX-License-Identifier: Apache-2.0 +# + +# Runs clang-format (preferrably version 16) on +# files that are modified according to git + +if git rev-parse --verify HEAD >/dev/null 2>&1; then + BASE=HEAD +else + # Initial commit: diff BASE an empty tree object + BASE=$(git hash-object -t tree /dev/null) +fi + +# check clang-format binary +CLANG_FORMAT=$(which clang-format-16 2>/dev/null) +if [ -z "${CLANG_FORMAT}" ]; then + CLANG_FORMAT=$(which clang-format) + if [ -z "${CLANG_FORMAT}" ]; then + echo "Command clang-format is not found" >&2 + echo "Please, install clang-format version 16 to enable checkup C++-files formatting over git pre-commit hook" >&2 + exit 1 + fi +fi + +FILES=$(git diff --staged --diff-filter=ACMR --name-only) + +for FILE in $(echo "$FILES" | grep -e "\\.[ch]pp$"); do + echo "Format $FILE" + ${CLANG_FORMAT} -i --style=file "$FILE" +done From cd3f8a198709ccdb3bd37f8e455d46798cc9911f Mon Sep 17 00:00:00 2001 From: Harrm Date: Fri, 6 Jun 2025 17:04:36 +0300 Subject: [PATCH 09/11] Fixup for sync --- core/api/jrpc/value_converter.hpp | 10 +- core/api/service/impl/api_service_impl.cpp | 48 ++++--- core/blockchain/impl/block_tree_impl.cpp | 12 +- core/blockchain/impl/block_tree_impl.hpp | 19 ++- core/injector/application_injector.cpp | 5 +- core/network/impl/synchronizer_impl.cpp | 13 +- core/network/impl/synchronizer_impl.hpp | 5 +- .../parachain/peer_relay_parent_knowledge.hpp | 16 +-- .../pvf/kagome_pvf_worker_injector.hpp | 4 - core/primitives/event_types.hpp | 30 +++-- core/runtime/common/module_instance.cpp | 6 - core/runtime/common/runtime_context.cpp | 4 +- .../runtime/common/runtime_instances_pool.cpp | 4 - .../common/runtime_upgrade_tracker_impl.cpp | 2 +- core/runtime/module_instance.hpp | 2 - core/storage/map_prefix/prefix.cpp | 2 +- core/storage/trie/impl/direct_storage.cpp | 119 ++++++++++++++++-- core/storage/trie/impl/direct_storage.hpp | 34 +++-- core/storage/trie/impl/trie_storage_impl.cpp | 47 ++++--- core/storage/trie/impl/trie_storage_impl.hpp | 12 +- .../polkadot_trie/polkadot_trie_cursor.hpp | 7 -- core/storage/trie/trie_storage.hpp | 2 - core/utils/kagome_db_editor.cpp | 19 ++- .../child_state/child_state_api_test.cpp | 2 +- test/core/authorship/block_builder_test.cpp | 2 - .../host_api/child_storage_extension_test.cpp | 2 +- .../core/host_api/offchain_extension_test.cpp | 2 +- test/core/host_api/storage_extension_test.cpp | 2 +- .../network/state_protocol_observer_test.cpp | 13 +- test/core/network/synchronizer_test.cpp | 3 +- test/core/parachain/pvf_test.cpp | 2 - test/core/runtime/runtime_test_base.hpp | 2 +- .../runtime/trie_storage_provider_test.cpp | 3 +- test/core/storage/trie/CMakeLists.txt | 6 + .../core/storage/trie/direct_storage_test.cpp | 63 ++++++++++ .../trie/polkadot_trie_cursor_dummy.hpp | 4 +- .../trie/trie_storage/trie_batch_test.cpp | 6 +- .../trie/trie_storage/trie_storage_test.cpp | 6 +- .../storage/trie_pruner/trie_pruner_test.cpp | 2 +- .../core/runtime/module_instance_mock.hpp | 2 - .../core/storage/generic_storage_mock.hpp | 2 + ...r_mock.h => polkadot_trie_cursor_mock.hpp} | 2 +- .../trie/trie_storage_backend_mock.hpp | 7 ++ .../core/storage/trie/trie_storage_mock.hpp | 2 - 44 files changed, 379 insertions(+), 178 deletions(-) create mode 100644 test/core/storage/trie/direct_storage_test.cpp rename test/mock/core/storage/trie/{polkadot_trie_cursor_mock.h => polkadot_trie_cursor_mock.hpp} (96%) diff --git a/core/api/jrpc/value_converter.hpp b/core/api/jrpc/value_converter.hpp index 6d8529666a..132f3b9bb7 100644 --- a/core/api/jrpc/value_converter.hpp +++ b/core/api/jrpc/value_converter.hpp @@ -38,7 +38,6 @@ namespace kagome::api { inline jsonrpc::Value makeValue(const uint64_t &); inline jsonrpc::Value makeValue(const std::nullptr_t &); inline jsonrpc::Value makeValue(const std::nullopt_t &); - inline jsonrpc::Value makeValue(const std::nullopt_t &); template inline jsonrpc::Value makeValue(const std::reference_wrapper &v); @@ -49,6 +48,9 @@ namespace kagome::api { template inline jsonrpc::Value makeValue(const boost::variant &v); + template + inline jsonrpc::Value makeValue(const std::variant &v); + template inline jsonrpc::Value makeValue(const std::pair &val); @@ -138,6 +140,12 @@ namespace kagome::api { [](const auto &value) { return makeValue(value); }); } + template + inline jsonrpc::Value makeValue(const std::variant &v) { + return visit_in_place(v, + [](const auto &value) { return makeValue(value); }); + } + template inline jsonrpc::Value makeValue(const std::pair &val) { jArray data; diff --git a/core/api/service/impl/api_service_impl.cpp b/core/api/service/impl/api_service_impl.cpp index e79df88a51..99c65cd45b 100644 --- a/core/api/service/impl/api_service_impl.cpp +++ b/core/api/service/impl/api_service_impl.cpp @@ -576,27 +576,41 @@ namespace kagome::api { SessionPtr &session, primitives::events::ChainEventType event_type, const primitives::events::ChainEventParams &event_params) { - std::string_view name; switch (event_type) { - case primitives::events::ChainEventType::kNewHeads: { - name = kRpcEventNewHeads; - } break; - case primitives::events::ChainEventType::kFinalizedHeads: { - name = kRpcEventFinalizedHeads; - } break; - case primitives::events::ChainEventType::kFinalizedRuntimeVersion: { - name = kRpcEventRuntimeVersion; - } break; - case primitives::events::ChainEventType::kNewRuntime: - return; + case primitives::events::ChainEventType::kNewHeads: + sendEvent(server_, + session, + logger_, + set_id, + kRpcEventNewHeads, + api::makeValue(std::get( + event_params))); + break; + case primitives::events::ChainEventType::kFinalizedHeads: + sendEvent(server_, + session, + logger_, + set_id, + kRpcEventFinalizedHeads, + api::makeValue(std::get( + event_params))); + break; + case primitives::events::ChainEventType::kFinalizedRuntimeVersion: + sendEvent(server_, + session, + logger_, + set_id, + kRpcEventRuntimeVersion, + api::makeValue( + std::get( + event_params))); + break; default: - BOOST_ASSERT(!"Unknown chain event"); + SL_WARN(logger_, + "Received unexpected chain event {}", + static_cast(event_type)); return; } - - BOOST_ASSERT(!name.empty()); - sendEvent( - server_, session, logger_, set_id, name, api::makeValue(event_params)); } void ApiServiceImpl::onExtrinsicEvent( diff --git a/core/blockchain/impl/block_tree_impl.cpp b/core/blockchain/impl/block_tree_impl.cpp index f56887a6a9..a72db88ba3 100644 --- a/core/blockchain/impl/block_tree_impl.cpp +++ b/core/blockchain/impl/block_tree_impl.cpp @@ -21,7 +21,6 @@ #include "crypto/blake2/blake2b.h" #include "log/profiling_logger.hpp" #include "storage/database_error.hpp" -#include "storage/trie/impl/direct_storage.hpp" #include "storage/trie_pruner/trie_pruner.hpp" #include "utils/pool_handler.hpp" @@ -127,7 +126,6 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, - std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool) { BOOST_ASSERT(storage != nullptr); @@ -274,7 +272,6 @@ namespace kagome::blockchain { std::move(justification_storage_policy), trie_storage, state_pruner, - trie_direct_storage, main_thread_pool)); // Add non-finalized block to the block tree for (auto &item : collected) { @@ -400,13 +397,11 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, - std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool) : block_tree_data_{BlockTreeData{ .storage_ = std::move(storage), .trie_storage_ = std::move(trie_storage), .state_pruner_ = std::move(state_pruner), - .trie_direct_storage_ = std::move(trie_direct_storage), .tree_ = std::make_unique(finalized), .hasher_ = std::move(hasher), .extrinsic_event_key_repo_ = std::move(extrinsic_event_key_repo), @@ -426,7 +421,6 @@ namespace kagome::blockchain { BOOST_ASSERT(p.justification_storage_policy_ != nullptr); BOOST_ASSERT(p.trie_storage_ != nullptr); BOOST_ASSERT(p.state_pruner_ != nullptr); - BOOST_ASSERT(p.trie_direct_storage_ != nullptr); // Register metrics BOOST_ASSERT(telemetry_ != nullptr); @@ -794,8 +788,6 @@ namespace kagome::blockchain { auto changes = p.tree_->finalize(node); OUTCOME_TRY(reorgAndPrune(p, changes)); - OUTCOME_TRY( - p.trie_direct_storage_->updateDirectState(header.state_root)); OUTCOME_TRY(pruneTrie(p, node->info.number)); notifyChainEventsEngine( @@ -1324,8 +1316,8 @@ namespace kagome::blockchain { } extrinsics.emplace_back(std::move(ext)); } - OUTCOME_TRY( - p.trie_direct_storage_->discardDiff(block_header.state_root)); + notifyChainEventsEngine( + primitives::events::ChainEventType::kDiscardedHeads, block_header); p.state_pruner_->schedulePrune( block_header.state_root, block_header.blockInfo(), diff --git a/core/blockchain/impl/block_tree_impl.hpp b/core/blockchain/impl/block_tree_impl.hpp index 73214b3141..6b2792584f 100644 --- a/core/blockchain/impl/block_tree_impl.hpp +++ b/core/blockchain/impl/block_tree_impl.hpp @@ -68,7 +68,6 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, - std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool); /// Recover block tree state at provided block @@ -181,7 +180,6 @@ namespace kagome::blockchain { std::shared_ptr storage_; std::shared_ptr trie_storage_; std::shared_ptr state_pruner_; - std::shared_ptr trie_direct_storage_; std::unique_ptr tree_; std::shared_ptr hasher_; std::shared_ptr @@ -210,34 +208,33 @@ namespace kagome::blockchain { justification_storage_policy, std::shared_ptr trie_storage, std::shared_ptr state_pruner, - std::shared_ptr trie_direct_storage, common::MainThreadPool &main_thread_pool); - outcome::result reorgAndPrune(BlockTreeData &p, + outcome::result reorgAndPrune(BlockTreeData &data, const ReorgAndPrune &changes); outcome::result getBlockHeaderNoLock( - const BlockTreeData &p, const primitives::BlockHash &block_hash) const; + const BlockTreeData &data, const primitives::BlockHash &block_hash) const; outcome::result pruneTrie(const BlockTreeData &block_tree_data, primitives::BlockNumber new_finalized); - primitives::BlockInfo getLastFinalizedNoLock(const BlockTreeData &p) const; - primitives::BlockInfo bestBlockNoLock(const BlockTreeData &p) const; + primitives::BlockInfo getLastFinalizedNoLock(const BlockTreeData &data) const; + primitives::BlockInfo bestBlockNoLock(const BlockTreeData &data) const; - bool hasDirectChainNoLock(const BlockTreeData &p, + bool hasDirectChainNoLock(const BlockTreeData &data, const primitives::BlockHash &ancestor, const primitives::BlockHash &descendant) const; std::vector getLeavesNoLock( - const BlockTreeData &p) const; + const BlockTreeData &data) const; BlockTree::BlockHashVecRes getDescendingChainToBlockNoLock( - const BlockTreeData &p, + const BlockTreeData &data, const primitives::BlockHash &to_block, uint64_t maximum) const; outcome::result addExistingBlockNoLock( - BlockTreeData &p, + BlockTreeData &data, const primitives::BlockHash &block_hash, const primitives::BlockHeader &block_header); diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index f74d5aeea2..9f05aa174d 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -371,7 +371,6 @@ namespace { injector.template create>(), injector.template create>(), injector.template create>(), - injector.template create>(), injector.template create()); // clang-format on @@ -877,7 +876,9 @@ namespace { auto& rocksdb = dynamic_cast(*storage); return storage::trie::DirectStorage::create( rocksdb.getRocksSpace(storage::Space::kTrieDirectKV), - rocksdb.getRocksSpace(storage::Space::kTrieDiff)).value(); + rocksdb.getRocksSpace(storage::Space::kTrieDiff), + injector.template create>(), + LazySPtr(injector)).value(); }), di::bind.template to(), bind_by_lambda([](const auto&) { diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index 123fdc7ba2..dfc68b80b7 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -116,8 +116,7 @@ namespace kagome::network { std::shared_ptr beefy, std::shared_ptr grandpa_environment, common::MainThreadPool &main_thread_pool, - std::shared_ptr block_storage, - std::shared_ptr direct_storage) + std::shared_ptr block_storage) : log_(log::createLogger("Synchronizer", "synchronizer")), block_tree_(std::move(block_tree)), block_appender_(std::move(block_appender)), @@ -137,8 +136,7 @@ namespace kagome::network { poolHandlerReadyMake(app_state_manager, main_thread_pool)}, block_storage_{std::move(block_storage)}, max_parallel_downloads_{app_config.maxParallelDownloads()}, - random_gen_{std::random_device{}()}, - trie_direct_storage_{direct_storage} { + random_gen_{std::random_device{}()} { BOOST_ASSERT(block_tree_); BOOST_ASSERT(block_executor_); BOOST_ASSERT(trie_node_db_); @@ -151,7 +149,6 @@ namespace kagome::network { BOOST_ASSERT(chain_sub_engine_); BOOST_ASSERT(main_pool_handler_); BOOST_ASSERT(block_storage_); - BOOST_ASSERT(trie_direct_storage_); sync_method_ = app_config.syncMethod(); @@ -1008,8 +1005,10 @@ namespace kagome::network { }}); SL_DEBUG(log_, "New direct storage root: {}", state_sync_flow_->root()); - OUTCOME_TRY(trie_direct_storage_->resetDirectState(state_sync_flow_->root(), - *trie)); + chain_sub_engine_->notify( + primitives::events::ChainEventType::kNewStateSynced, + primitives::events::NewStateSyncedParams{ + .state_root = state_sync_flow_->root(), .trie = *trie}); auto block = state_sync_flow_->blockInfo(); state_sync_flow_.reset(); diff --git a/core/network/impl/synchronizer_impl.hpp b/core/network/impl/synchronizer_impl.hpp index 32b8cd6515..c19491847c 100644 --- a/core/network/impl/synchronizer_impl.hpp +++ b/core/network/impl/synchronizer_impl.hpp @@ -71,7 +71,6 @@ namespace kagome::storage { namespace kagome::storage::trie { class TrieSerializer; class TrieStorage; - class DirectStorage; } // namespace kagome::storage::trie namespace kagome::storage::trie_pruner { @@ -136,8 +135,7 @@ namespace kagome::network { std::shared_ptr beefy, std::shared_ptr grandpa_environment, common::MainThreadPool &main_thread_pool, - std::shared_ptr block_storage, - std::shared_ptr direct_storage); + std::shared_ptr block_storage); /** @see AppStateManager::takeControl */ bool start(); @@ -281,7 +279,6 @@ namespace kagome::network { std::shared_ptr block_storage_; uint32_t max_parallel_downloads_; std::mt19937 random_gen_; - std::shared_ptr trie_direct_storage_; application::SyncMethod sync_method_; diff --git a/core/parachain/peer_relay_parent_knowledge.hpp b/core/parachain/peer_relay_parent_knowledge.hpp index 1b3e243d8f..f64ca9bb9a 100644 --- a/core/parachain/peer_relay_parent_knowledge.hpp +++ b/core/parachain/peer_relay_parent_knowledge.hpp @@ -12,22 +12,16 @@ #include "network/types/collator_messages.hpp" #include "parachain/candidate_view.hpp" -namespace kagome::parachain { - - struct PeerStatement { - network::CompactStatement compact_statement; - network::ValidatorIndex validator_index; - }; - -} // namespace kagome::parachain - -// SCALE_TIE_HASH_STD(kagome::parachain::PeerStatement); - namespace kagome::parachain { /// knowledge that a peer has about goings-on in a relay parent. struct PeerRelayParentKnowledge { using CandidateHash = network::CandidateHash; + struct PeerStatement { + network::CompactStatement compact_statement; + network::ValidatorIndex validator_index; + }; + std::unordered_set sent_candidates; /// candidates that the peer is aware of because we /// sent statements to it. This indicates that we can diff --git a/core/parachain/pvf/kagome_pvf_worker_injector.hpp b/core/parachain/pvf/kagome_pvf_worker_injector.hpp index 539593b3e9..3b6e091644 100644 --- a/core/parachain/pvf/kagome_pvf_worker_injector.hpp +++ b/core/parachain/pvf/kagome_pvf_worker_injector.hpp @@ -70,10 +70,6 @@ namespace kagome::parachain { const OnNodeLoaded &on_node_loaded) const override { return nullptr; } - - virtual storage::trie::DirectStorage &DEBUG_getDirectStorage() { - std::abort(); - } }; template diff --git a/core/primitives/event_types.hpp b/core/primitives/event_types.hpp index c5a4aa8f41..d6663550cf 100644 --- a/core/primitives/event_types.hpp +++ b/core/primitives/event_types.hpp @@ -20,6 +20,7 @@ #include "primitives/block_id.hpp" #include "primitives/extrinsic.hpp" #include "primitives/version.hpp" +#include "storage/trie/types.hpp" #include "subscription/subscriber.hpp" #include "subscription/subscription_engine.hpp" @@ -31,11 +32,16 @@ namespace kagome::primitives { struct BlockHeader; } +namespace kagome::storage::trie { + class PolkadotTrie; +} + namespace kagome::primitives::events { template using ref_t = std::reference_wrapper; + // TODO(Harrm) for review: why Heads? Is it short for Headers? It looks odd. enum struct ChainEventType : uint8_t { kNewHeads = 1, kFinalizedHeads = 2, @@ -44,6 +50,9 @@ namespace kagome::primitives::events { kNewRuntime = 5, kDeactivateAfterFinalization = 6, // TODO(kamilsa): #2369 might not be // triggered on every leaf deactivated + kDiscardedHeads = 7, + kNewStateSynced = 8, // TODO(Harrm) it's arguable that it belongs here but + // it's very convenient for Direct Storage }; enum struct PeerEventType : uint8_t { @@ -64,12 +73,17 @@ namespace kagome::primitives::events { std::vector removed; primitives::BlockNumber finalized{}; }; + struct NewStateSyncedParams { + storage::trie::RootHash state_root; + const storage::trie::PolkadotTrie ≜ + }; - using ChainEventParams = boost::variant; + using ChainEventParams = std::variant; using SyncStateEventParams = consensus::SyncState; @@ -308,11 +322,11 @@ namespace kagome::primitives::events { struct ChainSub { ChainSub(ChainSubscriptionEnginePtr engine) : sub{std::make_shared( - std::move(engine))} {} + std::move(engine))} {} void onBlock(ChainEventType type, auto f) { subscribe(*sub, type, [f{std::move(f)}](const ChainEventParams &args) { - auto &block = boost::get(args).get(); + auto &block = std::get(args).get(); if constexpr (std::is_invocable_v) { f(); } else { @@ -332,7 +346,7 @@ namespace kagome::primitives::events { subscribe(*sub, ChainEventType::kDeactivateAfterFinalization, [f{std::move(f)}](const ChainEventParams &args) { - f(boost::get(args)); + f(std::get(args)); }); } diff --git a/core/runtime/common/module_instance.cpp b/core/runtime/common/module_instance.cpp index a66f139a28..68c46450dc 100644 --- a/core/runtime/common/module_instance.cpp +++ b/core/runtime/common/module_instance.cpp @@ -68,10 +68,4 @@ namespace kagome::runtime { return outcome::success(); } - outcome::result ModuleInstance::stateless() { - OUTCOME_TRY(getEnvironment().storage_provider->setToEphemeralAt( - storage::trie::kEmptyRootHash)); - OUTCOME_TRY(resetMemory()); - return outcome::success(); - } } // namespace kagome::runtime diff --git a/core/runtime/common/runtime_context.cpp b/core/runtime/common/runtime_context.cpp index 0e3c0daecf..e98e5b2cab 100644 --- a/core/runtime/common/runtime_context.cpp +++ b/core/runtime/common/runtime_context.cpp @@ -37,7 +37,9 @@ namespace kagome::runtime { outcome::result RuntimeContextFactory::stateless( std::shared_ptr instance) { - OUTCOME_TRY(instance->stateless()); + OUTCOME_TRY(instance->getEnvironment().storage_provider->setToEphemeralAt( + storage::trie::kEmptyRootHash)); + OUTCOME_TRY(instance->resetMemory()); return RuntimeContext{instance}; } diff --git a/core/runtime/common/runtime_instances_pool.cpp b/core/runtime/common/runtime_instances_pool.cpp index 1518da24f6..ff7c52145d 100644 --- a/core/runtime/common/runtime_instances_pool.cpp +++ b/core/runtime/common/runtime_instances_pool.cpp @@ -73,10 +73,6 @@ namespace kagome::runtime { return instance_->resetEnvironment(); } - outcome::result stateless() override { - return instance_->stateless(); - } - private: std::weak_ptr pool_; common::Hash256 hash_; diff --git a/core/runtime/common/runtime_upgrade_tracker_impl.cpp b/core/runtime/common/runtime_upgrade_tracker_impl.cpp index f5f4dbb727..1e2a91e435 100644 --- a/core/runtime/common/runtime_upgrade_tracker_impl.cpp +++ b/core/runtime/common/runtime_upgrade_tracker_impl.cpp @@ -179,7 +179,7 @@ namespace kagome::runtime { primitives::events::ChainEventType::kNewRuntime, [this](const primitives::events::ChainEventParams &event_params) { const auto &block_hash = - boost::get( + std::get( event_params) .get(); auto res = push(block_hash); diff --git a/core/runtime/module_instance.hpp b/core/runtime/module_instance.hpp index 23add7d6ec..16bf6b577b 100644 --- a/core/runtime/module_instance.hpp +++ b/core/runtime/module_instance.hpp @@ -124,8 +124,6 @@ namespace kagome::runtime { virtual outcome::result resetEnvironment() = 0; outcome::result resetMemory(); - - virtual outcome::result stateless(); }; } // namespace kagome::runtime diff --git a/core/storage/map_prefix/prefix.cpp b/core/storage/map_prefix/prefix.cpp index bf004ef3d5..8942636b45 100644 --- a/core/storage/map_prefix/prefix.cpp +++ b/core/storage/map_prefix/prefix.cpp @@ -27,7 +27,7 @@ namespace kagome::storage { : map{map}, cursor{std::move(cursor)} {} outcome::result MapPrefix::Cursor::seekFirst() { - OUTCOME_TRY(cursor->seek(map.prefix)); + OUTCOME_TRY(cursor->seekLowerBound(map.prefix)); return isValid(); } diff --git a/core/storage/trie/impl/direct_storage.cpp b/core/storage/trie/impl/direct_storage.cpp index 522ec1f8e8..717f1fe0a7 100644 --- a/core/storage/trie/impl/direct_storage.cpp +++ b/core/storage/trie/impl/direct_storage.cpp @@ -6,6 +6,8 @@ #include "storage/trie/impl/direct_storage.hpp" #include "common/monadic_utils.hpp" +#include "consensus/timeline/timeline.hpp" +#include "primitives/block_header.hpp" #include "storage/database_error.hpp" #include "storage/map_prefix/prefix.hpp" #include "storage/rocksdb/rocksdb.hpp" @@ -133,13 +135,64 @@ namespace kagome::storage::trie { } } - outcome::result> DirectStorage::create( - std::shared_ptr direct_db, - std::shared_ptr diff_db) { - std::unique_ptr storage{new DirectStorage{}}; + outcome::result> DirectStorage::create( + std::shared_ptr direct_db, + std::shared_ptr diff_db, + primitives::events::ChainSubscriptionEnginePtr chain_sub_engine, + //primitives::events::SyncStateSubscriptionEnginePtr sync_sub_engine, + LazySPtr timeline) { + std::shared_ptr storage{new DirectStorage{timeline}}; storage->direct_state_db_ = direct_db; storage->diff_db_ = diff_db; + storage->chain_event_sub_ = + std::make_shared( + std::move(chain_sub_engine)); + storage->chain_sub_id_ = + storage->chain_event_sub_->generateSubscriptionSetId(); + storage->chain_event_sub_->subscribe( + storage->chain_sub_id_, + primitives::events::ChainEventType::kDiscardedHeads); + storage->chain_event_sub_->subscribe( + storage->chain_sub_id_, + primitives::events::ChainEventType::kFinalizedHeads); + storage->chain_event_sub_->subscribe( + storage->chain_sub_id_, + primitives::events::ChainEventType::kNewStateSynced); + storage->chain_event_sub_->setCallback( + [weak = storage->weak_from_this()]( + subscription::SubscriptionSetId id, + auto, + primitives::events::ChainEventType type, + const primitives::events::ChainEventParams ¶ms) { + if (auto self = weak.lock()) { + self->onChainEvent(id, nullptr, type, params); + } + }); + + // storage->sync_event_sub_ = + // std::make_shared( + // std::move(sync_sub_engine)); + // storage->sync_sub_id_ = + // storage->sync_event_sub_->generateSubscriptionSetId(); + // storage->sync_event_sub_->subscribe( + // storage->sync_sub_id_, + // primitives::events::SyncStateEventType::kSyncState); + // storage->sync_event_sub_->setCallback( + // [weak = storage->weak_from_this()]( + // subscription::SubscriptionSetId id, + // auto, + // primitives::events::SyncStateEventType type, + // const primitives::events::SyncStateEventParams ¶ms) { + + // }); + OUTCOME_TRY(state_root, direct_db->tryGet(kLatestFinalizedStateKey)); + SL_DEBUG(storage->logger_, + "Fetched last finalized state key: {}", + common::map_optional( + common::map_optional(state_root, &BufferOrView::view), + &BufferView::toHex) + .value_or("")); BOOST_OUTCOME_TRY( storage->state_root_, RootHash::fromSpan(common::map_optional(state_root, &BufferOrView::view) @@ -153,6 +206,50 @@ namespace kagome::storage::trie { return storage; } + DirectStorage::DirectStorage(LazySPtr timeline) + : timeline_(timeline) {} + + void DirectStorage::onChainEvent( + subscription::SubscriptionSetId id, + void *, + primitives::events::ChainEventType type, + const primitives::events::ChainEventParams ¶ms) { + BOOST_ASSERT(id == chain_sub_id_); + if (type == primitives::events::ChainEventType::kDiscardedHeads) { + const primitives::BlockHeader &header = + std::get(params).get(); + auto res = discardDiff(header.state_root); + if (!res) { + SL_ERROR(logger_, + "Failed to discard diff for block {}, state root {}: {}", + header.blockInfo(), + header.state_root, + res.error()); + } + } else if (type == primitives::events::ChainEventType::kFinalizedHeads) { + const primitives::BlockHeader &header = + std::get(params).get(); + auto res = updateDirectState(header.state_root); + if (!res) { + SL_ERROR(logger_, + "Failed to set direct state at block {}, state root {}: {}", + header.blockInfo(), + header.state_root, + res.error()); + } + } else if (type == primitives::events::ChainEventType::kNewStateSynced) { + const auto &[root, trie] = + std::get(params); + auto res = resetDirectState(root, trie); + if (!res) { + SL_ERROR(logger_, + "Failed to reset direct state after state sync at root {}: {}", + root, + res.error()); + } + } + } + const RootHash &DirectStorage::getDirectStateRoot() const { return state_root_; } @@ -188,8 +285,9 @@ namespace kagome::storage::trie { "Inserted total of {} keys into direct storage with root {}", count, new_state_root); - OUTCOME_TRY(batch->commit()); OUTCOME_TRY(batch->put(kLatestFinalizedStateKey, new_state_root)); + OUTCOME_TRY(batch->commit()); + SL_DEBUG(logger_, "Put kLatestFinalizedStateKey {}", new_state_root); state_root_ = new_state_root; return outcome::success(); } @@ -248,7 +346,6 @@ namespace kagome::storage::trie { } auto diff_batch = diff_db_->batch(); - OUTCOME_TRY(diff_batch->put(roots.to, Buffer{1})); for (auto &[key, val] : diff) { Buffer full_key; full_key.put(roots.to); @@ -269,6 +366,11 @@ namespace kagome::storage::trie { "Store new diff for state transition from {} to {}", roots.from, roots.to); + if (!timeline_.get()->wasSynchronized()) { + OUTCOME_TRY(updateDirectState(roots.to)); + SL_DEBUG( + logger_, "Since node the node is not synchronized, update to this state."); + } return outcome::success(); } @@ -295,6 +397,7 @@ namespace kagome::storage::trie { outcome::result> DirectStorage::getViewAt( const RootHash &state_root) const { OUTCOME_TRY(diff_known, diff_db_->contains(state_root)); + if (state_root != state_root_ && !diff_known) { SL_DEBUG(logger_, "Failed to get direct storage view at state {}: no such state " @@ -344,7 +447,7 @@ namespace kagome::storage::trie { return DirectStorageError::APPLY_UNKNOWN_DIFF; } - MapPrefix diff_prefix{new_root, direct_state_db_}; + MapPrefix diff_prefix{new_root, diff_db_}; auto batch = direct_state_db_->batch(); size_t num = 0; @@ -364,9 +467,11 @@ namespace kagome::storage::trie { OUTCOME_TRY(batch->put(key, std::move(value_buf))); } ++num; + OUTCOME_TRY(iter->next()); } OUTCOME_TRY(batch->put(kLatestFinalizedStateKey, new_root)); + SL_DEBUG(logger_, "Put kLatestFinalizedStateKey {}", new_root); OUTCOME_TRY(batch->commit()); SL_DEBUG(logger_, "Applied diff to state {} with {} writes", new_root, num); diff --git a/core/storage/trie/impl/direct_storage.hpp b/core/storage/trie/impl/direct_storage.hpp index 77099a32a6..32d9675918 100644 --- a/core/storage/trie/impl/direct_storage.hpp +++ b/core/storage/trie/impl/direct_storage.hpp @@ -9,7 +9,9 @@ #include #include "common/buffer.hpp" +#include "injector/lazy.hpp" #include "log/logger.hpp" +#include "primitives/event_types.hpp" #include "storage/buffer_map_types.hpp" #include "storage/trie/types.hpp" @@ -17,6 +19,10 @@ namespace kagome::storage { class RocksDbSpace; } +namespace kagome::consensus { + class Timeline; +} + namespace kagome::storage::trie { class PolkadotTrie; @@ -52,9 +58,6 @@ namespace kagome::storage::trie { } private: - outcome::result>> - findLatestSource(BufferView key); - std::shared_ptr storage_; RootHash state_root_; }; @@ -65,9 +68,11 @@ namespace kagome::storage::trie { public: friend class DirectStorageView; - static outcome::result> create( - std::shared_ptr direct_db, - std::shared_ptr diff_db); + static outcome::result> create( + std::shared_ptr direct_db, + std::shared_ptr diff_db, + primitives::events::ChainSubscriptionEnginePtr chain_sub_engine, + LazySPtr timeline); const RootHash &getDirectStateRoot() const; @@ -87,6 +92,13 @@ namespace kagome::storage::trie { const RootHash &state_root) const; private: + explicit DirectStorage(LazySPtr timeline); + + void onChainEvent(subscription::SubscriptionSetId id, + void *, + primitives::events::ChainEventType type, + const primitives::events::ChainEventParams ¶ms); + struct ValueDeleted {}; outcome::result>> getAt(const RootHash &state, common::BufferView key) const; @@ -96,8 +108,14 @@ namespace kagome::storage::trie { outcome::result applyDiff(const RootHash &new_root); RootHash state_root_; - std::shared_ptr direct_state_db_; - std::shared_ptr diff_db_; + std::shared_ptr direct_state_db_; + std::shared_ptr diff_db_; + subscription::SubscriptionSetId chain_sub_id_ {}; + //subscription::SubscriptionSetId sync_sub_id_; + primitives::events::ChainEventSubscriberPtr chain_event_sub_; + //primitives::events::SyncStateEventSubscriberPtr sync_event_sub_; + LazySPtr timeline_; + log::Logger logger_ = log::createLogger("DirectStorage", "storage"); }; diff --git a/core/storage/trie/impl/trie_storage_impl.cpp b/core/storage/trie/impl/trie_storage_impl.cpp index 7104fa08a4..a0166966a5 100644 --- a/core/storage/trie/impl/trie_storage_impl.cpp +++ b/core/storage/trie/impl/trie_storage_impl.cpp @@ -6,6 +6,7 @@ #include "storage/trie/impl/trie_storage_impl.hpp" +#include #include #include "outcome/outcome.hpp" @@ -20,7 +21,7 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv) { + std::optional> direct_kv) { // will never be used, so content of the callback doesn't matter auto empty_trie = trie_factory->createEmpty(); // ensure retrieval of empty trie succeeds @@ -37,7 +38,7 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv) { + std::optional> direct_kv) { return std::unique_ptr( new TrieStorageImpl(std::move(codec), std::move(serializer), @@ -49,7 +50,7 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv) + std::optional> direct_kv) : codec_{std::move(codec)}, serializer_{std::move(serializer)}, state_pruner_{std::move(state_pruner)}, @@ -58,7 +59,7 @@ namespace kagome::storage::trie { BOOST_ASSERT(codec_ != nullptr); BOOST_ASSERT(state_pruner_ != nullptr); BOOST_ASSERT(serializer_ != nullptr); - BOOST_ASSERT(direct_kv_ != nullptr); + BOOST_ASSERT(!direct_kv_ || *direct_kv_ != nullptr); } outcome::result> @@ -68,28 +69,36 @@ namespace kagome::storage::trie { SL_DEBUG(logger_, "Initialize persistent trie batch with root: {}", root.toHex()); - OUTCOME_TRY(direct_view, direct_kv_->getViewAt(root)); - return std::make_unique(codec_, - serializer_, - changes_tracker, - std::move(trie), - state_pruner_, - direct_kv_, - std::move(direct_view)); + if (direct_kv_) { + OUTCOME_TRY(direct_view, (*direct_kv_)->getViewAt(root)); + return std::make_unique(codec_, + serializer_, + changes_tracker, + std::move(trie), + state_pruner_, + *direct_kv_, + std::move(direct_view)); + } + return std::make_unique( + codec_, serializer_, changes_tracker, std::move(trie), state_pruner_); } outcome::result> TrieStorageImpl::getEphemeralBatchAt(const RootHash &root) const { SL_DEBUG(logger_, "Initialize ephemeral trie batch with root: {}", root); OUTCOME_TRY(trie, serializer_->retrieveTrie(root, nullptr)); - OUTCOME_TRY(direct_view, direct_kv_->getViewAt(root)); + if (direct_kv_ && root != kEmptyRootHash) { + OUTCOME_TRY(direct_view, (*direct_kv_)->getViewAt(root)); - return std::make_unique(codec_, - std::move(trie), - serializer_, - nullptr, - direct_kv_, - std::move(direct_view)); + return std::make_unique(codec_, + std::move(trie), + serializer_, + nullptr, + *direct_kv_, + std::move(direct_view)); + } + return std::make_unique( + codec_, std::move(trie), serializer_, nullptr); } outcome::result> diff --git a/core/storage/trie/impl/trie_storage_impl.hpp b/core/storage/trie/impl/trie_storage_impl.hpp index 3fbc27968c..0008caabc7 100644 --- a/core/storage/trie/impl/trie_storage_impl.hpp +++ b/core/storage/trie/impl/trie_storage_impl.hpp @@ -30,13 +30,13 @@ namespace kagome::storage::trie { std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv); + std::optional> direct_kv = std::nullopt); static outcome::result> createFromStorage( std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv); + std::optional> direct_kv = std::nullopt); TrieStorageImpl(const TrieStorageImpl &) = delete; void operator=(const TrieStorageImpl &) = delete; @@ -53,22 +53,18 @@ namespace kagome::storage::trie { const RootHash &root, const OnNodeLoaded &on_node_loaded) const override; - DirectStorage &DEBUG_getDirectStorage() override { - return *direct_kv_; - } - protected: TrieStorageImpl( std::shared_ptr codec, std::shared_ptr serializer, std::shared_ptr state_pruner, - std::shared_ptr direct_kv); + std::optional> direct_kv); private: std::shared_ptr codec_; std::shared_ptr serializer_; std::shared_ptr state_pruner_; - std::shared_ptr direct_kv_; + std::optional> direct_kv_; log::Logger logger_; }; diff --git a/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp b/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp index 4f46f4eb37..d074e04648 100644 --- a/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp +++ b/core/storage/trie/polkadot_trie/polkadot_trie_cursor.hpp @@ -15,13 +15,6 @@ namespace kagome::storage::trie { class PolkadotTrieCursor : public BufferStorageCursor { public: - /** - * Seek the first element with key not less than \arg key - * @return true if the trie is not empty - */ - virtual outcome::result seekLowerBound( - const common::BufferView &key) = 0; - /** * Seek the first element with key greater than \arg key * @return true if the trie is not empty diff --git a/core/storage/trie/trie_storage.hpp b/core/storage/trie/trie_storage.hpp index 89f90f467c..a483395bb9 100644 --- a/core/storage/trie/trie_storage.hpp +++ b/core/storage/trie/trie_storage.hpp @@ -43,8 +43,6 @@ namespace kagome::storage::trie { virtual outcome::result> getProofReaderBatchAt( const RootHash &root, const OnNodeLoaded &on_node_loaded) const = 0; - - virtual DirectStorage &DEBUG_getDirectStorage() = 0; }; } // namespace kagome::storage::trie diff --git a/core/utils/kagome_db_editor.cpp b/core/utils/kagome_db_editor.cpp index 6f360dfe83..b85b72d5b5 100644 --- a/core/utils/kagome_db_editor.cpp +++ b/core/utils/kagome_db_editor.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "consensus/timeline/timeline.hpp" #include "injector/idle_trie_pruner.hpp" #include "storage/trie/impl/direct_storage.hpp" #include "storage/trie/trie_batches.hpp" @@ -228,6 +229,19 @@ Kagome DB Editor - a storage pruner. Allows to reduce occupied disk space. and std::strlen(s) == common::Hash256::size() * 2 + 2; } + class TimelineStub final : public consensus::Timeline { + public: + consensus::SyncState getCurrentState() const { + return consensus::SyncState::SYNCHRONIZED; + } + + bool wasSynchronized() const { + return true; + } + + void checkAndReportEquivocation(const primitives::BlockHeader &) override {} + }; + int db_editor_main(int argc, const char **argv) { #if defined(BACKWARD_HAS_BACKTRACE) backward::SignalHandling sh; @@ -284,7 +298,10 @@ Kagome DB Editor - a storage pruner. Allows to reduce occupied disk space. std::shared_ptr direct_storage = storage::trie::DirectStorage::create( storage->getRocksSpace(storage::Space::kTrieDirectKV), - storage->getRocksSpace(storage::Space::kTrieDiff)) + storage->getRocksSpace(storage::Space::kTrieDiff), + std::make_shared(), + LazySPtr{di::make_injector( + di::bind.template to())}) .value(); auto injector = di::make_injector( diff --git a/test/core/api/service/child_state/child_state_api_test.cpp b/test/core/api/service/child_state/child_state_api_test.cpp index 272f6f0118..f8117ae2c8 100644 --- a/test/core/api/service/child_state/child_state_api_test.cpp +++ b/test/core/api/service/child_state/child_state_api_test.cpp @@ -13,7 +13,7 @@ #include "mock/core/blockchain/block_tree_mock.hpp" #include "mock/core/runtime/core_mock.hpp" #include "mock/core/runtime/metadata_mock.hpp" -#include "mock/core/storage/trie/polkadot_trie_cursor_mock.h" +#include "mock/core/storage/trie/polkadot_trie_cursor_mock.hpp" #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "mock/core/storage/trie/trie_storage_mock.hpp" #include "primitives/block_header.hpp" diff --git a/test/core/authorship/block_builder_test.cpp b/test/core/authorship/block_builder_test.cpp index 1e4d90e72f..0d8f6fb83f 100644 --- a/test/core/authorship/block_builder_test.cpp +++ b/test/core/authorship/block_builder_test.cpp @@ -56,8 +56,6 @@ class BlockBuilderTest : public ::testing::Test { parent_block_ = BlockInfo{block_number_ - 1, expected_header_.parent_hash}; auto instance_mock = std::make_shared(); - EXPECT_CALL(*instance_mock, stateless()) - .WillOnce(Return(outcome::success())); block_builder_ = std::make_shared( expected_header_, std::make_unique( diff --git a/test/core/host_api/child_storage_extension_test.cpp b/test/core/host_api/child_storage_extension_test.cpp index b9fac0940f..b7246c68f2 100644 --- a/test/core/host_api/child_storage_extension_test.cpp +++ b/test/core/host_api/child_storage_extension_test.cpp @@ -13,7 +13,7 @@ #include "common/monadic_utils.hpp" #include "mock/core/runtime/memory_provider_mock.hpp" #include "mock/core/runtime/trie_storage_provider_mock.hpp" -#include "mock/core/storage/trie/polkadot_trie_cursor_mock.h" +#include "mock/core/storage/trie/polkadot_trie_cursor_mock.hpp" #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "runtime/ptr_size.hpp" #include "scale/encode_append.hpp" diff --git a/test/core/host_api/offchain_extension_test.cpp b/test/core/host_api/offchain_extension_test.cpp index 5e9e63b48f..e1b6108e2a 100644 --- a/test/core/host_api/offchain_extension_test.cpp +++ b/test/core/host_api/offchain_extension_test.cpp @@ -15,7 +15,7 @@ #include "mock/core/offchain/offchain_worker_pool_mock.hpp" #include "mock/core/runtime/memory_provider_mock.hpp" #include "mock/core/runtime/trie_storage_provider_mock.hpp" -#include "mock/core/storage/trie/polkadot_trie_cursor_mock.h" +#include "mock/core/storage/trie/polkadot_trie_cursor_mock.hpp" #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "offchain/types.hpp" #include "runtime/ptr_size.hpp" diff --git a/test/core/host_api/storage_extension_test.cpp b/test/core/host_api/storage_extension_test.cpp index 3ba4c1fa03..fe3df8dd3a 100644 --- a/test/core/host_api/storage_extension_test.cpp +++ b/test/core/host_api/storage_extension_test.cpp @@ -18,7 +18,7 @@ #include "mock/core/api/service/state/state_api_mock.hpp" #include "mock/core/runtime/memory_provider_mock.hpp" #include "mock/core/runtime/trie_storage_provider_mock.hpp" -#include "mock/core/storage/trie/polkadot_trie_cursor_mock.h" +#include "mock/core/storage/trie/polkadot_trie_cursor_mock.hpp" #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "runtime/ptr_size.hpp" #include "scale/encode_append.hpp" diff --git a/test/core/network/state_protocol_observer_test.cpp b/test/core/network/state_protocol_observer_test.cpp index 8e45b1f17b..c0cba5ff68 100644 --- a/test/core/network/state_protocol_observer_test.cpp +++ b/test/core/network/state_protocol_observer_test.cpp @@ -60,8 +60,7 @@ std::shared_ptr makeEmptyInMemoryTrie() { trie_factory, codec, serializer, - state_pruner, - std::make_shared()) + state_pruner) .value(); } @@ -75,11 +74,11 @@ BlockHeader makeBlockHeader(RootHash hash) { uint32_t num = 1; std::string str_num = std::to_string(num); return kagome::primitives::BlockHeader{ - num, // number - makeHash("block_genesis_hash"), // parent - hash, // state root - makeHash("block_" + str_num + "_ext_root"), // extrinsics root - {}, // digest + .number=num, // number + .parent_hash=makeHash("block_genesis_hash"), // parent + .state_root=hash, // state root + .extrinsics_root=makeHash("block_" + str_num + "_ext_root"), // extrinsics root + .digest={}, // digest }; } diff --git a/test/core/network/synchronizer_test.cpp b/test/core/network/synchronizer_test.cpp index a02415b467..9074a261b8 100644 --- a/test/core/network/synchronizer_test.cpp +++ b/test/core/network/synchronizer_test.cpp @@ -111,8 +111,7 @@ class SynchronizerTest nullptr, grandpa_environment, *main_thread_pool, - block_storage, - spaced_storage); + block_storage); } void TearDown() override { diff --git a/test/core/parachain/pvf_test.cpp b/test/core/parachain/pvf_test.cpp index ecf422fbf1..7a92fe5ac6 100644 --- a/test/core/parachain/pvf_test.cpp +++ b/test/core/parachain/pvf_test.cpp @@ -147,8 +147,6 @@ class PvfTest : public testing::Test { ON_CALL(*instance, callExportFunction(_, "validate_block", _)) .WillByDefault(Return(Buffer{encode(ValidationResult{}).value()})); ON_CALL(*instance, getCodeHash()).WillByDefault(Return(code_hash)); - EXPECT_CALL(*instance, stateless()) - .WillRepeatedly(Return(outcome::success())); return instance; }); return module; diff --git a/test/core/runtime/runtime_test_base.hpp b/test/core/runtime/runtime_test_base.hpp index bdf3eb0cd1..be76ed0f77 100644 --- a/test/core/runtime/runtime_test_base.hpp +++ b/test/core/runtime/runtime_test_base.hpp @@ -33,7 +33,7 @@ #include "mock/core/runtime/runtime_properties_cache_mock.hpp" #include "mock/core/runtime/trie_storage_provider_mock.hpp" #include "mock/core/storage/spaced_storage_mock.hpp" -#include "mock/core/storage/trie/polkadot_trie_cursor_mock.h" +#include "mock/core/storage/trie/polkadot_trie_cursor_mock.hpp" #include "mock/core/storage/trie/serialization/trie_serializer_mock.hpp" #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "mock/core/storage/trie/trie_storage_mock.hpp" diff --git a/test/core/runtime/trie_storage_provider_test.cpp b/test/core/runtime/trie_storage_provider_test.cpp index 8a56359d27..0374dc6254 100644 --- a/test/core/runtime/trie_storage_provider_test.cpp +++ b/test/core/runtime/trie_storage_provider_test.cpp @@ -55,8 +55,7 @@ class TrieStorageProviderTest : public ::testing::Test { trie_factory, codec, serializer, - state_pruner, - std::make_shared()) + state_pruner) .value(); storage_provider_ = diff --git a/test/core/storage/trie/CMakeLists.txt b/test/core/storage/trie/CMakeLists.txt index 21c06635d7..8d14114a53 100644 --- a/test/core/storage/trie/CMakeLists.txt +++ b/test/core/storage/trie/CMakeLists.txt @@ -6,3 +6,9 @@ add_subdirectory(polkadot_trie) add_subdirectory(trie_storage) + +addtest(direct_storage_test direct_storage_test.cpp) +target_link_libraries(direct_storage_test + storage + logger_for_tests +) diff --git a/test/core/storage/trie/direct_storage_test.cpp b/test/core/storage/trie/direct_storage_test.cpp new file mode 100644 index 0000000000..c1b0766d3c --- /dev/null +++ b/test/core/storage/trie/direct_storage_test.cpp @@ -0,0 +1,63 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "storage/trie/impl/direct_storage.hpp" + +#include +#include + +#include "mock/core/storage/generic_storage_mock.hpp" +#include "storage/in_memory/in_memory_storage.hpp" +#include "testutil/literals.hpp" +#include "testutil/prepare_loggers.hpp" + +using namespace kagome; +using namespace storage; +using namespace storage::trie; + +TEST(DirectStorageTest, DirectStorageTest) { + testutil::prepareLoggers(); + auto direct_storage_db = std::make_shared(); + auto diff_db = std::make_shared(); + auto chain_sub_engine = + std::make_shared(); + { + std::shared_ptr storage = + DirectStorage::create(direct_storage_db, diff_db, chain_sub_engine) + .value(); + + ASSERT_EQ(storage->getDirectStateRoot(), kEmptyRootHash); + StateDiff diff1{{"key1"_buf, "val1"_buf}}; + ASSERT_OUTCOME_SUCCESS_void(storage->storeDiff( + DirectStorage::DiffRoots{.from = kEmptyRootHash, .to = "root1"_hash256}, + std::move(diff1))); + ASSERT_OUTCOME_SUCCESS_void(storage->updateDirectState("root1"_hash256)); + ASSERT_EQ(storage->getDirectStateRoot(), "root1"_hash256); + ASSERT_OUTCOME_SUCCESS(view, storage->getViewAt("root1"_hash256)); + + ASSERT_EQ(view->get("key1"_buf).value(), "val1"_buf); + StateDiff diff2{{"key1"_buf, "val2"_buf}}; + ASSERT_OUTCOME_SUCCESS_void( + storage->storeDiff(DirectStorage::DiffRoots{.from = "root1"_hash256, + .to = "root2"_hash256}, + std::move(diff2))); + ASSERT_OUTCOME_SUCCESS(view2, storage->getViewAt("root2"_hash256)); + ASSERT_EQ(view->get("key1"_buf).value(), "val1"_buf); + + ASSERT_EQ(view2->get("key1"_buf).value(), "val2"_buf); + } + { + std::shared_ptr storage = + DirectStorage::create(direct_storage_db, diff_db, chain_sub_engine) + .value(); + ASSERT_OUTCOME_SUCCESS(view, storage->getViewAt("root1"_hash256)); + + ASSERT_OUTCOME_SUCCESS(view2, storage->getViewAt("root2"_hash256)); + ASSERT_EQ(view->get("key1"_buf).value(), "val1"_buf); + + ASSERT_EQ(view2->get("key1"_buf).value(), "val2"_buf); + } +} diff --git a/test/core/storage/trie/polkadot_trie_cursor_dummy.hpp b/test/core/storage/trie/polkadot_trie_cursor_dummy.hpp index 6f4eb97246..e206dfcb80 100644 --- a/test/core/storage/trie/polkadot_trie_cursor_dummy.hpp +++ b/test/core/storage/trie/polkadot_trie_cursor_dummy.hpp @@ -31,10 +31,10 @@ namespace kagome::storage::trie { return current_ != key_val_.end(); } - outcome::result seekLowerBound( + outcome::result seekLowerBound( const common::BufferView &key) override { current_ = key_val_.lower_bound(key); - return outcome::success(); + return current_ != key_val_.end(); } outcome::result seekUpperBound( diff --git a/test/core/storage/trie/trie_storage/trie_batch_test.cpp b/test/core/storage/trie/trie_storage/trie_batch_test.cpp index c23b54c49a..1d688b6d1b 100644 --- a/test/core/storage/trie/trie_storage/trie_batch_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_batch_test.cpp @@ -67,8 +67,7 @@ class TrieBatchTest : public test::BaseRocksDB_Test { factory, codec, serializer, - state_pruner, - std::make_shared()) + state_pruner) .value(); } @@ -221,8 +220,7 @@ TEST_F(TrieBatchTest, ConsistentOnFailure) { factory, codec, serializer, - state_pruner, - std::make_shared()) + state_pruner) .value(); auto batch = trie->getPersistentBatchAt(empty_hash, std::nullopt).value(); diff --git a/test/core/storage/trie/trie_storage/trie_storage_test.cpp b/test/core/storage/trie/trie_storage/trie_storage_test.cpp index 4cc4947be6..b881ef29c3 100644 --- a/test/core/storage/trie/trie_storage/trie_storage_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_storage_test.cpp @@ -70,8 +70,7 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { factory, codec, serializer, - state_pruner, - std::make_shared()) + state_pruner) .value(); auto batch = @@ -92,8 +91,7 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { auto storage = TrieStorageImpl::createFromStorage( codec, serializer, - state_pruner, - std::make_shared()) + state_pruner) .value(); auto batch = storage->getPersistentBatchAt(root, std::nullopt).value(); ASSERT_OUTCOME_SUCCESS(v1, batch->get("123"_buf)); diff --git a/test/core/storage/trie_pruner/trie_pruner_test.cpp b/test/core/storage/trie_pruner/trie_pruner_test.cpp index 0821d837ea..0187c71133 100644 --- a/test/core/storage/trie_pruner/trie_pruner_test.cpp +++ b/test/core/storage/trie_pruner/trie_pruner_test.cpp @@ -21,7 +21,7 @@ #include "mock/core/blockchain/block_tree_mock.hpp" #include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/spaced_storage_mock.hpp" -#include "mock/core/storage/trie/polkadot_trie_cursor_mock.h" +#include "mock/core/storage/trie/polkadot_trie_cursor_mock.hpp" #include "mock/core/storage/trie/serialization/codec_mock.hpp" #include "mock/core/storage/trie/serialization/trie_serializer_mock.hpp" #include "mock/core/storage/trie/trie_storage_backend_mock.hpp" diff --git a/test/mock/core/runtime/module_instance_mock.hpp b/test/mock/core/runtime/module_instance_mock.hpp index 2c65f4b135..c6f6f22f04 100644 --- a/test/mock/core/runtime/module_instance_mock.hpp +++ b/test/mock/core/runtime/module_instance_mock.hpp @@ -44,7 +44,5 @@ namespace kagome::runtime { (const, override)); MOCK_METHOD(outcome::result, resetEnvironment, (), (override)); - - MOCK_METHOD(outcome::result, stateless, (), (override)); }; } // namespace kagome::runtime diff --git a/test/mock/core/storage/generic_storage_mock.hpp b/test/mock/core/storage/generic_storage_mock.hpp index 55fd3b1ab8..b89ac79f08 100644 --- a/test/mock/core/storage/generic_storage_mock.hpp +++ b/test/mock/core/storage/generic_storage_mock.hpp @@ -49,6 +49,8 @@ namespace kagome::storage::face { } MOCK_METHOD1_T(remove, outcome::result(const View &)); + MOCK_METHOD1_T(removePrefix, outcome::result(const View &)); + MOCK_METHOD(outcome::result, clear, ()); MOCK_CONST_METHOD0_T(size, size_t()); }; diff --git a/test/mock/core/storage/trie/polkadot_trie_cursor_mock.h b/test/mock/core/storage/trie/polkadot_trie_cursor_mock.hpp similarity index 96% rename from test/mock/core/storage/trie/polkadot_trie_cursor_mock.h rename to test/mock/core/storage/trie/polkadot_trie_cursor_mock.hpp index 38619a7645..681324de3a 100644 --- a/test/mock/core/storage/trie/polkadot_trie_cursor_mock.h +++ b/test/mock/core/storage/trie/polkadot_trie_cursor_mock.hpp @@ -17,7 +17,7 @@ namespace kagome::storage::trie { MOCK_METHOD(outcome::result, seek, (const BufferView &), (override)); - MOCK_METHOD(outcome::result, + MOCK_METHOD(outcome::result, seekLowerBound, (const BufferView &), (override)); diff --git a/test/mock/core/storage/trie/trie_storage_backend_mock.hpp b/test/mock/core/storage/trie/trie_storage_backend_mock.hpp index c27288b9f7..4f8c62787d 100644 --- a/test/mock/core/storage/trie/trie_storage_backend_mock.hpp +++ b/test/mock/core/storage/trie/trie_storage_backend_mock.hpp @@ -45,6 +45,13 @@ namespace kagome::storage::trie { remove, (const BufferView &key), (override)); + + MOCK_METHOD(outcome::result, + removePrefix, + (const BufferView &prefix), + (override)); + + MOCK_METHOD(outcome::result, clear, (), (override)); }; } // namespace kagome::storage::trie diff --git a/test/mock/core/storage/trie/trie_storage_mock.hpp b/test/mock/core/storage/trie/trie_storage_mock.hpp index a342ffb12b..00565c9680 100644 --- a/test/mock/core/storage/trie/trie_storage_mock.hpp +++ b/test/mock/core/storage/trie/trie_storage_mock.hpp @@ -28,8 +28,6 @@ namespace kagome::storage::trie { getProofReaderBatchAt, (const RootHash &root, const OnNodeLoaded &on_node_loaded), (const, override)); - - MOCK_METHOD(BufferStorage &, DEBUG_getDirectStorage, (), (override)); }; } // namespace kagome::storage::trie From b2f8e03d449819d939aa7d9196c0d6db1bc2ffd2 Mon Sep 17 00:00:00 2001 From: Harrm Date: Fri, 6 Jun 2025 17:04:43 +0300 Subject: [PATCH 10/11] Fixup for sync --- core/blockchain/impl/block_tree_impl.hpp | 6 ++++-- .../common/runtime_upgrade_tracker_impl.cpp | 3 +-- core/storage/trie/impl/direct_storage.cpp | 5 +++-- core/storage/trie/impl/direct_storage.hpp | 6 +++--- .../network/state_protocol_observer_test.cpp | 16 +++++++--------- .../runtime/trie_storage_provider_test.cpp | 5 +---- .../trie/trie_storage/trie_batch_test.cpp | 18 ++++++------------ .../trie/trie_storage/trie_storage_test.cpp | 17 ++++++----------- 8 files changed, 31 insertions(+), 45 deletions(-) diff --git a/core/blockchain/impl/block_tree_impl.hpp b/core/blockchain/impl/block_tree_impl.hpp index 6b2792584f..198a08434d 100644 --- a/core/blockchain/impl/block_tree_impl.hpp +++ b/core/blockchain/impl/block_tree_impl.hpp @@ -214,12 +214,14 @@ namespace kagome::blockchain { const ReorgAndPrune &changes); outcome::result getBlockHeaderNoLock( - const BlockTreeData &data, const primitives::BlockHash &block_hash) const; + const BlockTreeData &data, + const primitives::BlockHash &block_hash) const; outcome::result pruneTrie(const BlockTreeData &block_tree_data, primitives::BlockNumber new_finalized); - primitives::BlockInfo getLastFinalizedNoLock(const BlockTreeData &data) const; + primitives::BlockInfo getLastFinalizedNoLock( + const BlockTreeData &data) const; primitives::BlockInfo bestBlockNoLock(const BlockTreeData &data) const; bool hasDirectChainNoLock(const BlockTreeData &data, diff --git a/core/runtime/common/runtime_upgrade_tracker_impl.cpp b/core/runtime/common/runtime_upgrade_tracker_impl.cpp index 1e2a91e435..76edd58010 100644 --- a/core/runtime/common/runtime_upgrade_tracker_impl.cpp +++ b/core/runtime/common/runtime_upgrade_tracker_impl.cpp @@ -179,8 +179,7 @@ namespace kagome::runtime { primitives::events::ChainEventType::kNewRuntime, [this](const primitives::events::ChainEventParams &event_params) { const auto &block_hash = - std::get( - event_params) + std::get(event_params) .get(); auto res = push(block_hash); if (res.has_value() and res.value().second) { diff --git a/core/storage/trie/impl/direct_storage.cpp b/core/storage/trie/impl/direct_storage.cpp index 717f1fe0a7..43f803ca22 100644 --- a/core/storage/trie/impl/direct_storage.cpp +++ b/core/storage/trie/impl/direct_storage.cpp @@ -139,7 +139,7 @@ namespace kagome::storage::trie { std::shared_ptr direct_db, std::shared_ptr diff_db, primitives::events::ChainSubscriptionEnginePtr chain_sub_engine, - //primitives::events::SyncStateSubscriptionEnginePtr sync_sub_engine, + // primitives::events::SyncStateSubscriptionEnginePtr sync_sub_engine, LazySPtr timeline) { std::shared_ptr storage{new DirectStorage{timeline}}; storage->direct_state_db_ = direct_db; @@ -369,7 +369,8 @@ namespace kagome::storage::trie { if (!timeline_.get()->wasSynchronized()) { OUTCOME_TRY(updateDirectState(roots.to)); SL_DEBUG( - logger_, "Since node the node is not synchronized, update to this state."); + logger_, + "Since node the node is not synchronized, update to this state."); } return outcome::success(); } diff --git a/core/storage/trie/impl/direct_storage.hpp b/core/storage/trie/impl/direct_storage.hpp index 32d9675918..0f10921607 100644 --- a/core/storage/trie/impl/direct_storage.hpp +++ b/core/storage/trie/impl/direct_storage.hpp @@ -110,10 +110,10 @@ namespace kagome::storage::trie { RootHash state_root_; std::shared_ptr direct_state_db_; std::shared_ptr diff_db_; - subscription::SubscriptionSetId chain_sub_id_ {}; - //subscription::SubscriptionSetId sync_sub_id_; + subscription::SubscriptionSetId chain_sub_id_{}; + // subscription::SubscriptionSetId sync_sub_id_; primitives::events::ChainEventSubscriberPtr chain_event_sub_; - //primitives::events::SyncStateEventSubscriberPtr sync_event_sub_; + // primitives::events::SyncStateEventSubscriberPtr sync_event_sub_; LazySPtr timeline_; log::Logger logger_ = log::createLogger("DirectStorage", "storage"); diff --git a/test/core/network/state_protocol_observer_test.cpp b/test/core/network/state_protocol_observer_test.cpp index c0cba5ff68..5052dc2d16 100644 --- a/test/core/network/state_protocol_observer_test.cpp +++ b/test/core/network/state_protocol_observer_test.cpp @@ -57,10 +57,7 @@ std::shared_ptr makeEmptyInMemoryTrie() { .WillByDefault(Return(outcome::success())); return kagome::storage::trie::TrieStorageImpl::createEmpty( - trie_factory, - codec, - serializer, - state_pruner) + trie_factory, codec, serializer, state_pruner) .value(); } @@ -74,11 +71,12 @@ BlockHeader makeBlockHeader(RootHash hash) { uint32_t num = 1; std::string str_num = std::to_string(num); return kagome::primitives::BlockHeader{ - .number=num, // number - .parent_hash=makeHash("block_genesis_hash"), // parent - .state_root=hash, // state root - .extrinsics_root=makeHash("block_" + str_num + "_ext_root"), // extrinsics root - .digest={}, // digest + .number = num, // number + .parent_hash = makeHash("block_genesis_hash"), // parent + .state_root = hash, // state root + .extrinsics_root = + makeHash("block_" + str_num + "_ext_root"), // extrinsics root + .digest = {}, // digest }; } diff --git a/test/core/runtime/trie_storage_provider_test.cpp b/test/core/runtime/trie_storage_provider_test.cpp index 0374dc6254..b5b2c97ca7 100644 --- a/test/core/runtime/trie_storage_provider_test.cpp +++ b/test/core/runtime/trie_storage_provider_test.cpp @@ -52,10 +52,7 @@ class TrieStorageProviderTest : public ::testing::Test { std::make_shared(); auto trieDb = kagome::storage::trie::TrieStorageImpl::createEmpty( - trie_factory, - codec, - serializer, - state_pruner) + trie_factory, codec, serializer, state_pruner) .value(); storage_provider_ = diff --git a/test/core/storage/trie/trie_storage/trie_batch_test.cpp b/test/core/storage/trie/trie_storage/trie_batch_test.cpp index 1d688b6d1b..87432fc830 100644 --- a/test/core/storage/trie/trie_storage/trie_batch_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_batch_test.cpp @@ -63,12 +63,9 @@ class TrieBatchTest : public test::BaseRocksDB_Test { testing::A(), _)) .WillByDefault(Return(outcome::success())); - trie = TrieStorageImpl::createEmpty( - factory, - codec, - serializer, - state_pruner) - .value(); + trie = + TrieStorageImpl::createEmpty(factory, codec, serializer, state_pruner) + .value(); } static const std::vector> data; @@ -216,12 +213,9 @@ TEST_F(TrieBatchTest, ConsistentOnFailure) { addNewState(testing::A(), _)) .WillByDefault(Return(outcome::success())); - auto trie = TrieStorageImpl::createEmpty( - factory, - codec, - serializer, - state_pruner) - .value(); + auto trie = + TrieStorageImpl::createEmpty(factory, codec, serializer, state_pruner) + .value(); auto batch = trie->getPersistentBatchAt(empty_hash, std::nullopt).value(); ASSERT_OUTCOME_SUCCESS(batch->put("123"_buf, "111"_buf)); diff --git a/test/core/storage/trie/trie_storage/trie_storage_test.cpp b/test/core/storage/trie/trie_storage/trie_storage_test.cpp index b881ef29c3..633a31a593 100644 --- a/test/core/storage/trie/trie_storage/trie_storage_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_storage_test.cpp @@ -66,12 +66,9 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { testing::A(), _)) .WillByDefault(Return(outcome::success())); - auto storage = TrieStorageImpl::createEmpty( - factory, - codec, - serializer, - state_pruner) - .value(); + auto storage = + TrieStorageImpl::createEmpty(factory, codec, serializer, state_pruner) + .value(); auto batch = storage @@ -88,11 +85,9 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { auto serializer = std::make_shared( factory, codec, std::make_shared(new_rocks_db)); auto state_pruner = std::make_shared(); - auto storage = TrieStorageImpl::createFromStorage( - codec, - serializer, - state_pruner) - .value(); + auto storage = + TrieStorageImpl::createFromStorage(codec, serializer, state_pruner) + .value(); auto batch = storage->getPersistentBatchAt(root, std::nullopt).value(); ASSERT_OUTCOME_SUCCESS(v1, batch->get("123"_buf)); ASSERT_EQ(v1, "abc"_buf); From 45f503ee29430301cf9110e9293cacac70e2987c Mon Sep 17 00:00:00 2001 From: Harrm Date: Tue, 10 Jun 2025 17:37:39 +0300 Subject: [PATCH 11/11] Fixes --- core/storage/trie/impl/direct_storage.cpp | 13 +++++++------ core/storage/trie/impl/direct_storage.hpp | 3 ++- .../storage/trie/impl/ephemeral_trie_batch_impl.cpp | 1 + .../trie/impl/persistent_trie_batch_impl.cpp | 1 + 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/core/storage/trie/impl/direct_storage.cpp b/core/storage/trie/impl/direct_storage.cpp index 43f803ca22..85c6653e5b 100644 --- a/core/storage/trie/impl/direct_storage.cpp +++ b/core/storage/trie/impl/direct_storage.cpp @@ -59,8 +59,6 @@ namespace kagome::storage::trie { // typically pretty small, if we accumulate a lot of diffs it means that // something likely is wrong - constexpr size_t kExpectedMaxDiffNum = 16; - DirectStorageView::DirectStorageView( std::shared_ptr storage, RootHash state_root) : storage_{std::move(storage)}, state_root_{state_root} { @@ -293,7 +291,7 @@ namespace kagome::storage::trie { } outcome::result DirectStorage::updateDirectState( - const RootHash &target_state) { + const RootHash &target_state, bool discardOlderDiffs) { std::vector diffs; RootHash current_state = target_state; size_t idx = 0; @@ -320,8 +318,9 @@ namespace kagome::storage::trie { for (auto root : diffs | std::views::reverse) { OUTCOME_TRY(applyDiff(root)); - OUTCOME_TRY(discardDiff(root)); - state_root_ = root; + if (discardOlderDiffs) { + OUTCOME_TRY(discardDiff(root)); + } } // TODO(Harrm): maybe cleanup now orphaned diffs, although they should be @@ -367,7 +366,7 @@ namespace kagome::storage::trie { roots.from, roots.to); if (!timeline_.get()->wasSynchronized()) { - OUTCOME_TRY(updateDirectState(roots.to)); + OUTCOME_TRY(updateDirectState(roots.to, false)); SL_DEBUG( logger_, "Since node the node is not synchronized, update to this state."); @@ -477,6 +476,8 @@ namespace kagome::storage::trie { OUTCOME_TRY(batch->commit()); SL_DEBUG(logger_, "Applied diff to state {} with {} writes", new_root, num); + state_root_ = new_root; + return outcome::success(); } diff --git a/core/storage/trie/impl/direct_storage.hpp b/core/storage/trie/impl/direct_storage.hpp index 0f10921607..9148bae8d9 100644 --- a/core/storage/trie/impl/direct_storage.hpp +++ b/core/storage/trie/impl/direct_storage.hpp @@ -79,7 +79,8 @@ namespace kagome::storage::trie { outcome::result resetDirectState(const RootHash &new_state_root, const PolkadotTrie &new_state); - outcome::result updateDirectState(const RootHash &target_state); + outcome::result updateDirectState(const RootHash &target_state, + bool discardOlderDiffs = false); struct DiffRoots { const RootHash &from; diff --git a/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp b/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp index 7e23ad0aa5..058befae02 100644 --- a/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp +++ b/core/storage/trie/impl/ephemeral_trie_batch_impl.cpp @@ -18,6 +18,7 @@ namespace kagome::storage::trie { : TrieBatchBase{std::move(codec), std::move(serializer), std::move(trie)}, on_child_node_loaded_{std::move(on_child_node_loaded)} { // on_child_node_loaded_ can be zero + SL_WARN(logger_, "EphemeralTrieBatchImpl created without direct storage"); } EphemeralTrieBatchImpl::EphemeralTrieBatchImpl( diff --git a/core/storage/trie/impl/persistent_trie_batch_impl.cpp b/core/storage/trie/impl/persistent_trie_batch_impl.cpp index de859e018a..c13b290402 100644 --- a/core/storage/trie/impl/persistent_trie_batch_impl.cpp +++ b/core/storage/trie/impl/persistent_trie_batch_impl.cpp @@ -30,6 +30,7 @@ namespace kagome::storage::trie { BOOST_ASSERT((changes_.has_value() && changes_.value() != nullptr) or not changes_.has_value()); BOOST_ASSERT(state_pruner_ != nullptr); + SL_WARN(logger_, "EphemeralTrieBatchImpl created without direct storage"); } PersistentTrieBatchImpl::PersistentTrieBatchImpl(