-
Notifications
You must be signed in to change notification settings - Fork 380
Wait for block import in parachain consensus #271
Conversation
There was a bug in the parachain consensus that when importing a relay chain block that sets a new best parachain block, but the required parachain block was not yet imported. This pr fixes this by waiting for the block to be imported.
…rt-new-best-stuff
consensus/src/lib.rs
Outdated
fn finalize_block<T, Block, B>(client: &T, hash: Block::Hash) -> ClientResult<bool> | ||
/// Follow the finalized head of the given parachain. | ||
/// | ||
/// For every finalized block of the relay chain, |
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.
trailing doc
} | ||
}; | ||
|
||
let hash = header.hash(); |
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.
does the header actually need to be decoded for this? blake2(&finalized_head[..])
should give the same hash. We would get an UnknownBlock
error if it's an invalid header or we haven't yet seen the header.
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.
As the header is generic, a user could have whatever implementation for hash()
they wants.
if parachain.usage_info().chain.finalized_hash != hash { | ||
if let Err(e) = parachain.finalize_block(BlockId::hash(hash), None, true) { | ||
match e { | ||
ClientError::UnknownBlock(_) => tracing::debug!( |
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.
I suppose here there is a similar error to the one that the PR fixes. If the relay chain informs us about a finalized header before we've imported that header, then we won't finalize it, even if we import that same header immediately afterwards.
We will correctly finalize probably soon after, when we get a finality notification corresponding to a header we have already imported.
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.
That is right, but also done intentionally by me. I do not expect that we are that much behind the tip that we can not finalize the block, because we did not have this block yet. If that is happening, we probably have other problems.
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.
Maybe should be filed for the "wishlist" but yeah I agree. Even the slight lag in finality should mean that blocks are well propagated before finality.
// The unset best header of the parachain. Will be `Some(_)` when we have imported a relay chain | ||
// block before the parachain block it included. In this case we need to wait for this block to | ||
// be imported to set it as new best. | ||
let mut unset_best_header = None; |
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.
Down the line, we may even need to trigger an availability request to recover the PoV. It is possible that the initial collator is withholding the block.
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.
I thought that we don't wanted to prevent to ask the relay chain validators to recover the povblocks?
But yeah, in general you are right. However this is not yet implemented anywhere.
tracing::debug!( | ||
target: "cumulus-consensus", | ||
block_hash = ?hash, | ||
"Skipping set new best block, because block is already the best.", |
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.
i'm curious if we do see duplicate notifications like this often.
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.
Anecdotally, I've never noticed it.
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.
Every relay chain block that does not includes a new parachain block, will trigger this log line. So, with rococo v1 at least every second block.
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.
Ah, yeah, it's a debug line. I've never seen it because I never run with that debug target.
Ok(BlockStatus::InChainWithState) => { | ||
unset_best_header.take(); | ||
|
||
import_block_as_new_best(hash, parachain_head, parachain, announce_block); |
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.
why do we import as new best even though it's already InChainWithState
? Is it a fork-choice rule thing? is there no other API?
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.
AFAIK there is no other API for this. In the end we just import the header a second time and say that it should be imported as best block.
There once existed an API for this, but it was removed at some point.
tracing::error!( | ||
target: "cumulus-collator", | ||
block_hash = ?hash, | ||
"Trying to set pruned block as new best!", |
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.
I guess this might happen as a chain reversion, but probably not since pruning happens after finality
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.
Thanks for tests
What was the downside of that bug? I suppose collators would still build on the right head because that's determined by the collation generation subsystem. So the RPC would show incorrect data as the "best block" was not updated? |
This is not mainly about collators. Collators would still not be able to build a block, if they don't know the parachain block yet. So, we may need a similar fix for the collation as well. We already have something similar for block announcement validation, but there we need to wait for a relay chain block. This is fix is more for full nodes like rpc or whatever nodes. With rococo v0 we had seen slow nodes like raspberry pi suffering under this problem that one of the blocks was always imported first. Maybe it was a combination of multiple things that lead to this to the seen problems. However, I more like to have this thing fixed to be sure we don't see this. |
bot merge |
Waiting for commit status. |
There was a bug in the parachain consensus that when importing a relay
chain block that sets a new best parachain block, but the required
parachain block was not yet imported. This pr fixes this by waiting for
the block to be imported.
Closes: #203