Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 6cbe177

Browse files
coderobedavxy
andauthored
Fork-Tree import requires post-order DFS traversal (#11531) (#11535)
* Fork-tree insert requires post-order dfs traversal * Add dedicated test for methods requireing post-order traversal Co-authored-by: Davide Galassi <davxy@datawok.net>
1 parent 6c1d754 commit 6cbe177

File tree

1 file changed

+44
-20
lines changed

1 file changed

+44
-20
lines changed

utils/fork-tree/src/lib.rs

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ where
126126
/// imported in order.
127127
///
128128
/// Returns `true` if the imported node is a root.
129+
// WARNING: some users of this method (i.e. consensus epoch changes tree) currently silently
130+
// rely on a **post-order DFS** traversal. If we are using instead a top-down traversal method
131+
// then the `is_descendent_of` closure, when used after a warp-sync, may end up querying the
132+
// backend for a block (the one corresponding to the root) that is not present and thus will
133+
// return a wrong result.
129134
pub fn import<F, E>(
130135
&mut self,
131136
hash: H,
@@ -143,29 +148,20 @@ where
143148
}
144149
}
145150

146-
let mut children = &mut self.roots;
147-
let mut i = 0;
148-
while i < children.len() {
149-
let child = &children[i];
150-
if child.hash == hash {
151-
return Err(Error::Duplicate)
152-
}
153-
if child.number < number && is_descendent_of(&child.hash, &hash)? {
154-
children = &mut children[i].children;
155-
i = 0;
156-
} else {
157-
i += 1;
158-
}
151+
let (children, is_root) =
152+
match self.find_node_where_mut(&hash, &number, is_descendent_of, &|_| true)? {
153+
Some(parent) => (&mut parent.children, false),
154+
None => (&mut self.roots, true),
155+
};
156+
157+
if children.iter().any(|elem| elem.hash == hash) {
158+
return Err(Error::Duplicate)
159159
}
160160

161-
let is_first = children.is_empty();
162161
children.push(Node { data, hash, number, children: Default::default() });
163162

164-
// Quick way to check if the pushed node is a root
165-
let is_root = children.as_ptr() == self.roots.as_ptr();
166-
167-
if is_first {
168-
// Rebalance is required only if we've extended the branch depth.
163+
if children.len() == 1 {
164+
// Rebalance may be required only if we've extended the branch depth.
169165
self.rebalance();
170166
}
171167

@@ -293,7 +289,7 @@ where
293289
// rely on a **post-order DFS** traversal. If we are using instead a top-down traversal method
294290
// then the `is_descendent_of` closure, when used after a warp-sync, will end up querying the
295291
// backend for a block (the one corresponding to the root) that is not present and thus will
296-
// return a wrong result. Here we are implementing a post-order DFS.
292+
// return a wrong result.
297293
pub fn find_node_index_where<F, E, P>(
298294
&self,
299295
hash: &H,
@@ -1507,4 +1503,32 @@ mod test {
15071503
let node = tree.find_node_where(&"N", &6, &is_descendent_of, &|_| true).unwrap().unwrap();
15081504
assert_eq!((node.hash, node.number), ("M", 5));
15091505
}
1506+
1507+
#[test]
1508+
fn post_order_traversal_requirement() {
1509+
let (mut tree, is_descendent_of) = test_fork_tree();
1510+
1511+
// Test for the post-order DFS traversal requirement as specified by the
1512+
// `find_node_index_where` and `import` comments.
1513+
let is_descendent_of_for_post_order = |parent: &&str, child: &&str| match *parent {
1514+
"A" => Err(TestError),
1515+
"K" if *child == "Z" => Ok(true),
1516+
_ => is_descendent_of(parent, child),
1517+
};
1518+
1519+
// Post order traversal requirement for `find_node_index_where`
1520+
let path = tree
1521+
.find_node_index_where(&"N", &6, &is_descendent_of_for_post_order, &|_| true)
1522+
.unwrap()
1523+
.unwrap();
1524+
assert_eq!(path, [0, 1, 0, 0, 0]);
1525+
1526+
// Post order traversal requirement for `import`
1527+
let res = tree.import(&"Z", 100, (), &is_descendent_of_for_post_order);
1528+
assert_eq!(res, Ok(false));
1529+
assert_eq!(
1530+
tree.iter().map(|node| *node.0).collect::<Vec<_>>(),
1531+
vec!["A", "B", "C", "D", "E", "F", "H", "L", "M", "O", "I", "G", "J", "K", "Z"],
1532+
);
1533+
}
15101534
}

0 commit comments

Comments
 (0)