Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor block tree #1679

Merged
merged 10 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 49 additions & 187 deletions core/blockchain/impl/block_tree_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,26 +503,20 @@ namespace kagome::blockchain {
if (!parent) {
return BlockTreeError::NO_PARENT;
}
OUTCOME_TRY(block_hash, p.storage_->putBlockHeader(header));
OUTCOME_TRY(p.storage_->putBlockHeader(header));

// update local meta with the new block
auto new_node = std::make_shared<TreeNode>(
header.blockInfo(), parent, isPrimary(header));

p.tree_->updateMeta(new_node);

OUTCOME_TRY(reorganizeNoLock(p));

OUTCOME_TRY(p.storage_->setBlockTreeLeaves(p.tree_->leafHashes()));

metric_known_chain_leaves_->set(p.tree_->leafCount());
metric_best_block_height_->set(bestBlockNoLock(p).number);
auto reorg = p.tree_->add(new_node);
OUTCOME_TRY(reorgAndPrune(p, {std::move(reorg), {}}));

notifyChainEventsEngine(primitives::events::ChainEventType::kNewHeads,
header);
SL_VERBOSE(log_,
"Block {} has been added into block tree",
primitives::BlockInfo(header.number, block_hash));
header.blockInfo());

return outcome::success();
});
Expand All @@ -545,11 +539,8 @@ namespace kagome::blockchain {
auto new_node = std::make_shared<TreeNode>(
block.header.blockInfo(), parent, isPrimary(block.header));

p.tree_->updateMeta(new_node);

OUTCOME_TRY(reorganizeNoLock(p));

OUTCOME_TRY(p.storage_->setBlockTreeLeaves(p.tree_->leafHashes()));
auto reorg = p.tree_->add(new_node);
OUTCOME_TRY(reorgAndPrune(p, {std::move(reorg), {}}));

notifyChainEventsEngine(primitives::events::ChainEventType::kNewHeads,
block.header);
Expand All @@ -570,12 +561,9 @@ namespace kagome::blockchain {
}
}

metric_known_chain_leaves_->set(p.tree_->leafCount());
metric_best_block_height_->set(bestBlockNoLock(p).number);

SL_VERBOSE(log_,
"Block {} has been added into block tree",
primitives::BlockInfo(block.header.number, block_hash));
block.header.blockInfo());
return outcome::success();
});
}
Expand All @@ -602,20 +590,8 @@ namespace kagome::blockchain {
if (not p.tree_->isLeaf(block_hash)) {
return BlockTreeError::BLOCK_IS_NOT_LEAF;
}

auto node = p.tree_->find(block_hash);
BOOST_ASSERT_MSG(node != nullptr,
"As checked before, block exists as one of leaves");

p.tree_->removeFromMeta(node);

OUTCOME_TRY(reorganizeNoLock(p));

// Remove from storage
OUTCOME_TRY(p.storage_->removeBlock(node->info.hash));

OUTCOME_TRY(p.storage_->setBlockTreeLeaves(p.tree_->leafHashes()));

auto changes = p.tree_->removeLeaf(block_hash);
OUTCOME_TRY(reorgAndPrune(p, changes));
return outcome::success();
});
}
Expand Down Expand Up @@ -767,18 +743,12 @@ namespace kagome::blockchain {
auto new_node = std::make_shared<TreeNode>(
block_header.blockInfo(), parent, isPrimary(block_header));

p.tree_->updateMeta(new_node);

OUTCOME_TRY(reorganizeNoLock(p));

OUTCOME_TRY(p.storage_->setBlockTreeLeaves(p.tree_->leafHashes()));

metric_known_chain_leaves_->set(p.tree_->leafCount());
metric_best_block_height_->set(bestBlockNoLock(p).number);
auto reorg = p.tree_->add(new_node);
OUTCOME_TRY(reorgAndPrune(p, {std::move(reorg), {}}));

SL_VERBOSE(log_,
"Block {} has been restored in block tree from storage",
primitives::BlockInfo(block_header.number, block_hash));
block_header.blockInfo());

return outcome::success();
}
Expand Down Expand Up @@ -822,14 +792,14 @@ namespace kagome::blockchain {

OUTCOME_TRY(p.storage_->putJustification(justification, block_hash));
turuslan marked this conversation as resolved.
Show resolved Hide resolved

OUTCOME_TRY(pruneNoLock(p, node));
OUTCOME_TRY(pruneTrie(p, node->info.number));

p.tree_->updateTreeRoot(node);

OUTCOME_TRY(reorganizeNoLock(p));
std::vector<primitives::BlockHash> retired_hashes;
for (auto parent = node->parent(); parent; parent = parent->parent()) {
retired_hashes.emplace_back(parent->info.hash);
}

OUTCOME_TRY(p.storage_->setBlockTreeLeaves(p.tree_->leafHashes()));
auto changes = p.tree_->finalize(node);
OUTCOME_TRY(reorgAndPrune(p, changes));
OUTCOME_TRY(pruneTrie(p, node->info.number));

notifyChainEventsEngine(
primitives::events::ChainEventType::kFinalizedHeads, header);
Expand All @@ -853,6 +823,16 @@ namespace kagome::blockchain {
}
}

main_thread_.execute([weak{weak_from_this()},
retired_hashes{std::move(retired_hashes)}] {
if (auto self = weak.lock()) {
self->chain_events_engine_->notify(
primitives::events::ChainEventType::
kDeactivateAfterFinalization,
retired_hashes);
}
});

log_->info("Finalized block {}", node->info);
telemetry_->notifyBlockFinalized(node->info);
telemetry_->pushBlockStats();
Expand Down Expand Up @@ -1094,8 +1074,14 @@ namespace kagome::blockchain {
const BlockTreeData &p,
const primitives::BlockHash &ancestor,
const primitives::BlockHash &descendant) const {
if (ancestor == descendant) {
return true;
}
auto ancestor_node_ptr = p.tree_->find(ancestor);
auto descendant_node_ptr = p.tree_->find(descendant);
if (ancestor_node_ptr and descendant_node_ptr) {
return canDescend(descendant_node_ptr, ancestor_node_ptr);
}

/*
* check that ancestor is above descendant
Expand Down Expand Up @@ -1132,35 +1118,15 @@ namespace kagome::blockchain {
return false;
}

// if both nodes are in our light tree, we can use this representation
// only
if (ancestor_node_ptr && descendant_node_ptr) {
return canDescend(descendant_node_ptr, ancestor_node_ptr);
}

// else, we need to use a database

// Try to use optimal way, if ancestor and descendant in the finalized
// chain
if (descendant_depth <= getLastFinalizedNoLock(p).number) {
auto res = p.header_repo_->getHashByNumber(descendant_depth);
BOOST_ASSERT_MSG(res.has_value(),
"Any finalized block must be accessible by number");
// Check if descendant in finalised chain
if (res.value() == descendant) {
res = p.header_repo_->getHashByNumber(ancestor_depth);
BOOST_ASSERT_MSG(res.has_value(),
"Any finalized block must be accessible by number");
if (res.value() == ancestor) {
// Ancestor and descendant in the finalized chain,
// therefore they have direct chain between each other
return true;
} else {
// Ancestor in the finalized chain, but descendant is not,
// therefore they can not have direct chain between each other
return false;
}
}
auto finalized = [&](const primitives::BlockHash &hash,
primitives::BlockNumber number) {
return number <= getLastFinalizedNoLock(p).number
and p.header_repo_->getHashByNumber(number) == outcome::success(hash);
};
if (descendant_node_ptr or finalized(descendant, descendant_depth)) {
return finalized(ancestor, ancestor_depth);
}

auto current_hash = descendant;
Expand Down Expand Up @@ -1299,57 +1265,23 @@ namespace kagome::blockchain {
for (auto &block : changes.prune) {
OUTCOME_TRY(p.storage_->removeBlock(block.hash));
}
// TODO(turuslan): #1679, move code from pruneNoLock
return outcome::success();
}

outcome::result<void> BlockTreeImpl::pruneNoLock(
BlockTreeData &p, const std::shared_ptr<TreeNode> &lastFinalizedNode) {
std::deque<std::shared_ptr<TreeNode>> to_remove;

auto following_node = lastFinalizedNode;

for (auto current_node = following_node->parent(); current_node;
current_node = current_node->parent()) {
// DFS-on-deque
to_remove.emplace_back(); // Waterbreak
std::copy_if(current_node->children.begin(),
current_node->children.end(),
std::back_inserter(to_remove),
[&](const auto &child) { return child != following_node; });
auto last = to_remove.back();
while (last != nullptr) {
to_remove.pop_back();
std::copy(last->children.begin(),
last->children.end(),
std::back_inserter(to_remove));
to_remove.emplace_front(std::move(last));
last = to_remove.back();
}
to_remove.pop_back(); // Remove waterbreak

// remove (in memory) all child, except main chain block
current_node->children = {following_node};
following_node = current_node;
}

std::vector<primitives::Extrinsic> extrinsics;
std::vector<primitives::BlockHash> retired_hashes;

// remove from storage
retired_hashes.reserve(to_remove.size());
for (const auto &node : to_remove) {
OUTCOME_TRY(block_header_opt,
p.storage_->getBlockHeader(node->info.hash));
OUTCOME_TRY(block_body_opt, p.storage_->getBlockBody(node->info.hash));
retired_hashes.reserve(changes.prune.size());
for (const auto &block : changes.prune) {
OUTCOME_TRY(block_header_opt, p.storage_->getBlockHeader(block.hash));
OUTCOME_TRY(block_body_opt, p.storage_->getBlockBody(block.hash));
if (block_body_opt.has_value()) {
extrinsics.reserve(extrinsics.size() + block_body_opt.value().size());
for (auto &ext : block_body_opt.value()) {
auto extrinsic_hash = p.hasher_->blake2b_256(ext.data);
if (auto key = p.extrinsic_event_key_repo_->get(extrinsic_hash)) {
main_thread_.execute([wself{weak_from_this()},
key{key.value()},
block_hash{node->info.hash}]() {
block_hash{block.hash}]() {
if (auto self = wself.lock()) {
self->extrinsic_events_engine_->notify(
key,
Expand All @@ -1363,20 +1295,8 @@ namespace kagome::blockchain {
BOOST_ASSERT(block_header_opt.has_value());
OUTCOME_TRY(p.state_pruner_->pruneDiscarded(block_header_opt.value()));
}

retired_hashes.emplace_back(node->info.hash);
p.tree_->removeFromMeta(node);
OUTCOME_TRY(p.storage_->removeBlock(node->info.hash));
}

auto last_finalized_block_info = getLastFinalizedNoLock(p);
for (primitives::BlockNumber n = last_finalized_block_info.number;
n < lastFinalizedNode->info.number;
++n) {
if (auto result = p.storage_->getBlockHash(n);
result.has_value() && result.value()) {
retired_hashes.emplace_back(std::move(*result.value()));
}
retired_hashes.emplace_back(block.hash);
OUTCOME_TRY(p.storage_->removeBlock(block.hash));
}

// trying to return extrinsics back to transaction pool
Expand Down Expand Up @@ -1441,64 +1361,6 @@ namespace kagome::blockchain {
return outcome::success();
}

outcome::result<void> BlockTreeImpl::reorganizeNoLock(BlockTreeData &p) {
auto block = BlockTreeImpl::bestBlockNoLock(p);

// Remove assigning of obsolete best upper blocks chain
auto prev_max_best_block_number = block.number;
for (;;) {
auto hash_res =
p.header_repo_->getHashByNumber(prev_max_best_block_number + 1);
if (hash_res.has_error()) {
if (hash_res == outcome::failure(BlockTreeError::HEADER_NOT_FOUND)) {
break;
}
return hash_res.as_failure();
}
++prev_max_best_block_number;
}
for (auto number = prev_max_best_block_number; number > block.number;
--number) {
OUTCOME_TRY(p.storage_->deassignNumberToHash(number));
}

auto hash_res = p.header_repo_->getHashByNumber(block.number);
if (hash_res.has_error()) {
if (hash_res != outcome::failure(BlockTreeError::HEADER_NOT_FOUND)) {
return hash_res.as_failure();
}
} else if (block.hash == hash_res.value()) {
return outcome::success();
}

// Rewrite earlier blocks sequence
size_t count = 0;
for (;;) {
OUTCOME_TRY(p.storage_->assignNumberToHash(block));
if (block.number == 0) {
break;
}
OUTCOME_TRY(header, getBlockHeaderNoLock(p, block.hash));
auto parent_hash_res = p.header_repo_->getHashByNumber(block.number - 1);
if (parent_hash_res.has_error()) {
if (parent_hash_res
!= outcome::failure(BlockTreeError::HEADER_NOT_FOUND)) {
return parent_hash_res.as_failure();
}
} else if (header.parent_hash == parent_hash_res.value()) {
break;
}
++count;
block = {block.number - 1, header.parent_hash};
}

if (count > 1) {
SL_DEBUG(log_, "Best chain reorganized for {} blocks deep", count);
}

return outcome::success();
}

void BlockTreeImpl::warp(const primitives::BlockInfo &block_info) {
block_tree_data_.exclusiveAccess([&](BlockTreeData &p) {
p.tree_ = std::make_unique<CachedTree>(block_info);
Expand Down
5 changes: 0 additions & 5 deletions core/blockchain/impl/block_tree_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,12 @@ namespace kagome::blockchain {
outcome::result<void> reorgAndPrune(BlockTreeData &p,
const ReorgAndPrune &changes);

outcome::result<void> pruneNoLock(
BlockTreeData &p, const std::shared_ptr<TreeNode> &lastFinalizedNode);

outcome::result<primitives::BlockHeader> getBlockHeaderNoLock(
const BlockTreeData &p, const primitives::BlockHash &block_hash) const;

outcome::result<void> pruneTrie(const BlockTreeData &block_tree_data,
primitives::BlockNumber new_finalized);

outcome::result<void> reorganizeNoLock(BlockTreeData &p);

primitives::BlockInfo getLastFinalizedNoLock(const BlockTreeData &p) const;
primitives::BlockInfo bestBlockNoLock(const BlockTreeData &p) const;

Expand Down
Loading