use crate::std::{
collections::{BTreeMap, BTreeSet},
fmt::Debug,
ops::AddAssign,
vec::Vec,
};
use super::{BlockNumberOps, Chain, Error};
#[cfg_attr(any(feature = "std", test), derive(Debug))]
struct Entry<H, N, V> {
number: N,
ancestors: Vec<H>,
descendents: Vec<H>, cumulative_vote: V,
}
impl<H: Ord + PartialEq + Clone, N: BlockNumberOps, V> Entry<H, N, V> {
fn in_direct_ancestry(&self, hash: &H, number: N) -> Option<bool> {
self.ancestor_block(number).map(|h| h == hash)
}
fn ancestor_block(&self, number: N) -> Option<&H> {
if number >= self.number {
return None
}
let offset = self.number - number - N::one();
self.ancestors.get(offset.as_())
}
fn ancestor_node(&self) -> Option<H> {
self.ancestors.last().cloned()
}
}
struct Subchain<H, N> {
hashes: Vec<H>, best_number: N,
}
impl<H: Clone, N: Copy + BlockNumberOps> Subchain<H, N> {
fn best(&self) -> Option<(H, N)> {
self.hashes.last().map(|x| (x.clone(), self.best_number))
}
}
pub struct VoteGraph<H: Ord + Eq, N, V> {
entries: BTreeMap<H, Entry<H, N, V>>,
heads: BTreeSet<H>,
base: H,
base_number: N,
}
impl<H, N, V> VoteGraph<H, N, V>
where
H: Eq + Clone + Ord + Debug,
V: for<'a> AddAssign<&'a V> + Default + Clone + Debug,
N: Copy + Debug + BlockNumberOps,
{
pub fn new(base_hash: H, base_number: N, base_node: V) -> Self {
let mut entries = BTreeMap::new();
entries.insert(
base_hash.clone(),
Entry {
number: base_number,
ancestors: Vec::new(),
descendents: Vec::new(),
cumulative_vote: base_node,
},
);
let mut heads = BTreeSet::new();
heads.insert(base_hash.clone());
VoteGraph { entries, heads, base: base_hash, base_number }
}
pub fn base(&self) -> (H, N) {
(self.base.clone(), self.base_number)
}
pub fn adjust_base(&mut self, ancestry_proof: &[H]) {
let new_hash = match ancestry_proof.last() {
None => return, Some(h) => h,
};
if ancestry_proof.len() > self.base_number.as_() {
return
}
let new_number = {
let mut new_number = self.base_number;
for _ in 0..ancestry_proof.len() {
new_number = new_number - N::one();
}
new_number
};
let entry = {
let old_entry =
self.entries.get_mut(&self.base).expect("base hash entry always exists; qed");
old_entry.ancestors.extend(ancestry_proof.iter().cloned());
Entry {
number: new_number,
ancestors: Vec::new(),
descendents: vec![self.base.clone()],
cumulative_vote: old_entry.cumulative_vote.clone(),
}
};
self.entries.insert(new_hash.clone(), entry);
self.base = new_hash.clone();
self.base_number = new_number;
}
pub fn insert<C: Chain<H, N>, W>(
&mut self,
hash: H,
number: N,
vote: W,
chain: &C,
) -> Result<(), Error>
where
V: for<'a> AddAssign<&'a W>,
{
if let Some(containing) = self.find_containing_nodes(hash.clone(), number) {
if containing.is_empty() {
self.append(hash.clone(), number, chain)?;
} else {
self.introduce_branch(containing, hash.clone(), number);
}
} else {
}
let mut inspecting_hash = hash;
loop {
let active_entry = self
.entries
.get_mut(&inspecting_hash)
.expect("vote-node and its ancestry always exist after initial phase; qed");
active_entry.cumulative_vote += &vote;
match active_entry.ancestor_node() {
Some(parent) => inspecting_hash = parent,
None => break,
}
}
Ok(())
}
pub fn find_ancestor<F>(&self, mut hash: H, mut number: N, condition: F) -> Option<(H, N)>
where
F: Fn(&V) -> bool,
{
loop {
match self.find_containing_nodes(hash.clone(), number) {
None => {
let node =
self.entries.get(&hash).expect("by defn of find_containing_nodes; qed");
if condition(&node.cumulative_vote) {
return Some((hash, number))
}
match node.ancestors.get(0) {
None => return None,
Some(a) => {
hash = a.clone();
number = node.number - N::one();
},
}
},
Some(children) => {
if children.is_empty() {
return None
}
let mut v = V::default();
for c in &children {
let e = self.entries.get(c).expect("all children in graph; qed");
v += &e.cumulative_vote;
}
if condition(&v) {
return Some((hash, number))
}
let child = children.last().expect("children not empty; qed");
let entry = self.entries.get(child).expect("all children in graph; qed");
let offset = (entry.number - number).as_();
match entry.ancestors.get(offset) {
None => return None, Some(parent) => {
hash = parent.clone();
number = number - N::one();
},
}
},
}
}
}
pub fn cumulative_vote<'a>(&'a self, hash: H, number: N) -> V {
let entries = &self.entries;
let get_node = |hash: &_| -> &'a _ {
entries
.get(hash)
.expect("node either base or referenced by other in graph; qed")
};
match self.find_containing_nodes(hash.clone(), number) {
None => get_node(&hash).cumulative_vote.clone(),
Some(nodes) => {
let mut v = Default::default();
for node in nodes {
v += &get_node(&node).cumulative_vote;
}
v
},
}
}
pub fn find_ghost<'a, F>(&'a self, current_best: Option<(H, N)>, condition: F) -> Option<(H, N)>
where
F: Fn(&V) -> bool,
{
let entries = &self.entries;
let get_node = |hash: &_| -> &'a _ {
entries
.get(hash)
.expect("node either base or referenced by other in graph; qed")
};
let (mut node_key, mut force_constrain) = current_best
.clone()
.and_then(|(hash, number)| match self.find_containing_nodes(hash.clone(), number) {
None => Some((hash, false)),
Some(ref x) if !x.is_empty() => {
let ancestor = get_node(&x[0])
.ancestor_node()
.expect("node containing non-node in history always has ancestor; qed");
Some((ancestor, true))
},
Some(_) => None,
})
.unwrap_or_else(|| (self.base.clone(), false));
let mut active_node = get_node(&node_key);
if !condition(&active_node.cumulative_vote) {
return None
}
loop {
let next_descendent = active_node
.descendents
.iter()
.map(|d| (d.clone(), get_node(d)))
.filter(|&(_, node)| {
if let (true, Some(&(ref h, n))) = (force_constrain, current_best.as_ref()) {
node.in_direct_ancestry(h, n).unwrap_or(false)
} else {
true
}
})
.find(|&(_, node)| condition(&node.cumulative_vote));
match next_descendent {
Some((key, node)) => {
force_constrain = false;
node_key = key;
active_node = node;
},
None => break,
}
}
self.ghost_find_merge_point(
node_key,
active_node,
if force_constrain { current_best } else { None },
condition,
)
.best()
}
fn ghost_find_merge_point<'a, F>(
&'a self,
node_key: H,
active_node: &'a Entry<H, N, V>,
force_constrain: Option<(H, N)>,
condition: F,
) -> Subchain<H, N>
where
F: Fn(&V) -> bool,
{
let mut descendent_nodes: Vec<_> = active_node
.descendents
.iter()
.map(|h| self.entries.get(h).expect("descendents always present in node storage; qed"))
.filter(|n| {
if let Some((ref h, num)) = force_constrain {
n.in_direct_ancestry(h, num).unwrap_or(false)
} else {
true
}
})
.collect();
let base_number = active_node.number;
let mut best_number = active_node.number;
let mut descendent_blocks = Vec::with_capacity(descendent_nodes.len());
let mut hashes = vec![node_key];
let mut offset = N::zero();
loop {
offset = offset + N::one();
let mut new_best = None;
for d_node in &descendent_nodes {
if let Some(d_block) = d_node.ancestor_block(base_number + offset) {
match descendent_blocks.binary_search_by_key(&d_block, |(x, _)| x) {
Ok(idx) => {
descendent_blocks[idx].1 += &d_node.cumulative_vote;
if condition(&descendent_blocks[idx].1) {
new_best = Some(d_block.clone());
break
}
},
Err(idx) => descendent_blocks
.insert(idx, (d_block.clone(), d_node.cumulative_vote.clone())),
}
}
}
match new_best {
Some(new_best) => {
best_number = best_number + N::one();
descendent_blocks.clear();
descendent_nodes
.retain(|n| n.in_direct_ancestry(&new_best, best_number).unwrap_or(false));
hashes.push(new_best);
},
None => break,
}
}
Subchain { hashes, best_number }
}
fn find_containing_nodes(&self, hash: H, number: N) -> Option<Vec<H>> {
if self.entries.contains_key(&hash) {
return None
}
let mut containing_keys = Vec::new();
let mut visited = BTreeSet::new();
for mut head in self.heads.iter().cloned() {
let mut active_entry;
loop {
active_entry = match self.entries.get(&head) {
Some(e) => e,
None => break,
};
if !visited.insert(head.clone()) {
break
}
match active_entry.in_direct_ancestry(&hash, number) {
Some(true) => {
containing_keys.push(head.clone());
},
Some(false) => {}, None =>
if let Some(prev) = active_entry.ancestor_node() {
head = prev;
continue },
}
break
}
}
Some(containing_keys)
}
fn introduce_branch(&mut self, descendents: Vec<H>, ancestor_hash: H, ancestor_number: N) {
let produced_entry = descendents.into_iter().fold(None, |mut maybe_entry, descendent| {
let entry = self
.entries
.get_mut(&descendent)
.expect("this function only invoked with keys of vote-nodes; qed");
debug_assert!(entry.in_direct_ancestry(&ancestor_hash, ancestor_number).unwrap());
{
let prev_ancestor = entry.ancestor_node();
let offset_usize: usize = if ancestor_number > entry.number {
panic!("this function only invoked with direct ancestors; qed")
} else {
(entry.number - ancestor_number).as_()
};
let new_ancestors = entry.ancestors.drain(offset_usize..);
let &mut (ref mut new_entry, _) = maybe_entry.get_or_insert_with(move || {
let new_entry = Entry {
number: ancestor_number,
ancestors: new_ancestors.collect(),
descendents: vec![],
cumulative_vote: V::default(),
};
(new_entry, prev_ancestor)
});
new_entry.descendents.push(descendent);
new_entry.cumulative_vote += &entry.cumulative_vote;
}
maybe_entry
});
if let Some((new_entry, prev_ancestor)) = produced_entry {
if let Some(prev_ancestor) = prev_ancestor {
let prev_ancestor_node = self
.entries
.get_mut(&prev_ancestor)
.expect("Prior ancestor is referenced from a node; qed");
prev_ancestor_node.descendents.retain(|h| !new_entry.descendents.contains(h));
prev_ancestor_node.descendents.push(ancestor_hash.clone());
}
assert!(
self.entries.insert(ancestor_hash, new_entry).is_none(),
"this function is only invoked when there is no entry for the ancestor already; qed",
)
}
}
fn append<C: Chain<H, N>>(&mut self, hash: H, number: N, chain: &C) -> Result<(), Error> {
let mut ancestry = chain.ancestry(self.base.clone(), hash.clone())?;
ancestry.push(self.base.clone()); let mut ancestor_index = None;
for (i, ancestor) in ancestry.iter().enumerate() {
if let Some(entry) = self.entries.get_mut(ancestor) {
entry.descendents.push(hash.clone());
ancestor_index = Some(i);
break
}
}
let ancestor_index = ancestor_index.expect(
"base is kept; \
chain returns ancestry only if the block is a descendent of base; qed",
);
let ancestor_hash = ancestry[ancestor_index].clone();
ancestry.truncate(ancestor_index + 1);
self.entries.insert(
hash.clone(),
Entry {
number,
ancestors: ancestry,
descendents: Vec::new(),
cumulative_vote: V::default(),
},
);
self.heads.remove(&ancestor_hash);
self.heads.insert(hash);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::chain::{DummyChain, GENESIS_HASH};
#[test]
fn graph_fork_not_at_node() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C"]);
chain.push_blocks("C", &["D1", "E1", "F1"]);
chain.push_blocks("C", &["D2", "E2", "F2"]);
tracker.insert("A", 2, 100, &chain).unwrap();
tracker.insert("E1", 6, 100, &chain).unwrap();
tracker.insert("F2", 7, 100, &chain).unwrap();
assert!(tracker.heads.contains("E1"));
assert!(tracker.heads.contains("F2"));
assert!(!tracker.heads.contains("A"));
let a_entry = tracker.entries.get("A").unwrap();
assert_eq!(a_entry.descendents, vec!["E1", "F2"]);
assert_eq!(a_entry.cumulative_vote, 300);
let e_entry = tracker.entries.get("E1").unwrap();
assert_eq!(e_entry.ancestor_node().unwrap(), "A");
assert_eq!(e_entry.cumulative_vote, 100);
let f_entry = tracker.entries.get("F2").unwrap();
assert_eq!(f_entry.ancestor_node().unwrap(), "A");
assert_eq!(f_entry.cumulative_vote, 100);
}
#[test]
fn graph_fork_at_node() {
let mut chain = DummyChain::new();
let mut tracker1 = VoteGraph::new(GENESIS_HASH, 1, 0u32);
let mut tracker2 = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C"]);
chain.push_blocks("C", &["D1", "E1", "F1"]);
chain.push_blocks("C", &["D2", "E2", "F2"]);
tracker1.insert("C", 4, 100, &chain).unwrap();
tracker1.insert("E1", 6, 100, &chain).unwrap();
tracker1.insert("F2", 7, 100, &chain).unwrap();
tracker2.insert("E1", 6, 100, &chain).unwrap();
tracker2.insert("F2", 7, 100, &chain).unwrap();
tracker2.insert("C", 4, 100, &chain).unwrap();
for tracker in &[&tracker1, &tracker2] {
assert!(tracker.heads.contains("E1"));
assert!(tracker.heads.contains("F2"));
assert!(!tracker.heads.contains("C"));
let c_entry = tracker.entries.get("C").unwrap();
assert!(c_entry.descendents.contains(&"E1"));
assert!(c_entry.descendents.contains(&"F2"));
assert_eq!(c_entry.ancestor_node().unwrap(), GENESIS_HASH);
assert_eq!(c_entry.cumulative_vote, 300);
let e_entry = tracker.entries.get("E1").unwrap();
assert_eq!(e_entry.ancestor_node().unwrap(), "C");
assert_eq!(e_entry.cumulative_vote, 100);
let f_entry = tracker.entries.get("F2").unwrap();
assert_eq!(f_entry.ancestor_node().unwrap(), "C");
assert_eq!(f_entry.cumulative_vote, 100);
}
}
#[test]
fn ghost_merge_at_node() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C"]);
chain.push_blocks("C", &["D1", "E1", "F1"]);
chain.push_blocks("C", &["D2", "E2", "F2"]);
tracker.insert("B", 3, 0, &chain).unwrap();
tracker.insert("C", 4, 100, &chain).unwrap();
tracker.insert("E1", 6, 100, &chain).unwrap();
tracker.insert("F2", 7, 100, &chain).unwrap();
assert_eq!(tracker.find_ghost(None, |&x| x >= 250), Some(("C", 4)));
assert_eq!(tracker.find_ghost(Some(("C", 4)), |&x| x >= 250), Some(("C", 4)));
assert_eq!(tracker.find_ghost(Some(("B", 3)), |&x| x >= 250), Some(("C", 4)));
}
#[test]
fn ghost_merge_not_at_node_one_side_weighted() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
chain.push_blocks("F", &["G1", "H1", "I1"]);
chain.push_blocks("F", &["G2", "H2", "I2"]);
tracker.insert("B", 3, 0, &chain).unwrap();
tracker.insert("G1", 8, 100, &chain).unwrap();
tracker.insert("H2", 9, 150, &chain).unwrap();
assert_eq!(tracker.find_ghost(None, |&x| x >= 250), Some(("F", 7)));
assert_eq!(tracker.find_ghost(Some(("F", 7)), |&x| x >= 250), Some(("F", 7)));
assert_eq!(tracker.find_ghost(Some(("C", 4)), |&x| x >= 250), Some(("F", 7)));
assert_eq!(tracker.find_ghost(Some(("B", 3)), |&x| x >= 250), Some(("F", 7)));
}
#[test]
fn ghost_introduce_branch() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
chain.push_blocks("E", &["EA", "EB", "EC", "ED"]);
chain.push_blocks("F", &["FA", "FB", "FC"]);
tracker.insert("FC", 10, 5, &chain).unwrap();
tracker.insert("ED", 10, 7, &chain).unwrap();
assert_eq!(tracker.find_ghost(None, |&x| x >= 10), Some(("E", 6)));
assert_eq!(tracker.entries.get(GENESIS_HASH).unwrap().descendents, vec!["FC", "ED"]);
tracker.insert("E", 6, 3, &chain).unwrap();
assert_eq!(tracker.entries.get(GENESIS_HASH).unwrap().descendents, vec!["E"]);
let descendents = &tracker.entries.get("E").unwrap().descendents;
assert_eq!(descendents.len(), 2);
assert!(descendents.contains(&"ED"));
assert!(descendents.contains(&"FC"));
assert_eq!(tracker.find_ghost(None, |&x| x >= 10), Some(("E", 6)));
assert_eq!(tracker.find_ghost(Some(("C", 4)), |&x| x >= 10), Some(("E", 6)));
assert_eq!(tracker.find_ghost(Some(("E", 6)), |&x| x >= 10), Some(("E", 6)));
}
#[test]
fn walk_back_from_block_in_edge_fork_below() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C"]);
chain.push_blocks("C", &["D1", "E1", "F1", "G1", "H1", "I1"]);
chain.push_blocks("C", &["D2", "E2", "F2", "G2", "H2", "I2"]);
tracker.insert("B", 3, 10, &chain).unwrap();
tracker.insert("F1", 7, 5, &chain).unwrap();
tracker.insert("G2", 8, 5, &chain).unwrap();
let test_cases = &["D1", "D2", "E1", "E2", "F1", "F2", "G2"];
for block in test_cases {
let number = chain.number(block);
assert_eq!(tracker.find_ancestor(block, number, |&x| x > 5).unwrap(), ("C", 4));
}
}
#[test]
fn walk_back_from_fork_block_node_below() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D"]);
chain.push_blocks("D", &["E1", "F1", "G1", "H1", "I1"]);
chain.push_blocks("D", &["E2", "F2", "G2", "H2", "I2"]);
tracker.insert("B", 3, 10, &chain).unwrap();
tracker.insert("F1", 7, 5, &chain).unwrap();
tracker.insert("G2", 8, 5, &chain).unwrap();
assert_eq!(tracker.find_ancestor("G2", 8, |&x| x > 5).unwrap(), ("D", 5));
let test_cases = &["E1", "E2", "F1", "F2", "G2"];
for block in test_cases {
let number = chain.number(block);
assert_eq!(tracker.find_ancestor(block, number, |&x| x > 5).unwrap(), ("D", 5));
}
}
#[test]
fn walk_back_at_node() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 1, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C"]);
chain.push_blocks("C", &["D1", "E1", "F1", "G1", "H1", "I1"]);
chain.push_blocks("C", &["D2", "E2", "F2"]);
tracker.insert("C", 4, 10, &chain).unwrap();
tracker.insert("F1", 7, 5, &chain).unwrap();
tracker.insert("F2", 7, 5, &chain).unwrap();
tracker.insert("I1", 10, 1, &chain).unwrap();
let test_cases = &["C", "D1", "D2", "E1", "E2", "F1", "F2", "I1"];
for block in test_cases {
let number = chain.number(block);
assert_eq!(tracker.find_ancestor(block, number, |&x| x >= 20).unwrap(), ("C", 4));
}
}
#[test]
fn adjust_base() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new("E", 6, 0u32);
chain.push_blocks(GENESIS_HASH, &["A", "B", "C", "D", "E", "F"]);
chain.push_blocks("E", &["EA", "EB", "EC", "ED"]);
chain.push_blocks("F", &["FA", "FB", "FC"]);
tracker.insert("FC", 10, 5, &chain).unwrap();
tracker.insert("ED", 10, 7, &chain).unwrap();
assert_eq!(tracker.base(), ("E", 6));
tracker.adjust_base(&["D", "C", "B", "A"]);
assert_eq!(tracker.base(), ("A", 2));
chain.push_blocks("A", &["3", "4", "5"]);
tracker.adjust_base(&[GENESIS_HASH]);
assert_eq!(tracker.base(), (GENESIS_HASH, 1));
assert_eq!(tracker.entries.get(GENESIS_HASH).unwrap().cumulative_vote, 12);
tracker.insert("5", 5, 3, &chain).unwrap();
assert_eq!(tracker.entries.get(GENESIS_HASH).unwrap().cumulative_vote, 15);
}
#[test]
fn find_ancestor_is_largest() {
let mut chain = DummyChain::new();
let mut tracker = VoteGraph::new(GENESIS_HASH, 0, 0);
chain.push_blocks(GENESIS_HASH, &["A"]);
chain.push_blocks(GENESIS_HASH, &["B"]);
chain.push_blocks("A", &["A1"]);
chain.push_blocks("A", &["A2"]);
chain.push_blocks("B", &["B1"]);
chain.push_blocks("B", &["B2"]);
tracker.insert("B1", 2, 1, &chain).unwrap();
tracker.insert("B2", 2, 1, &chain).unwrap();
tracker.insert("A1", 2, 1, &chain).unwrap();
tracker.insert("A2", 2, 1, &chain).unwrap();
let actual = tracker.find_ancestor("A", 1, |x| x >= &2).unwrap();
assert_eq!(actual, ("A", 1));
}
}