17 #include "scale/scale.hpp" 23 const std::shared_ptr<application::AppStateManager> &app_state_manager,
24 std::shared_ptr<storage::BufferStorage> persistent_storage,
25 std::shared_ptr<blockchain::BlockTree> block_tree,
26 std::shared_ptr<blockchain::BlockHeaderRepository> header_repo,
27 std::shared_ptr<runtime::BabeApi> babe_api,
28 std::shared_ptr<crypto::Hasher> hasher,
32 : persistent_storage_(
std::move(persistent_storage)),
33 block_tree_(
std::move(block_tree)),
34 header_repo_(
std::move(header_repo)),
35 babe_api_(
std::move(babe_api)),
36 hasher_(
std::move(hasher)),
38 BOOST_ASSERT(chain_events_engine !=
nullptr);
39 return std::make_shared<primitives::events::ChainEventSubscriber>(
49 BOOST_ASSERT(
hasher_ !=
nullptr);
51 BOOST_ASSERT(app_state_manager !=
nullptr);
52 app_state_manager->atPrepare([
this] {
return prepare(); });
56 auto load_res =
load();
57 if (load_res.has_error()) {
58 SL_VERBOSE(
logger_,
"Can not load state: {}", load_res.error());
65 [wp = weak_from_this()](
71 if (
auto self = wp.lock()) {
73 boost::get<primitives::events::HeadsEventParams>(event).
get();
75 self->hasher_->blake2b_256(scale::encode(header).value());
77 auto save_res =
self->save();
78 if (save_res.has_error()) {
79 SL_WARN(self->logger_,
80 "Can not save state at finalization: {}",
83 self->prune({header.number, hash});
92 const auto finalized_block =
block_tree_->getLastFinalized();
95 if (finalized_block.number > 0) {
96 OUTCOME_TRY(first_block_header,
block_tree_->getBlockHeader(1));
99 BOOST_ASSERT_MSG(babe_digest_res.has_value(),
100 "Any non genesis block must contain babe digest");
101 auto first_slot_number = babe_digest_res.value().second.slot_number;
103 syncEpoch([&] {
return std::tuple(first_slot_number,
true); });
107 OUTCOME_TRY(encoded_last_state_opt,
111 if (encoded_last_state_opt.has_value()) {
112 auto last_state_res = scale::decode<std::shared_ptr<BabeConfigNode>>(
113 encoded_last_state_opt.value());
115 if (last_state_res.has_value()) {
116 auto &last_state = last_state_res.value();
117 if (last_state->block.number <= finalized_block.number) {
118 root_ = std::move(last_state);
120 "State was initialized by last saved on block {}",
125 "Last state not match with last finalized; Try to use savepoint");
129 logger_,
"Can not decode last state: {}", last_state_res.error());
136 if (
root_ ==
nullptr) {
137 for (
auto block_number =
142 OUTCOME_TRY(encoded_saved_state_opt,
146 if (not encoded_saved_state_opt.has_value()) {
150 auto saved_state_res = scale::decode<std::shared_ptr<BabeConfigNode>>(
151 encoded_saved_state_opt.value());
153 if (saved_state_res.has_error()) {
155 "Can not decode state saved on block {}: {}",
157 saved_state_res.error());
163 root_ = std::move(saved_state_res.value());
165 "State was initialized by savepoint on block {}",
172 if (
root_ ==
nullptr) {
174 if (babe_config_res.has_error()) {
176 "Can't get babe config over babe API on genesis block: {}",
177 babe_config_res.error());
178 return babe_config_res.as_failure();
180 const auto &babe_config = babe_config_res.value();
184 std::make_shared<const primitives::BabeConfiguration>(babe_config));
185 SL_DEBUG(
logger_,
"State was initialized by genesis block");
188 BOOST_ASSERT_MSG(
root_ !=
nullptr,
"The root must be initialized by now");
191 auto slot_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
192 root_->config->slot_duration);
193 BOOST_ASSERT_MSG(slot_duration.count() > 0,
194 "Slot duration must be greater zero");
196 auto epoch_length =
root_->config->epoch_length;
197 BOOST_ASSERT_MSG(epoch_length,
"Epoch length must be greater zero");
201 bool need_to_save =
false;
202 for (
auto block_number =
root_->block.number + 1;
203 block_number <= finalized_block.number;
205 auto block_header_res =
block_tree_->getBlockHeader(block_number);
206 if (block_header_res.has_error()) {
208 "Can't get header of some finalized block: {}",
209 block_header_res.error());
210 return block_header_res.as_failure();
212 const auto &block_header = block_header_res.value();
215 hasher_->blake2b_256(scale::encode(block_header).value());
218 for (
auto &item : block_header.digest) {
219 auto res = visit_in_place(
225 scale::decode<consensus::BabeBlockHeader>(msg.data));
227 return onDigest(block_info, digest_item);
229 return outcome::success();
233 OUTCOME_TRY(digest_item,
234 scale::decode<primitives::BabeDigest>(msg.
data));
236 return onDigest(block_info, digest_item);
238 return outcome::success();
240 [](
const auto &) {
return outcome::success(); });
241 if (res.has_error()) {
243 "Can't apply babe digest of finalized block #{}: {}",
246 return res.as_failure();
254 auto save_res =
save();
255 if (save_res.has_error()) {
256 SL_WARN(
logger_,
"Can't re-make savepoint: {}", save_res.error());
258 need_to_save =
false;
267 if (
auto save_res =
save(); save_res.has_error()) {
268 SL_WARN(
logger_,
"Can't re-save state: {}", save_res.error());
279 for (
auto &leave_hash : leaves) {
280 for (
auto hash = leave_hash;;) {
281 auto block_header_res =
block_tree_->getBlockHeader(hash);
282 if (block_header_res.has_error()) {
284 "Can't get header of some finalized block: {}",
285 block_header_res.error());
286 return block_header_res.as_failure();
288 const auto &block_header = block_header_res.value();
291 if (block_header.number <= finalized_block.number) {
295 primitives::BlockInfo block_info{block_header.number, hash};
298 if (digests.find(block_info) != digests.end()) {
302 auto &digest_of_block = digests[block_info];
305 for (
auto &item : block_header.digest) {
306 auto res = visit_in_place(
311 scale::decode<consensus::BabeBlockHeader>(msg.data);
312 if (res.has_error()) {
313 return res.as_failure();
315 const auto &digest_item = res.value();
317 digest_of_block.emplace_back(digest_item);
319 return outcome::success();
323 auto res = scale::decode<primitives::BabeDigest>(msg.data);
324 if (res.has_error()) {
325 return res.as_failure();
327 const auto &digest_item = res.value();
329 digest_of_block.emplace_back(digest_item);
331 return outcome::success();
333 [](
const auto &) {
return outcome::success(); });
334 if (res.has_error()) {
336 "Can't collect babe digest of non-finalized block {}: {}",
339 return res.as_failure();
343 hash = block_header.parent_hash;
347 for (
const auto &[block_info_tmp, digests_of_block] : digests) {
348 const auto &block_info = block_info_tmp;
349 for (
const auto &digest : digests_of_block) {
350 auto res = visit_in_place(digest, [&](
const auto &digest_item) {
351 return onDigest(block_info, digest_item);
353 if (res.has_error()) {
355 "Can't apply babe digest of non-finalized block {}: {}",
358 return res.as_failure();
363 prune(finalized_block);
365 return outcome::success();
369 const auto finalized_block =
block_tree_->getLastFinalized();
373 auto saving_state_node =
getNode(finalized_block);
374 BOOST_ASSERT_MSG(saving_state_node !=
nullptr,
375 "Finalized block must have associated node");
376 const auto saving_state_block = saving_state_node->block;
380 return outcome::success();
383 const auto last_savepoint =
387 const auto new_savepoint =
392 if (new_savepoint > last_savepoint) {
393 auto hash_res =
header_repo_->getHashByNumber(new_savepoint);
394 if (hash_res.has_value()) {
397 auto ancestor_node =
getNode(savepoint_block);
398 if (ancestor_node !=
nullptr) {
399 auto node = ancestor_node->block == savepoint_block
401 : ancestor_node->makeDescendant(savepoint_block);
405 if (res.has_error()) {
407 "Can't make savepoint on block {}: {}",
410 return res.as_failure();
412 SL_DEBUG(
logger_,
"Savepoint has made on block {}", savepoint_block);
416 "Can't take hash of savepoint block {}: {}",
425 if (res.has_error()) {
427 "Can't save last state on block {}: {}",
430 return res.as_failure();
432 SL_DEBUG(
logger_,
"Last state has saved on block {}", saving_state_block);
436 return outcome::success();
439 std::shared_ptr<const primitives::BabeConfiguration>
451 "Slot duration is not initialized");
456 BOOST_ASSERT_MSG(
epoch_length_ != 0,
"Epoch length is not initialized");
468 node->epoch != epoch_number ? log::Level::DEBUG : log::Level::TRACE,
469 "BabeBlockHeader babe-digest on block {}: " 470 "slot {}, epoch {}, authority #{}, {}",
480 if (node->block == block) {
485 if (node->epoch != epoch_number) {
486 auto new_node = node->makeDescendant(block, epoch_number);
488 node->descendants.emplace_back(std::move(new_node));
491 return outcome::success();
497 return visit_in_place(
501 "NextEpochData babe-digest on block {}: " 502 "{} authorities, randomness {}",
511 "OnDisabled babe-digest on block {}: " 512 "disable authority #{}; ignored (it is checked only by runtime)",
514 msg.authority_index);
522 return outcome::success();
525 return visit_in_place(
529 "NextConfigData babe-digest on block {}: " 530 "ratio={}/{}, second_slot={}",
539 "Unsupported NextConfigData babe-digest on block {}: " 548 "Unsupported babe-digest on block {}: variant #{}",
551 throw std::runtime_error(
"RUN BREAKER");
561 if (node->block != block) {
565 auto config = node->next_config.value_or(node->config);
570 std::make_shared<primitives::BabeConfiguration>(*config);
573 node->next_config = std::move(new_config);
576 return outcome::success();
584 if (node->block != block) {
588 auto config = node->next_config.value_or(node->config);
593 std::make_shared<primitives::BabeConfiguration>(*config);
594 new_config->leadership_rate = msg.
ratio;
596 node->next_config = std::move(new_config);
599 return outcome::success();
604 BOOST_ASSERT(
root_ !=
nullptr);
608 || (
root_->block != block
613 std::shared_ptr<BabeConfigNode> ancestor =
root_;
614 while (ancestor->block != block) {
615 bool goto_next_generation =
false;
616 for (
const auto &node : ancestor->descendants) {
619 goto_next_generation =
true;
623 if (not goto_next_generation) {
634 "Looking if direct chain exists between {} and {}",
648 if (block ==
root_->block) {
662 if (node->block != block) {
664 auto new_node = node->makeDescendant(block);
665 auto descendants = std::move(node->descendants);
666 for (
auto &descendant : descendants) {
668 new_node->descendants.emplace_back(std::move(descendant));
671 node = std::move(new_node);
674 root_ = std::move(node);
676 SL_TRACE(
logger_,
"Prune upto block {}", block);
680 auto ancestor =
getNode(block);
682 if (ancestor ==
nullptr) {
683 SL_TRACE(
logger_,
"Can't remove node of block {}: no ancestor", block);
687 if (ancestor ==
root_) {
689 SL_TRACE(
logger_,
"Can't remove node of block {}: it is root", block);
693 if (ancestor->block == block) {
696 BOOST_ASSERT_MSG(ancestor !=
nullptr,
"Non root node must have a parent");
699 auto it = std::find_if(ancestor->descendants.begin(),
700 ancestor->descendants.end(),
701 [&block](std::shared_ptr<BabeConfigNode> node) {
702 return node->block == block;
705 if (it != ancestor->descendants.end()) {
706 if (not(*it)->descendants.empty()) {
709 "Can't remove node of block {}: " 710 "not found such descendant of ancestor",
715 ancestor->descendants.erase(it);
716 SL_DEBUG(
logger_,
"Node of block {} has removed", block);
721 std::function<std::tuple<BabeSlotNumber, bool>()> &&f) {
723 auto [first_block_slot_number, is_first_block_finalized] = f();
725 is_first_block_finalized_ = is_first_block_finalized;
728 "Epoch beginning is synchronized: first block slot number is {} now",
748 if (deadline > now) {
749 return deadline - now;
773 auto genesis_slot_number =
775 if (slot > genesis_slot_number) {
776 return (slot - genesis_slot_number) /
epochLength();
783 auto genesis_slot_number =
785 if (slot > genesis_slot_number) {
786 return (slot - genesis_slot_number) %
epochLength();
bool directChainExists(const primitives::BlockInfo &ancestor, const primitives::BlockInfo &descendant) const
Check if one block is direct ancestor of second one.
Class represents arbitrary (including empty) byte buffer.
common::Buffer kBabeConfigRepoStateLookupKey(Tag tag)
BabeDuration slotDuration() const override
outcome::result< std::pair< Seal, BabeBlockHeader > > getBabeDigests(const primitives::BlockHeader &block_header)
A secondary deterministic slot assignment.
BabeSlotNumber getCurrentSlot() const override
std::string_view to_string(SlotType s)
primitives::AuthorityList authorities
The authorities actual for corresponding epoch.
A secondary deterministic slot assignment with VRF outputs.
std::shared_ptr< crypto::Hasher > hasher_
outcome::result< void > onNextEpochData(const primitives::BlockInfo &block, const primitives::NextEpochData &msg)
void cancel(const primitives::BlockInfo &block) override
ConsensusEngineId consensus_engine_id
primitives::BlockNumber last_saved_state_block_
bool is_first_block_finalized_
BabeConfigRepositoryImpl(const std::shared_ptr< application::AppStateManager > &app_state_manager, std::shared_ptr< storage::BufferStorage > persistent_storage, std::shared_ptr< blockchain::BlockTree > block_tree, std::shared_ptr< blockchain::BlockHeaderRepository > header_repo, std::shared_ptr< runtime::BabeApi > babe_api, std::shared_ptr< crypto::Hasher > hasher, primitives::events::ChainSubscriptionEnginePtr chain_events_engine, const primitives::GenesisBlockHeader &genesis_block_header, const BabeClock &clock)
std::shared_ptr< storage::BufferStorage > persistent_storage_
std::shared_ptr< runtime::BabeApi > babe_api_
boost::variant< std::nullopt_t, HeadsEventParams, RuntimeVersionEventParams, NewRuntimeEventParams > ChainEventParams
BabeTimePoint slotFinishTime(BabeSlotNumber slot) const override
std::shared_ptr< const primitives::BabeConfiguration > config(const primitives::BlockInfo &parent_block, consensus::EpochNumber epoch_number) override
const EpochLength epoch_length_
BabeDuration remainToFinishOfSlot(BabeSlotNumber slot) const override
void prune(const primitives::BlockInfo &block)
std::shared_ptr< BabeConfigNode > root_
std::shared_ptr< ChainSubscriptionEngine > ChainSubscriptionEnginePtr
outcome::result< void > save()
std::shared_ptr< blockchain::BlockHeaderRepository > header_repo_
outcome::result< void > onNextConfigData(const primitives::BlockInfo &block, const primitives::NextConfigDataV1 &msg)
EpochNumber slotToEpoch(BabeSlotNumber slot) const override
BabeDuration remainToStartOfSlot(BabeSlotNumber slot) const override
std::pair< uint64_t, uint64_t > ratio
outcome::result< void > onDigest(const primitives::BlockInfo &block, const consensus::BabeBlockHeader &digest) override
BabeClock::TimePoint BabeTimePoint
BABE uses system clock's time points.
boost::variant< Unused< 0 >, NextConfigDataV1 > NextConfigData
std::shared_ptr< primitives::events::ChainEventSubscriber > chain_sub_
BabeSlotNumber slotInEpoch(BabeSlotNumber slot) const override
std::optional< BabeSlotNumber > first_block_slot_number_
uint64_t BabeSlotNumber
slot number of the Babe production
outcome::result< void > load()
A primary VRF-based slot assignment.
common::SLBuffer< consensus::kMaxValidatorsNumber *1024 > data
std::shared_ptr< blockchain::BlockTree > block_tree_
uint32_t SubscriptionSetId
BabeTimePoint slotStartTime(BabeSlotNumber slot) const override
detail::BlockInfoT< struct BlockInfoTag > BlockInfo
static const primitives::BlockNumber kSavepointEachSuchBlock
boost::variant< Unused< 0 >, NextEpochData, OnDisabled, NextConfigData > BabeDigest
https://github.com/paritytech/substrate/blob/polkadot-v0.9.8/primitives/consensus/babe/src/lib.rs#L130
Logger createLogger(const std::string &tag)
primitives::BlockHash genesis_block_hash_
static std::shared_ptr< BabeConfigNode > createAsRoot(primitives::BlockInfo block, std::shared_ptr< const primitives::BabeConfiguration > config)
uint64_t EpochNumber
number of the epoch in the Babe production
BabeSlotNumber getFirstBlockSlotNumber()
EpochLength epochLength() const override
const BabeDuration slot_duration_
BabeSlotNumber syncEpoch(std::function< std::tuple< BabeSlotNumber, bool >()> &&f) override
virtual TimePoint now() const =0
BabeClock::Duration BabeDuration
Randomness randomness
The value of randomness to use for the slot-assignment.
std::shared_ptr< BabeConfigNode > getNode(const primitives::BlockInfo &block) const
Find schedule_node according to the block.