Skip to content
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

Remove synced tips and use last valid hash #10439

Merged
merged 18 commits into from
Apr 6, 2022

Conversation

potuz
Copy link
Contributor

@potuz potuz commented Mar 28, 2022

This PR removes the use of synced tips in the protoarray fork choice implementation. It also uses the last valid hash returned from the EL when removing invalid blocks from forkchoice.

Rationale

The biggest advantage of synced tips is that it provides us with an effective way of tracking optimistic status of block that may be outside of forkchoice (eg. after pruned by finalization from ForkChoice).

The disadvantage is the increased complexity added and the number of edge cases we have to deal with.

Prysm is the unique client (currently to my knowledge) that does not prune and serves non-canonical historical blocks. By removing the synced tips structure we lose the hability of knowing their optimistic status. We would have to resort to serve them as optimistic.

Fixes #10445

potuz added 2 commits March 27, 2022 21:26
use last valid hash in removing invalid nodes.
@potuz potuz requested a review from a team as a code owner March 28, 2022 00:37
@potuz potuz added Ready For Review Merge PRs related to the great milestone the merge Optimistic-sync Anything that relates to optimistic sync labels Mar 28, 2022
Copy link
Member

@terencechain terencechain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am reviewing this in two parts. First part is doubly linked tree

if firstInvalid.parent == nil {
firstInvalid = node
}
s.nodesLock.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we can't defer s.nodesLock.Unlock() at the top?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because removeNode needs the lock.

lastValidIndex, ok := f.store.payloadIndices[payload]
if !ok || lastValidIndex == NonExistentNode {
f.store.nodesLock.Unlock()
return invalidRoots, errInvalidFinalizedNode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errInvalidFinalizedNode doesn't really imply !ok, but I don't really mind it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, here I'm assuming that the EL is not buggy, so if we don't have the LVH in forkchoice it means that it's past finalization.

beacon-chain/forkchoice/protoarray/optimistic_sync.go Outdated Show resolved Hide resolved
beacon-chain/forkchoice/protoarray/optimistic_sync.go Outdated Show resolved Hide resolved
beacon-chain/forkchoice/protoarray/types.go Outdated Show resolved Hide resolved
beacon-chain/forkchoice/protoarray/types.go Outdated Show resolved Hide resolved
beacon-chain/forkchoice/protoarray/types.go Outdated Show resolved Hide resolved
beacon-chain/forkchoice/protoarray/types.go Outdated Show resolved Hide resolved
Comment on lines 90 to 92
} else {
firstInvalidIndex = f.store.nodesIndices[node.root]
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't think this else condition is necessary. The for loop should account for it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is firstInvalidIndex is updated in the loop above. If LHV was found it will have the right first invalid node, but if it wasn't found in the ancestry of the invalid node, then it will have a bogus value, so we need to set it to the only invalid node we want to remove.

Comment on lines +145 to +147
invalidNode.status = invalid
invalidIndices[index] = true
invalidNode.weight = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to set these for invalidNode if we were just going to delete it anyway down below?

delete(f.store.nodesIndices, invalidNode.root)
delete(f.store.canonicalNodes, invalidNode.root)
delete(f.store.payloadIndices, invalidNode.payloadHash)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we delete the nodes from the maps, but we do not delete the node from the protoarray, so anyone accessing the nodes directly from store.nodes will have access to them, we need to mark them as invalid.

beacon-chain/forkchoice/protoarray/optimistic_sync.go Outdated Show resolved Hide resolved
@prylabs-bulldozer prylabs-bulldozer bot merged commit 83a8327 into develop Apr 6, 2022
@delete-merged-branch delete-merged-branch bot deleted the forkchoice-use-lastvalid branch April 6, 2022 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Merge PRs related to the great milestone the merge Optimistic-sync Anything that relates to optimistic sync
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Reset proposer boost from INVALID nodes
2 participants