diff --git a/core/application/app_configuration.hpp b/core/application/app_configuration.hpp index 39293d3629..1c81852401 100644 --- a/core/application/app_configuration.hpp +++ b/core/application/app_configuration.hpp @@ -254,6 +254,8 @@ namespace kagome::application { virtual std::optional statePruningDepth() const = 0; + virtual bool shouldPruneDiscardedStates() const = 0; + /** * @return database state cache size in MiB */ diff --git a/core/application/impl/app_configuration_impl.cpp b/core/application/impl/app_configuration_impl.cpp index 6e11431336..269e775c9a 100644 --- a/core/application/impl/app_configuration_impl.cpp +++ b/core/application/impl/app_configuration_impl.cpp @@ -790,7 +790,7 @@ namespace kagome::application { ("db-cache", po::value()->default_value(def_db_cache_size), "Limit the memory the database cache can use ") ("enable-offchain-indexing", po::value(), "enable Offchain Indexing API, which allow block import to write to offchain DB)") ("recovery", po::value(), "recovers block storage to state after provided block presented by number or hash, and stop after that") - ("state-pruning", po::value()->default_value("archive"), "state pruning policy. 'archive' or the number of finalized blocks to keep.") + ("state-pruning", po::value()->default_value("archive"), "state pruning policy. 'archive', 'prune-discarded', or the number of finalized blocks to keep.") ; po::options_description network_desc("Network options"); @@ -1449,9 +1449,12 @@ namespace kagome::application { if (auto state_pruning_opt = find_argument(vm, "state-pruning"); state_pruning_opt.has_value()) { - const auto& val = state_pruning_opt.value(); + const auto &val = state_pruning_opt.value(); if (val == "archive") { state_pruning_depth_ = std::nullopt; + } else if (val == "prune-discarded") { + state_pruning_depth_ = std::nullopt; + prune_discarded_states_ = true; } else { uint32_t depth{}; auto [_, err] = std::from_chars(&*val.begin(), &*val.end(), depth); diff --git a/core/application/impl/app_configuration_impl.hpp b/core/application/impl/app_configuration_impl.hpp index 0fcefe000a..a1ff4ef520 100644 --- a/core/application/impl/app_configuration_impl.hpp +++ b/core/application/impl/app_configuration_impl.hpp @@ -187,6 +187,9 @@ namespace kagome::application { std::optional statePruningDepth() const override { return state_pruning_depth_; } + bool shouldPruneDiscardedStates() const override { + return state_pruning_depth_.has_value() || prune_discarded_states_; + } std::optional devMnemonicPhrase() const override { if (dev_mnemonic_phrase_) { return *dev_mnemonic_phrase_; @@ -341,6 +344,7 @@ namespace kagome::application { StorageBackend storage_backend_ = StorageBackend::RocksDB; uint32_t db_cache_size_; std::optional state_pruning_depth_; + bool prune_discarded_states_ = false; std::optional dev_mnemonic_phrase_; std::string node_wss_pem_; std::optional benchmark_config_; diff --git a/core/blockchain/impl/block_tree_impl.cpp b/core/blockchain/impl/block_tree_impl.cpp index 77dc6984e8..7c0853fb36 100644 --- a/core/blockchain/impl/block_tree_impl.cpp +++ b/core/blockchain/impl/block_tree_impl.cpp @@ -17,7 +17,6 @@ #include "crypto/blake2/blake2b.h" #include "log/profiling_logger.hpp" #include "storage/database_error.hpp" -#include "storage/trie_pruner/recover_pruner_state.hpp" #include "storage/trie_pruner/trie_pruner.hpp" namespace { @@ -306,8 +305,7 @@ namespace kagome::blockchain { log, "Existing non-finalized block {} is added to block tree", block); } - OUTCOME_TRY( - storage::trie_pruner::recoverPrunerState(*state_pruner, *block_tree)); + OUTCOME_TRY(state_pruner->recoverState(*block_tree)); return block_tree; } diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index b5a494044a..ecade0a23e 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -96,10 +96,11 @@ namespace kagome::consensus::babe { std::unique_lock lock{indexer_mutex_}; auto finalized = block_tree_->getLastFinalized(); auto finalized_header = block_tree_->getBlockHeader(finalized.hash).value(); + if (finalized.number - indexer_.last_finalized_indexed_.number > kMaxUnindexedBlocksNum and trie_storage_->getEphemeralBatchAt(finalized_header.state_root)) { - warp(finalized); + warp(lock, finalized); } auto genesis_res = config({block_tree_->getGenesisBlockHash(), 0}, false); @@ -237,9 +238,14 @@ namespace kagome::consensus::babe { return 0; } + void BabeConfigRepositoryImpl::warp(std::unique_lock &lock, + const primitives::BlockInfo &block) { + indexer_.put(block, {}, true); + } + void BabeConfigRepositoryImpl::warp(const primitives::BlockInfo &block) { std::unique_lock lock{indexer_mutex_}; - indexer_.put(block, {}, true); + warp(lock, block); } outcome::result> diff --git a/core/consensus/babe/impl/babe_config_repository_impl.hpp b/core/consensus/babe/impl/babe_config_repository_impl.hpp index 72ea518548..345a8122a3 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.hpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.hpp @@ -52,7 +52,7 @@ namespace kagome::consensus::babe { std::optional> state; /** * Next epoch lazily computed from `config` and digests. - */ + */ std::optional> next_state; }; @@ -125,6 +125,9 @@ namespace kagome::consensus::babe { outcome::result> loadPrev(const std::optional &prev) const; + void warp(std::unique_lock &lock, + const primitives::BlockInfo &block); + std::shared_ptr persistent_storage_; bool config_warp_sync_; std::shared_ptr block_tree_; diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 162aae9194..f9aa320a04 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -85,6 +85,7 @@ #include "injector/bind_by_lambda.hpp" #include "injector/calculate_genesis_state.hpp" #include "injector/get_peer_keypair.hpp" +#include "injector/idle_trie_pruner.hpp" #include "log/configurator.hpp" #include "log/logger.hpp" #include "metrics/impl/exposer_impl.hpp" @@ -729,7 +730,20 @@ namespace { di::bind.template to(), di::bind.template to(), di::bind.template to(), - di::bind.template to(), + bind_by_lambda( + [](const auto &injector) + -> sptr { + const application::AppConfiguration &config = + injector.template create< + application::AppConfiguration const &>(); + if (config.statePruningDepth() == std::nullopt + && !config.shouldPruneDiscardedStates()) { + return std::make_shared< + storage::trie_pruner::IdleTriePruner>(); + } + return injector.template create< + sptr>(); + }), di::bind.template to(), bind_by_lambda([](const auto &injector) { const application::AppConfiguration &config = diff --git a/core/injector/idle_trie_pruner.hpp b/core/injector/idle_trie_pruner.hpp new file mode 100644 index 0000000000..6dc7f85430 --- /dev/null +++ b/core/injector/idle_trie_pruner.hpp @@ -0,0 +1,52 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "storage/trie_pruner/trie_pruner.hpp" + +namespace kagome::storage::trie_pruner { + + /** + * IdleTriePruner does nothing, so it can be used to disabled pruning + */ + class IdleTriePruner : public storage::trie_pruner::TriePruner { + public: + outcome::result addNewState( + const storage::trie::RootHash &state_root, + storage::trie::StateVersion version) override { + return outcome::success(); + } + + outcome::result addNewState( + const storage::trie::PolkadotTrie &new_trie, + storage::trie::StateVersion version) override { + return outcome::success(); + } + + outcome::result pruneFinalized( + const primitives::BlockHeader &state) override { + return outcome::success(); + } + + outcome::result pruneDiscarded( + const primitives::BlockHeader &state) override { + return outcome::success(); + } + + outcome::result recoverState( + const blockchain::BlockTree &block_tree) override { + return outcome::success(); + } + + std::optional getLastPrunedBlock() const override { + return std::nullopt; + } + + std::optional getPruningDepth() const override { + return std::nullopt; + } + }; +} // namespace kagome::storage::trie_pruner \ No newline at end of file diff --git a/core/runtime/wavm/module.cpp b/core/runtime/wavm/module.cpp index 864c36bbad..0e3cd549ac 100644 --- a/core/runtime/wavm/module.cpp +++ b/core/runtime/wavm/module.cpp @@ -115,7 +115,7 @@ namespace kagome::runtime::wavm { WAVM::Runtime::ImportBindings ModuleImpl::link( IntrinsicResolver &resolver) const { - auto &ir_module = WAVM::Runtime::getModuleIR(module_); + const WAVM::IR::Module &ir_module = WAVM::Runtime::getModuleIR(module_); auto link_result = WAVM::Runtime::linkModule(ir_module, resolver); if (!link_result.success) { diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt index b3b59c8aa1..560bb4d124 100644 --- a/core/storage/CMakeLists.txt +++ b/core/storage/CMakeLists.txt @@ -25,7 +25,6 @@ add_library(storage trie/serialization/trie_serializer_impl.cpp trie/serialization/polkadot_codec.cpp trie_pruner/impl/trie_pruner_impl.cpp - trie_pruner/impl/recover_pruner_state.cpp ) target_link_libraries(storage blob diff --git a/core/storage/trie_pruner/impl/recover_pruner_state.cpp b/core/storage/trie_pruner/impl/recover_pruner_state.cpp deleted file mode 100644 index d754f3e26f..0000000000 --- a/core/storage/trie_pruner/impl/recover_pruner_state.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "storage/trie_pruner/recover_pruner_state.hpp" - -#include "blockchain/block_tree.hpp" -#include "log/logger.hpp" -#include "storage/trie_pruner/trie_pruner.hpp" - -namespace kagome::storage::trie_pruner { - - outcome::result recoverPrunerState( - TriePruner &pruner, const blockchain::BlockTree &block_tree) { - static log::Logger logger = - log::createLogger("PrunerStateRecovery", "storage"); - auto last_pruned_block = pruner.getLastPrunedBlock(); - if (!last_pruned_block.has_value()) { - OUTCOME_TRY(first_hash_opt, block_tree.getBlockHash(1)); - if (first_hash_opt.has_value()) { - SL_WARN(logger, - "Running pruner on a non-empty non-pruned storage may lead to " - "skipping some stored states."); - OUTCOME_TRY( - last_finalized, - block_tree.getBlockHeader(block_tree.getLastFinalized().hash)); - - if (auto res = pruner.restoreState(last_finalized, block_tree); - res.has_error()) { - SL_ERROR(logger, - "Failed to restore trie pruner state starting from last " - "finalized " - "block: {}", - res.error().message()); - return res.as_failure(); - } - } else { - OUTCOME_TRY( - genesis_header, - block_tree.getBlockHeader(block_tree.getGenesisBlockHash())); - OUTCOME_TRY(pruner.addNewState(genesis_header.state_root, - trie::StateVersion::V0)); - } - } else { - OUTCOME_TRY(base_block_header, - block_tree.getBlockHeader(last_pruned_block.value().hash)); - BOOST_ASSERT(block_tree.getLastFinalized().number - >= last_pruned_block.value().number); - if (auto res = pruner.restoreState(base_block_header, block_tree); - res.has_error()) { - SL_WARN(logger, - "Failed to restore trie pruner state starting from base " - "block {}: {}", - last_pruned_block.value(), - res.error().message()); - } - } - return outcome::success(); - } - -} // namespace kagome::storage::trie_pruner \ No newline at end of file diff --git a/core/storage/trie_pruner/impl/trie_pruner_impl.cpp b/core/storage/trie_pruner/impl/trie_pruner_impl.cpp index fbf0d1b174..718d37a41e 100644 --- a/core/storage/trie_pruner/impl/trie_pruner_impl.cpp +++ b/core/storage/trie_pruner/impl/trie_pruner_impl.cpp @@ -267,7 +267,7 @@ namespace kagome::storage::trie_pruner { if (value_ref_it == value_ref_count_.end()) { values_unknown++; } else { - auto& value_ref_count = value_ref_it->second; + auto &value_ref_count = value_ref_it->second; value_ref_count--; if (value_ref_count == 0) { OUTCOME_TRY(batch.remove(hash)); @@ -421,7 +421,55 @@ namespace kagome::storage::trie_pruner { return *root_hash.asHash(); } - outcome::result TriePrunerImpl::restoreState( + outcome::result TriePrunerImpl::recoverState( + const blockchain::BlockTree &block_tree) { + static log::Logger logger = + log::createLogger("PrunerStateRecovery", "storage"); + auto last_pruned_block = getLastPrunedBlock(); + if (!last_pruned_block.has_value()) { + OUTCOME_TRY(first_hash_opt, block_tree.getBlockHash(1)); + if (first_hash_opt.has_value()) { + SL_WARN(logger, + "Running pruner on a non-empty non-pruned storage may lead to " + "skipping some stored states."); + OUTCOME_TRY( + last_finalized, + block_tree.getBlockHeader(block_tree.getLastFinalized().hash)); + + if (auto res = restoreStateAt(last_finalized, block_tree); + res.has_error()) { + SL_ERROR(logger, + "Failed to restore trie pruner state starting from last " + "finalized " + "block: {}", + res.error().message()); + return res.as_failure(); + } + } else { + OUTCOME_TRY( + genesis_header, + block_tree.getBlockHeader(block_tree.getGenesisBlockHash())); + OUTCOME_TRY( + addNewState(genesis_header.state_root, trie::StateVersion::V0)); + } + } else { + OUTCOME_TRY(base_block_header, + block_tree.getBlockHeader(last_pruned_block.value().hash)); + BOOST_ASSERT(block_tree.getLastFinalized().number + >= last_pruned_block.value().number); + if (auto res = restoreStateAt(base_block_header, block_tree); + res.has_error()) { + SL_WARN(logger, + "Failed to restore trie pruner state starting from base " + "block {}: {}", + last_pruned_block.value(), + res.error().message()); + } + } + return outcome::success(); + } + + outcome::result TriePrunerImpl::restoreStateAt( const primitives::BlockHeader &last_pruned_block, const blockchain::BlockTree &block_tree) { KAGOME_PROFILE_START_L(logger_, restore_state); diff --git a/core/storage/trie_pruner/impl/trie_pruner_impl.hpp b/core/storage/trie_pruner/impl/trie_pruner_impl.hpp index bcd0593109..e11dae80b3 100644 --- a/core/storage/trie_pruner/impl/trie_pruner_impl.hpp +++ b/core/storage/trie_pruner/impl/trie_pruner_impl.hpp @@ -90,10 +90,6 @@ namespace kagome::storage::trie_pruner { virtual outcome::result pruneDiscarded( const primitives::BlockHeader &state) override; - virtual outcome::result restoreState( - const primitives::BlockHeader &last_pruned_block, - const blockchain::BlockTree &block_tree) override; - std::optional getLastPrunedBlock() const override { return last_pruned_block_; } @@ -118,7 +114,14 @@ namespace kagome::storage::trie_pruner { return pruning_depth_; } + outcome::result recoverState( + const blockchain::BlockTree &block_tree) override; + private: + outcome::result restoreStateAt( + const primitives::BlockHeader &last_pruned_block, + const blockchain::BlockTree &block_tree); + outcome::result prune(BufferBatch &batch, const storage::trie::RootHash &state); diff --git a/core/storage/trie_pruner/recover_pruner_state.hpp b/core/storage/trie_pruner/recover_pruner_state.hpp deleted file mode 100644 index 35b9307bd6..0000000000 --- a/core/storage/trie_pruner/recover_pruner_state.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright Soramitsu Co., Ltd. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef KAGOME_RECOVER_PRUNER_STATE_HPP -#define KAGOME_RECOVER_PRUNER_STATE_HPP - -#include "outcome/outcome.hpp" - -namespace kagome::blockchain { - class BlockTree; -} - -namespace kagome::storage::trie_pruner { - - class TriePruner; - - outcome::result recoverPrunerState( - TriePruner &pruner, const blockchain::BlockTree &block_tree); - -} - -#endif // KAGOME_RECOVER_PRUNER_STATE_HPP diff --git a/core/storage/trie_pruner/trie_pruner.hpp b/core/storage/trie_pruner/trie_pruner.hpp index 5555df46a8..6ca8b7968f 100644 --- a/core/storage/trie_pruner/trie_pruner.hpp +++ b/core/storage/trie_pruner/trie_pruner.hpp @@ -71,11 +71,10 @@ namespace kagome::storage::trie_pruner { const primitives::BlockHeader &state) = 0; /** - * Resets the pruner state, sets the provided block as the last pruned, - * adds all its children states to the pruner reference counter + * Resets the pruner state, collects info about node reference count starting + * from the last finalized block */ - virtual outcome::result restoreState( - const primitives::BlockHeader &last_pruned_block, + virtual outcome::result recoverState( const blockchain::BlockTree &block_tree) = 0; /** diff --git a/test/core/blockchain/block_tree_test.cpp b/test/core/blockchain/block_tree_test.cpp index c605cfe858..7da67ddcee 100644 --- a/test/core/blockchain/block_tree_test.cpp +++ b/test/core/blockchain/block_tree_test.cpp @@ -136,7 +136,7 @@ struct BlockTreeTest : public testing::Test { return outcome::success(); })); - ON_CALL(*state_pruner_, restoreState(_, _)) + ON_CALL(*state_pruner_, recoverState(_)) .WillByDefault(Return(outcome::success())); ON_CALL(*state_pruner_, pruneDiscarded(_)) diff --git a/test/core/storage/trie_pruner/trie_pruner_test.cpp b/test/core/storage/trie_pruner/trie_pruner_test.cpp index 8a5cdebb2d..76c82f3055 100644 --- a/test/core/storage/trie_pruner/trie_pruner_test.cpp +++ b/test/core/storage/trie_pruner/trie_pruner_test.cpp @@ -25,7 +25,6 @@ #include "storage/trie/serialization/polkadot_codec.hpp" #include "storage/trie/serialization/trie_serializer_impl.hpp" #include "storage/trie_pruner/impl/trie_pruner_impl.hpp" -#include "storage/trie_pruner/recover_pruner_state.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" #include "testutil/prepare_loggers.hpp" @@ -222,7 +221,7 @@ class TriePrunerTest : public testing::Test { hasher, config_mock)); BOOST_ASSERT(pruner->prepare()); - ASSERT_OUTCOME_SUCCESS_TRY(recoverPrunerState(*pruner, block_tree)); + ASSERT_OUTCOME_SUCCESS_TRY(pruner->recoverState(block_tree)); } auto makeTrie(TrieNodeDesc desc) const { @@ -814,7 +813,7 @@ TEST_F(TriePrunerTest, FastSyncScenario) { .WillRepeatedly(Return(DatabaseError::NOT_FOUND)); } - ASSERT_OUTCOME_SUCCESS_TRY(recoverPrunerState(*pruner, *block_tree)); + ASSERT_OUTCOME_SUCCESS_TRY(pruner->recoverState(*block_tree)); for (BlockNumber n = 80; n < LAST_BLOCK_NUMBER; n++) { mock_full_block(n); diff --git a/test/mock/core/application/app_configuration_mock.hpp b/test/mock/core/application/app_configuration_mock.hpp index 30ab8c5a61..81f60826fe 100644 --- a/test/mock/core/application/app_configuration_mock.hpp +++ b/test/mock/core/application/app_configuration_mock.hpp @@ -149,6 +149,8 @@ namespace kagome::application { (), (const, override)); + MOCK_METHOD(bool, shouldPruneDiscardedStates, (), (const, override)); + MOCK_METHOD(StorageBackend, storageBackend, (), (const, override)); MOCK_METHOD(uint32_t, dbCacheSize, (), (const, override)); diff --git a/test/mock/core/storage/trie_pruner/trie_pruner_mock.hpp b/test/mock/core/storage/trie_pruner/trie_pruner_mock.hpp index 9917982e8b..e01e590fe9 100644 --- a/test/mock/core/storage/trie_pruner/trie_pruner_mock.hpp +++ b/test/mock/core/storage/trie_pruner/trie_pruner_mock.hpp @@ -37,9 +37,8 @@ namespace kagome::storage::trie_pruner { (override)); MOCK_METHOD(outcome::result, - restoreState, - (const primitives::BlockHeader &base_block, - const blockchain::BlockTree &block_tree), + recoverState, + (const blockchain::BlockTree &block_tree), (override)); MOCK_METHOD(std::optional,