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

Blinded block APIs #10331

Merged
merged 41 commits into from
Apr 25, 2022
Merged

Blinded block APIs #10331

merged 41 commits into from
Apr 25, 2022

Conversation

rkapka
Copy link
Contributor

@rkapka rkapka commented Mar 9, 2022

What type of PR is this?

Feature

What does this PR do? Why is it needed?

Implements publishBlindedBlock and produceBlindedBlock API endpoints as described in ethereum/beacon-APIs#194

@rkapka rkapka changed the title Define blinded blocks Blinded block APIs Mar 11, 2022
@rkapka rkapka marked this pull request as ready for review March 16, 2022 12:56
@rkapka rkapka requested a review from a team as a code owner March 16, 2022 12:56
@rkapka rkapka added Ready For Review A pull request ready for code review API Api related tasks Merge PRs related to the great milestone the merge labels Mar 16, 2022
}

var actualRespContainer interface{}
if strings.EqualFold(respContainer.Version, strings.ToLower(ethpbv2.Version_PHASE0.String())) {
Copy link
Member

Choose a reason for hiding this comment

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

Using a switch statement on respContainer.Version will look much nicer than if else if else if else

Comment on lines 625 to 730

root, err := phase0Blk.HashTreeRoot()
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}

// 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: wrappedPhase0Blk},
})
}()

// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wrappedPhase0Blk.Proto()); err != nil {
return status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}

if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedPhase0Blk, root); err != nil {
return status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}

return nil
}

func (bs *Server) submitAltairBlock(ctx context.Context, altairBlk *ethpbv2.BeaconBlockAltair, sig []byte) error {
v1alpha1Blk, err := migration.AltairToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockAltair{Message: altairBlk, Signature: sig})
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
}
wrappedAltairBlk, err := wrapper.WrappedAltairSignedBeaconBlock(v1alpha1Blk)
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not prepare Altair block")
}

root, err := altairBlk.HashTreeRoot()
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}

// 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: wrappedAltairBlk},
})
}()

// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wrappedAltairBlk.Proto()); err != nil {
return status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}

if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedAltairBlk, root); err != nil {
return status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}

return nil
}

func (bs *Server) submitBellatrixBlock(ctx context.Context, bellatrixBlk *ethpbv2.BeaconBlockBellatrix, sig []byte) error {
v1alpha1Blk, err := migration.BellatrixToV1Alpha1SignedBlock(&ethpbv2.SignedBeaconBlockBellatrix{Message: bellatrixBlk, Signature: sig})
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block")
}
wrappedBellatrixBlk, err := wrapper.WrappedBellatrixSignedBeaconBlock(v1alpha1Blk)
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not prepare bellatrix block")
}

root, err := bellatrixBlk.HashTreeRoot()
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}

// 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},
})
}()

// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wrappedBellatrixBlk.Proto()); err != nil {
return status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}

if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedBellatrixBlk, root); err != nil {
return status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}

return nil
Copy link
Member

Choose a reason for hiding this comment

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

Could you dedup it the same way that is similar to #10368 ?

It'll make @prestonvanloon very happy :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You had similar feedback in #10324 (comment). Can I merge the two into a separate PR?

# Conflicts:
#	beacon-chain/rpc/apimiddleware/custom_hooks.go
#	beacon-chain/rpc/eth/validator/validator.go
}
if b.ExecutionPayloadHeader == nil {
b.ExecutionPayloadHeader = &enginev1.ExecutionPayloadHeader{
ParentHash: make([]byte, 32),
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use named constants for these numbers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated all roots to constants. Although you could argue that hashes should use the same constant, we are not doing this at the moment.

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.

Another pass. I am ~33% there

Comment on lines +317 to +320
if currentEpoch < params.BeaconConfig().AltairForkEpoch {
endpoint.PostRequest = &signedBeaconBlockContainerJson{}
} else if currentEpoch < params.BeaconConfig().BellatrixForkEpoch {
endpoint.PostRequest = &signedBeaconBlockAltairContainerJson{}
Copy link
Member

Choose a reason for hiding this comment

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

Are the blocks before Bellatrix blinded as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, we return regular blocks in that case.

@@ -270,6 +270,12 @@ type produceBlockResponseV2Json struct {
Data *beaconBlockContainerV2Json `json:"data"`
}

// produceBlindedBlockResponseJson is used in /v1/validator/blinded_blocks/{slot} API endpoint.
type produceBlindedBlockResponseJson struct {
Copy link
Member

Choose a reason for hiding this comment

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

What's the difference between bellatrixProduceBlindedBlockResponseJson?

Copy link
Contributor Author

@rkapka rkapka Mar 31, 2022

Choose a reason for hiding this comment

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

produceBlindedBlockResponseJson comes back from gRPC and includes a Data field, which itself has 3 fields:

  • Phase0Block
  • AltairBlock
  • BellatrixBlock

This is because we use a oneof in protobuf. The purpose of bellatrixProduceBlindedBlockResponseJson is to contain only one field (apart from version) which gets returned from HTTP - the bellatrix block.

Comment on lines 715 to 737
root, err := phase0Blk.HashTreeRoot()
if err != nil {
return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
}

// 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: wrappedPhase0Blk},
})
}()

// Broadcast the new block to the network.
if err := bs.Broadcaster.Broadcast(ctx, wrappedPhase0Blk.Proto()); err != nil {
return status.Errorf(codes.Internal, "Could not broadcast block: %v", err)
}

if err := bs.BlockReceiver.ReceiveBlock(ctx, wrappedPhase0Blk, root); err != nil {
return status.Errorf(codes.Internal, "Could not process beacon block: %v", err)
}
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if you could share these four calls among phase0, altair and bellatrix (not blinded)

// gRPC expects either an XXX_block field in the JSON object, but we have a message field at this point.
// We do a simple conversion depending on the type of endpoint.PostRequest
// (which was filled out previously in setInitialPublishBlockPostRequest).
func preparePublishedBlindedBlock(endpoint *apimiddleware.Endpoint, _ http.ResponseWriter, _ *http.Request) apimiddleware.ErrorJson {
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we have checks for phase0 and altair blocks here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The spec allows publishing regular blocks through this endpoint for these forks. See https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/publishBlindedBlock (you need to click the Schema button and expand the oneOf)

// `BeaconBlock`, and a successful response (20X) only indicates that the broadcast has been
// successful. The beacon node is expected to integrate the new block into its state, and
// therefore validate the block internally, however blocks which fail the validation are still
// broadcast but a different status code is returned (202).
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't 20X cover 202?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I copied this description from the spec. I think what this tries to say is that a code different than 200 is returned.

root, err := wsb.Block().HashTreeRoot()
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err)
phase0BlkContainer, ok := req.Message.(*ethpbv2.SignedBlindedBeaconBlockContainer_Phase0Block)
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: does it make sense to invert the order of these requests? I suppose we are more likely to submit a blinded bellatrix block.

Comment on lines 761 to 768
root, err := blindedBellatrixBlk.HashTreeRoot()
if err != nil {
return status.Errorf(codes.Internal, "Could not calculate block root: %v", err)
}
signedBlk, err := bs.BeaconDB.Block(ctx, root)
if err != nil {
return status.Errorf(codes.Internal, "Could not calculate get non-blinded block from DB: %v", err)
}
Copy link
Member

Choose a reason for hiding this comment

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

This is not quite right. We won't have the block in DB.

We have to process the blinded block. I have a PR for this, I can open it next week, so this makes the right ReceiveBlock call

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not quite right. We won't have the block in DB.

Wow, of course. This would come up while testing on a testnet.

Copy link
Member

Choose a reason for hiding this comment

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

Please use #10473 :)

Signature: sig,
})
if err != nil {
return status.Errorf(codes.Internal, "Could not get non-blinded block: %v", err)
Copy link
Member

Choose a reason for hiding this comment

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

Are these error messages correct? These are blinded blocks no?

@rkapka rkapka merged commit 957d853 into develop Apr 25, 2022
@rkapka rkapka deleted the define-blinded-block branch April 25, 2022 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API Api related tasks Merge PRs related to the great milestone the merge Ready For Review A pull request ready for code review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants