-
-
Notifications
You must be signed in to change notification settings - Fork 289
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
Update forkchoice using latestValidHash updates from execution engine #4182
Conversation
Performance Report✔️ no performance regression detected Full benchmark results
|
ff89835
to
b5d6d0d
Compare
2e6beb1
to
c9a28af
Compare
} | ||
} | ||
|
||
propagateInValidExecutionStatusByIndex(invalidateFromIndex: number, latestValidHashIndex: number): void { |
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 you make the 1st param name consistent to the consumer? this is called invalidateFromIndex
while the consumer calls invalidateTillBlockIndex
also it's nice to have some descriptions in the method header, or draw some ascii texts representing forkchoice branches
/** invalidateAll is a flag which indicates detection of consensus failure */ | ||
let invalidateAll = false; | ||
let invalidateIndex: number | undefined = invalidateFromIndex; | ||
while (invalidateIndex !== undefined && invalidateFromIndex > latestValidHashIndex) { |
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.
should it be
while (invalidateIndex !== undefined && invalidateIndex > latestValidHashIndex) {
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 adding some unit tests for those new methods should help
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.
fixed! thanks for spotting it @tuyennhv ! yes will add a test case around this as well.
7373342
to
adedd33
Compare
|
||
if (execResponse.executionStatus === ExecutionStatus.Valid) { | ||
const {latestValidExecHash} = execResponse; | ||
let latestValidHashIndex = this.nodes.length - 1; |
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 default latest valid hash index should not be the last node, maybe make it undefined
or null
?
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 the input, i have initialized to -1, keeping in line with "not found" assignments of the indices. if you still feel strongly for undefined or null to represent not found
, do respond 🙂
const {latestValidExecHash} = execResponse; | ||
let latestValidHashIndex = this.nodes.length - 1; | ||
|
||
for (; latestValidHashIndex >= 0; latestValidHashIndex--) { |
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.
perhaps extract this to an array util so that it's reusable and testable
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.
its not really generic, and is covered by the validation test cases.
@@ -193,17 +206,23 @@ export async function verifyBlockStateTransition( | |||
switch (execResult.status) { | |||
case ExecutePayloadStatus.VALID: | |||
executionStatus = ExecutionStatus.Valid; | |||
chain.forkChoice.validateLatestHash(execResult.latestValidHash, null); | |||
chain.forkChoice.validateLatestHash({ |
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.
@g11tech my understanding is latestValidHash
is the current execution payload which we have not imported into forkchoice, is it an issue?
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.
@tuyennhv its not an issue here, since the latestValidHash should be the parent of the current payload, as current payload should not have become part of the chain as it has not been fcU-ed yet. But I will validate this.
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.
@g11tech what I see is go-ethereum
returns the current execution payload as latestValidHash
most of the time so we should handle it https://github.com/ethereum/go-ethereum/blob/e108d36575fd93e6088259cdabeed6dddf76458c/eth/catalyst/api.go#L324
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.
modified, just directly passing the parent's exechash now, so no matter what EL responds as LVH (block or its parent), we pass the correct one
1f5bbb5
to
e893be4
Compare
latestValidExecHash: parentBlock.executionPayloadBlockHash, | ||
invalidateTillBlockHash: null, | ||
}); | ||
} | ||
break; // OK | ||
|
||
case ExecutePayloadStatus.INVALID: { |
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.
should there be executionStatus = ExecutionStatus.Invalid
here?
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 are just throwing below after validateLatestHash
invalid status propagation, so its not getting used.
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.
also added some typesafety to it:
- let executionStatus: ExecutionStatus;
+ let executionStatus: MaybeValidExecutionStatus;
@g11tech take a look at ethereum/consensus-specs#2954 |
yes, the PR is the result of the discussions we already had on discord interop channel. |
node.executionStatus = ExecutionStatus.Valid; | ||
break; | ||
|
||
default: |
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.
Empty default clause?
@@ -482,6 +747,7 @@ export class ProtoArray { | |||
* head. | |||
*/ | |||
nodeIsViableForHead(node: ProtoNode): boolean { | |||
if (node.executionStatus === ExecutionStatus.Invalid) return false; |
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.
if (node.executionStatus === ExecutionStatus.Invalid) return false; | |
if (node.executionStatus === ExecutionStatus.Invalid) { | |
return false; | |
} | |
// Only invalidate if this is post merge, and either parent is invalid or the | ||
// concensus has failed | ||
if (node.executionStatus !== ExecutionStatus.PreMerge && parent?.executionStatus === ExecutionStatus.Invalid) { | ||
if (node.executionStatus === ExecutionStatus.Valid) { |
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.
Should this two if statements be merged?
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.
simplified the conditions, hopefully its more clear now
f7a1937
to
e40a529
Compare
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.
✔️ meta-approving! Looks really good, thanks for the simplifications.
Leaving un-approved until we cut 0.42 release. Left some minor comments, if you want to address before then
@@ -17,6 +21,7 @@ export class ProtoArray { | |||
finalizedRoot: RootHex; | |||
nodes: ProtoNode[] = []; | |||
indices = new Map<RootHex, number>(); | |||
lvhError?: LVHExecError; |
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.
This PR should include the mechanism to kill beacon process from the beacon package. Just check every slot or at some predicatable spot
packages/fork-choice/test/unit/protoArray/executionStatusUpdates.test.ts
Outdated
Show resolved
Hide resolved
private propagateValidExecutionStatusByIndex(validNodeIndex: number): void { | ||
let nodeIndex: number | undefined = validNodeIndex; | ||
// propagate till we keep encountering syncing status | ||
while (nodeIndex !== undefined) { |
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.
This loop should also stop when the parent is already valid as a cases in addition to going until invalid, premerge, or thru all ancestors.
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.
updated the loop break condition to !== syncing 👍
* extend forkchoice execution type * handle the invalid status in protoarray * add the pre execution status validation testcase * relocate validateLatestHash to protoarray for better testing * add case for invalidating a branch * add invalidations and validations * add a bit more desc to test case title * fix the invalidate index bug * refac the reverse lookup and throw if forkchoice becomes invalidated * relax the LVH conditions in invalid responses * only invalidate post merge * modify the invalidations to allow for various LVH scenarios * add invalidation case * simplyfy lvh search and be more forgiving when not found * add a fail hard case * correct the lvh passed in valid response * refactor how to extract lvh in valid scenario * replace invalidate all with proper error handling to be more verbose and swallow less * add test for invalid to valid * add type safety to executionStatus * linting if for better visual clarity * fix tests * refac the invalidation condition in propagate invalid lvh * remove empty default clause * simplify segment exec response * error logging * always throw if there is segment error
Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com>
Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com>
e4d6d3f
to
6b75d90
Compare
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.
lgtm
Motivation
Post merge, engine can give optimistic
syncing
status on fcU heads which can (should be) eventually resolved intovalid
/invalid
.This need to be propagated to forkchoice and remove the consideration of invalids and their descendant from the forkchoice,
This PR implements the fcU updates coming from from the execution via latestValidHash
TODO testcases:
for propagate valid
- [ ] verify is_merge_block if the mergeblock has not yet been validatedmoved to a separate issue as doesn't addressing this may be a bit more evolved, though EL already should have run terminal validations and hence de duping work isn't really necessary.for propagate invalid
real life optimistic sync testing against ELs
Closes #3508
Testcases summary: