Kagome
Polkadot Runtime Engine in C++17
cached_tree.cpp
Go to the documentation of this file.
1 
7 
8 #include <queue>
9 
10 #include <iostream>
11 
14  switch (e) {
15  case E::NO_CHAIN_BETWEEN_BLOCKS:
16  return "no chain exists between given blocks";
17  }
18  return "unknown error";
19 }
20 
21 namespace kagome::blockchain {
22 
25  bool finalized)
26  : block_hash{hash}, depth{depth}, finalized{finalized} {}
27 
30  const std::shared_ptr<TreeNode> &parent,
31  bool finalized)
33 
34  outcome::result<void> TreeNode::applyToChain(
35  const primitives::BlockInfo &chain_end,
36  std::function<outcome::result<ExitToken>(TreeNode const &node)> const &op)
37  const {
38  using ChildIdx = size_t;
39  std::map<primitives::BlockHash, ChildIdx> fork_choice;
40 
41  const auto *current_node = findByHash(chain_end.hash).get();
42  if (current_node == nullptr) {
44  }
45 
46  // mostly to catch typos in tests, but who knows
47  BOOST_ASSERT(current_node->depth == chain_end.number);
48  // now we must memorize where to go on forks in order to traverse
49  // from this to chain_end
50  while (current_node->depth > this->depth) {
51  auto curren_parent = current_node->parent.lock();
52  BOOST_ASSERT(curren_parent != nullptr);
53  // if there's a fork at the parent, memorize which branch to take
54  if (curren_parent->children.size() > 1) {
55  const auto child_idx = std::find_if(curren_parent->children.begin(),
56  curren_parent->children.end(),
57  [current_node](auto &sptr) {
58  return sptr.get() == current_node;
59  })
60  - curren_parent->children.begin();
61  fork_choice[curren_parent->block_hash] = child_idx;
62  }
63  current_node = curren_parent.get();
64  }
65  if (current_node->block_hash != this->block_hash) {
67  }
68 
69  current_node = this;
70  do {
71  OUTCOME_TRY(exit_token, op(*current_node));
72  if (exit_token == ExitToken::EXIT) {
73  return outcome::success();
74  }
75  if (current_node->children.size() == 1) {
76  current_node = current_node->children.front().get();
77  } else if (current_node->children.size() > 1) {
78  auto child_idx = fork_choice[current_node->block_hash];
79  current_node = current_node->children[child_idx].get();
80  } else {
81  break;
82  }
83  } while (current_node->depth <= chain_end.number);
84 
85  return outcome::success();
86  }
87 
88  std::shared_ptr<const TreeNode> TreeNode::findByHash(
89  const primitives::BlockHash &hash) const {
90  // standard BFS
91  std::queue<std::shared_ptr<const TreeNode>> nodes_to_scan;
92  nodes_to_scan.push(shared_from_this());
93  while (!nodes_to_scan.empty()) {
94  const auto &node = nodes_to_scan.front();
95  if (node->block_hash == hash) {
96  return node;
97  }
98  for (const auto &child : node->children) {
99  nodes_to_scan.push(child);
100  }
101  nodes_to_scan.pop();
102  }
103  return nullptr;
104  }
105 
106  bool TreeNode::operator==(const TreeNode &other) const {
107  const auto &other_parent = other.parent;
108  auto parents_equal = (parent.expired() && other_parent.expired())
109  || (!parent.expired() && !other_parent.expired()
110  && parent.lock() == other_parent.lock());
111 
112  return parents_equal && block_hash == other.block_hash
113  && depth == other.depth;
114  }
115 
116  bool TreeNode::operator!=(const TreeNode &other) const {
117  return !(*this == other);
118  }
119 
121  const std::shared_ptr<TreeNode> &subtree_root_node,
122  std::optional<primitives::Justification> last_finalized_justification)
123  : deepest_leaf{subtree_root_node},
124  last_finalized{subtree_root_node},
126  std::function<void(std::shared_ptr<TreeNode>)> handle =
127  [&](std::shared_ptr<TreeNode> node) {
128  // avoid deep recursion
129  while (node->children.size() == 1) {
130  node = node->children.front();
131  }
132 
133  // is a leaf
134  if (node->children.empty()) {
135  leaves.emplace(node->block_hash);
136  // NOLINTNEXTLINE(bugprone-lambda-function-name)
137  BOOST_ASSERT(not deepest_leaf.expired());
138  if (node->depth > deepest_leaf.lock()->depth) {
139  deepest_leaf = node;
140  }
141  } else {
142  // follow descendants recursively
143  for (const auto &child : node->children) {
144  handle(child);
145  }
146  }
147  };
148 
149  handle(subtree_root_node);
150  }
151 
152  TreeMeta::TreeMeta(std::unordered_set<primitives::BlockHash> leaves,
153  const std::shared_ptr<TreeNode> &deepest_leaf,
154  const std::shared_ptr<TreeNode> &last_finalized,
156  : leaves{std::move(leaves)},
160 
161  void CachedTree::updateTreeRoot(std::shared_ptr<TreeNode> new_trie_root,
162  primitives::Justification justification) {
163  auto prev_root = root_;
164  auto prev_node = new_trie_root->parent.lock();
165 
166  // now node won't be deleted while cleaning children
167  root_ = std::move(new_trie_root);
168 
169  // cleanup children from child to parent, because otherwise
170  // when they are cleaned up when their parent shared ptr is deleted,
171  // recursive calls of shared pointer destructors break the stack
172  while (prev_node && prev_node != prev_root) {
173  prev_node->children.clear();
174  prev_node = prev_node->parent.lock();
175  }
176 
177  metadata_ = std::make_shared<TreeMeta>(root_, justification);
178  root_->parent.reset();
179  }
180 
181  TreeNode const &CachedTree::getRoot() const {
182  BOOST_ASSERT(root_ != nullptr);
183  return *root_;
184  }
185 
187  BOOST_ASSERT(root_ != nullptr);
188  return *root_;
189  }
190 
192  BOOST_ASSERT(metadata_ != nullptr);
193  return *metadata_;
194  }
195 
196  void CachedTree::updateMeta(const std::shared_ptr<TreeNode> &new_node) {
197  auto parent = new_node->parent.lock();
198  parent->children.push_back(new_node);
199 
200  metadata_->leaves.insert(new_node->block_hash);
201  metadata_->leaves.erase(parent->block_hash);
202  BOOST_ASSERT(metadata_->deepest_leaf.lock() != nullptr);
203  if (new_node->depth > metadata_->deepest_leaf.lock()->depth) {
204  metadata_->deepest_leaf = new_node;
205  }
206  }
207 
208  void CachedTree::removeFromMeta(const std::shared_ptr<TreeNode> &node) {
209  auto parent = node->parent.lock();
210  if (parent == nullptr) {
211  // Already removed with removed subtree
212  return;
213  }
214 
215  auto it = std::find(parent->children.begin(), parent->children.end(), node);
216  if (it != parent->children.end()) {
217  parent->children.erase(it);
218  }
219 
220  metadata_->leaves.erase(node->block_hash);
221  if (parent->children.empty()) {
222  metadata_->leaves.insert(parent->block_hash);
223  }
224 
225  BOOST_ASSERT(not metadata_->deepest_leaf.expired());
226  if (node == metadata_->deepest_leaf.lock()) {
227  metadata_->deepest_leaf = parent;
228  for (auto it = metadata_->leaves.begin();
229  it != metadata_->leaves.end();) {
230  const auto &hash = *it++;
231  const auto leaf_node = root_->findByHash(hash);
232  if (leaf_node == nullptr) {
233  // Already removed with removed subtree
234  metadata_->leaves.erase(hash);
235  } else if (leaf_node->depth > parent->depth) {
236  metadata_->deepest_leaf = leaf_node;
237  break;
238  }
239  }
240  }
241  }
242 } // namespace kagome::blockchain
void removeFromMeta(const std::shared_ptr< TreeNode > &node)
A reversal of updateMeta - it&#39;s called upon block tree branch prunung to remove pruned block from lea...
bool operator==(const TreeNode &other) const
outcome::result< void > applyToChain(const primitives::BlockInfo &chain_end, std::function< outcome::result< ExitToken >(TreeNode const &node)> const &op) const
Definition: cached_tree.cpp:34
primitives::BlockNumber depth
Definition: cached_tree.hpp:38
std::weak_ptr< TreeNode > deepest_leaf
std::shared_ptr< T > sptr
TreeMeta const & getMetadata() const
bool operator!=(const TreeNode &other) const
primitives::BlockHash block_hash
Definition: cached_tree.hpp:37
TreeMeta(const std::shared_ptr< TreeNode > &subtree_root_node, std::optional< primitives::Justification > last_finalized_justification)
uint32_t BlockNumber
Definition: common.hpp:18
std::weak_ptr< TreeNode > last_finalized
TreeNode const & getRoot() const
std::shared_ptr< const TreeNode > findByHash(const primitives::BlockHash &hash) const
Definition: cached_tree.cpp:88
TreeNode(const primitives::BlockHash &hash, primitives::BlockNumber depth, bool finalized=false)
Definition: cached_tree.cpp:23
void updateMeta(const std::shared_ptr< TreeNode > &new_node)
std::unordered_set< primitives::BlockHash > leaves
Definition: cached_tree.hpp:99
OUTCOME_CPP_DEFINE_CATEGORY(kagome::blockchain, TreeNode::Error, e)
Definition: cached_tree.cpp:12
std::weak_ptr< TreeNode > parent
Definition: cached_tree.hpp:39
std::optional< primitives::Justification > last_finalized_justification
void updateTreeRoot(std::shared_ptr< TreeNode > new_trie_root, primitives::Justification justification)