From deb30936b0c56b3682b13d74186bbde41330f166 Mon Sep 17 00:00:00 2001 From: turuslan Date: Mon, 17 Jul 2023 16:14:27 +0300 Subject: [PATCH 1/9] simplify babe genesis slot Signed-off-by: turuslan --- core/blockchain/block_tree.hpp | 5 + core/consensus/babe/babe.hpp | 16 - core/consensus/babe/babe_error.cpp | 2 + core/consensus/babe/babe_error.hpp | 1 + core/consensus/babe/babe_util.hpp | 37 +- .../babe/impl/babe_config_repository_impl.cpp | 186 ++++---- .../babe/impl/babe_config_repository_impl.hpp | 19 +- .../consensus/babe/impl/babe_digests_util.cpp | 5 + .../consensus/babe/impl/babe_digests_util.hpp | 3 + core/consensus/babe/impl/babe_impl.cpp | 412 ++++-------------- core/consensus/babe/impl/babe_impl.hpp | 17 +- core/consensus/babe/impl/backoff.hpp | 6 +- .../babe/impl/block_appender_base.cpp | 54 +-- core/network/impl/synchronizer_impl.cpp | 121 ----- core/network/impl/synchronizer_impl.hpp | 4 - core/network/synchronizer.hpp | 8 - .../approval/approval_distribution.cpp | 14 +- core/primitives/babe_configuration.hpp | 17 +- core/primitives/block_header.hpp | 7 + core/runtime/runtime_api/babe_api.hpp | 6 + core/runtime/runtime_api/impl/babe_api.cpp | 6 +- core/runtime/runtime_api/impl/babe_api.hpp | 5 +- core/storage/predefined_keys.hpp | 3 + .../trie_pruner/impl/recover_pruner_state.cpp | 3 +- .../babe/babe_config_repository_test.cpp | 10 +- test/core/consensus/babe/babe_test.cpp | 24 +- .../consensus/babe/block_executor_test.cpp | 5 +- test/mock/core/consensus/babe/babe_mock.hpp | 2 - .../core/consensus/babe/babe_util_mock.hpp | 28 +- test/mock/core/network/synchronizer_mock.hpp | 13 - test/mock/core/runtime/babe_api_mock.hpp | 5 + 31 files changed, 282 insertions(+), 762 deletions(-) diff --git a/core/blockchain/block_tree.hpp b/core/blockchain/block_tree.hpp index 4b86d48145..ef12d9d945 100644 --- a/core/blockchain/block_tree.hpp +++ b/core/blockchain/block_tree.hpp @@ -192,6 +192,11 @@ namespace kagome::blockchain { const primitives::BlockHash &ancestor, const primitives::BlockHash &descendant) const = 0; + bool hasDirectChain(const primitives::BlockInfo &ancestor, + const primitives::BlockInfo &descendant) const { + return hasDirectChain(ancestor.hash, descendant.hash); + } + /** * Get a best leaf of the tree * @return best leaf diff --git a/core/consensus/babe/babe.hpp b/core/consensus/babe/babe.hpp index 3c3db1ca2f..f235a7569c 100644 --- a/core/consensus/babe/babe.hpp +++ b/core/consensus/babe/babe.hpp @@ -39,22 +39,6 @@ namespace kagome::consensus::babe { // peer doing block production }; - /** - * Start a Babe production - * @param epoch - epoch, which is going to be run - * epoch.starting_slot_finish_time - when the slot, from which the BABE - * starts, ends; for example, we start from - * 5th slot of the some epoch. Then, we need to set time when 5th slot - * finishes; most probably, that time will be calculated using Median - * algorithm - * - * @note the function will automatically continue launching all further - * epochs of the Babe production - * @note in fact, it is an implementation of "Invoke-Block-Authoring" from - * the spec - */ - virtual void runEpoch(EpochDescriptor epoch) = 0; - /** * @returns current state */ diff --git a/core/consensus/babe/babe_error.cpp b/core/consensus/babe/babe_error.cpp index 6f93b514e3..d18eb45d9f 100644 --- a/core/consensus/babe/babe_error.cpp +++ b/core/consensus/babe/babe_error.cpp @@ -14,6 +14,8 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::consensus::babe, BabeError, e) { return "bad order of digest item; PreRuntime must be first"; case E::UNKNOWN_DIGEST_TYPE: return "unknown type of digest"; + case E::SLOT_BEFORE_GENESIS: + return "slot before genesis"; } return "unknown error"; } diff --git a/core/consensus/babe/babe_error.hpp b/core/consensus/babe/babe_error.hpp index 2b3dcfd660..3460a2b758 100644 --- a/core/consensus/babe/babe_error.hpp +++ b/core/consensus/babe/babe_error.hpp @@ -13,6 +13,7 @@ namespace kagome::consensus::babe { MISSING_PROOF = 1, BAD_ORDER_OF_DIGEST_ITEM, UNKNOWN_DIGEST_TYPE, + SLOT_BEFORE_GENESIS, }; enum class BlockAdditionError { diff --git a/core/consensus/babe/babe_util.hpp b/core/consensus/babe/babe_util.hpp index 5918139267..ddef0cd631 100644 --- a/core/consensus/babe/babe_util.hpp +++ b/core/consensus/babe/babe_util.hpp @@ -8,6 +8,7 @@ #include "consensus/babe/common.hpp" #include "consensus/babe/types/epoch_descriptor.hpp" +#include "primitives/common.hpp" namespace kagome::consensus::babe { @@ -21,47 +22,35 @@ namespace kagome::consensus::babe { virtual ~BabeUtil() = default; /** - * Init inner state by call {@param f} returning first block slot and flag - * if first block is already finalized + * @returns slot for time */ - virtual BabeSlotNumber syncEpoch( - std::function()> &&f) = 0; - - /** - * @returns current unix time slot number - */ - virtual BabeSlotNumber getCurrentSlot() const = 0; + virtual BabeSlotNumber timeToSlot(BabeTimePoint time) const = 0; /** * @returns timepoint of start of slot #{@param slot} */ virtual BabeTimePoint slotStartTime(BabeSlotNumber slot) const = 0; - /** - * @returns duration to start of slot #{@param slot} - */ - virtual BabeDuration remainToStartOfSlot(BabeSlotNumber slot) const = 0; - /** * @returns timepoint of finish of slot #{@param slot} */ virtual BabeTimePoint slotFinishTime(BabeSlotNumber slot) const = 0; /** - * @returns duration to finish of slot #{@param slot} - */ - virtual BabeDuration remainToFinishOfSlot(BabeSlotNumber slot) const = 0; - - /** - * @returns number of epoch by provided {@param slot_number} + * @returns epoch descriptor for given parent and slot */ - virtual EpochNumber slotToEpoch(BabeSlotNumber slot_number) const = 0; + virtual outcome::result slotToEpochDescriptor( + const primitives::BlockInfo &parent_info, + BabeSlotNumber slot_number) = 0; /** - * @returns ordinal number of the slot in the corresponding epoch by - * provided {@param slot_number} + * @returns epoch number for given parent and slot */ - virtual BabeSlotNumber slotInEpoch(BabeSlotNumber slot_number) const = 0; + outcome::result slotToEpoch( + const primitives::BlockInfo &parent_info, BabeSlotNumber slot_number) { + OUTCOME_TRY(epoch, slotToEpochDescriptor(parent_info, slot_number)); + return epoch.epoch_number; + } }; } // namespace kagome::consensus::babe diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index 1efc03e66d..110a92209f 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -30,8 +30,7 @@ namespace kagome::consensus::babe { std::shared_ptr babe_api, std::shared_ptr hasher, std::shared_ptr trie_storage, - primitives::events::ChainSubscriptionEnginePtr chain_events_engine, - const BabeClock &clock) + primitives::events::ChainSubscriptionEnginePtr chain_events_engine) : persistent_storage_( persistent_storage->getSpace(storage::Space::kDefault)), config_warp_sync_{app_config.syncMethod() @@ -46,7 +45,6 @@ namespace kagome::consensus::babe { return std::make_shared( chain_events_engine); }()), - clock_(clock), logger_(log::createLogger("BabeConfigRepo", "babe_config_repo")) { BOOST_ASSERT(persistent_storage_ != nullptr); BOOST_ASSERT(block_tree_ != nullptr); @@ -103,19 +101,12 @@ namespace kagome::consensus::babe { outcome::result BabeConfigRepositoryImpl::load() { const auto finalized_block = block_tree_->getLastFinalized(); - // First, look up slot number of block number 1 sync epochs - if (finalized_block.number > 0) { - OUTCOME_TRY(first_block_hash_opt, block_tree_->getBlockHash(1)); - - OUTCOME_TRY(first_block_header, - block_tree_->getBlockHeader(first_block_hash_opt.value())); - - auto babe_digest_res = getBabeDigests(first_block_header); - BOOST_ASSERT_MSG(babe_digest_res.has_value(), - "Any non genesis block must contain babe digest"); - auto first_slot_number = babe_digest_res.value().second.slot_number; - - syncEpoch([&] { return std::tuple(first_slot_number, true); }); + OUTCOME_TRY(genesis_slot_raw, + persistent_storage_->tryGet( + storage::kBabeConfigRepositoryImplGenesisSlot)); + if (genesis_slot_raw) { + BOOST_OUTCOME_TRY(first_block_slot_number_, + scale::decode(*genesis_slot_raw)); } // 1. Load last state @@ -214,6 +205,8 @@ namespace kagome::consensus::babe { BOOST_ASSERT_MSG(epoch_length, "Epoch length must be greater zero"); const_cast(epoch_length_) = epoch_length; + std::list refs; + // 4. Apply digests before last finalized bool need_to_save = false; for (auto block_number = root_->block.number + 1; @@ -238,10 +231,13 @@ namespace kagome::consensus::babe { block_header_res.error()); return block_header_res.as_failure(); } - const auto &block_header = block_header_res.value(); + const auto &block_header = + refs.emplace_back(std::move(block_header_res.value())); primitives::BlockContext context{ - .block_info = {block_number, block_hash}}; + .block_info = {block_number, block_hash}, + .header = block_header, + }; for (auto &item : block_header.digest) { auto res = visit_in_place( @@ -313,7 +309,8 @@ namespace kagome::consensus::babe { block_header_res.error()); return block_header_res.as_failure(); } - const auto &block_header = block_header_res.value(); + const auto &block_header = + refs.emplace_back(std::move(block_header_res.value())); // This block is finalized if (block_header.number <= finalized_block.number) { @@ -321,7 +318,9 @@ namespace kagome::consensus::babe { } primitives::BlockContext context{ - .block_info = {block_header.number, hash}}; + .block_info = {block_header.number, hash}, + .header = block_header, + }; // This block was meet earlier if (digests.find(context) != digests.end()) { @@ -492,7 +491,9 @@ namespace kagome::consensus::babe { outcome::result BabeConfigRepositoryImpl::onDigest( const primitives::BlockContext &context, const consensus::babe::BabeBlockHeader &digest) { - EpochNumber epoch_number = slotToEpoch(digest.slot_number); + OUTCOME_TRY( + epoch_number, + slotToEpoch(*context.header->get().parentInfo(), digest.slot_number)); auto node = getNode(context); BOOST_ASSERT(node != nullptr); @@ -769,38 +770,15 @@ namespace kagome::consensus::babe { } } - BabeSlotNumber BabeConfigRepositoryImpl::syncEpoch( - std::function()> &&f) { - if (not is_first_block_finalized_) { - auto [first_block_slot_number, is_first_block_finalized] = f(); - first_block_slot_number_.emplace(first_block_slot_number); - is_first_block_finalized_ = is_first_block_finalized; - SL_TRACE( - logger_, - "Epoch beginning is synchronized: first block slot number is {} now", - first_block_slot_number_.value()); - } - return first_block_slot_number_.value(); - } - - BabeSlotNumber BabeConfigRepositoryImpl::getCurrentSlot() const { - return static_cast(clock_.now().time_since_epoch() + BabeSlotNumber BabeConfigRepositoryImpl::timeToSlot( + BabeTimePoint time) const { + return static_cast(time.time_since_epoch() / slotDuration()); } BabeTimePoint BabeConfigRepositoryImpl::slotStartTime( BabeSlotNumber slot) const { - return clock_.zero() + slot * slotDuration(); - } - - BabeDuration BabeConfigRepositoryImpl::remainToStartOfSlot( - BabeSlotNumber slot) const { - auto deadline = slotStartTime(slot); - auto now = clock_.now(); - if (deadline > now) { - return deadline - now; - } - return BabeDuration{}; + return BabeTimePoint{} + slot * slotDuration(); } BabeTimePoint BabeConfigRepositoryImpl::slotFinishTime( @@ -808,58 +786,61 @@ namespace kagome::consensus::babe { return slotStartTime(slot + 1); } - BabeDuration BabeConfigRepositoryImpl::remainToFinishOfSlot( - BabeSlotNumber slot) const { - return remainToStartOfSlot(slot + 1); - } - - BabeSlotNumber BabeConfigRepositoryImpl::getFirstBlockSlotNumber() { - if (first_block_slot_number_.has_value()) { - return first_block_slot_number_.value(); + outcome::result + BabeConfigRepositoryImpl::getFirstBlockSlotNumber( + const primitives::BlockInfo &parent_info) { + auto slot1 = first_block_slot_number_; + if (not slot1) { + auto finalized = block_tree_->getLastFinalized(); + OUTCOME_TRY(parent, block_tree_->getBlockHeader(parent_info.hash)); + if (parent.number == 1) { + BOOST_OUTCOME_TRY(slot1, getBabeSlot(parent)); + } else if (finalized.number != 0) { + OUTCOME_TRY(hash1, block_tree_->getBlockHash(1)); + if (hash1) { + OUTCOME_TRY(header1, block_tree_->getBlockHeader(*hash1)); + BOOST_OUTCOME_TRY(slot1, getBabeSlot(header1)); + } + } + if (not slot1 and trie_storage_->getEphemeralBatchAt(parent.state_root)) { + OUTCOME_TRY(epoch, babe_api_->next_epoch(parent_info.hash)); + slot1 = epoch.start_slot - epoch.epoch_index * epoch.duration; + } + if (not slot1) { + auto header1 = parent; + while (header1.number != 1) { + BOOST_OUTCOME_TRY(header1, + block_tree_->getBlockHeader(header1.parent_hash)); + } + BOOST_OUTCOME_TRY(slot1, getBabeSlot(header1)); + } + if (finalized.number != 0 + and block_tree_->hasDirectChain(finalized, parent_info)) { + first_block_slot_number_ = slot1; + OUTCOME_TRY(persistent_storage_->put( + storage::kBabeConfigRepositoryImplGenesisSlot, + scale::encode(slot1).value())); + } } - - return getCurrentSlot(); + return slot1.value(); } - EpochNumber BabeConfigRepositoryImpl::slotToEpoch(BabeSlotNumber slot) const { - auto genesis_slot_number = - const_cast(*this).getFirstBlockSlotNumber(); - if (slot > genesis_slot_number) { - return (slot - genesis_slot_number) / epochLength(); + outcome::result + BabeConfigRepositoryImpl::slotToEpochDescriptor( + const primitives::BlockInfo &parent_info, BabeSlotNumber slot) { + if (parent_info.number == 0) { + return EpochDescriptor{0, slot}; } - return 0; - } - - BabeSlotNumber BabeConfigRepositoryImpl::slotInEpoch( - BabeSlotNumber slot) const { - auto genesis_slot_number = - const_cast(*this).getFirstBlockSlotNumber(); - if (slot > genesis_slot_number) { - return (slot - genesis_slot_number) % epochLength(); + OUTCOME_TRY(slot1, getFirstBlockSlotNumber(parent_info)); + if (slot < slot1) { + return BabeError::SLOT_BEFORE_GENESIS; } - return 0; + auto slots = slot - slot1; + return EpochDescriptor{slots / epochLength(), slots % epochLength()}; } void BabeConfigRepositoryImpl::readFromState( const primitives::BlockInfo &block) { - auto hash1_opt_res = block_tree_->getBlockHash(1); - if (!hash1_opt_res) { - logger_->error( - "readFromState {}, error: {}", block, hash1_opt_res.error()); - return; - } - if (!hash1_opt_res.value().has_value()) { - logger_->error( - "readFromState {}, error: \"Block #1 not present in the storage\"", - block); - return; - } - auto header1_res = block_tree_->getBlockHeader(*hash1_opt_res.value()); - if (!header1_res) { - logger_->error("readFromState {}, error: {}", block, header1_res.error()); - return; - } - if (auto r = readFromStateOutcome(block); not r) { logger_->error("readFromState {}, error: {}", block, r.error()); } @@ -868,29 +849,20 @@ namespace kagome::consensus::babe { outcome::result BabeConfigRepositoryImpl::readFromStateOutcome( const primitives::BlockInfo &block) { OUTCOME_TRY(header, block_tree_->getBlockHeader(block.hash)); - auto parent = header; - std::optional next_epoch; - while (parent.number != 0) { - if (auto _digest = getNextEpochDigest(parent)) { - next_epoch = std::move(_digest.value()); - break; - } - OUTCOME_TRY(header, block_tree_->getBlockHeader(parent.parent_hash)); - parent = std::move(header); - } OUTCOME_TRY(config, babe_api_->configuration(block.hash)); root_ = BabeConfigNode::createAsRoot( block, std::make_shared(std::move(config))); if (block.number != 0) { - OUTCOME_TRY(digests, getBabeDigests(header)); - root_->epoch = slotToEpoch(digests.second.slot_number); - } - if (next_epoch) { + OUTCOME_TRY(slot, getBabeSlot(header)); + BOOST_OUTCOME_TRY(root_->epoch, slotToEpoch(block, slot)); + OUTCOME_TRY(next, babe_api_->next_epoch(block.hash)); auto config = std::make_shared(*root_->config); - config->authorities = std::move(next_epoch->authorities); - config->randomness = next_epoch->randomness; + config->authorities = std::move(next.authorities); + config->randomness = next.randomness; + config->leadership_rate = next.leadership_rate; + config->allowed_slots = next.allowed_slots; root_->next_config = config; } OUTCOME_TRY( diff --git a/core/consensus/babe/impl/babe_config_repository_impl.hpp b/core/consensus/babe/impl/babe_config_repository_impl.hpp index b2a9ee13f3..55df465b05 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.hpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.hpp @@ -53,8 +53,7 @@ namespace kagome::consensus::babe { std::shared_ptr babe_api, std::shared_ptr hasher, std::shared_ptr trie_storage, - primitives::events::ChainSubscriptionEnginePtr chain_events_engine, - const BabeClock &clock); + primitives::events::ChainSubscriptionEnginePtr chain_events_engine); bool prepare(); @@ -83,18 +82,13 @@ namespace kagome::consensus::babe { // BabeUtil - BabeSlotNumber syncEpoch( - std::function()> &&f) override; - - BabeSlotNumber getCurrentSlot() const override; + BabeSlotNumber timeToSlot(BabeTimePoint time) const override; BabeTimePoint slotStartTime(BabeSlotNumber slot) const override; - BabeDuration remainToStartOfSlot(BabeSlotNumber slot) const override; BabeTimePoint slotFinishTime(BabeSlotNumber slot) const override; - BabeDuration remainToFinishOfSlot(BabeSlotNumber slot) const override; - EpochNumber slotToEpoch(BabeSlotNumber slot) const override; - BabeSlotNumber slotInEpoch(BabeSlotNumber slot) const override; + outcome::result slotToEpochDescriptor( + const primitives::BlockInfo &parent_info, BabeSlotNumber slot) override; private: outcome::result load(); @@ -127,7 +121,8 @@ namespace kagome::consensus::babe { bool directChainExists(const primitives::BlockInfo &ancestor, const primitives::BlockInfo &descendant) const; - BabeSlotNumber getFirstBlockSlotNumber(); + outcome::result getFirstBlockSlotNumber( + const primitives::BlockInfo &parent_info); outcome::result readFromStateOutcome( const primitives::BlockInfo &block); @@ -147,9 +142,7 @@ namespace kagome::consensus::babe { std::shared_ptr root_; primitives::BlockNumber last_saved_state_block_ = 0; - const BabeClock &clock_; std::optional first_block_slot_number_; - bool is_first_block_finalized_ = false; log::Logger logger_; }; diff --git a/core/consensus/babe/impl/babe_digests_util.cpp b/core/consensus/babe/impl/babe_digests_util.cpp index 4bfb4d88d0..2593186eef 100644 --- a/core/consensus/babe/impl/babe_digests_util.cpp +++ b/core/consensus/babe/impl/babe_digests_util.cpp @@ -24,6 +24,11 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::consensus::babe, DigestError, e) { } namespace kagome::consensus::babe { + outcome::result getBabeSlot( + const primitives::BlockHeader &header) { + OUTCOME_TRY(digests, getBabeDigests(header)); + return digests.second.slot_number; + } outcome::result> getBabeDigests( const primitives::BlockHeader &block_header) { diff --git a/core/consensus/babe/impl/babe_digests_util.hpp b/core/consensus/babe/impl/babe_digests_util.hpp index a225cf5da1..6447123155 100644 --- a/core/consensus/babe/impl/babe_digests_util.hpp +++ b/core/consensus/babe/impl/babe_digests_util.hpp @@ -37,6 +37,9 @@ namespace kagome::consensus::babe { [](const auto &) { return std::nullopt; }); } + outcome::result getBabeSlot( + const primitives::BlockHeader &header); + outcome::result> getBabeDigests( const primitives::BlockHeader &header); diff --git a/core/consensus/babe/impl/babe_impl.cpp b/core/consensus/babe/impl/babe_impl.cpp index d3f194b558..d475c1bacd 100644 --- a/core/consensus/babe/impl/babe_impl.cpp +++ b/core/consensus/babe/impl/babe_impl.cpp @@ -26,7 +26,6 @@ #include "crypto/sr25519_provider.hpp" #include "network/block_announce_transmitter.hpp" #include "network/helpers/peer_id_formatter.hpp" -#include "network/peer_manager.hpp" #include "network/synchronizer.hpp" #include "network/types/collator_messages.hpp" #include "network/warp/protocol.hpp" @@ -48,10 +47,15 @@ namespace kagome::consensus::babe { using ParachainInherentData = network::ParachainInherentData; using SyncMethod = application::AppConfiguration::SyncMethod; + inline auto fmtRemains(const BabeClock &clock, BabeTimePoint time) { + auto ms = std::chrono::duration_cast( + std::max(time - clock.now(), {})); + return fmt::format("{:.2f} sec", ms.count() / 1000.0); + } + BabeImpl::BabeImpl( const application::AppConfiguration &app_config, std::shared_ptr app_state_manager, - std::shared_ptr peer_manager, std::shared_ptr lottery, std::shared_ptr babe_config_repo, std::shared_ptr proposer, @@ -81,7 +85,6 @@ namespace kagome::consensus::babe { primitives::events::BabeStateSubscriptionEnginePtr babe_status_observable) : sync_method_(app_config.syncMethod()), app_state_manager_(app_state_manager), - peer_manager_{std::move(peer_manager)}, lottery_{std::move(lottery)}, babe_config_repo_{std::move(babe_config_repo)}, proposer_{std::move(proposer)}, @@ -189,15 +192,7 @@ namespace kagome::consensus::babe { } // namespace bool BabeImpl::prepare() { - auto initial_epoch_res = getInitialEpochDescriptor(); - if (initial_epoch_res.has_error()) { - SL_CRITICAL(log_, - "Can't get initial epoch descriptor: {}", - initial_epoch_res.error()); - return false; - } - - best_block_ = block_tree_->bestLeaf(); + updateSlot(clock_->now()); auto best_block_header_res = block_tree_->getBlockHeader(best_block_.hash); if (best_block_header_res.has_error()) { @@ -212,10 +207,8 @@ namespace kagome::consensus::babe { // Calculate lag our best block by slots BabeSlotNumber lag_slots = 0; - if (auto babe_digests_res = getBabeDigests(best_block_header); - babe_digests_res.has_value()) { - auto &[seal, babe_header] = babe_digests_res.value(); - lag_slots = babe_util_->getCurrentSlot() - babe_header.slot_number; + if (auto slot_res = getBabeSlot(best_block_header)) { + lag_slots = babe_util_->timeToSlot(clock_->now()) - slot_res.value(); } auto &&[warp_sync_duration, fast_sync_duration, full_sync_duration] = @@ -285,8 +278,6 @@ namespace kagome::consensus::babe { break; } - current_epoch_ = initial_epoch_res.value(); - chain_sub_->subscribe(chain_sub_->generateSubscriptionSetId(), primitives::events::ChainEventType::kFinalizedHeads); chain_sub_->setCallback([wp = weak_from_this()]( @@ -297,7 +288,6 @@ namespace kagome::consensus::babe { &event) { if (type == primitives::events::ChainEventType::kFinalizedHeads) { if (auto self = wp.lock()) { - self->warpSync(); if (self->current_state_ != Babe::State::HEADERS_LOADING and self->current_state_ != Babe::State::STATE_LOADING) { const auto &header = @@ -326,7 +316,7 @@ namespace kagome::consensus::babe { } bool BabeImpl::start() { - best_block_ = block_tree_->bestLeaf(); + updateSlot(clock_->now()); SL_DEBUG(log_, "Babe is starting with syncing from block {}", best_block_); @@ -375,107 +365,24 @@ namespace kagome::consensus::babe { return true; } - outcome::result BabeImpl::getInitialEpochDescriptor() { - auto best_block = block_tree_->bestLeaf(); - - if (best_block.number == 0) { - EpochDescriptor epoch_descriptor{ - .epoch_number = 0, - .start_slot = - static_cast(clock_->now().time_since_epoch() - / babe_config_repo_->slotDuration()) - + 1}; - return outcome::success(epoch_descriptor); - } - - // Look up slot number of best block - auto best_block_header_res = block_tree_->getBlockHeader(best_block.hash); - BOOST_ASSERT_MSG(best_block_header_res.has_value(), - "Best block must be known whenever"); - const auto &best_block_header = best_block_header_res.value(); - auto babe_digest_res = getBabeDigests(best_block_header); - BOOST_ASSERT_MSG(babe_digest_res.has_value(), - "Any non genesis block must contain babe digest"); - auto last_slot_number = babe_digest_res.value().second.slot_number; - - EpochDescriptor epoch_descriptor{ - .epoch_number = babe_util_->slotToEpoch(last_slot_number), - .start_slot = - last_slot_number - babe_util_->slotInEpoch(last_slot_number)}; - - return outcome::success(epoch_descriptor); - } - - void BabeImpl::adjustEpochDescriptor() { - auto first_slot_number = babe_util_->syncEpoch([&]() { - auto hash_res = block_tree_->getBlockHash(primitives::BlockNumber(1)); - if (hash_res.has_error() || !hash_res.value().has_value()) { - SL_TRACE(log_, - "First block slot is {}: no first block (at adjusting)", - babe_util_->getCurrentSlot()); - return std::tuple(babe_util_->getCurrentSlot(), false); - } - - auto first_block_header_res = - block_tree_->getBlockHeader(*hash_res.value()); - if (first_block_header_res.has_error()) { - SL_CRITICAL(log_, - "Database is not consistent: " - "Not found block header for existing num-to-hash record"); - throw std::runtime_error( - "Not found block header for existing num-to-hash record"); - } - - const auto &first_block_header = first_block_header_res.value(); - auto babe_digest_res = getBabeDigests(first_block_header); - BOOST_ASSERT_MSG(babe_digest_res.has_value(), - "Any non genesis block must contain babe digest"); - auto first_slot_number = babe_digest_res.value().second.slot_number; - - auto is_first_block_finalized = - block_tree_->getLastFinalized().number > 0; - - SL_TRACE( - log_, - "First block slot is {}: by {}finalized first block (at adjusting)", - first_slot_number, - is_first_block_finalized ? "" : "non-"); - return std::tuple(first_slot_number, is_first_block_finalized); - }); - - auto current_epoch_start_slot = - first_slot_number - + current_epoch_.epoch_number * babe_config_repo_->epochLength(); - - if (current_epoch_.start_slot != current_epoch_start_slot) { - SL_WARN(log_, - "Start-slot of current epoch {} has updated from {} to {}", - current_epoch_.epoch_number, - current_epoch_.start_slot, - current_epoch_start_slot); - - current_epoch_.start_slot = current_epoch_start_slot; + bool BabeImpl::updateSlot(BabeTimePoint now) { + best_block_ = block_tree_->bestLeaf(); + current_slot_ = babe_util_->timeToSlot(now); + auto epoch_res = + babe_util_->slotToEpochDescriptor(best_block_, current_slot_); + if (not epoch_res) { + SL_ERROR(log_, "updateSlot: can't get epoch: {}", epoch_res.error()); + return false; } + current_epoch_ = epoch_res.value(); + return true; } - void BabeImpl::runEpoch(EpochDescriptor epoch) { + void BabeImpl::runEpoch() { bool already_active = false; if (not active_.compare_exchange_strong(already_active, true)) { return; } - - adjustEpochDescriptor(); - - SL_DEBUG(log_, - "Starting an epoch {}. Secondary slots allowed={}", - epoch.epoch_number, - babe_config_repo_ - ->config({.block_info = best_block_}, epoch.epoch_number) - ->get() - .isSecondarySlotsAllowed()); - current_epoch_ = epoch; - current_slot_ = current_epoch_.start_slot; - runSlot(); } @@ -604,43 +511,12 @@ namespace kagome::consensus::babe { }); } - bool BabeImpl::canWarpSync() const { - return sync_method_ == SyncMethod::Warp - and current_state_ == State::HEADERS_LOADING - and block_tree_->getLastFinalized().number >= 1 - and block_tree_->getBlockHash(1); - } - - void BabeImpl::warpSync() { - if (not canWarpSync()) { - return; - } - if (warp_sync_busy_) { - return; - } - auto target = warp_sync_->request(); - auto finalized = block_tree_->getLastFinalized().number; - std::optional> - warp; - peer_manager_->forEachPeer([&](const libp2p::peer::PeerId &peer_id) { - if (warp) { - return; - } - if (auto state = peer_manager_->getPeerState(peer_id)) { - auto &peer_last = state->get().best_block.number; - if (target ? peer_last > target->number : peer_last >= finalized) { - warp.emplace(peer_id, peer_last); - } - } - }); - if (warp) { - warpSync(warp->first, warp->second); - } - } - bool BabeImpl::warpSync(const libp2p::peer::PeerId &peer_id, primitives::BlockNumber block_number) { - if (not canWarpSync()) { + if (current_state_ != State::HEADERS_LOADING) { + return false; + } + if (sync_method_ != SyncMethod::Warp) { return false; } auto target = warp_sync_->request(); @@ -800,52 +676,32 @@ namespace kagome::consensus::babe { block_at_state, peer_id); - synchronizer_->syncBabeDigest( + synchronizer_->syncState( peer_id, block_at_state, - [=, weak{weak_from_this()}](outcome::result res) { - auto self = weak.lock(); - if (not self) { - return; - } - if (not res) { - SL_WARN(self->log_, - "Syncing of babe digests with {} on block {} is failed: {}", - peer_id, - block_at_state, - res.error()); - return; + [wp = weak_from_this(), block_at_state, peer_id](auto res) mutable { + if (auto self = wp.lock()) { + if (res.has_error()) { + SL_WARN(self->log_, + "Syncing of state with {} on block {} is failed: {}", + peer_id, + block_at_state, + res.error()); + return; + } + + self->babe_config_repo_->readFromState(block_at_state); + self->justification_observer_->reload(); + self->block_tree_->notifyBestAndFinalized(); + + SL_INFO(self->log_, + "State on block {} is synced successfully", + block_at_state); + self->current_state_ = Babe::State::CATCHING_UP; + self->babe_status_observable_->notify( + primitives::events::BabeStateEventType::kSyncState, + self->current_state_); } - synchronizer_->syncState( - peer_id, - block_at_state, - [wp = weak_from_this(), block_at_state, peer_id]( - auto res) mutable { - if (auto self = wp.lock()) { - if (res.has_error()) { - SL_WARN( - self->log_, - "Syncing of state with {} on block {} is failed: {}", - peer_id, - block_at_state, - res.error()); - return; - } - - self->adjustEpochDescriptor(); - self->babe_config_repo_->readFromState(block_at_state); - self->justification_observer_->reload(); - self->block_tree_->notifyBestAndFinalized(); - - SL_INFO(self->log_, - "State on block {} is synced successfully", - block_at_state); - self->current_state_ = Babe::State::CATCHING_UP; - self->babe_status_observable_->notify( - primitives::events::BabeStateEventType::kSyncState, - self->current_state_); - } - }); }); } @@ -855,10 +711,9 @@ namespace kagome::consensus::babe { if (not was_synchronized_) { auto header_opt = block_tree_->getBlockHeader(block.hash); BOOST_ASSERT_MSG(header_opt.has_value(), "Just added block; deq"); - auto res = getBabeDigests(header_opt.value()); - if (res.has_value()) { - auto &[_, babe_header] = res.value(); - if (babe_util_->getCurrentSlot() > babe_header.slot_number + 1) { + auto slot_res = getBabeSlot(header_opt.value()); + if (slot_res.has_value()) { + if (babe_util_->timeToSlot(clock_->now()) > slot_res.value() + 1) { current_state_ = Babe::State::WAIT_REMOTE_STATUS; babe_status_observable_->notify( primitives::events::BabeStateEventType::kSyncState, @@ -886,7 +741,7 @@ namespace kagome::consensus::babe { if (not active_) { best_block_ = block_tree_->bestLeaf(); SL_DEBUG(log_, "Babe is synchronized on block {}", best_block_); - runEpoch(current_epoch_); + runEpoch(); } } @@ -895,78 +750,48 @@ namespace kagome::consensus::babe { } void BabeImpl::runSlot() { - bool rewind_slots; // NOLINT - auto slot = current_slot_; - - clock::SystemClock::TimePoint now; - do { - // check that we are really in the middle of the slot, as expected; we - // can cooperate with a relatively little (kMaxLatency) latency, as our - // node will be able to retrieve - now = clock_->now(); - - auto finish_time = babe_util_->slotFinishTime(current_slot_); - - rewind_slots = now > finish_time - and (now - finish_time) > babe_config_repo_->slotDuration(); - - if (rewind_slots) { - // we are too far behind; after skipping some slots (but not epochs) - // control will be returned to this method - - ++current_slot_; - - if (current_epoch_.epoch_number - != babe_util_->slotToEpoch(current_slot_)) { - startNextEpoch(); - } else { - adjustEpochDescriptor(); + auto now = clock_->now(); + if (not updateSlot(now)) { + timer_->expiresAt(babe_util_->slotFinishTime(current_slot_)); + timer_->asyncWait([this](boost::system::error_code ec) { + if (ec) { + log_->error("error happened while waiting on the timer: {}", ec); + return; } - } else if (slot < current_slot_) { - SL_VERBOSE(log_, "Slots {}..{} was skipped", slot, current_slot_ - 1); - } - } while (rewind_slots); + runSlot(); + }); + return; + } SL_VERBOSE(log_, - "Starting a slot {} in epoch {} (remains {:.2f} sec.)", + "Starting a slot {} in epoch {} (remains {})", current_slot_, current_epoch_.epoch_number, - std::chrono::duration_cast( - babe_util_->remainToFinishOfSlot(current_slot_)) - .count() - / 1000.); + fmtRemains(*clock_, babe_util_->slotFinishTime(current_slot_))); processSlot(now); } void BabeImpl::processSlot(clock::SystemClock::TimePoint slot_timestamp) { - best_block_ = block_tree_->bestLeaf(); - - // Resolve slot collisions: if best block slot greater than current slot, - // that select his ancestor as best - for (;;) { - const auto &hash = best_block_.hash; - const auto header_res = block_tree_->getBlockHeader(hash); - BOOST_ASSERT(header_res.has_value()); - const auto &header = header_res.value(); - const auto babe_digests_res = getBabeDigests(header); - if (babe_digests_res.has_value()) { - const auto &babe_digests = babe_digests_res.value(); - auto best_block_slot = babe_digests.second.slot_number; - if (current_slot_ > best_block_slot) { // Condition met - break; - } - SL_DEBUG(log_, "Detected collision in slot {}", current_slot_); - // Shift to parent block and check again - best_block_ = - primitives::BlockInfo(header.number - 1, header.parent_hash); - continue; + if (best_block_.number != 0) { + auto parent_header = + block_tree_->getBlockHeader(best_block_.hash).value(); + auto parent_slot = getBabeSlot(parent_header).value(); + if (parent_slot > current_slot_) { + SL_ERROR(log_, + "best block {} from future, current slot {}, clock is lagging", + best_block_, + current_slot_); + return; } - if (best_block_.number == 0) { - // Only genesis block header might not have a babe digest - break; + if (parent_slot == current_slot_) { + // fork or wait next slot + SL_INFO(log_, + "concurrent best block {}, current slot {}, will fork", + best_block_, + current_slot_); + best_block_ = *parent_header.parentInfo(); } - BOOST_ASSERT(babe_digests_res.has_value()); } auto babe_config_opt = babe_config_repo_->config( @@ -1052,24 +877,13 @@ namespace kagome::consensus::babe { current_slot_, current_epoch_.epoch_number); - ++current_slot_; - - if (current_epoch_.epoch_number != babe_util_->slotToEpoch(current_slot_)) { - startNextEpoch(); - } else { - adjustEpochDescriptor(); - } - - auto start_time = babe_util_->slotStartTime(current_slot_); + auto start_time = babe_util_->slotFinishTime(current_slot_); SL_DEBUG(log_, - "Slot {} in epoch {} will start after {:.2f} sec.", - current_slot_, + "Slot {} in epoch {} will start after {}.", + current_slot_ + 1, current_epoch_.epoch_number, - std::chrono::duration_cast( - babe_util_->remainToStartOfSlot(current_slot_)) - .count() - / 1000.); + fmtRemains(*clock_, babe_util_->slotFinishTime(current_slot_))); // everything is OK: wait for the end of the slot timer_->expiresAt(start_time); @@ -1268,9 +1082,8 @@ namespace kagome::consensus::babe { // add seal digest item block.header.digest.emplace_back(seal_res.value()); - if (babe_util_->remainToFinishOfSlot(current_slot_ + kMaxBlockSlotsOvertime) - .count() - == 0) { + if (clock_->now() + >= babe_util_->slotFinishTime(current_slot_ + kMaxBlockSlotsOvertime)) { SL_WARN(log_, "Block was not built in time. " "Allowed slots ({}) have passed. " @@ -1337,7 +1150,7 @@ namespace kagome::consensus::babe { "Announced block number {} in slot {} (epoch {}) with timestamp {}", block.header.number, current_slot_, - babe_util_->slotToEpoch(current_slot_), + current_epoch_.epoch_number, now); last_finalized_block = block_tree_->getLastFinalized(); @@ -1369,51 +1182,4 @@ namespace kagome::consensus::babe { lottery_->changeEpoch(epoch, babe_config.randomness, threshold, *keypair_); } - - void BabeImpl::startNextEpoch() { - SL_DEBUG(log_, - "Epoch {} has finished. Start epoch {}", - current_epoch_.epoch_number, - current_epoch_.epoch_number + 1); - - ++current_epoch_.epoch_number; - current_epoch_.start_slot = current_slot_; - - babe_util_->syncEpoch([&]() { - auto hash_opt_res = block_tree_->getBlockHash(primitives::BlockNumber(1)); - if (hash_opt_res.has_error() || !hash_opt_res.value().has_value()) { - SL_TRACE(log_, - "First block slot is {}: no first block (at start next epoch)", - babe_util_->getCurrentSlot()); - return std::tuple(babe_util_->getCurrentSlot(), false); - } - - auto first_block_header_res = - block_tree_->getBlockHeader(*hash_opt_res.value()); - if (first_block_header_res.has_error()) { - SL_CRITICAL(log_, - "Database is not consistent: " - "Not found block header for existing num-to-hash record"); - throw std::runtime_error( - "Not found block header for existing num-to-hash record"); - } - - const auto &first_block_header = first_block_header_res.value(); - auto babe_digest_res = getBabeDigests(first_block_header); - BOOST_ASSERT_MSG(babe_digest_res.has_value(), - "Any non genesis block must contain babe digest"); - auto first_slot_number = babe_digest_res.value().second.slot_number; - - auto is_first_block_finalized = - block_tree_->getLastFinalized().number > 0; - - SL_WARN(log_, - "First block slot is {}: " - "by {}finalized first block (at start next epoch)", - first_slot_number, - is_first_block_finalized ? "" : "non-"); - return std::tuple(first_slot_number, is_first_block_finalized); - }); - } - } // namespace kagome::consensus::babe diff --git a/core/consensus/babe/impl/babe_impl.hpp b/core/consensus/babe/impl/babe_impl.hpp index 7cf54d57da..11e1e052c8 100644 --- a/core/consensus/babe/impl/babe_impl.hpp +++ b/core/consensus/babe/impl/babe_impl.hpp @@ -55,7 +55,6 @@ namespace kagome::network { class BlockAnnounceTransmitter; class WarpSync; class WarpProtocol; - class PeerManager; } // namespace kagome::network namespace kagome::runtime { @@ -95,7 +94,6 @@ namespace kagome::consensus::babe { BabeImpl( const application::AppConfiguration &app_config, std::shared_ptr app_state_manager, - std::shared_ptr peer_manager, std::shared_ptr lottery, std::shared_ptr babe_config_repo, std::shared_ptr proposer, @@ -133,7 +131,7 @@ namespace kagome::consensus::babe { /** @see AppStateManager::takeControl */ bool start(); - void runEpoch(EpochDescriptor epoch) override; + void runEpoch(); State getCurrentState() const override; @@ -147,9 +145,6 @@ namespace kagome::consensus::babe { bool wasSynchronized() const override; private: - bool canWarpSync() const; - void warpSync(); - /** * Warp sync from `peer_id` if `block_number`. * @return false if can't warp sync @@ -157,9 +152,7 @@ namespace kagome::consensus::babe { bool warpSync(const libp2p::peer::PeerId &peer_id, primitives::BlockNumber block_number); - outcome::result getInitialEpochDescriptor(); - - void adjustEpochDescriptor(); + bool updateSlot(BabeTimePoint now); void startCatchUp(const libp2p::peer::PeerId &peer_id, const primitives::BlockInfo &target_block); @@ -189,11 +182,6 @@ namespace kagome::consensus::babe { std::optional> output, primitives::AuthorityIndex authority_index); - /** - * Finish the Babe epoch - */ - void startNextEpoch(); - void changeLotteryEpoch( const EpochDescriptor &epoch, primitives::AuthorityIndex authority_index, @@ -209,7 +197,6 @@ namespace kagome::consensus::babe { application::AppConfiguration::SyncMethod sync_method_; std::shared_ptr app_state_manager_; - std::shared_ptr peer_manager_; std::shared_ptr lottery_; std::shared_ptr babe_config_repo_; std::shared_ptr proposer_; diff --git a/core/consensus/babe/impl/backoff.hpp b/core/consensus/babe/impl/backoff.hpp index 9f6371f0fa..f3661a423a 100644 --- a/core/consensus/babe/impl/backoff.hpp +++ b/core/consensus/babe/impl/backoff.hpp @@ -20,11 +20,11 @@ namespace kagome::consensus::babe { constexpr auto kUnfinalizedSlack = 50; constexpr auto kAuthoringBias = 2; - auto digest = getBabeDigests(best); - if (not digest) { + auto slot_res = getBabeSlot(best); + if (not slot_res) { return false; } - auto best_slot = digest.value().second.slot_number; + auto best_slot = slot_res.value(); if (slot <= best_slot) { return false; } diff --git a/core/consensus/babe/impl/block_appender_base.cpp b/core/consensus/babe/impl/block_appender_base.cpp index cf29b9ee8f..1edda70e35 100644 --- a/core/consensus/babe/impl/block_appender_base.cpp +++ b/core/consensus/babe/impl/block_appender_base.cpp @@ -153,50 +153,9 @@ namespace kagome::consensus::babe { auto slot_number = babe_header.slot_number; - babe_util_->syncEpoch([&] { - auto hash_res = block_tree_->getBlockHash(primitives::BlockNumber(1)); - if (hash_res.has_error() || !hash_res.value().has_value()) { - if (block.header.number == 1) { - SL_TRACE(logger_, - "First block slot is {}: it is first block (at executing)", - slot_number); - return std::tuple(slot_number, false); - } else { - SL_TRACE(logger_, - "First block slot is {}: no first block (at executing)", - babe_util_->getCurrentSlot()); - return std::tuple(babe_util_->getCurrentSlot(), false); - } - } - - auto first_block_header_res = - block_tree_->getBlockHeader(*hash_res.value()); - if (first_block_header_res.has_error()) { - SL_CRITICAL(logger_, - "Database is not consistent: " - "Not found block header for existing num-to-hash record"); - throw std::runtime_error( - "Not found block header for existing num-to-hash record"); - } - - const auto &first_block_header = first_block_header_res.value(); - auto babe_digest_res = getBabeDigests(first_block_header); - BOOST_ASSERT_MSG(babe_digest_res.has_value(), - "Any non genesis block must contain babe digest"); - auto first_slot_number = babe_digest_res.value().second.slot_number; - - auto is_first_block_finalized = - block_tree_->getLastFinalized().number > 0; - - SL_TRACE( - logger_, - "First block slot is {}: by {}finalized first block (at executing)", - first_slot_number, - is_first_block_finalized ? "" : "non-"); - return std::tuple(first_slot_number, is_first_block_finalized); - }); - - auto epoch_number = babe_util_->slotToEpoch(slot_number); + OUTCOME_TRY( + epoch_number, + babe_util_->slotToEpoch(*block.header.parentInfo(), slot_number)); SL_VERBOSE( logger_, @@ -249,12 +208,7 @@ namespace kagome::consensus::babe { outcome::result BlockAppenderBase::getSlotInfo( const primitives::BlockHeader &header) const { - OUTCOME_TRY(babe_digests, getBabeDigests(header)); - - const auto &babe_header = babe_digests.second; - - auto slot_number = babe_header.slot_number; - + OUTCOME_TRY(slot_number, getBabeSlot(header)); auto start_time = babe_util_->slotStartTime(slot_number); auto slot_duration = babe_config_repo_->slotDuration(); return outcome::success(SlotInfo{start_time, slot_duration}); diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index 82269c1d36..627aa81c3a 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -52,7 +52,6 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::network, SynchronizerImpl::Error, e) { namespace { constexpr const char *kImportQueueLength = "kagome_import_queue_blocks_submitted"; - constexpr uint32_t kBabeDigestBatch = 100; kagome::network::BlockAttributes attributesForSync( kagome::application::AppConfiguration::SyncMethod method) { @@ -973,126 +972,6 @@ namespace kagome::network { protocol->request(peer_id, std::move(request), std::move(response_handler)); } - void SynchronizerImpl::syncBabeDigest(const libp2p::peer::PeerId &peer_id, - const primitives::BlockInfo &_block, - CbResultVoid &&cb) { - auto block = _block; - - auto hash_res = block_tree_->getBlockHash(1); - if (!hash_res) { - SL_ERROR( - log_, "Error retrieving the first block hash: {}", hash_res.error()); - return; - } - auto& hash_opt = hash_res.value(); - // BabeConfigRepositoryImpl first block slot - if (not hash_opt or not block_tree_->getBlockHeader(hash_opt.value())) { - auto cb2 = [=, cb{std::move(cb)}, weak{weak_from_this()}]( - outcome::result _res) mutable { - auto self = weak.lock(); - if (not self) { - return; - } - if (not _res) { - cb(_res.error()); - return; - } - auto &res = _res.value(); - if (res.blocks.empty()) { - cb(Error::EMPTY_RESPONSE); - return; - } - auto &header = res.blocks[0].header; - if (not header) { - cb(Error::RESPONSE_WITHOUT_BLOCK_HEADER); - return; - } - if (header->number != 1) { - cb(Error::INVALID_HASH); - return; - } - if (header->parent_hash != block_tree_->getGenesisBlockHash()) { - cb(Error::INVALID_HASH); - return; - } - auto hash = self->block_storage_->putBlockHeader(*header).value(); - if (header->number < self->block_tree_->getLastFinalized().number) { - self->block_storage_->assignNumberToHash({header->number, hash}) - .value(); - } - self->syncBabeDigest(peer_id, block, std::move(cb)); - }; - router_->getSyncProtocol()->request( - peer_id, - {BlockAttribute::HEADER, 1, Direction::DESCENDING, 1}, - std::move(cb2)); - return; - } - // BabeConfigRepositoryImpl NextEpoch - while (block.number != 0) { - if (auto _header = block_tree_->getBlockHeader(block.hash)) { - auto &header = _header.value(); - if (consensus::babe::getNextEpochDigest(header)) { - break; - } - block = {header.number - 1, header.parent_hash}; - continue; - } - auto cb2 = [=, weak{weak_from_this()}, cb{std::move(cb)}]( - outcome::result _res) mutable { - auto self = weak.lock(); - if (not self) { - return; - } - if (not _res) { - cb(_res.error()); - return; - } - auto &res = _res.value(); - if (res.blocks.empty()) { - cb(Error::EMPTY_RESPONSE); - return; - } - for (auto &item : res.blocks) { - auto &header = item.header; - if (not header) { - cb(Error::RESPONSE_WITHOUT_BLOCK_HEADER); - return; - } - primitives::BlockInfo info{ - header->number, - self->hasher_->blake2b_256(scale::encode(*header).value())}; - if (info != block) { - cb(Error::INVALID_HASH); - return; - } - self->block_storage_->putBlockHeader(*header).value(); - if (block.number < self->block_tree_->getLastFinalized().number) { - self->block_storage_->assignNumberToHash(block).value(); - } - if (consensus::babe::getNextEpochDigest(*header)) { - cb(outcome::success()); - return; - } - if (block.number != 0) { - block = {header->number - 1, header->parent_hash}; - } - } - self->syncBabeDigest(peer_id, block, std::move(cb)); - }; - router_->getSyncProtocol()->request(peer_id, - { - BlockAttribute::HEADER, - block.hash, - Direction::DESCENDING, - kBabeDigestBatch, - }, - std::move(cb2)); - return; - } - cb(outcome::success()); - } - void SynchronizerImpl::syncState(const libp2p::peer::PeerId &peer_id, const primitives::BlockInfo &block, SyncResultHandler &&handler) { diff --git a/core/network/impl/synchronizer_impl.hpp b/core/network/impl/synchronizer_impl.hpp index 282df8f252..311477e5e1 100644 --- a/core/network/impl/synchronizer_impl.hpp +++ b/core/network/impl/synchronizer_impl.hpp @@ -138,10 +138,6 @@ namespace kagome::network { const primitives::BlockInfo &block, SyncResultHandler &&handler) override; - void syncBabeDigest(const libp2p::peer::PeerId &peer_id, - const primitives::BlockInfo &block, - CbResultVoid &&cb) override; - /// Finds best common block with peer {@param peer_id} in provided interval. /// It is using tail-recursive algorithm, till {@param hint} is /// the needed block diff --git a/core/network/synchronizer.hpp b/core/network/synchronizer.hpp index 4d6666a1c2..de6a554c30 100644 --- a/core/network/synchronizer.hpp +++ b/core/network/synchronizer.hpp @@ -54,14 +54,6 @@ namespace kagome::network { virtual void syncState(const libp2p::peer::PeerId &peer_id, const primitives::BlockInfo &block, SyncResultHandler &&handler) = 0; - - /** - * Sync block at height 1 for babe genesis slot. - * Sync block with latest babe consensus digest. - */ - virtual void syncBabeDigest(const libp2p::peer::PeerId &peer_id, - const primitives::BlockInfo &block, - CbResultVoid &&cb) = 0; }; } // namespace kagome::network diff --git a/core/parachain/approval/approval_distribution.cpp b/core/parachain/approval/approval_distribution.cpp index 24e92ee684..e807487a42 100644 --- a/core/parachain/approval/approval_distribution.cpp +++ b/core/parachain/approval/approval_distribution.cpp @@ -830,12 +830,14 @@ namespace kagome::parachain { const primitives::BlockHash &block_hash) { OUTCOME_TRY(babe_digests, consensus::babe::getBabeDigests(block_header)); OUTCOME_TRY(babe_config, babe_api_->configuration(block_hash)); - - return std::make_tuple( - babe_util_->slotToEpoch(babe_digests.second.slot_number), - std::move(babe_digests.second), - std::move(babe_config.authorities), - std::move(babe_config.randomness)); + OUTCOME_TRY(epoch, + babe_util_->slotToEpoch(*block_header.parentInfo(), + babe_digests.second.slot_number)); + + return std::make_tuple(epoch, + std::move(babe_digests.second), + std::move(babe_config.authorities), + std::move(babe_config.randomness)); } outcome::result diff --git a/core/primitives/babe_configuration.hpp b/core/primitives/babe_configuration.hpp index 040ef2174a..c8ccc96415 100644 --- a/core/primitives/babe_configuration.hpp +++ b/core/primitives/babe_configuration.hpp @@ -64,12 +64,11 @@ namespace kagome::primitives { Randomness randomness; // can be changed by NextEpochData /// Type of allowed slots. - AllowedSlots allowed_slots; // can be changed by NextConfigData + AllowedSlots allowed_slots{}; // can be changed by NextConfigData bool isSecondarySlotsAllowed() const { return allowed_slots == primitives::AllowedSlots::PrimaryAndSecondaryPlain - or allowed_slots - == primitives::AllowedSlots::PrimaryAndSecondaryVRF; + or allowed_slots == primitives::AllowedSlots::PrimaryAndSecondaryVRF; } }; @@ -96,6 +95,18 @@ namespace kagome::primitives { config.allowed_slots = static_cast(allowed_slots); return s; } + + struct Epoch { + SCALE_TIE(7); + + consensus::babe::EpochNumber epoch_index; + consensus::babe::BabeSlotNumber start_slot; + consensus::babe::EpochLength duration; + AuthorityList authorities; + Randomness randomness; + std::pair leadership_rate; + AllowedSlots allowed_slots; + }; } // namespace kagome::primitives #endif // KAGOME_CORE_PRIMITIVES_BABE_CONFIGURATION_HPP diff --git a/core/primitives/block_header.hpp b/core/primitives/block_header.hpp index 8647dffadf..1abcdd9dbd 100644 --- a/core/primitives/block_header.hpp +++ b/core/primitives/block_header.hpp @@ -41,6 +41,13 @@ namespace kagome::primitives { bool operator!=(const BlockHeader &rhs) const { return !operator==(rhs); } + + std::optional parentInfo() const { + if (number != 0) { + return primitives::BlockInfo{number - 1, parent_hash}; + } + return std::nullopt; + } }; struct BlockHeaderReflection { diff --git a/core/runtime/runtime_api/babe_api.hpp b/core/runtime/runtime_api/babe_api.hpp index affe0008c2..fd56786d53 100644 --- a/core/runtime/runtime_api/babe_api.hpp +++ b/core/runtime/runtime_api/babe_api.hpp @@ -25,6 +25,12 @@ namespace kagome::runtime { */ virtual outcome::result configuration( const primitives::BlockHash &block) = 0; + + /** + * Get next config from last digest. + */ + virtual outcome::result next_epoch( + const primitives::BlockHash &block) = 0; }; } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/babe_api.cpp b/core/runtime/runtime_api/impl/babe_api.cpp index 46d2bfbbe6..f73d2a6d61 100644 --- a/core/runtime/runtime_api/impl/babe_api.cpp +++ b/core/runtime/runtime_api/impl/babe_api.cpp @@ -15,9 +15,13 @@ namespace kagome::runtime { } outcome::result BabeApiImpl::configuration( - primitives::BlockHash const &block) { + const primitives::BlockHash &block) { return executor_->callAt( block, "BabeApi_configuration"); } + outcome::result BabeApiImpl::next_epoch( + const primitives::BlockHash &block) { + return executor_->callAt(block, "BabeApi_next_epoch"); + } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/babe_api.hpp b/core/runtime/runtime_api/impl/babe_api.hpp index f930fbef44..438514bddd 100644 --- a/core/runtime/runtime_api/impl/babe_api.hpp +++ b/core/runtime/runtime_api/impl/babe_api.hpp @@ -17,7 +17,10 @@ namespace kagome::runtime { explicit BabeApiImpl(std::shared_ptr executor); outcome::result configuration( - primitives::BlockHash const &block) override; + const primitives::BlockHash &block) override; + + outcome::result next_epoch( + const primitives::BlockHash &block) override; private: std::shared_ptr executor_; diff --git a/core/storage/predefined_keys.hpp b/core/storage/predefined_keys.hpp index 025653f14b..b272b06d72 100644 --- a/core/storage/predefined_keys.hpp +++ b/core/storage/predefined_keys.hpp @@ -40,6 +40,9 @@ namespace kagome::storage { inline const common::Buffer kWarpSyncOp = ":kagome:WarpSync:op"_buf; + inline const common::Buffer kBabeConfigRepositoryImplGenesisSlot = + ":kagome:BabeConfigRepositoryImpl:genesis_slot"_buf; + template inline common::Buffer kBabeConfigRepoStateLookupKey(Tag tag) { return common::Buffer::fromString( diff --git a/core/storage/trie_pruner/impl/recover_pruner_state.cpp b/core/storage/trie_pruner/impl/recover_pruner_state.cpp index d754f3e26f..c331ebf9ef 100644 --- a/core/storage/trie_pruner/impl/recover_pruner_state.cpp +++ b/core/storage/trie_pruner/impl/recover_pruner_state.cpp @@ -17,8 +17,7 @@ namespace kagome::storage::trie_pruner { 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()) { + if (block_tree.bestLeaf().number != 0) { SL_WARN(logger, "Running pruner on a non-empty non-pruned storage may lead to " "skipping some stored states."); diff --git a/test/core/consensus/babe/babe_config_repository_test.cpp b/test/core/consensus/babe/babe_config_repository_test.cpp index 5af1973ca0..6500439647 100644 --- a/test/core/consensus/babe/babe_config_repository_test.cpp +++ b/test/core/consensus/babe/babe_config_repository_test.cpp @@ -12,7 +12,6 @@ #include "mock/core/application/app_state_manager_mock.hpp" #include "mock/core/blockchain/block_header_repository_mock.hpp" #include "mock/core/blockchain/block_tree_mock.hpp" -#include "mock/core/clock/clock_mock.hpp" #include "mock/core/crypto/hasher_mock.hpp" #include "mock/core/runtime/babe_api_mock.hpp" #include "mock/core/storage/persistent_map_mock.hpp" @@ -26,7 +25,6 @@ using namespace kagome; using application::AppConfigurationMock; using blockchain::BlockHeaderRepositoryMock; using blockchain::BlockTreeMock; -using clock::SystemClockMock; using common::Buffer; using common::BufferView; using consensus::babe::BabeConfigRepositoryImpl; @@ -86,7 +84,6 @@ class BabeConfigRepositoryTest : public testing::Test { hasher = std::make_shared(); trie_storage = std::make_shared(); chain_events_engine = std::make_shared(); - clock = std::make_shared(); babe_config_repo_ = std::make_shared(*app_state_manager, @@ -97,8 +94,7 @@ class BabeConfigRepositoryTest : public testing::Test { babe_api, hasher, trie_storage, - chain_events_engine, - *clock); + chain_events_engine); } primitives::BabeConfiguration babe_config; @@ -113,7 +109,6 @@ class BabeConfigRepositoryTest : public testing::Test { std::shared_ptr hasher; std::shared_ptr trie_storage; primitives::events::ChainSubscriptionEnginePtr chain_events_engine; - std::shared_ptr clock; std::shared_ptr babe_config_repo_; }; @@ -128,8 +123,7 @@ TEST_F(BabeConfigRepositoryTest, getCurrentSlot) { .WillOnce(Return(outcome::success())); babe_config_repo_->prepare(); auto time = std::chrono::system_clock::now(); - EXPECT_CALL(*clock, now()).Times(1).WillOnce(Return(time)); EXPECT_EQ(static_cast(time.time_since_epoch() / babe_config.slot_duration), - babe_config_repo_->getCurrentSlot()); + babe_config_repo_->timeToSlot(time)); } diff --git a/test/core/consensus/babe/babe_test.cpp b/test/core/consensus/babe/babe_test.cpp index 29fb9a3b0d..6275015358 100644 --- a/test/core/consensus/babe/babe_test.cpp +++ b/test/core/consensus/babe/babe_test.cpp @@ -131,7 +131,8 @@ class BabeTest : public testing::Test { .WillByDefault(Return(babe_config_->epoch_length)); babe_util_ = std::make_shared(); - EXPECT_CALL(*babe_util_, slotToEpoch(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*babe_util_, slotToEpochDescriptor(_, _)) + .WillRepeatedly(Return(EpochDescriptor{})); storage_sub_engine_ = std::make_shared(); @@ -169,7 +170,6 @@ class BabeTest : public testing::Test { babe_ = std::make_shared( app_config_, app_state_manager_, - nullptr, lottery_, babe_config_repo_, proposer_, @@ -273,8 +273,6 @@ class BabeTest : public testing::Test { Block created_block_{block_header_, {extrinsic_}}; Hash256 created_block_hash_{"block#1"_hash256}; - - consensus::babe::EpochDigest expected_epoch_digest; }; ACTION_P(CheckBlockHeader, expected_block_header) { @@ -307,13 +305,9 @@ TEST_F(BabeTest, Success) { .WillRepeatedly(Return(clock::SystemClockMock::zero())); EXPECT_CALL(*babe_config_repo_, slotDuration()).WillRepeatedly(Return(1ms)); - EXPECT_CALL(*babe_util_, slotStartTime(_)) - .WillRepeatedly(Return(clock::SystemClockMock::zero())); EXPECT_CALL(*babe_util_, slotFinishTime(_)) - .WillRepeatedly(Return(clock::SystemClockMock::zero())); - EXPECT_CALL(*babe_util_, remainToStartOfSlot(_)).WillRepeatedly(Return(1ms)); - EXPECT_CALL(*babe_util_, remainToFinishOfSlot(_)).WillRepeatedly(Return(1ms)); - EXPECT_CALL(*babe_util_, syncEpoch(_)).Times(testing::AnyNumber()); + .WillRepeatedly(Return(clock::SystemClockMock::zero() + + babe_config_repo_->slotDuration())); testing::Sequence s; auto breaker = [](const boost::system::error_code &ec) { @@ -356,7 +350,7 @@ TEST_F(BabeTest, Success) { EXPECT_CALL(*block_announce_transmitter_, blockAnnounce(_)) .WillOnce(CheckBlockHeader(created_block_.header)); - babe_->runEpoch(epoch_); + babe_->runEpoch(); ASSERT_NO_THROW(on_run_slot_2({})); } @@ -370,14 +364,10 @@ TEST_F(BabeTest, NotAuthority) { EXPECT_CALL(*babe_util_, slotFinishTime(_)).Times(testing::AnyNumber()); EXPECT_CALL(*block_tree_, bestLeaf()).WillRepeatedly(Return(best_leaf)); - EXPECT_CALL(*block_tree_, getBlockHeader(best_block_hash_)) - .WillOnce(Return(best_block_header_)); - EXPECT_CALL(*babe_util_, syncEpoch(_)).Times(2); - EXPECT_CALL(*babe_util_, slotStartTime(_)); - expected_epoch_digest.authorities.clear(); + babe_config_->authorities.clear(); EXPECT_CALL(*timer_, expiresAt(_)); EXPECT_CALL(*timer_, asyncWait(_)); - babe_->runEpoch(epoch_); + babe_->runEpoch(); } diff --git a/test/core/consensus/babe/block_executor_test.cpp b/test/core/consensus/babe/block_executor_test.cpp index 9eb15df17c..45eec47eb0 100644 --- a/test/core/consensus/babe/block_executor_test.cpp +++ b/test/core/consensus/babe/block_executor_test.cpp @@ -43,6 +43,7 @@ using kagome::consensus::babe::BlockValidator; using kagome::consensus::babe::BlockValidatorMock; using kagome::consensus::babe::ConsistencyGuard; using kagome::consensus::babe::ConsistencyKeeperMock; +using kagome::consensus::babe::EpochDescriptor; using kagome::consensus::babe::EpochDigest; using kagome::consensus::grandpa::Environment; using kagome::consensus::grandpa::EnvironmentMock; @@ -130,8 +131,8 @@ class BlockExecutorTest : public testing::Test { digest_tracker_ = std::make_shared(); babe_util_ = std::make_shared(); - ON_CALL(*babe_util_, syncEpoch(_)).WillByDefault(Return(1)); - ON_CALL(*babe_util_, slotToEpoch(_)).WillByDefault(Return(1)); + ON_CALL(*babe_util_, slotToEpochDescriptor(_, _)) + .WillByDefault(Return(EpochDescriptor{1, 0})); offchain_worker_api_ = std::make_shared(); storage_sub_engine_ = std::make_shared< diff --git a/test/mock/core/consensus/babe/babe_mock.hpp b/test/mock/core/consensus/babe/babe_mock.hpp index 425323510a..71c188f83e 100644 --- a/test/mock/core/consensus/babe/babe_mock.hpp +++ b/test/mock/core/consensus/babe/babe_mock.hpp @@ -14,8 +14,6 @@ namespace kagome::consensus::babe { class BabeMock final : public Babe { public: - MOCK_METHOD(void, runEpoch, (EpochDescriptor epoch), (override)); - MOCK_METHOD(State, getCurrentState, (), (const, override)); MOCK_METHOD(void, diff --git a/test/mock/core/consensus/babe/babe_util_mock.hpp b/test/mock/core/consensus/babe/babe_util_mock.hpp index 85ae270870..1a90272e38 100644 --- a/test/mock/core/consensus/babe/babe_util_mock.hpp +++ b/test/mock/core/consensus/babe/babe_util_mock.hpp @@ -16,40 +16,22 @@ namespace kagome::consensus::babe { public: using SyncFunctor = std::function()>; - MOCK_METHOD(BabeSlotNumber, syncEpoch, (SyncFunctor &), ()); - - BabeSlotNumber syncEpoch(SyncFunctor &&func) override { - return syncEpoch(func); - } - - MOCK_METHOD(BabeSlotNumber, getCurrentSlot, (), (const, override)); + MOCK_METHOD(BabeSlotNumber, timeToSlot, (BabeTimePoint), (const, override)); MOCK_METHOD(BabeTimePoint, slotStartTime, (BabeSlotNumber slot), (const, override)); - MOCK_METHOD(BabeDuration, - remainToStartOfSlot, - (BabeSlotNumber slot), - (const, override)); - MOCK_METHOD(BabeTimePoint, slotFinishTime, (BabeSlotNumber slot), (const, override)); - MOCK_METHOD(BabeDuration, - remainToFinishOfSlot, - (BabeSlotNumber slot), - (const, override)); - - MOCK_METHOD(EpochNumber, slotToEpoch, (BabeSlotNumber), (const, override)); - - MOCK_METHOD(BabeSlotNumber, - slotInEpoch, - (BabeSlotNumber), - (const, override)); + MOCK_METHOD(outcome::result, + slotToEpochDescriptor, + (const primitives::BlockInfo &, BabeSlotNumber), + (override)); }; } // namespace kagome::consensus::babe diff --git a/test/mock/core/network/synchronizer_mock.hpp b/test/mock/core/network/synchronizer_mock.hpp index 5ec091482d..90be8c7217 100644 --- a/test/mock/core/network/synchronizer_mock.hpp +++ b/test/mock/core/network/synchronizer_mock.hpp @@ -66,19 +66,6 @@ namespace kagome::network { SyncResultHandler &&handler) override { return syncState(peer_id, block_info, handler); } - - MOCK_METHOD(void, - syncBabeDigest, - (const libp2p::peer::PeerId &, - const primitives::BlockInfo &, - const CbResultVoid &), - ()); - - void syncBabeDigest(const libp2p::peer::PeerId &peer_id, - const primitives::BlockInfo &block_info, - CbResultVoid &&cb) override { - return syncBabeDigest(peer_id, block_info, cb); - } }; } // namespace kagome::network diff --git a/test/mock/core/runtime/babe_api_mock.hpp b/test/mock/core/runtime/babe_api_mock.hpp index 8c44031b26..17b01fc7aa 100644 --- a/test/mock/core/runtime/babe_api_mock.hpp +++ b/test/mock/core/runtime/babe_api_mock.hpp @@ -18,6 +18,11 @@ namespace kagome::runtime { configuration, (const primitives::BlockHash &block), (override)); + + MOCK_METHOD(outcome::result, + next_epoch, + (const primitives::BlockHash &), + (override)); }; } // namespace kagome::runtime From cb832ce72456f37b7b021d95388f08b15068448a Mon Sep 17 00:00:00 2001 From: turuslan Date: Wed, 19 Jul 2023 06:59:41 +0300 Subject: [PATCH 2/9] fix trie test Signed-off-by: turuslan --- test/core/storage/trie_pruner/trie_pruner_test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/core/storage/trie_pruner/trie_pruner_test.cpp b/test/core/storage/trie_pruner/trie_pruner_test.cpp index 8a5cdebb2d..150ddbc6fe 100644 --- a/test/core/storage/trie_pruner/trie_pruner_test.cpp +++ b/test/core/storage/trie_pruner/trie_pruner_test.cpp @@ -814,6 +814,7 @@ TEST_F(TriePrunerTest, FastSyncScenario) { .WillRepeatedly(Return(DatabaseError::NOT_FOUND)); } + EXPECT_CALL(*block_tree, bestLeaf()).WillOnce(Return(BlockInfo{1, {}})); ASSERT_OUTCOME_SUCCESS_TRY(recoverPrunerState(*pruner, *block_tree)); for (BlockNumber n = 80; n < LAST_BLOCK_NUMBER; n++) { From e0a850f0365b7a1f05be5392c8ed11fbed6089fb Mon Sep 17 00:00:00 2001 From: turuslan Date: Mon, 24 Jul 2023 10:30:25 +0300 Subject: [PATCH 3/9] fix after merge Signed-off-by: turuslan --- .../babe/impl/babe_config_repository_impl.cpp | 39 ++++++++----------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index 4e458be2a0..d65e175e23 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -160,8 +160,10 @@ namespace kagome::consensus::babe { auto epoch_changed = true; if (parent_info.number != 0) { OUTCOME_TRY(parent_header, block_tree_->getBlockHeader(parent_info.hash)); - OUTCOME_TRY(parent_digest, getBabeDigests(parent_header)); - auto parent_epoch = slotToEpoch(parent_digest.second.slot_number); + OUTCOME_TRY(parent_slot, getBabeSlot(parent_header)); + auto mut_this = const_cast(this); + OUTCOME_TRY(parent_epoch, + mut_this->slotToEpoch(parent_info, parent_slot)); epoch_changed = epoch_number != parent_epoch; } std::unique_lock lock{indexer_mutex_}; @@ -270,28 +272,19 @@ namespace kagome::consensus::babe { auto state = std::make_shared( std::move(_state)); BabeIndexedValue value{getConfig(*state), state, state}; - if (info.number == 0) { - indexer_.put(info, {value, std::nullopt}, true); - } else { - std::vector refs; - while (true) { - OUTCOME_TRY(header, block_tree_->getBlockHeader(info.hash)); - if (HasBabeConsensusDigest digests{header}) { - value.next_state = applyDigests(value.config, digests); - indexer_.put(info, {value, std::nullopt}, true); - if (not refs.empty()) { - indexer_.remove(refs.front()); - } - break; - } - refs.emplace_back(info); - info = *header.parentInfo(); - } - std::reverse(refs.begin(), refs.end()); - for (auto &block : refs) { - indexer_.put(block, {std::nullopt, info, true}, false); - } + if (info.number != 0) { + OUTCOME_TRY(next, babe_api_->next_epoch(info.hash)); + value.next_state = std::make_shared( + primitives::BabeConfiguration{ + state->slot_duration, + state->epoch_length, + next.leadership_rate, + std::move(next.authorities), + next.randomness, + next.allowed_slots, + }); } + indexer_.put(info, {value, std::nullopt}, true); if (i_first == i_last) { return outcome::success(); } From 791d14e27cfa5917a5982d46a19fa637dfbe2a8d Mon Sep 17 00:00:00 2001 From: turuslan Date: Mon, 24 Jul 2023 10:57:22 +0300 Subject: [PATCH 4/9] fix log Signed-off-by: turuslan --- core/consensus/babe/impl/babe_impl.cpp | 9 ++++++--- core/consensus/babe/impl/babe_impl.hpp | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/consensus/babe/impl/babe_impl.cpp b/core/consensus/babe/impl/babe_impl.cpp index 5f5b65e99b..d39aca3ac1 100644 --- a/core/consensus/babe/impl/babe_impl.cpp +++ b/core/consensus/babe/impl/babe_impl.cpp @@ -84,6 +84,7 @@ namespace kagome::consensus::babe { std::shared_ptr trie_storage, primitives::events::BabeStateSubscriptionEnginePtr babe_status_observable) : sync_method_(app_config.syncMethod()), + app_config_validator_{app_config.roles().flags.authority != 0}, app_state_manager_(app_state_manager), lottery_{std::move(lottery)}, babe_config_repo_{std::move(babe_config_repo)}, @@ -799,9 +800,11 @@ namespace kagome::consensus::babe { auto &babe_config = *babe_config_opt.value(); auto keypair = session_keys_->getBabeKeyPair(babe_config.authorities); if (not keypair) { - SL_ERROR(log_, - "Authority not known, skipping slot processing. " - "Probably authority list has changed."); + if (app_config_validator_) { + SL_ERROR(log_, + "Authority not known, skipping slot processing. " + "Probably authority list has changed."); + } } else { keypair_ = std::move(keypair->first); const auto &authority_index = keypair->second; diff --git a/core/consensus/babe/impl/babe_impl.hpp b/core/consensus/babe/impl/babe_impl.hpp index 11e1e052c8..dd205c5d8a 100644 --- a/core/consensus/babe/impl/babe_impl.hpp +++ b/core/consensus/babe/impl/babe_impl.hpp @@ -196,6 +196,7 @@ namespace kagome::consensus::babe { const primitives::Block &block) const; application::AppConfiguration::SyncMethod sync_method_; + bool app_config_validator_; std::shared_ptr app_state_manager_; std::shared_ptr lottery_; std::shared_ptr babe_config_repo_; From 1f22be9642b0f43b9a342191506462a76f745a2f Mon Sep 17 00:00:00 2001 From: turuslan Date: Wed, 26 Jul 2023 11:53:37 +0300 Subject: [PATCH 5/9] fix bug Signed-off-by: turuslan --- core/consensus/babe/impl/babe_config_repository_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index d65e175e23..8b070faa7c 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -230,7 +230,7 @@ namespace kagome::consensus::babe { first_block_slot_number_ = slot1; OUTCOME_TRY(persistent_storage_->put( storage::kBabeConfigRepositoryImplGenesisSlot, - scale::encode(slot1).value())); + scale::encode(*slot1).value())); } } return slot1.value(); From e38bc85396ceeced1fcea929221f3ffe097a39c4 Mon Sep 17 00:00:00 2001 From: turuslan Date: Wed, 26 Jul 2023 13:34:10 +0300 Subject: [PATCH 6/9] fix after merge Signed-off-by: turuslan --- core/storage/trie_pruner/impl/trie_pruner_impl.cpp | 3 +-- test/core/storage/trie_pruner/trie_pruner_test.cpp | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/storage/trie_pruner/impl/trie_pruner_impl.cpp b/core/storage/trie_pruner/impl/trie_pruner_impl.cpp index 718d37a41e..faf4bb89ab 100644 --- a/core/storage/trie_pruner/impl/trie_pruner_impl.cpp +++ b/core/storage/trie_pruner/impl/trie_pruner_impl.cpp @@ -427,8 +427,7 @@ namespace kagome::storage::trie_pruner { 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()) { + if (block_tree.bestLeaf().number != 0) { SL_WARN(logger, "Running pruner on a non-empty non-pruned storage may lead to " "skipping some stored states."); diff --git a/test/core/storage/trie_pruner/trie_pruner_test.cpp b/test/core/storage/trie_pruner/trie_pruner_test.cpp index 76c82f3055..ce094505d0 100644 --- a/test/core/storage/trie_pruner/trie_pruner_test.cpp +++ b/test/core/storage/trie_pruner/trie_pruner_test.cpp @@ -813,6 +813,7 @@ TEST_F(TriePrunerTest, FastSyncScenario) { .WillRepeatedly(Return(DatabaseError::NOT_FOUND)); } + EXPECT_CALL(*block_tree, bestLeaf()).WillOnce(Return(BlockInfo{1, {}})); ASSERT_OUTCOME_SUCCESS_TRY(pruner->recoverState(*block_tree)); for (BlockNumber n = 80; n < LAST_BLOCK_NUMBER; n++) { From 8ed2cbb437fdc4683c6bdd95e47e85081ef384e5 Mon Sep 17 00:00:00 2001 From: turuslan Date: Fri, 28 Jul 2023 10:05:44 +0300 Subject: [PATCH 7/9] babe indexer warp no digest Signed-off-by: turuslan --- .../babe/impl/babe_config_repository_impl.cpp | 31 ++++++++++++------- .../babe/impl/babe_config_repository_impl.hpp | 7 ++++- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index 8819f71d3a..c6e0d30095 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -277,18 +277,20 @@ namespace kagome::consensus::babe { OUTCOME_TRY(_state, babe_api_->configuration(info.hash)); auto state = std::make_shared( std::move(_state)); - BabeIndexedValue value{getConfig(*state), state, state}; + BabeIndexedValue value{getConfig(*state), state, std::nullopt, state}; if (info.number != 0) { OUTCOME_TRY(next, babe_api_->next_epoch(info.hash)); - value.next_state = std::make_shared( - primitives::BabeConfiguration{ - state->slot_duration, - state->epoch_length, - next.leadership_rate, - std::move(next.authorities), - next.randomness, - next.allowed_slots, - }); + value.next_state_warp = + std::make_shared( + primitives::BabeConfiguration{ + state->slot_duration, + state->epoch_length, + next.leadership_rate, + std::move(next.authorities), + next.randomness, + next.allowed_slots, + }); + value.next_state = value.next_state_warp; } indexer_.put(info, {value, std::nullopt}, true); if (i_first == i_last) { @@ -306,7 +308,12 @@ namespace kagome::consensus::babe { BOOST_OUTCOME_TRY(prev_state, loadPrev(prev)); } auto state = applyDigests(getConfig(*prev_state), digests); - BabeIndexedValue value{getConfig(*state), std::nullopt, state}; + BabeIndexedValue value{ + getConfig(*state), + std::nullopt, + std::nullopt, + state, + }; indexer_.put(info, {value, prev}, block_tree_->isFinalized(info)); prev = info; prev_state = state; @@ -366,6 +373,8 @@ namespace kagome::consensus::babe { if (block.number == 0) { BOOST_ASSERT(item.value->state); item.value->next_state = item.value->state; + } else if (item.value->next_state_warp) { + item.value->next_state = item.value->next_state_warp; } else { OUTCOME_TRY(header, block_tree_->getBlockHeader(block.hash)); item.value->next_state = applyDigests(item.value->config, {header}); diff --git a/core/consensus/babe/impl/babe_config_repository_impl.hpp b/core/consensus/babe/impl/babe_config_repository_impl.hpp index 5d745b66c5..87168ff234 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.hpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.hpp @@ -39,7 +39,7 @@ namespace kagome::storage::trie { namespace kagome::consensus::babe { struct BabeIndexedValue { - SCALE_TIE_ONLY(config, state); + SCALE_TIE_ONLY(config, state, next_state_warp); /** * `NextConfigData` is rare digest, so always store recent config. @@ -50,6 +50,11 @@ namespace kagome::consensus::babe { * Used at genesis and after warp sync. */ std::optional> state; + /** + * Next epoch after warp sync, when there is no block with digest. + */ + std::optional> + next_state_warp; /** * Next epoch lazily computed from `config` and digests. */ From c8746e2946e76af3cd2972d18aeb481962685583 Mon Sep 17 00:00:00 2001 From: turuslan Date: Thu, 10 Aug 2023 15:15:26 +0300 Subject: [PATCH 8/9] const mutable Signed-off-by: turuslan --- core/consensus/babe/babe_util.hpp | 5 +++-- core/consensus/babe/impl/babe_config_repository_impl.cpp | 8 +++----- core/consensus/babe/impl/babe_config_repository_impl.hpp | 7 ++++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/consensus/babe/babe_util.hpp b/core/consensus/babe/babe_util.hpp index ddef0cd631..1f3a277016 100644 --- a/core/consensus/babe/babe_util.hpp +++ b/core/consensus/babe/babe_util.hpp @@ -41,13 +41,14 @@ namespace kagome::consensus::babe { */ virtual outcome::result slotToEpochDescriptor( const primitives::BlockInfo &parent_info, - BabeSlotNumber slot_number) = 0; + BabeSlotNumber slot_number) const = 0; /** * @returns epoch number for given parent and slot */ outcome::result slotToEpoch( - const primitives::BlockInfo &parent_info, BabeSlotNumber slot_number) { + const primitives::BlockInfo &parent_info, + BabeSlotNumber slot_number) const { OUTCOME_TRY(epoch, slotToEpochDescriptor(parent_info, slot_number)); return epoch.epoch_number; } diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index c6e0d30095..be0480b4d2 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -162,9 +162,7 @@ namespace kagome::consensus::babe { if (parent_info.number != 0) { OUTCOME_TRY(parent_header, block_tree_->getBlockHeader(parent_info.hash)); OUTCOME_TRY(parent_slot, getBabeSlot(parent_header)); - auto mut_this = const_cast(this); - OUTCOME_TRY(parent_epoch, - mut_this->slotToEpoch(parent_info, parent_slot)); + OUTCOME_TRY(parent_epoch, slotToEpoch(parent_info, parent_slot)); epoch_changed = epoch_number != parent_epoch; } std::unique_lock lock{indexer_mutex_}; @@ -200,7 +198,7 @@ namespace kagome::consensus::babe { outcome::result BabeConfigRepositoryImpl::getFirstBlockSlotNumber( - const primitives::BlockInfo &parent_info) { + const primitives::BlockInfo &parent_info) const { auto slot1 = first_block_slot_number_; if (not slot1) { auto finalized = block_tree_->getLastFinalized(); @@ -239,7 +237,7 @@ namespace kagome::consensus::babe { outcome::result BabeConfigRepositoryImpl::slotToEpochDescriptor( - const primitives::BlockInfo &parent_info, BabeSlotNumber slot) { + const primitives::BlockInfo &parent_info, BabeSlotNumber slot) const { if (parent_info.number == 0) { return EpochDescriptor{0, slot}; } diff --git a/core/consensus/babe/impl/babe_config_repository_impl.hpp b/core/consensus/babe/impl/babe_config_repository_impl.hpp index 87168ff234..9bef046a84 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.hpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.hpp @@ -103,13 +103,14 @@ namespace kagome::consensus::babe { BabeTimePoint slotFinishTime(BabeSlotNumber slot) const override; outcome::result slotToEpochDescriptor( - const primitives::BlockInfo &parent_info, BabeSlotNumber slot) override; + const primitives::BlockInfo &parent_info, + BabeSlotNumber slot) const override; void warp(const primitives::BlockInfo &block) override; private: outcome::result getFirstBlockSlotNumber( - const primitives::BlockInfo &parent_info); + const primitives::BlockInfo &parent_info) const; outcome::result> config(const primitives::BlockInfo &block, bool next_epoch) const; @@ -142,7 +143,7 @@ namespace kagome::consensus::babe { BabeDuration slot_duration_{}; EpochLength epoch_length_{}; - std::optional first_block_slot_number_; + mutable std::optional first_block_slot_number_; log::Logger logger_; }; From 2e3ca25608f845f8574bb3b4b2a2e352b0431704 Mon Sep 17 00:00:00 2001 From: turuslan Date: Tue, 15 Aug 2023 16:29:56 +0300 Subject: [PATCH 9/9] mock Signed-off-by: turuslan --- test/mock/core/consensus/babe/babe_util_mock.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mock/core/consensus/babe/babe_util_mock.hpp b/test/mock/core/consensus/babe/babe_util_mock.hpp index 9d3b0be3fb..b95a74e8c6 100644 --- a/test/mock/core/consensus/babe/babe_util_mock.hpp +++ b/test/mock/core/consensus/babe/babe_util_mock.hpp @@ -29,7 +29,7 @@ namespace kagome::consensus::babe { MOCK_METHOD(outcome::result, slotToEpochDescriptor, (const primitives::BlockInfo &, BabeSlotNumber), - (override)); + (const, override)); }; } // namespace kagome::consensus::babe