@@ -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