Skip to content

Commit

Permalink
Fix block tree queries (#1341)
Browse files Browse the repository at this point in the history
Signed-off-by: turuslan <turuslan.devbox@gmail.com>
  • Loading branch information
turuslan authored Sep 20, 2022
1 parent 31dd93a commit c48739c
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 355 deletions.
44 changes: 7 additions & 37 deletions core/blockchain/block_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,6 @@ namespace kagome::blockchain {
const primitives::BlockHash &block,
const primitives::Justification &justification) = 0;

/**
* Get a chain of blocks from the specified as a (\param block) up to the
* closest finalized one
* @param block to get a chain from
* @return chain of blocks in top-to-bottom order (from the last finalized
* block to the provided one) or error
*/
virtual BlockHashVecRes getChainByBlock(
const primitives::BlockHash &block) const = 0;

enum class GetChainDirection { ASCEND, DESCEND };

/**
Expand All @@ -174,26 +164,15 @@ namespace kagome::blockchain {
const primitives::BlockHash &block, uint64_t maximum) const = 0;

/**
* Get a chain of blocks
* @param top_block - block, which is at the top of the chain
* @param bottom_block - block, which is the bottom of the chain
* @return chain of blocks in top-to-bottom order or error
*/
virtual BlockHashVecRes getChainByBlocks(
const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block) const = 0;

/**
* Get a chain of blocks
* @param top_block - block, which is at the top of the chain
* @param bottom_block - block, which is the bottom of the chain
* @param max_count - maximum blocks in the chain
* @return chain of blocks in top-to-bottom order or error
* Get a chain of blocks.
* Implies `hasDirectChain(ancestor, descendant)`.
* @param ancestor - block, which is closest to the genesis
* @param descendant - block, which is farthest from the genesis
* @return chain of blocks in ascending order or error
*/
virtual BlockHashVecRes getChainByBlocks(
const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block,
uint32_t max_count) const = 0;
const primitives::BlockHash &ancestor,
const primitives::BlockHash &descendant) const = 0;

/**
* Check if one block is ancestor of second one (direct chain exists)
Expand All @@ -205,15 +184,6 @@ namespace kagome::blockchain {
const primitives::BlockHash &ancestor,
const primitives::BlockHash &descendant) const = 0;

/**
* Get a longest path (chain of blocks) from the last finalized block down
* to the deepest leaf
* @return chain of blocks or error
*
* @note this function is equivalent to "getChainByBlock(deepestLeaf())"
*/
virtual BlockHashVecRes longestPath() const = 0;

/**
* Get a deepest leaf of the tree
* @return deepest leaf
Expand Down
176 changes: 40 additions & 136 deletions core/blockchain/impl/block_tree_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -917,12 +917,6 @@ namespace kagome::blockchain {
return BlockTreeError::JUSTIFICATION_NOT_FOUND;
}

BlockTreeImpl::BlockHashVecRes BlockTreeImpl::getChainByBlock(
const primitives::BlockHash &block) const {
return getChainByBlocks(
tree_->getMetadata().last_finalized.lock()->block_hash, block);
}

BlockTree::BlockHashVecRes BlockTreeImpl::getBestChainFromBlock(
const primitives::BlockHash &block, uint64_t maximum) const {
auto block_number_res = header_repo_->getNumberByHash(block);
Expand All @@ -934,16 +928,24 @@ namespace kagome::blockchain {
}
auto start_block_number = block_number_res.value();

if (maximum == 1) {
return std::vector{block};
}

auto deepest_leaf = tree_->getMetadata().deepest_leaf.lock();
BOOST_ASSERT(deepest_leaf != nullptr);
auto current_depth = deepest_leaf->depth;

primitives::BlockNumber finish_block_number =
start_block_number + maximum - 1;
if (current_depth < finish_block_number) {
finish_block_number = current_depth;
if (start_block_number >= current_depth) {
return std::vector{block};
}

auto count =
std::min<uint64_t>(current_depth - start_block_number + 1, maximum);

primitives::BlockNumber finish_block_number =
start_block_number + count - 1;

auto finish_block_hash_res =
header_repo_->getHashByNumber(finish_block_number);
if (finish_block_hash_res.has_error()) {
Expand All @@ -954,25 +956,29 @@ namespace kagome::blockchain {
}
const auto &finish_block_hash = finish_block_hash_res.value();

return getChainByBlocks(block, finish_block_hash, maximum);
OUTCOME_TRY(chain, getDescendingChainToBlock(finish_block_hash, count));
if (chain.back() != block) {
return std::vector{block};
}
std::reverse(chain.begin(), chain.end());
return std::move(chain);
}

BlockTree::BlockHashVecRes BlockTreeImpl::getDescendingChainToBlock(
const primitives::BlockHash &to_block, uint64_t maximum) const {
std::deque<primitives::BlockHash> chain;
std::vector<primitives::BlockHash> chain;

auto hash = to_block;

// Try to retrieve from cached tree
if (auto node = tree_->getRoot().findByHash(hash)) {
chain.emplace_back(hash);
while (maximum > chain.size()) {
auto parent = node->parent.lock();
if (not parent) {
hash = node->block_hash;
break;
}
chain.emplace_back(parent->block_hash);
chain.emplace_back(node->block_hash);
node = parent;
}
}
Expand All @@ -992,135 +998,32 @@ namespace kagome::blockchain {

chain.emplace_back(hash);

if (header.parent_hash == primitives::BlockHash{}) {
if (header.number == 0) {
break;
}

hash = header.parent_hash;
}

return std::vector<primitives::BlockHash>(chain.begin(), chain.end());
}

BlockTreeImpl::BlockHashVecRes BlockTreeImpl::getChainByBlocks(
const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block,
const uint32_t max_count) const {
return getChainByBlocks(
top_block, bottom_block, std::make_optional(max_count));
return chain;
}

BlockTreeImpl::BlockHashVecRes BlockTreeImpl::getChainByBlocks(
const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block,
std::optional<uint32_t> max_count) const {
OUTCOME_TRY(from, header_repo_->getNumberByHash(top_block));
OUTCOME_TRY(to, header_repo_->getNumberByHash(bottom_block));

if (auto chain_res = tryGetChainByBlocksFromCache(
primitives::BlockInfo{from, top_block},
primitives::BlockInfo{to, bottom_block},
max_count)) {
return chain_res.value();
}

std::vector<primitives::BlockHash> result;
const primitives::BlockHash &ancestor,
const primitives::BlockHash &descendant) const {
OUTCOME_TRY(from, header_repo_->getNumberByHash(ancestor));
OUTCOME_TRY(to, header_repo_->getNumberByHash(descendant));
if (to < from) {
return result;
}

const auto response_length =
max_count ? std::min(to - from + 1, max_count.value())
: (to - from + 1);
result.reserve(response_length);

SL_TRACE(log_,
"Try to create {} length chain from number {} to {}.",
response_length,
from,
to);

auto current_hash = bottom_block;

std::deque<primitives::BlockHash> chain;
chain.emplace_back(current_hash);
size_t count = 0;
while (current_hash != top_block && result.size() < response_length) {
if (max_count.has_value() && ++count > max_count.value()) {
log_->warn(
"impossible to get chain by blocks: "
"max count exceeded at intermediate block {}",
current_hash);
break;
}
auto header_res = header_repo_->getBlockHeader(current_hash);
if (!header_res) {
log_->warn(
"impossible to get chain by blocks: "
"intermediate block {} was not added to block tree before",
current_hash);
return BlockTreeError::SOME_BLOCK_IN_CHAIN_NOT_FOUND;
}
current_hash = header_res.value().parent_hash;
if (chain.size() >= response_length) {
chain.pop_front();
}
chain.emplace_back(current_hash);
return BlockTreeError::TARGET_IS_PAST_MAX;
}

result.assign(chain.crbegin(), chain.crend());
return result;
}

std::optional<std::vector<primitives::BlockHash>>
BlockTreeImpl::tryGetChainByBlocksFromCache(
const primitives::BlockInfo &top_block,
const primitives::BlockInfo &bottom_block,
std::optional<uint32_t> max_count) const {
if (auto from = tree_->getRoot().findByHash(top_block.hash)) {
if (bottom_block.number < from->depth) {
return std::nullopt;
}
const auto in_tree_branch_len = bottom_block.number - from->depth + 1;
const auto response_length =
max_count ? std::min(in_tree_branch_len, max_count.value())
: in_tree_branch_len;

std::vector<primitives::BlockHash> result;
result.reserve(response_length);

auto res = from->applyToChain(
bottom_block,
[&result, response_length](auto &node) -> TreeNode::ExitToken {
result.emplace_back(node.block_hash);
if (result.size() == response_length) {
return TreeNode::ExitToken::EXIT;
}
return TreeNode::ExitToken::CONTINUE;
});
if (res.has_error()) {
SL_DEBUG(log_,
"Failed to collect a chain of blocks from {} to {}: {}",
top_block,
bottom_block,
res.error().message());
return std::nullopt;
}
SL_TRACE(log_,
"Create {} length chain from number {} to {} from cache.",
response_length,
from->depth,
bottom_block.number);

return result;
auto count = to - from + 1;
OUTCOME_TRY(chain, getDescendingChainToBlock(descendant, count));
BOOST_ASSERT(chain.size() == count);
if (chain.back() != ancestor) {
return BlockTreeError::BLOCK_ON_DEAD_END;
}
return std::nullopt;
}

BlockTreeImpl::BlockHashVecRes BlockTreeImpl::getChainByBlocks(
const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block) const {
return getChainByBlocks(top_block, bottom_block, std::nullopt);
std::reverse(chain.begin(), chain.end());
return std::move(chain);
}

bool BlockTreeImpl::hasDirectChain(
Expand Down Expand Up @@ -1169,6 +1072,9 @@ namespace kagome::blockchain {
if (ancestor_node_ptr && descendant_node_ptr) {
auto current_node = descendant_node_ptr;
while (current_node != ancestor_node_ptr) {
if (current_node->depth <= ancestor_node_ptr->depth) {
return false;
}
if (auto parent = current_node->parent; !parent.expired()) {
current_node = parent.lock();
} else {
Expand Down Expand Up @@ -1209,17 +1115,15 @@ namespace kagome::blockchain {
if (!current_header_res) {
return false;
}
if (current_header_res.value().number <= ancestor_depth) {
return false;
}
current_hash = current_header_res.value().parent_hash;
}
KAGOME_PROFILE_END(search_finalized_chain)
return true;
}

BlockTreeImpl::BlockHashVecRes BlockTreeImpl::longestPath() const {
auto &&[_, block_hash] = deepestLeaf();
return getChainByBlock(block_hash);
}

primitives::BlockInfo BlockTreeImpl::deepestLeaf() const {
auto &&leaf = tree_->getMetadata().deepest_leaf.lock();
BOOST_ASSERT(leaf != nullptr);
Expand Down
22 changes: 2 additions & 20 deletions core/blockchain/impl/block_tree_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,22 +111,15 @@ namespace kagome::blockchain {
const primitives::BlockHash &block_hash,
const primitives::Justification &justification) override;

BlockHashVecRes getChainByBlock(
const primitives::BlockHash &block) const override;

BlockHashVecRes getChainByBlocks(const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block,
uint32_t max_count) const override;

BlockHashVecRes getBestChainFromBlock(const primitives::BlockHash &block,
uint64_t maximum) const override;

BlockHashVecRes getDescendingChainToBlock(
const primitives::BlockHash &block, uint64_t maximum) const override;

BlockHashVecRes getChainByBlocks(
const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block) const override;
const primitives::BlockHash &ancestor,
const primitives::BlockHash &descendant) const override;

std::optional<primitives::Version> runtimeVersion() const override {
return actual_runtime_version_;
Expand All @@ -135,8 +128,6 @@ namespace kagome::blockchain {
bool hasDirectChain(const primitives::BlockHash &ancestor,
const primitives::BlockHash &descendant) const override;

BlockHashVecRes longestPath() const override;

primitives::BlockInfo deepestLeaf() const override;

outcome::result<primitives::BlockInfo> getBestContaining(
Expand Down Expand Up @@ -185,15 +176,6 @@ namespace kagome::blockchain {
const primitives::BlockHash &start,
const primitives::BlockNumber &limit) const;

std::optional<std::vector<primitives::BlockHash>>
tryGetChainByBlocksFromCache(const primitives::BlockInfo &top_block,
const primitives::BlockInfo &bottom_block,
std::optional<uint32_t> max_count) const;

BlockHashVecRes getChainByBlocks(const primitives::BlockHash &top_block,
const primitives::BlockHash &bottom_block,
std::optional<uint32_t> max_count) const;

/**
* @returns the tree leaves sorted by their depth
*/
Expand Down
Loading

0 comments on commit c48739c

Please sign in to comment.