mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
* Forkchoice: expose if no tip is viable This PR changes the behavior on when the node is considered optimistic. A call to `blockchain.IsOptimistic()` relies solely on forkchoice, if all tips are invalid, then it's optimistic. If the current headroot is not in forkchoice then it's optimistic. A call to `blockchain.IsOptimisticForRoot()` will return true if the requested root is headroot and it's not found in forkchoice * update comment Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
119 lines
3.4 KiB
Go
119 lines
3.4 KiB
Go
package doublylinkedtree
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/config/params"
|
|
)
|
|
|
|
func (s *Store) setOptimisticToInvalid(ctx context.Context, root, parentRoot, payloadHash [32]byte) ([][32]byte, error) {
|
|
s.nodesLock.Lock()
|
|
invalidRoots := make([][32]byte, 0)
|
|
node, ok := s.nodeByRoot[root]
|
|
if !ok {
|
|
node, ok = s.nodeByRoot[parentRoot]
|
|
if !ok || node == nil {
|
|
s.nodesLock.Unlock()
|
|
return invalidRoots, errors.Wrap(ErrNilNode, "could not set node to invalid")
|
|
}
|
|
// return early if the parent is LVH
|
|
if node.payloadHash == payloadHash {
|
|
s.nodesLock.Unlock()
|
|
return invalidRoots, nil
|
|
}
|
|
} else {
|
|
if node == nil {
|
|
s.nodesLock.Unlock()
|
|
return invalidRoots, errors.Wrap(ErrNilNode, "could not set node to invalid")
|
|
}
|
|
if node.parent.root != parentRoot {
|
|
s.nodesLock.Unlock()
|
|
return invalidRoots, errInvalidParentRoot
|
|
}
|
|
}
|
|
firstInvalid := node
|
|
for ; firstInvalid.parent != nil && firstInvalid.parent.payloadHash != payloadHash; firstInvalid = firstInvalid.parent {
|
|
if ctx.Err() != nil {
|
|
s.nodesLock.Unlock()
|
|
return invalidRoots, ctx.Err()
|
|
}
|
|
}
|
|
// Deal with the case that the last valid payload is in a different fork
|
|
// This means we are dealing with an EE that does not follow the spec
|
|
if firstInvalid.parent == nil {
|
|
// return early if the invalid node was not imported
|
|
if node.root == parentRoot {
|
|
s.nodesLock.Unlock()
|
|
return invalidRoots, nil
|
|
}
|
|
firstInvalid = node
|
|
}
|
|
s.nodesLock.Unlock()
|
|
return s.removeNode(ctx, firstInvalid)
|
|
}
|
|
|
|
// removeNode removes the node with the given root and all of its children
|
|
// from the Fork Choice Store.
|
|
func (s *Store) removeNode(ctx context.Context, node *Node) ([][32]byte, error) {
|
|
s.nodesLock.Lock()
|
|
defer s.nodesLock.Unlock()
|
|
invalidRoots := make([][32]byte, 0)
|
|
|
|
if node == nil {
|
|
return invalidRoots, errors.Wrap(ErrNilNode, "could not remove node")
|
|
}
|
|
if !node.optimistic || node.parent == nil {
|
|
return invalidRoots, errInvalidOptimisticStatus
|
|
}
|
|
|
|
children := node.parent.children
|
|
if len(children) == 1 {
|
|
node.parent.children = []*Node{}
|
|
} else {
|
|
for i, n := range children {
|
|
if n == node {
|
|
if i != len(children)-1 {
|
|
children[i] = children[len(children)-1]
|
|
}
|
|
node.parent.children = children[:len(children)-1]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return s.removeNodeAndChildren(ctx, node, invalidRoots)
|
|
}
|
|
|
|
// removeNodeAndChildren removes `node` and all of its descendant from the Store
|
|
func (s *Store) removeNodeAndChildren(ctx context.Context, node *Node, invalidRoots [][32]byte) ([][32]byte, error) {
|
|
var err error
|
|
for _, child := range node.children {
|
|
if ctx.Err() != nil {
|
|
return invalidRoots, ctx.Err()
|
|
}
|
|
if invalidRoots, err = s.removeNodeAndChildren(ctx, child, invalidRoots); err != nil {
|
|
return invalidRoots, err
|
|
}
|
|
}
|
|
invalidRoots = append(invalidRoots, node.root)
|
|
s.proposerBoostLock.Lock()
|
|
if node.root == s.proposerBoostRoot {
|
|
s.proposerBoostRoot = [32]byte{}
|
|
}
|
|
if node.root == s.previousProposerBoostRoot {
|
|
s.previousProposerBoostRoot = params.BeaconConfig().ZeroHash
|
|
s.previousProposerBoostScore = 0
|
|
}
|
|
s.proposerBoostLock.Unlock()
|
|
delete(s.nodeByRoot, node.root)
|
|
delete(s.nodeByPayload, node.payloadHash)
|
|
return invalidRoots, nil
|
|
}
|
|
|
|
// AllTipsAreInvalid returns true if no forkchoice tip is viable for head
|
|
func (f *ForkChoice) AllTipsAreInvalid() bool {
|
|
f.store.nodesLock.RLock()
|
|
defer f.store.nodesLock.RUnlock()
|
|
return f.store.allTipsAreInvalid
|
|
}
|