-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Rewrite fork tree functions using loops. #7337
Conversation
|
#[test]
fn find_index_where() {
let (mut tree, is_descendent_of) = test_fork_tree();
assert_eq!(
tree.find_node_index_where(&"A", &1, &is_descendent_of, &|&()| true),
Ok(Some(vec![0]))
);
}
|
|
I've written out some tests for that now. |
|
Why did you remove the custom decode implementation? |
|
(The one that you have written) |
I haven't seen a node crash because of that yet, but I'll put it if I encounter that. |
|
Please bring it back. I have seen this. First the node crashes because of import and on restart the decode fails. |
|
I've been rewriting the #[test]
fn find_node_where_specific_value() {
let mut tree = ForkTree::new();
//
// A - B
// \
// — C
//
let is_descendent_of = |base: &&str, block: &&str| -> Result<bool, TestError> {
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 == 4)
.map(|opt| opt.map(|node| node.hash)),
Ok(Some("C"))
);
}and it fails with: at present. However I believe this test should pass, as |
|
@expenses In that example let is_descendent_of = |base: &&str, block: &&str| -> Result<bool, TestError> {
match (*base, *block) {
("A", b) => Ok(b == "B" || b == "C" || b == "D"),
("C", b) => Ok(b == "D"),
("0", _) => Ok(true),
_ => Ok(false),
}
};I think the current implementation will early exit after checking |
|
@andresilva so this is caused by a bad |
| impl<H: Decode, N: Decode, V: Decode> Decode for Node<H, N, V> { | ||
| fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> { | ||
| let complete = |node: &Self| { | ||
| node.children.len() == node.children.capacity() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat :)
I've implemented the suggested changes in #7337 (comment) and #7337 (comment) and this issue seems to have been resolved 🎉 |
andresilva
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a test for encoding/decoding and then making sure both structures are equal?
We will need to do a resync from scratch again of Polkadot and Kusama (with a full node), but we might as well do it just before we are ready to merge. It's a bit annoying but this touches consensus critical code and we really need to be sure we aren't breaking anything.
utils/fork-tree/src/lib.rs
Outdated
| // beneath it. | ||
| if stack.last().map(complete).unwrap_or(false) { | ||
| let last = stack.pop().expect("We already checked this"); | ||
| let latest = stack.last_mut().expect("We know there were 2 items on the stack"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you expand this proof, or maybe add it as a comment on why there must be at least 2 items on the stack. My understanding is that the only situation where we'd only have one item on the stack is if the root node has no children, otherwise we'll always have at least 2 nodes which are the root and whatever current children (or grandchildren..) we're currently handling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
|
||
| #[test] | ||
| fn find_node_where_value_2() { | ||
| let mut tree = ForkTree::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a comment on what's going on in this test. IIRC this is the test with the funky is_descendent_of where D is a child on two different forks, what is the behavior we are testing here? Initially we wanted to test that we bail out early when we test against B and the predicate fails (i.e. we wouldn't test C since D was already a known descendent of B).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, after the changes I don't think we're testing for anything specific here, just correctness.
Co-authored-by: André Silva <[email protected]>
|
@andresilva Any suggestions on how I could fix the failing grandpa test? |
|
When I tried to sync polkadot on the light client using this, I get the following at block 9,593: |
|
I guess the implementations I posted have a bug, the error you're getting means that some transition wasn't applied correctly. And the grandpa test that's failing also indicates the same. I'll have to look into the grandpa test. |
|
Regarding the grandpa test the issue is with the test itself, we add two pending changes that are supposed to be on different forks but we pass an There's still something wrong though if you're getting that light client error when syncing. I wonder if it's related to rebalancing, maybe just commenting that code and trying to replicate the issue would allow us to pinpoint it. |
|
Nevermind I don't think it could be related to |
|
@cheme would it be a good idea to merge this branch: master...cheme:iter_forktree into this? |
|
I am not against it, it depends how much we want to avoid unsafe code (for my part I find it fine, but it can be a vast debate). There is also the option to keep recursive code since it is a bit more readable. (I think that the stack overflow is a limit case related to block not being finalized, so it could be an acceptable to skip the switch). |
|
No, let's not add
For me recursive code is a lot easier to read, especially in tree structures, although I know it's not like that for everyone. It is true that this is an edge case that shouldn't happen under normal operation (i.e. if we finalize something every now and then), but it would still be nice if we didn't stack overflow. Given that we already spent some time on this I'd be in favor of still trying to merge these changes to make the algorithms iterative. I will try to debug this but I cannot make any promises right now. |
Sunk cost fallacy 😉 All I want is to stop the stack overflow issue from happening so the light client is usable. How we solve this problem doesn't really matter. It'd probably better if we didn't use recursion because this issue could come back if we solve it in a different way, but we can keep this branch around for that case. |
|
Oh hold for now. |
This PR is intended to fix #5998. Flaming Fir currently panics when
--lightis used on the debug build, but not on the release build.importis done,find_node_index_whereis in progress.