-
Notifications
You must be signed in to change notification settings - Fork 990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix invalid root #3005
Fix invalid root #3005
Conversation
@lehnberg Actually - I think we should just nudge people to use |
// We want to use the current head of header chain here. | ||
// Caller is responsible for rewinding the header MMR back | ||
// to a previous header as necessary when processing a fork. | ||
let head = batch.header_head()?; |
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.
👍 the puzzle of header head
and body head
:-) thanks very much for finding this long standing bug.
I'm a little bit confused on the comment:
Caller is responsible for rewinding the header MMR back to a previous header as necessary when processing a fork.
Does it ONLY refer to a calling of the extension.force_rollback()
to set that flag?
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.
Say we are at block height 5 on both main chain and header chain.
1 <- 2 <- 3 <- 4 <- 5
If we now receive a block at height 4' that forks from the main chain then we need to "rewind" back to block height 3 to be able to process this new block.
1 <- 2 <- 3 <- 4'
We would then rollback the MMR as this block did not increase the total work, i.e. not a reorg, just a fork.
If we then saw 5' that did increase total work compared to 5 then we would rewind as before and then persist the MMR and update the head
(and header_head
) to reflect the chain after the reorg.
Merging this. |
This PR fixes a long standing bug that surfaces with an error like -
This happens when we process two competing forks simultaneously. But only if we process header first or compact block header, and have not yet processed the first full block.
A
, updatingheader_head
but not yet updatinghead
(full block chain head)A'
(fork at same height asA
)A'
sprev_header
tohead
(should beheader_head
which was updated in previous step).A
so we incorrectly determine the header MMR does not need rewinding.A'
to the header MMR that already containsA
and hence build an invalid MMR.We basically have a race condition here between headers
A
andA'
and we do not rewind the header MMR correctly for the latest one processed.Note - This problem is hidden as soon as we process the full block
A
ashead
andheader_head
are identical at that point. So usinghead
is safe here and the rewind works as expected.This is a simple fix - when creating header extensions for
A
andA'
we need to treatheader_head
as the rightmost item in the MMR as this is consistent with the current MMR state.Added test coverage to exercise this scenario.
Also took the opportunity to clean up some of the existing test setup code.