18 template <
typename Vector,
typename Func>
19 inline void filter_if(Vector &&v, Func &&func) {
20 v.erase(std::remove_if(
21 v.begin(), v.end(), std::not_fn(std::forward<Func>(func))),
26 template <
typename Collection,
typename Item>
27 inline bool contains(
const Collection &collection,
const Item &item) {
28 auto it = collection.find(item);
29 return it != collection.end();
33 inline bool inDirectAncestry(
const VoteGraph::Entry &entry,
37 auto opt = entry.getAncestorBlockBy(number);
38 return opt && opt == hash;
43 std::shared_ptr<VoterSet> voter_set,
44 std::shared_ptr<Chain> chain)
46 voter_set_(
std::move(voter_set)),
47 chain_(
std::move(chain)) {
52 std::optional<std::vector<primitives::BlockHash>>
58 std::vector<BlockHash> containing;
59 std::unordered_set<BlockHash> visited;
66 const auto &active_entry = it->second;
69 if (
auto [_, inserted] = visited.emplace(head); not inserted) {
73 auto ancestor_opt = active_entry.getAncestorBlockBy(block.
number);
74 if (ancestor_opt.has_value()) {
75 if (*ancestor_opt == block.
hash) {
76 containing.push_back(head);
79 if (not active_entry.ancestors.empty()) {
80 head = active_entry.ancestors.back();
95 auto inw_res =
voter_set_->indexAndWeight(voter);
96 if (!inw_res.has_value()) {
99 const auto [index, weight] = inw_res.value();
102 containing_opt.has_value()) {
103 if (containing_opt->empty()) {
104 OUTCOME_TRY(
append(block));
117 Entry &active_entry =
entries_.at(inspecting_hash);
118 active_entry.cumulative_vote.set(vote_type, index, weight);
119 auto parent_it = active_entry.ancestors.rbegin();
120 if (parent_it != active_entry.ancestors.rend()) {
121 inspecting_hash = *parent_it;
127 return outcome::success();
131 auto inw_res =
voter_set_->indexAndWeight(voter);
132 if (inw_res.has_value()) {
133 const auto [index, weight] = inw_res.value();
135 entry.cumulative_vote.unset(vote_type, index, weight);
142 return outcome::success();
150 BOOST_ASSERT_MSG(not ancestry.empty(),
151 "ancestry always contains at least 1 element - base");
153 ancestry.front() == block.
hash,
154 "ancestry always contains provided block as the first element");
155 BOOST_ASSERT_MSG(ancestry.back() ==
base_.
hash,
156 "ancestry always contains base block as the last element");
162 auto ancestry_it = std::find_if(ancestry.begin() + 1,
164 [
this, &entry_it](
auto &ancestor) {
165 return entry_it =
entries_.find(ancestor),
168 BOOST_ASSERT_MSG(ancestry_it != ancestry.end(),
169 "at least one entry presents ancestor of appending block");
173 Entry &entry = entry_it->second;
174 entry.descendants.push_back(block.
hash);
179 std::vector<BlockHash> ancestors(ancestry.begin() + 1, ancestry_it + 1);
182 if (!ancestors.empty()) {
183 BlockHash ancestor_hash = ancestors.back();
184 heads_.erase(ancestor_hash);
190 newEntry.number = block.
number;
191 newEntry.ancestors = std::move(ancestors);
194 return outcome::success();
198 const std::vector<primitives::BlockHash> &descendants,
201 new_entry.number = ancestor.
number;
203 std::optional<BlockHash> prev_ancestor_opt;
205 for (
const auto &descendant : descendants) {
206 Entry &entry =
entries_.at(descendant);
208 BOOST_ASSERT_MSG(inDirectAncestry(entry, ancestor.
hash, ancestor.
number),
209 "not in direct ancestry");
210 BOOST_ASSERT_MSG(ancestor.
number <= entry.number,
211 "this function only invoked with direct ancestors; qed");
213 size_t offset_size = entry.number - ancestor.
number;
214 auto ancestors_copy = entry.ancestors;
215 BOOST_ASSERT(entry.ancestors.begin() + offset_size
216 < entry.ancestors.end());
220 new_entry.ancestors = std::vector<BlockHash>{
221 entry.ancestors.begin() + offset_size, entry.ancestors.end()};
223 auto last_it = entry.ancestors.rbegin();
224 if (last_it != entry.ancestors.rend()) {
225 prev_ancestor_opt = *last_it;
227 prev_ancestor_opt = std::nullopt;
233 entry.ancestors = std::vector<BlockHash>{
234 entry.ancestors.begin(), entry.ancestors.begin() + offset_size};
236 new_entry.descendants.push_back(descendant);
238 new_entry.cumulative_vote.merge(entry.cumulative_vote,
voter_set_);
241 if (prev_ancestor_opt) {
242 auto it =
entries_.find(*prev_ancestor_opt);
243 BOOST_ASSERT_MSG(it !=
entries_.end(),
244 "Prior ancestor is referenced from a node; qed");
246 auto &prev_ancestor_entry = it->second;
247 std::unordered_set<BlockHash>
set{new_entry.descendants.begin(),
248 new_entry.descendants.end()};
251 filter_if(prev_ancestor_entry.descendants,
252 [&
set](
const BlockHash &hash) { return !contains(set, hash); });
253 prev_ancestor_entry.descendants.push_back(ancestor.
hash);
261 const std::optional<BlockInfo> ¤t_best,
263 bool force_constrain =
false;
266 if (current_best.has_value()) {
268 if (containing_opt.has_value()) {
269 auto &containing = containing_opt.value();
270 if (containing.empty()) {
274 const Entry &entry =
entries_.at(containing[0]);
275 auto ancestor_it = std::rbegin(entry.ancestors);
277 ancestor_it != std::rend(entry.ancestors),
278 "node containing non-node in history always has ancestor; qed");
279 node_key = *ancestor_it;
280 force_constrain =
true;
282 node_key = current_best->hash;
283 force_constrain =
false;
287 Entry active_node =
entries_.at(node_key);
288 if (not condition(active_node.cumulative_vote)) {
293 std::stack<std::reference_wrapper<const Entry>> nodes;
295 nodes.push(active_node);
296 while (not nodes.empty()) {
297 auto &node = nodes.top().get();
300 for (
auto &descendant_hash : node.descendants) {
301 auto &descendant =
entries_.at(descendant_hash);
303 if (force_constrain and current_best) {
304 if (not inDirectAncestry(
305 descendant, current_best->hash, current_best->number)) {
309 if (not condition(descendant.cumulative_vote)) {
313 if (descendant.number > active_node.number
314 or (descendant.number == active_node.number
315 and active_node.cumulative_vote.sum(vote_type)
316 < descendant.cumulative_vote.sum(vote_type))) {
317 node_key = descendant_hash;
318 active_node = descendant;
320 nodes.push(descendant);
324 force_constrain =
false;
327 std::optional<BlockInfo> info =
328 force_constrain ? current_best : std::nullopt;
332 auto &hashes = subchain.
hashes;
334 if (hashes.empty()) {
346 const std::optional<BlockInfo> &force_constrain,
349 filter_if(descendants, [&](
const BlockHash &hash) {
350 if (not force_constrain) {
353 return inDirectAncestry(
354 entries_.at(hash), force_constrain->hash, force_constrain->number);
357 const auto base_number = active_node.
number;
358 auto best_number = active_node.
number;
359 std::vector<BlockHash> hashes{active_node_hash};
361 std::unordered_map<BlockHash, VoteWeight> descendant_blocks;
365 std::optional<BlockHash> new_best;
366 std::optional<VoteWeight> new_best_vote_weight;
369 for (
const auto &d_node : descendants) {
370 const Entry &entry =
entries_.at(d_node);
371 auto ancestor_opt = entry.getAncestorBlockBy(base_number + offset);
372 if (not ancestor_opt.has_value()) {
376 BlockHash &d_block = ancestor_opt.value();
377 auto it = descendant_blocks.find(d_block);
378 if (it == descendant_blocks.end()) {
380 descendant_blocks[d_block] = entry.cumulative_vote;
383 descendant_blocks[d_block].merge(entry.cumulative_vote,
voter_set_);
386 if (condition(descendant_blocks[d_block])) {
387 if (not new_best_vote_weight
388 or new_best_vote_weight->sum(vote_type)
389 < descendant_blocks[d_block].sum(vote_type)) {
392 new_best_vote_weight = descendant_blocks[d_block];
403 descendant_blocks.clear();
404 filter_if(descendants, [&](
const BlockHash &hash) {
405 return inDirectAncestry(
entries_.at(hash), *new_best, best_number);
408 hashes.push_back(*new_best);
411 return Subchain{hashes, best_number};
415 if (ancestry_proof.empty()) {
420 auto new_hash = *std::rbegin(ancestry_proof);
427 old_entry.ancestors.insert(old_entry.ancestors.end(),
428 ancestry_proof.begin(),
429 ancestry_proof.end());
434 new_entry.number = new_number;
435 new_entry.descendants.push_back(
base_.
hash);
436 new_entry.cumulative_vote = old_entry.cumulative_vote;
438 entries_[new_hash] = std::move(new_entry);
446 for (
auto block = block_arg;;) {
449 if (not nodes_opt.has_value()) {
451 const Entry &node =
entries_.at(block.hash);
454 if (condition(node.cumulative_vote)) {
459 if (node.ancestors.empty()) {
462 block.hash = node.ancestors[0];
463 block.number = node.number - 1;
466 const auto &children = nodes_opt.value();
470 if (children.empty()) {
477 for (
auto &child : children) {
478 const Entry &child_node =
entries_.at(child);
485 if (condition(cumulative_weight)) {
490 const auto &child = children.back();
491 const Entry &child_node =
entries_.at(child);
492 size_t offset = child_node.number - block.number;
493 if (offset < child_node.ancestors.size()) {
494 block.hash = child_node.ancestors[offset];
495 block.number = child_node.number - offset - 1;
std::unordered_map< BlockHash, Entry > entries_
void adjustBase(const std::vector< BlockHash > &ancestry_proof) override
outcome::result< void > append(const BlockInfo &block)
std::unordered_set< BlockHash > heads_
std::shared_ptr< VoterSet > voter_set_
outcome::result< void > insert(VoteType vote_type, const BlockInfo &block, const Id &voter) override
Insert vote {.
std::optional< BlockInfo > findGhost(VoteType vote_type, const std::optional< BlockInfo > ¤t_best, const Condition &condition) const override
std::shared_ptr< Chain > chain_
std::vector< BlockHash > hashes
primitives::BlockInfo BlockInfo
crypto::Ed25519PublicKey Id
Subchain ghostFindMergePoint(VoteType vote_type, const BlockHash &active_node_hash, const Entry &active_node, const std::optional< BlockInfo > &force_constrain, const Condition &condition) const
std::function< bool(const VoteWeight &)> Condition
void remove(VoteType vote_type, const Id &voter) override
Remove vote {.
void introduceBranch(const std::vector< primitives::BlockHash > &descendants, const BlockInfo &ancestor)
primitives::BlockNumber BlockNumber
void merge(const VoteWeight &other, const std::shared_ptr< VoterSet > &voter_set)
std::vector< BlockHash > descendants
primitives::BlockHash BlockHash
std::optional< BlockInfo > findAncestor(VoteType vote_type, const BlockInfo &block, const Condition &condition) const override
VoteGraphImpl(const BlockInfo &base, std::shared_ptr< VoterSet > voter_set, std::shared_ptr< Chain > chain)
std::optional< std::vector< primitives::BlockHash > > findContainingNodes(const BlockInfo &block) const