diff --git a/client/consensus/epochs/src/lib.rs b/client/consensus/epochs/src/lib.rs index acb07dd668a3c..a2e79e0c90621 100644 --- a/client/consensus/epochs/src/lib.rs +++ b/client/consensus/epochs/src/lib.rs @@ -312,7 +312,7 @@ fn fake_head_hash + AsMut<[u8]> + Clone>(parent_hash: &H) -> H { } impl Default for EpochChanges where - Hash: PartialEq + Ord, + Hash: Eq + Ord + Clone + std::hash::Hash, Number: Ord, { fn default() -> Self { @@ -321,7 +321,7 @@ impl Default for EpochChanges where } impl EpochChanges where - Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy, + Hash: Eq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy + std::hash::Hash, Number: Ord + One + Zero + Add + Copy, { /// Create a new epoch change. diff --git a/client/consensus/epochs/src/migration.rs b/client/consensus/epochs/src/migration.rs index e4717b5584e0e..e031b2d024604 100644 --- a/client/consensus/epochs/src/migration.rs +++ b/client/consensus/epochs/src/migration.rs @@ -32,7 +32,7 @@ pub struct EpochChangesV0 { pub type EpochChangesForV0 = EpochChangesV0<::Hash, NumberFor, Epoch>; impl EpochChangesV0 where - Hash: PartialEq + Ord + Copy, + Hash: Eq + Ord + Copy + std::hash::Hash, Number: Ord + Copy, { /// Create a new value of this type from raw. diff --git a/client/finality-grandpa/rpc/src/report.rs b/client/finality-grandpa/rpc/src/report.rs index a635728cb938a..bb06fcdaf61f7 100644 --- a/client/finality-grandpa/rpc/src/report.rs +++ b/client/finality-grandpa/rpc/src/report.rs @@ -20,6 +20,7 @@ use std::{ collections::{BTreeSet, HashSet}, fmt::Debug, ops::Add, + hash::Hash, }; use serde::{Deserialize, Serialize}; @@ -41,7 +42,7 @@ pub trait ReportVoterState { impl ReportAuthoritySet for SharedAuthoritySet where N: Add + Ord + Clone + Debug, - H: Clone + Debug + Eq, + H: Clone + Debug + Eq + Hash, { fn get(&self) -> (u64, HashSet) { let current_voters: HashSet = self diff --git a/client/finality-grandpa/src/authorities.rs b/client/finality-grandpa/src/authorities.rs index 2de169fc8285a..4a2d63ab947ec 100644 --- a/client/finality-grandpa/src/authorities.rs +++ b/client/finality-grandpa/src/authorities.rs @@ -30,6 +30,7 @@ use std::cmp::Ord; use std::fmt::Debug; use std::ops::Add; use std::sync::Arc; +use std::hash::Hash; /// Error type returned on operations on the `AuthoritySet`. #[derive(Debug, derive_more::Display)] @@ -88,7 +89,7 @@ impl SharedAuthoritySet { impl SharedAuthoritySet where N: Add + Ord + Clone + Debug, - H: Clone + Debug + H: Clone + Debug + Hash, { /// Get the earliest limit-block number that's higher or equal to the given /// min number, if any. @@ -156,7 +157,7 @@ pub struct AuthoritySet { impl AuthoritySet where - H: PartialEq, + H: Eq + Hash + Clone, N: Ord, { // authority sets must be non-empty and all weights must be greater than 0 @@ -206,7 +207,7 @@ where impl AuthoritySet where N: Add + Ord + Clone + Debug, - H: Clone + Debug, + H: Clone + Debug + Hash, { /// Returns the block hash and height at which the next pending change in /// the given chain (i.e. it includes `best_hash`) was signalled, `None` if @@ -793,8 +794,8 @@ mod tests { delay_kind: DelayKind::Finalized, }; - authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(true)).unwrap(); - authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(true)).unwrap(); + authorities.add_pending_change(change_a.clone(), &static_is_descendent_of(false)).unwrap(); + authorities.add_pending_change(change_b.clone(), &static_is_descendent_of(false)).unwrap(); assert_eq!( authorities.pending_changes().collect::>(), diff --git a/client/finality-grandpa/src/aux_schema.rs b/client/finality-grandpa/src/aux_schema.rs index 4ed96d058ac6b..b4554bbf0233b 100644 --- a/client/finality-grandpa/src/aux_schema.rs +++ b/client/finality-grandpa/src/aux_schema.rs @@ -18,6 +18,7 @@ use std::fmt::Debug; use std::sync::Arc; +use std::hash::Hash; use parity_scale_codec::{Encode, Decode}; use sc_client_api::backend::AuxStore; use sp_blockchain::{Result as ClientResult, Error as ClientError}; @@ -70,7 +71,7 @@ struct V0AuthoritySet { } impl Into> for V0AuthoritySet -where H: Clone + Debug + PartialEq, +where H: Clone + Debug + Eq + Hash, N: Clone + Debug + Ord, { fn into(self) -> AuthoritySet { diff --git a/utils/fork-tree/src/lib.rs b/utils/fork-tree/src/lib.rs index 1d01c53417649..c4ac34f6af38b 100644 --- a/utils/fork-tree/src/lib.rs +++ b/utils/fork-tree/src/lib.rs @@ -22,6 +22,7 @@ use std::cmp::Reverse; use std::fmt; +use std::hash::Hash; use codec::{Decode, Encode}; /// Error occurred when iterating with the tree. @@ -84,7 +85,7 @@ pub struct ForkTree { } impl ForkTree where - H: PartialEq + Clone, + H: Eq + Clone + Hash, N: Ord + Clone, V: Clone, { @@ -169,7 +170,7 @@ impl ForkTree where } impl ForkTree where - H: PartialEq, + H: Eq + Hash + Clone, N: Ord, { /// Create a new empty tree. @@ -190,9 +191,58 @@ impl ForkTree where /// for at any point will likely be in one of the deepest chains (i.e. the /// longest ones). pub fn rebalance(&mut self) { - self.roots.sort_by_key(|n| Reverse(n.max_depth())); - for root in &mut self.roots { - root.rebalance(); + use std::collections::HashMap; + use std::iter::FromIterator; + + let mut current_max_depth = 1; + let mut max_depths = HashMap::new(); + let mut stack = Vec::from_iter(self.roots.iter().map(|n| (n, 1, false))); + + while let Some((node, depth, expanded)) = stack.pop() { + // we've reached a leaf, record the max depth for this branch + if node.children.is_empty() { + current_max_depth = depth; + max_depths.insert(node.hash.clone(), depth); + continue; + } + + // this is not a leaf node and it has not been expanded yet + // we should reset the current max depth to the current depth + // since we will begin exploring a new branch + if !expanded { + for child in &node.children { + // we always backtrack to the parent after exploring one branch + // so that we can update its max depth before exploring the rest of + // the branches + stack.push((node, depth, true)); + stack.push((child, depth + 1, false)); + } + current_max_depth = depth; + continue; + } + + // we are backtracking so we need to update our depth with + // the maximum from all our children + let max_depth = max_depths.entry(node.hash.clone()).or_default(); + if current_max_depth > *max_depth { + *max_depth = current_max_depth; + } + } + + // iterate the tree in depth-first search and use the max depths + // map to sort each intermediary node + let mut stack = Vec::from_iter(self.roots.iter_mut()); + while let Some(node) = stack.pop() { + node.children.sort_by_key(|n| { + let max_depth = max_depths.get(&n.hash).expect( + "fails on unknown hash; \ + all tree node hashes have been added previously; \ + we only fetch tree node hashes; qed" + ); + Reverse(max_depth) + }); + + stack.extend(node.children.iter_mut()) } } @@ -204,9 +254,9 @@ impl ForkTree where /// Returns `true` if the imported node is a root. pub fn import( &mut self, - mut hash: H, - mut number: N, - mut data: V, + hash: H, + number: N, + data: V, is_descendent_of: &F, ) -> Result> where E: std::error::Error, @@ -218,31 +268,40 @@ impl ForkTree where } } - for root in self.roots.iter_mut() { - if root.hash == hash { - return Err(Error::Duplicate); - } + let is_descendent_of = |base: &H, target: &H| { + Ok(base == target || is_descendent_of(base, target)?) + }; - match root.import(hash, number, data, is_descendent_of)? { - Some((h, n, d)) => { - hash = h; - number = n; - data = d; - }, - None => return Ok(false), + let is_root = match self.find_node_where_mut(&hash, &number, &is_descendent_of, &|_| true)? { + Some(node) => { + if node.hash == hash { + return Err(Error::Duplicate) + } + + node.children.push(Node { + hash, + number, + data, + children: Vec::new(), + }); + + false } - } + None => { + self.roots.push(Node { + hash, + number, + data, + children: Vec::new(), + }); - self.roots.push(Node { - data, - hash: hash, - number: number, - children: Vec::new(), - }); + true + } + }; self.rebalance(); - Ok(true) + Ok(is_root) } /// Iterates over the existing roots in the tree. @@ -633,6 +692,7 @@ mod node_implementation { use super::*; /// The outcome of a search within a node. + #[derive(Debug, PartialEq)] pub enum FindOutcome { // this is the node we were looking for. Found(T), @@ -643,7 +703,7 @@ mod node_implementation { Abort, } - #[derive(Clone, Debug, Decode, Encode, PartialEq)] + #[derive(Clone, Debug, Encode, PartialEq)] pub struct Node { pub hash: H, pub number: N, @@ -651,26 +711,52 @@ mod node_implementation { pub children: Vec>, } - impl Node { - /// Rebalance the tree, i.e. sort child nodes by max branch depth (decreasing). - pub fn rebalance(&mut self) { - self.children.sort_by_key(|n| Reverse(n.max_depth())); - for child in &mut self.children { - child.rebalance(); - } - } + impl Decode for Node { + fn decode(input: &mut I) -> Result { + let complete = |node: &Self| { + node.children.len() == node.children.capacity() + }; - /// Finds the max depth among all branches descendent from this node. - pub fn max_depth(&self) -> usize { - let mut max = 0; + let mut stack = Vec::new(); + + // Loop until we've got a single completely decoded node on the stack. + while stack.len() != 1 || !stack.last().map(complete).unwrap_or(false) { + // If the top-most node is complete, pop it and push it as a child of the node + // beneath it. + if stack.last().map(complete).unwrap_or(false) { + let last = stack.pop().expect( + "If the stack was empty, the above statement would have returned none; qed" + ); + let latest = stack.last_mut().expect( + "In the above while statment, we check if the stack contains a number of \ + items different fron 1 OR if the top item is not complete; \ + In the above if statement, we check that the top item is complete; \ + Therefore, the number of items has to be different fron 1; \ + The only case where there are 0 items on the stack is at the start; \ + Therefore theremust be 2 or more items on the stack; QED" + ); + latest.children.push(last); + continue; + } - for node in &self.children { - max = node.max_depth().max(max) + // Otherwise, decode a node and push it onto the stack. + let hash = H::decode(input)?; + let number = N::decode(input)?; + let data = V::decode(input)?; + // `Vec`s use a compacted u32 for capacity. + let capacity = codec::Compact::::decode(input)?; + + stack.push(Node { + hash, number, data, + children: Vec::with_capacity(capacity.0 as usize), + }); } - max + 1 + Ok(stack.pop().expect("fails if stack is empty; exit condition of loop above ensures stack has one element; qed")) } + } + impl Node { /// Map node data into values of new types. pub fn map( self, @@ -694,47 +780,6 @@ mod node_implementation { } } - pub fn import( - &mut self, - mut hash: H, - mut number: N, - mut data: V, - is_descendent_of: &F, - ) -> Result, Error> - where E: fmt::Debug, - F: Fn(&H, &H) -> Result, - { - if self.hash == hash { - return Err(Error::Duplicate); - }; - - if number <= self.number { return Ok(Some((hash, number, data))); } - - for node in self.children.iter_mut() { - match node.import(hash, number, data, is_descendent_of)? { - Some((h, n, d)) => { - hash = h; - number = n; - data = d; - }, - None => return Ok(None), - } - } - - if is_descendent_of(&self.hash, &hash)? { - self.children.push(Node { - data, - hash: hash, - number: number, - children: Vec::new(), - }); - - Ok(None) - } else { - Ok(Some((hash, number, data))) - } - } - /// Find a node in the tree that is the deepest ancestor of the given /// block hash which also passes the given predicate, backtracking /// when the predicate fails. @@ -751,52 +796,68 @@ mod node_implementation { is_descendent_of: &F, predicate: &P, ) -> Result>, Error> - where E: std::error::Error, - F: Fn(&H, &H) -> Result, - P: Fn(&V) -> bool, + where E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, { - // stop searching this branch - if *number < self.number { - return Ok(FindOutcome::Failure(false)); - } + let mut stack = vec![(self, None, false)]; + let mut indices = Vec::new(); + + while let Some((node, index, expanded)) = stack.pop() { + // if we have already expanded this node we drop the index + // for the tree path + if expanded { + indices.pop(); + } - let mut known_descendent_of = false; - - // continue depth-first search through all children - for (i, node) in self.children.iter().enumerate() { - // found node, early exit - match node.find_node_index_where(hash, number, is_descendent_of, predicate)? { - FindOutcome::Abort => return Ok(FindOutcome::Abort), - FindOutcome::Found(mut x) => { - x.push(i); - return Ok(FindOutcome::Found(x)) - }, - FindOutcome::Failure(true) => { - // if the block was a descendent of this child, - // then it cannot be a descendent of any others, - // so we don't search them. - known_descendent_of = true; - break; - }, - FindOutcome::Failure(false) => {}, + // skip to the next node as we have traveled too deep + if *number < node.number { + continue; } - } - // node not found in any of the descendents, if the node we're - // searching for is a descendent of this node then we will stop the - // search here, since there aren't any more children and we found - // the correct node so we don't want to backtrack. - let is_descendent_of = known_descendent_of || is_descendent_of(&self.hash, hash)?; - if is_descendent_of { - // if the predicate passes we return the node - if predicate(&self.data) { - return Ok(FindOutcome::Found(Vec::new())); + // if we haven't expanded this node yet and it has children + // let's keep traversing in depth + if !expanded && !node.children.is_empty() { + // we re-add this node to the stack + // noting that it has already been expanded + stack.push((node, index, true)); + + // we also need add the index of this node + // in the tree path + if let Some(idx) = index { + indices.push(idx); + } + + // add all children to the stack + stack.extend(node.children.iter().enumerate().map(|(i, n)| (n, Some(i), false))); + + continue; + } + + // there's nothing left to expand in this branch, so before we backtrack + // we need to test the current node. + if is_descendent_of(&node.hash, hash)? { + // we're in the right branch + // and this node passes the predicate + if predicate(&node.data) { + // we must re-add its index (if any) since it was popped + // in the beginning of this iteration, and we also need + // to reverse the tree path + if let Some(idx) = index { + indices.push(idx); + indices.reverse(); + + return Ok(FindOutcome::Found(indices)); + } else { + // if there's no index it means that the found node + // is `self` so we return an empty tree path + return Ok(FindOutcome::Found(Vec::new())); + } + } } } - // otherwise, tell our ancestor that we failed, and whether - // the block was a descendent. - Ok(FindOutcome::Failure(is_descendent_of)) + Ok(FindOutcome::Failure(false)) } /// Find a node in the tree that is the deepest ancestor of the given @@ -910,6 +971,7 @@ impl Iterator for RemovedIterator { #[cfg(test)] mod test { use super::{FinalizationResult, ForkTree, Error}; + use codec::{Encode, Decode}; #[derive(Debug, PartialEq)] struct TestError; @@ -922,7 +984,7 @@ mod test { impl std::error::Error for TestError {} - fn test_fork_tree<'a>() -> (ForkTree<&'a str, u64, ()>, impl Fn(&&str, &&str) -> Result) { + fn test_fork_tree<'a>() -> (ForkTree<&'a str, u64, u64>, impl Fn(&&str, &&str) -> Result) { let mut tree = ForkTree::new(); // @@ -961,28 +1023,111 @@ mod test { } }; - tree.import("A", 1, (), &is_descendent_of).unwrap(); + tree.import("A", 1, 10, &is_descendent_of).unwrap(); - tree.import("B", 2, (), &is_descendent_of).unwrap(); - tree.import("C", 3, (), &is_descendent_of).unwrap(); - tree.import("D", 4, (), &is_descendent_of).unwrap(); - tree.import("E", 5, (), &is_descendent_of).unwrap(); + tree.import("B", 2, 9, &is_descendent_of).unwrap(); + tree.import("C", 3, 8, &is_descendent_of).unwrap(); + tree.import("D", 4, 7, &is_descendent_of).unwrap(); + tree.import("E", 5, 6, &is_descendent_of).unwrap(); - tree.import("F", 2, (), &is_descendent_of).unwrap(); - tree.import("G", 3, (), &is_descendent_of).unwrap(); + tree.import("F", 2, 5, &is_descendent_of).unwrap(); + tree.import("G", 3, 4, &is_descendent_of).unwrap(); - tree.import("H", 3, (), &is_descendent_of).unwrap(); - tree.import("I", 4, (), &is_descendent_of).unwrap(); - tree.import("L", 4, (), &is_descendent_of).unwrap(); - tree.import("M", 5, (), &is_descendent_of).unwrap(); - tree.import("O", 5, (), &is_descendent_of).unwrap(); + tree.import("H", 3, 3, &is_descendent_of).unwrap(); + tree.import("I", 4, 2, &is_descendent_of).unwrap(); + tree.import("L", 4, 1, &is_descendent_of).unwrap(); + tree.import("M", 5, 2, &is_descendent_of).unwrap(); + tree.import("O", 5, 3, &is_descendent_of).unwrap(); - tree.import("J", 2, (), &is_descendent_of).unwrap(); - tree.import("K", 3, (), &is_descendent_of).unwrap(); + tree.import("J", 2, 4, &is_descendent_of).unwrap(); + tree.import("K", 3, 11, &is_descendent_of).unwrap(); (tree, is_descendent_of) } + #[test] + fn find_node_index_where() { + let (tree, is_descendent_of) = test_fork_tree(); + + assert_eq!( + tree.find_node_index_where(&"B", &2, &is_descendent_of, &|_| true), + Ok(Some(vec![0])) + ); + + assert_eq!( + tree.find_node_index_where(&"C", &3, &is_descendent_of, &|_| true), + Ok(Some(vec![0, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"D", &4, &is_descendent_of, &|_| true), + Ok(Some(vec![0, 0, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"E", &5, &is_descendent_of, &|_| true), + Ok(Some(vec![0, 0, 0, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"F", &2, &is_descendent_of, &|_| true), + Ok(Some(vec![0])) + ); + + assert_eq!( + tree.find_node_index_where(&"G", &3, &is_descendent_of, &|_| true), + Ok(Some(vec![1, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"H", &3, &is_descendent_of, &|_| true), + Ok(Some(vec![1, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"I", &4, &is_descendent_of, &|_| true), + Ok(Some(vec![0, 1, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"L", &4, &is_descendent_of, &|_| true), + Ok(Some(vec![0, 1, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"M", &5, &is_descendent_of, &|_| true), + Ok(Some(vec![0, 0, 1, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"O", &5, &is_descendent_of, &|_| true), + Ok(Some(vec![0, 0, 1, 0])) + ); + + assert_eq!( + tree.find_node_index_where(&"J", &2, &is_descendent_of, &|_| true), + Ok(Some(vec![0])) + ); + + assert_eq!( + tree.find_node_index_where(&"K", &3, &is_descendent_of, &|_| true), + Ok(Some(vec![2, 0])) + ); + + for i in 0 .. 10 { + assert_eq!( + tree.find_node_index_where(&"A", &i, &is_descendent_of, &|_| true), + Ok(None), + "{}", i + ); + } + + assert_eq!( + tree.find_node_index_where(&"B", &0, &is_descendent_of, &|_| true), + Ok(None), + ); + } + #[test] fn import_doesnt_revert() { let (mut tree, is_descendent_of) = test_fork_tree(); @@ -995,7 +1140,7 @@ mod test { ); assert_eq!( - tree.import("A", 1, (), &is_descendent_of), + tree.import("A", 1, 1, &is_descendent_of), Err(Error::Revert), ); } @@ -1005,22 +1150,22 @@ mod test { let (mut tree, is_descendent_of) = test_fork_tree(); assert_eq!( - tree.import("A", 1, (), &is_descendent_of), + tree.import("A", 1, 1, &is_descendent_of), Err(Error::Duplicate), ); assert_eq!( - tree.import("I", 4, (), &is_descendent_of), + tree.import("I", 4, 1, &is_descendent_of), Err(Error::Duplicate), ); assert_eq!( - tree.import("G", 3, (), &is_descendent_of), + tree.import("G", 3, 1, &is_descendent_of), Err(Error::Duplicate), ); assert_eq!( - tree.import("K", 3, (), &is_descendent_of), + tree.import("K", 3, 1, &is_descendent_of), Err(Error::Duplicate), ); } @@ -1094,7 +1239,7 @@ mod test { // finalizing "A" opens up three possible forks assert_eq!( tree.finalize(&"A", 1, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + Ok(FinalizationResult::Changed(Some(10))), ); assert_eq!( @@ -1122,17 +1267,17 @@ mod test { // after finalizing "F" we can finalize "H" assert_eq!( tree.finalize(&"F", 2, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + Ok(FinalizationResult::Changed(Some(5))), ); assert_eq!( tree.finalize(&"H", 3, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + Ok(FinalizationResult::Changed(Some(3))), ); assert_eq!( tree.roots().map(|(h, n, _)| (h.clone(), n.clone())).collect::>(), - vec![("I", 4), ("L", 4)], + vec![("L", 4), ("I", 4)], ); // finalizing a node from another fork that isn't part of the tree clears the tree @@ -1161,7 +1306,7 @@ mod test { // finalizing "A" opens up three possible forks assert_eq!( tree.finalize_with_ancestors(&"A", 1, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + Ok(FinalizationResult::Changed(Some(10))), ); assert_eq!( @@ -1175,12 +1320,12 @@ mod test { // 3) finalizes the just opened root H (H -> I + L) assert_eq!( tree.finalize_with_ancestors(&"H", 3, &is_descendent_of), - Ok(FinalizationResult::Changed(Some(()))), + Ok(FinalizationResult::Changed(Some(3))), ); assert_eq!( tree.roots().map(|(h, n, _)| (h.clone(), n.clone())).collect::>(), - vec![("I", 4), ("L", 4)], + vec![("L", 4), ("I", 4)], ); assert_eq!( @@ -1353,12 +1498,19 @@ mod test { tree.iter().map(|(h, n, _)| (h.clone(), n.clone())).collect::>(), vec![ ("A", 1), - ("B", 2), ("C", 3), ("D", 4), ("E", 5), - ("F", 2), - ("G", 3), - ("H", 3), ("I", 4), - ("L", 4), ("M", 5), ("O", 5), - ("J", 2), ("K", 3) + ("B", 2), + ("C", 3), + ("D", 4), + ("E", 5), + ("F", 2), + ("H", 3), + ("L", 4), + ("M", 5), + ("O", 5), + ("I", 4), + ("G", 3), + ("J", 2), + ("K", 3), ], ); } @@ -1480,7 +1632,7 @@ mod test { assert_eq!( removed.map(|(hash, _, _)| hash).collect::>(), - vec!["A", "F", "G", "H", "I", "L", "M", "O", "J", "K"] + vec!["A", "F", "H", "L", "M", "O", "I", "G", "J", "K"] ); let removed = tree.prune( @@ -1547,7 +1699,7 @@ mod test { assert_eq!( tree.iter().map(|(h, _, _)| *h).collect::>(), - vec!["A", "B", "C", "D", "E", "F", "G", "H", "I", "L", "M", "O", "J", "K"], + vec!["A", "B", "C", "D", "E", "F", "H", "L", "M", "O", "I", "G", "J", "K"], ); // after rebalancing the tree we should iterate in preorder exploring @@ -1560,4 +1712,112 @@ mod test { ["A", "B", "C", "D", "E", "F", "H", "L", "M", "O", "I", "G", "J", "K"] ); } + + #[test] + fn find_node_where_value() { + let (tree, d) = test_fork_tree(); + assert_eq!( + tree.find_node_where(&"M", &5, &d, &|&n| n == 1 || n == 2) + .map(|opt| opt.map(|node| node.hash)), + Ok(Some("L")), + "{:?}", tree.find_node_index_where(&"M", &5, &d, &|&n| n == 1 || n == 2) + ); + } + + #[test] + fn find_node_where_value_2() { + let mut tree = ForkTree::new(); + + // + // A - B + // \ + // — C + // + let is_descendent_of = |base: &&str, block: &&str| -> Result { + match (*base, *block) { + ("A", b) => Ok(b == "B" || b == "C" || b == "D"), + ("B", b) | ("C", b) => Ok(b == "D"), + ("0", _) => Ok(true), + _ => Ok(false), + } + }; + + tree.import("A", 1, 1, &is_descendent_of).unwrap(); + tree.import("B", 2, 2, &is_descendent_of).unwrap(); + tree.import("C", 2, 4, &is_descendent_of).unwrap(); + + assert_eq!( + tree.find_node_where(&"D", &3, &is_descendent_of, &|&n| n == 1) + .map(|opt| opt.map(|node| node.hash)), + Ok(Some("A")) + ); + } + + #[test] + fn encoding_and_decoding_works() { + let tree = { + let mut tree = ForkTree::::new(); + + // + // - B - C - D - E + // / + // / - G + // / / + // A - F - H - I + // \ + // - L - M + // \ + // - O + // \ + // — J - K + // + // (where N is not a part of fork tree) + let is_descendent_of = |base: &String, block: &String| -> Result { + let letters = vec!["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "O"]; + match (&base[..], &block[..]) { + ("A", b) => Ok(letters.into_iter().any(|n| n == b)), + ("B", b) => Ok(b == "C" || b == "D" || b == "E"), + ("C", b) => Ok(b == "D" || b == "E"), + ("D", b) => Ok(b == "E"), + ("E", _) => Ok(false), + ("F", b) => Ok(b == "G" || b == "H" || b == "I" || b == "L" || b == "M" || b == "O"), + ("G", _) => Ok(false), + ("H", b) => Ok(b == "I" || b == "L" || b == "M" || b == "O"), + ("I", _) => Ok(false), + ("J", b) => Ok(b == "K"), + ("K", _) => Ok(false), + ("L", b) => Ok(b == "M" || b == "O"), + ("M", _) => Ok(false), + ("O", _) => Ok(false), + ("0", _) => Ok(true), + _ => Ok(false), + } + }; + + tree.import("A".into(), 1, 10, &is_descendent_of).unwrap(); + + tree.import("B".into(), 2, 9, &is_descendent_of).unwrap(); + tree.import("C".into(), 3, 8, &is_descendent_of).unwrap(); + tree.import("D".into(), 4, 7, &is_descendent_of).unwrap(); + tree.import("E".into(), 5, 6, &is_descendent_of).unwrap(); + + tree.import("F".into(), 2, 5, &is_descendent_of).unwrap(); + tree.import("G".into(), 3, 4, &is_descendent_of).unwrap(); + + tree.import("H".into(), 3, 3, &is_descendent_of).unwrap(); + tree.import("I".into(), 4, 2, &is_descendent_of).unwrap(); + tree.import("L".into(), 4, 1, &is_descendent_of).unwrap(); + tree.import("M".into(), 5, 2, &is_descendent_of).unwrap(); + tree.import("O".into(), 5, 3, &is_descendent_of).unwrap(); + + tree.import("J".into(), 2, 4, &is_descendent_of).unwrap(); + tree.import("K".into(), 3, 11, &is_descendent_of).unwrap(); + + tree + }; + + let encoded = tree.encode(); + let decoded = ForkTree::decode(&mut &encoded[..]).unwrap(); + assert_eq!(tree, decoded); + } }