-
Notifications
You must be signed in to change notification settings - Fork 995
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
Add execution_optimistic
field to API responses
#10389
Conversation
# Conflicts: # beacon-chain/rpc/eth/debug/debug_test.go
@@ -67,6 +67,10 @@ func (bs *Server) GetBlockHeader(ctx context.Context, req *ethpbv1.BlockRequest) | |||
if err != nil { | |||
return nil, status.Errorf(codes.Internal, "Could not determine if block root is canonical: %v", err) | |||
} | |||
isOptimistic, err := bs.HeadFetcher.IsOptimistic(ctx) |
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 is wrong.
The spec says:
- description: "True if the response references an unverified execution payload. Optimistic information may be invalidated at a later time. If the field is not present, assume the False value."
The isOptimistic
should reference to the responded block header not to the current head. Same for other places
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.
You're right. I changed the code to use IsOptimisticForRoot
everywhere. The only two places where we look at the head are head
and chain_reorg
events.
@@ -229,7 +229,7 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b | |||
if err := s.cfg.ForkChoiceStore.Prune(ctx, fRoot); err != nil { | |||
return errors.Wrap(err, "could not prune proto array fork choice nodes") | |||
} | |||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(ctx, s.headRoot()) | |||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(ctx, fRoot) |
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 wonder if this is better inside the goroutine, I don't think it makes a big difference but something worth considering
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 call above locks on notifyFockchoiceUpdate
so this one is guaranteed to return immediately.
beacon-chain/rpc/eth/beacon/state.go
Outdated
_, blocks, err := bs.BeaconDB.BlocksBySlot(ctx, st.Slot()) | ||
if err != nil { | ||
return nil, status.Errorf(codes.Internal, "Could not get blocks for state slot: %v", err) | ||
} | ||
isOptimistic, err := bs.blocksAreOptimistic(ctx, blocks) | ||
if err != nil { | ||
return nil, status.Errorf(codes.Internal, "Could not check if blocks are optimistic: %v", err) | ||
} |
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.
Here's a better and more accurate algorithm:
header := st.LatestBlockHeader()
header.StateRoot = stateRoot[:]
headRoot, err := header.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "error while computing block root using state data")
}
isOptimistic, err = bs.HeadFetcher.IsOptimisticForRoot(ctx, headRoot)
if err != nil {
return false, errors.Wrap(err, "could not check if block is optimistic")
}
"Block proposal received via RPC") | ||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{ | ||
Type: blockfeed.ReceivedBlock, | ||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk, IsOptimistic: isOptimistic}, |
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.
phase0 and altair blocks will always be non-optimistic. Optimistic is a concept introduced for Bellatrix
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk, IsOptimistic: isOptimistic}, | |
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk, IsOptimistic: 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.
+1, in addition the check for isOptimistic should not be there.
"Block proposal received via RPC") | ||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{ | ||
Type: blockfeed.ReceivedBlock, | ||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedAltairBlk, IsOptimistic: isOptimistic}, |
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.
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedAltairBlk, IsOptimistic: isOptimistic}, | |
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedAltairBlk, IsOptimistic: false}, |
@@ -327,6 +366,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) ( | |||
Message: ðpbv2.SignedBeaconBlockContainerV2_Phase0Block{Phase0Block: v1Blk.Block}, | |||
Signature: v1Blk.Signature, | |||
}, | |||
ExecutionOptimistic: isOptimistic, |
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.
ExecutionOptimistic: isOptimistic, | |
ExecutionOptimistic: false, |
@@ -349,6 +389,7 @@ func (bs *Server) GetBlockV2(ctx context.Context, req *ethpbv2.BlockRequestV2) ( | |||
Message: ðpbv2.SignedBeaconBlockContainerV2_AltairBlock{AltairBlock: v2Blk}, | |||
Signature: blk.Signature(), | |||
}, | |||
ExecutionOptimistic: isOptimistic, |
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.
ExecutionOptimistic: isOptimistic, | |
ExecutionOptimistic: false, |
root, err := blk.Block().HashTreeRoot() | ||
if err != nil { | ||
return nil, status.Errorf(codes.Internal, "Could not get block root: %v", err) | ||
} | ||
isOptimistic, err := bs.HeadFetcher.IsOptimisticForRoot(ctx, root) | ||
if err != nil { | ||
return nil, status.Errorf(codes.Internal, "Could not check if block is optimistic: %v", err) | ||
} |
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.
Move these before bellatrixBlk, err := blk.PbBellatrixBlock()
You can save HTR if it's phase 0 or Altair
@@ -577,7 +635,8 @@ func (bs *Server) ListBlockAttestations(ctx context.Context, req *ethpbv1.BlockR | |||
return nil, status.Errorf(codes.Internal, "Could not get signed beacon block: %v", err) | |||
} | |||
return ðpbv1.BlockAttestationsResponse{ | |||
Data: v2Blk.Body.Attestations, | |||
Data: v2Blk.Body.Attestations, | |||
ExecutionOptimistic: isOptimistic, |
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.
ExecutionOptimistic: isOptimistic, | |
ExecutionOptimistic: false, |
@@ -35,3 +37,21 @@ func ValidateSync(ctx context.Context, syncChecker sync.Checker, headFetcher blo | |||
} | |||
return status.Error(codes.Unavailable, "Syncing to latest head, not ready to respond") | |||
} | |||
|
|||
func IsOptimistic(ctx context.Context, st state.BeaconState, headFetcher blockchain.HeadFetcher) (bool, error) { |
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.
Godoc?
@@ -109,9 +109,15 @@ func (vs *Server) GetAttesterDuties(ctx context.Context, req *ethpbv1.AttesterDu | |||
return nil, status.Errorf(codes.Internal, "Could not get dependent root: %v", err) | |||
} | |||
|
|||
isOptimistic, err := rpchelpers.IsOptimistic(ctx, s, vs.HeadFetcher) |
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.
You want to move this before s, err = advanceState(ctx, s, req.Epoch, currentEpoch)
or else state s
gets mutated
@@ -171,9 +177,15 @@ func (vs *Server) GetProposerDuties(ctx context.Context, req *ethpbv1.ProposerDu | |||
return nil, status.Errorf(codes.Internal, "Could not get dependent root: %v", err) | |||
} | |||
|
|||
isOptimistic, err := rpchelpers.IsOptimistic(ctx, s, vs.HeadFetcher) |
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.
Same. Move this before advanceState(ctx, s, req.Epoch, currentEpoch)
@@ -245,8 +257,15 @@ func (vs *Server) GetSyncCommitteeDuties(ctx context.Context, req *ethpbv2.SyncC | |||
} else if err != nil { | |||
return nil, status.Errorf(codes.Internal, "Could not get duties: %v", err) | |||
} | |||
|
|||
isOptimistic, err := rpchelpers.IsOptimistic(ctx, st, vs.HeadFetcher) |
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 one is good
isOptimistic, err := s.IsOptimistic(ctx) | ||
if err != nil { | ||
return errors.Wrap(err, "could not check if node is optimistically synced") | ||
} | ||
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{ |
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.
There's a short time lapse between we set the head and the optimistic status of the node is settled. How much of a race issue can there be here if we return optimistic when soon after the head will become 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.
I can't say how event subscribers will use this info. Is there an easy way to sync things? If not, I would just not worry about it at this point.
isOptimistic, err := s.IsOptimistic(ctx) | ||
if err != nil { | ||
return errors.Wrap(err, "could not check if node is optimistically synced") | ||
} | ||
s.cfg.StateNotifier.StateFeed().Send(&feed.Event{ |
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.
Same here, is this function being called right after setting head or right after the return from the engine?
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.
Is this a question for me?
@@ -229,7 +229,7 @@ func (s *Service) onBlock(ctx context.Context, signed block.SignedBeaconBlock, b | |||
if err := s.cfg.ForkChoiceStore.Prune(ctx, fRoot); err != nil { | |||
return errors.Wrap(err, "could not prune proto array fork choice nodes") | |||
} | |||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(ctx, s.headRoot()) | |||
isOptimistic, err := s.cfg.ForkChoiceStore.IsOptimistic(ctx, fRoot) |
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 call above locks on notifyFockchoiceUpdate
so this one is guaranteed to return immediately.
@@ -126,6 +132,12 @@ func (bs *Server) ListBlockHeaders(ctx context.Context, req *ethpbv1.BlockHeader | |||
if err != nil { | |||
return nil, status.Errorf(codes.Internal, "Could not determine if block root is canonical: %v", err) | |||
} | |||
if !isOptimistic { |
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.
Here there's an assumption that all blocks in blks have the same optimistic status. Is this guaranteed to hold?
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 at least one block is optimistic, then isOptimistic
will become true
and stay this way.
"Block proposal received via RPC") | ||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{ | ||
Type: blockfeed.ReceivedBlock, | ||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedPhase0Blk, IsOptimistic: isOptimistic}, |
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.
+1, in addition the check for isOptimistic should not be there.
// Do not block proposal critical path with debug logging or block feed updates. | ||
defer func() { | ||
log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(root[:]))).Debugf( | ||
"Block proposal received via RPC") | ||
bs.BlockNotifier.BlockFeed().Send(&feed.Event{ | ||
Type: blockfeed.ReceivedBlock, | ||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedBellatrixBlk}, | ||
Data: &blockfeed.ReceivedBlockData{SignedBlock: wrappedBellatrixBlk, IsOptimistic: 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.
This one is bellatrix block, have we decided to just return false
anyway?
Because we don't care about this endpoint?
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.
@potuz mentioned that a proposed block is always optimistic. My thinking is that in this case we should always return true
here. I must have copy/pasted false
by mistake.
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.
Is it? Why would a validator propose an optimistic block? That's a violation of the spec
cc @potuz
# Conflicts: # beacon-chain/rpc/eth/beacon/blocks.go # proto/eth/v1/beacon_chain.pb.go # proto/eth/v2/beacon_state.pb.go
What type of PR is this?
Feature
What does this PR do? Why is it needed?
Adds a new
execution_optimistic
field to API responses. The design is based on ethereum/beacon-APIs#190