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

Commit f884296

Browse files
authored
fork-tree: fix tree rebalancing (#7616)
* fork-tree: rebalance tree when inserting inner node * fork-tree: fix tests for new rebalancing behavior * fork-tree: fix node iterator initial state * grandpa: fix tests
1 parent ba87972 commit f884296

File tree

2 files changed

+38
-17
lines changed

2 files changed

+38
-17
lines changed

client/finality-grandpa/src/authorities.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -758,9 +758,10 @@ mod tests {
758758
authorities.add_pending_change(change_d.clone(), &static_is_descendent_of(false)).unwrap();
759759
authorities.add_pending_change(change_e.clone(), &static_is_descendent_of(false)).unwrap();
760760

761+
// ordered by subtree depth
761762
assert_eq!(
762763
authorities.pending_changes().collect::<Vec<_>>(),
763-
vec![&change_b, &change_a, &change_c, &change_e, &change_d],
764+
vec![&change_a, &change_c, &change_b, &change_e, &change_d],
764765
);
765766
}
766767

@@ -798,7 +799,7 @@ mod tests {
798799

799800
assert_eq!(
800801
authorities.pending_changes().collect::<Vec<_>>(),
801-
vec![&change_b, &change_a],
802+
vec![&change_a, &change_b],
802803
);
803804

804805
// finalizing "hash_c" won't enact the change signaled at "hash_a" but it will prune out "hash_b"

utils/fork-tree/src/lib.rs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,10 @@ impl<H, N, V> ForkTree<H, N, V> where
229229
number = n;
230230
data = d;
231231
},
232-
None => return Ok(false),
232+
None => {
233+
self.rebalance();
234+
return Ok(false);
235+
},
233236
}
234237
}
235238

@@ -251,7 +254,9 @@ impl<H, N, V> ForkTree<H, N, V> where
251254
}
252255

253256
fn node_iter(&self) -> impl Iterator<Item=&Node<H, N, V>> {
254-
ForkTreeIterator { stack: self.roots.iter().collect() }
257+
// we need to reverse the order of roots to maintain the expected
258+
// ordering since the iterator uses a stack to track state.
259+
ForkTreeIterator { stack: self.roots.iter().rev().collect() }
255260
}
256261

257262
/// Iterates the nodes in the tree in pre-order.
@@ -939,6 +944,10 @@ mod test {
939944
// — J - K
940945
//
941946
// (where N is not a part of fork tree)
947+
//
948+
// NOTE: the tree will get automatically rebalance on import and won't be laid out like the
949+
// diagram above. the children will be ordered by subtree depth and the longest branches
950+
// will be on the leftmost side of the tree.
942951
let is_descendent_of = |base: &&str, block: &&str| -> Result<bool, TestError> {
943952
let letters = vec!["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "O"];
944953
match (*base, *block) {
@@ -1132,7 +1141,7 @@ mod test {
11321141

11331142
assert_eq!(
11341143
tree.roots().map(|(h, n, _)| (h.clone(), n.clone())).collect::<Vec<_>>(),
1135-
vec![("I", 4), ("L", 4)],
1144+
vec![("L", 4), ("I", 4)],
11361145
);
11371146

11381147
// finalizing a node from another fork that isn't part of the tree clears the tree
@@ -1180,7 +1189,7 @@ mod test {
11801189

11811190
assert_eq!(
11821191
tree.roots().map(|(h, n, _)| (h.clone(), n.clone())).collect::<Vec<_>>(),
1183-
vec![("I", 4), ("L", 4)],
1192+
vec![("L", 4), ("I", 4)],
11841193
);
11851194

11861195
assert_eq!(
@@ -1354,11 +1363,11 @@ mod test {
13541363
vec![
13551364
("A", 1),
13561365
("B", 2), ("C", 3), ("D", 4), ("E", 5),
1357-
("F", 2),
1366+
("F", 2), ("H", 3), ("L", 4), ("M", 5),
1367+
("O", 5),
1368+
("I", 4),
13581369
("G", 3),
1359-
("H", 3), ("I", 4),
1360-
("L", 4), ("M", 5), ("O", 5),
1361-
("J", 2), ("K", 3)
1370+
("J", 2), ("K", 3),
13621371
],
13631372
);
13641373
}
@@ -1480,7 +1489,7 @@ mod test {
14801489

14811490
assert_eq!(
14821491
removed.map(|(hash, _, _)| hash).collect::<Vec<_>>(),
1483-
vec!["A", "F", "G", "H", "I", "L", "M", "O", "J", "K"]
1492+
vec!["A", "F", "H", "L", "M", "O", "I", "G", "J", "K"]
14841493
);
14851494

14861495
let removed = tree.prune(
@@ -1545,19 +1554,30 @@ mod test {
15451554
fn tree_rebalance() {
15461555
let (mut tree, _) = test_fork_tree();
15471556

1557+
// the tree is automatically rebalanced on import, therefore we should iterate in preorder
1558+
// exploring the longest forks first. check the ascii art above to understand the expected
1559+
// output below.
15481560
assert_eq!(
15491561
tree.iter().map(|(h, _, _)| *h).collect::<Vec<_>>(),
1550-
vec!["A", "B", "C", "D", "E", "F", "G", "H", "I", "L", "M", "O", "J", "K"],
1562+
vec!["A", "B", "C", "D", "E", "F", "H", "L", "M", "O", "I", "G", "J", "K"],
15511563
);
15521564

1553-
// after rebalancing the tree we should iterate in preorder exploring
1554-
// the longest forks first. check the ascii art above to understand the
1555-
// expected output below.
1556-
tree.rebalance();
1565+
// let's add a block "P" which is a descendent of block "O"
1566+
let is_descendent_of = |base: &&str, block: &&str| -> Result<bool, TestError> {
1567+
match (*base, *block) {
1568+
(b, "P") => Ok(vec!["A", "F", "L", "O"].into_iter().any(|n| n == b)),
1569+
_ => Ok(false),
1570+
}
1571+
};
1572+
1573+
tree.import("P", 6, (), &is_descendent_of).unwrap();
15571574

1575+
// this should re-order the tree, since the branch "A -> B -> C -> D -> E" is no longer tied
1576+
// with 5 blocks depth. additionally "O" should be visited before "M" now, since it has one
1577+
// descendent "P" which makes that branch 6 blocks long.
15581578
assert_eq!(
15591579
tree.iter().map(|(h, _, _)| *h).collect::<Vec<_>>(),
1560-
["A", "B", "C", "D", "E", "F", "H", "L", "M", "O", "I", "G", "J", "K"]
1580+
["A", "F", "H", "L", "O", "P", "M", "I", "G", "B", "C", "D", "E", "J", "K"]
15611581
);
15621582
}
15631583
}

0 commit comments

Comments
 (0)