From 6c16e90fe90325a99a77de4dce2caf09523085f7 Mon Sep 17 00:00:00 2001 From: james-prysm <90280386+james-prysm@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:50:55 -0500 Subject: [PATCH] deneb - validator beacon rest apis (#12871) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * adding tests for deneb * adding deneb to get block * Update validator/client/beacon-api/get_beacon_block.go Co-authored-by: Radosław Kapka * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka --------- Co-authored-by: Radosław Kapka --- .../eth/shared/structs_blocks_conversions.go | 88 +++++++++++ validator/client/beacon-api/BUILD.bazel | 3 + .../client/beacon-api/get_beacon_block.go | 12 +- .../beacon-api/get_beacon_block_test.go | 46 ++++++ .../client/beacon-api/propose_beacon_block.go | 30 ++++ ...propose_beacon_block_blinded_deneb_test.go | 50 ++++++ .../propose_beacon_block_deneb_test.go | 50 ++++++ validator/client/beacon-api/stream_blocks.go | 20 +++ .../client/beacon-api/stream_blocks_test.go | 148 ++++++++++++++++++ 9 files changed, 446 insertions(+), 1 deletion(-) create mode 100644 validator/client/beacon-api/propose_beacon_block_blinded_deneb_test.go create mode 100644 validator/client/beacon-api/propose_beacon_block_deneb_test.go diff --git a/beacon-chain/rpc/eth/shared/structs_blocks_conversions.go b/beacon-chain/rpc/eth/shared/structs_blocks_conversions.go index cffb2bb00..ca96e45ec 100644 --- a/beacon-chain/rpc/eth/shared/structs_blocks_conversions.go +++ b/beacon-chain/rpc/eth/shared/structs_blocks_conversions.go @@ -2405,6 +2405,28 @@ func BlindedBeaconBlockContentsDenebFromConsensus(b *eth.BlindedBeaconBlockAndBl }, nil } +func SignedBlindedBeaconBlockContentsDenebFromConsensus(b *eth.SignedBlindedBeaconBlockAndBlobsDeneb) (*SignedBlindedBeaconBlockContentsDeneb, error) { + var blindedBlobSidecars []*SignedBlindedBlobSidecar + if len(b.Blobs) != 0 { + blindedBlobSidecars = make([]*SignedBlindedBlobSidecar, len(b.Blobs)) + for i, s := range b.Blobs { + signedBlob, err := SignedBlindedBlobSidecarFromConsensus(s) + if err != nil { + return nil, err + } + blindedBlobSidecars[i] = signedBlob + } + } + blindedBlock, err := SignedBlindedBeaconBlockDenebFromConsensus(b.Block) + if err != nil { + return nil, err + } + return &SignedBlindedBeaconBlockContentsDeneb{ + SignedBlindedBlock: blindedBlock, + SignedBlindedBlobSidecars: blindedBlobSidecars, + }, nil +} + func BeaconBlockContentsDenebFromConsensus(b *eth.BeaconBlockAndBlobsDeneb) (*BeaconBlockContentsDeneb, error) { var blobSidecars []*BlobSidecar if len(b.Blobs) != 0 { @@ -2427,6 +2449,28 @@ func BeaconBlockContentsDenebFromConsensus(b *eth.BeaconBlockAndBlobsDeneb) (*Be }, nil } +func SignedBeaconBlockContentsDenebFromConsensus(b *eth.SignedBeaconBlockAndBlobsDeneb) (*SignedBeaconBlockContentsDeneb, error) { + var blobSidecars []*SignedBlobSidecar + if len(b.Blobs) != 0 { + blobSidecars = make([]*SignedBlobSidecar, len(b.Blobs)) + for i, s := range b.Blobs { + blob, err := SignedBlobSidecarFromConsensus(s) + if err != nil { + return nil, err + } + blobSidecars[i] = blob + } + } + block, err := SignedBeaconBlockDenebFromConsensus(b.Block) + if err != nil { + return nil, err + } + return &SignedBeaconBlockContentsDeneb{ + SignedBlock: block, + SignedBlobSidecars: blobSidecars, + }, nil +} + func BlindedBeaconBlockDenebFromConsensus(b *eth.BlindedBeaconBlockDeneb) (*BlindedBeaconBlockDeneb, error) { proposerSlashings, err := ProposerSlashingsFromConsensus(b.Body.ProposerSlashings) if err != nil { @@ -2509,6 +2553,17 @@ func BlindedBeaconBlockDenebFromConsensus(b *eth.BlindedBeaconBlockDeneb) (*Blin }, nil } +func SignedBlindedBeaconBlockDenebFromConsensus(b *eth.SignedBlindedBeaconBlockDeneb) (*SignedBlindedBeaconBlockDeneb, error) { + block, err := BlindedBeaconBlockDenebFromConsensus(b.Block) + if err != nil { + return nil, err + } + return &SignedBlindedBeaconBlockDeneb{ + Message: block, + Signature: hexutil.Encode(b.Signature), + }, nil +} + func BeaconBlockDenebFromConsensus(b *eth.BeaconBlockDeneb) (*BeaconBlockDeneb, error) { proposerSlashings, err := ProposerSlashingsFromConsensus(b.Body.ProposerSlashings) if err != nil { @@ -2603,6 +2658,17 @@ func BeaconBlockDenebFromConsensus(b *eth.BeaconBlockDeneb) (*BeaconBlockDeneb, }, nil } +func SignedBeaconBlockDenebFromConsensus(b *eth.SignedBeaconBlockDeneb) (*SignedBeaconBlockDeneb, error) { + beaconBlock, err := BeaconBlockDenebFromConsensus(b.Block) + if err != nil { + return nil, err + } + return &SignedBeaconBlockDeneb{ + Message: beaconBlock, + Signature: hexutil.Encode(b.Signature), + }, nil +} + func BlindedBlobSidecarFromConsensus(b *eth.BlindedBlobSidecar) (*BlindedBlobSidecar, error) { return &BlindedBlobSidecar{ BlockRoot: hexutil.Encode(b.BlockRoot), @@ -2616,6 +2682,17 @@ func BlindedBlobSidecarFromConsensus(b *eth.BlindedBlobSidecar) (*BlindedBlobSid }, nil } +func SignedBlindedBlobSidecarFromConsensus(b *eth.SignedBlindedBlobSidecar) (*SignedBlindedBlobSidecar, error) { + blobSidecar, err := BlindedBlobSidecarFromConsensus(b.Message) + if err != nil { + return nil, err + } + return &SignedBlindedBlobSidecar{ + Message: blobSidecar, + Signature: hexutil.Encode(b.Signature), + }, nil +} + func BlobSidecarFromConsensus(b *eth.BlobSidecar) (*BlobSidecar, error) { return &BlobSidecar{ BlockRoot: hexutil.Encode(b.BlockRoot), @@ -2629,6 +2706,17 @@ func BlobSidecarFromConsensus(b *eth.BlobSidecar) (*BlobSidecar, error) { }, nil } +func SignedBlobSidecarFromConsensus(b *eth.SignedBlobSidecar) (*SignedBlobSidecar, error) { + blobSidecar, err := BlobSidecarFromConsensus(b.Message) + if err != nil { + return nil, err + } + return &SignedBlobSidecar{ + Message: blobSidecar, + Signature: hexutil.Encode(b.Signature), + }, nil +} + func ProposerSlashingsToConsensus(src []*ProposerSlashing) ([]*eth.ProposerSlashing, error) { if src == nil { return nil, errNilValue diff --git a/validator/client/beacon-api/BUILD.bazel b/validator/client/beacon-api/BUILD.bazel index 0ff51cd45..d790fdeed 100644 --- a/validator/client/beacon-api/BUILD.bazel +++ b/validator/client/beacon-api/BUILD.bazel @@ -89,7 +89,9 @@ go_test( "propose_beacon_block_bellatrix_test.go", "propose_beacon_block_blinded_bellatrix_test.go", "propose_beacon_block_blinded_capella_test.go", + "propose_beacon_block_blinded_deneb_test.go", "propose_beacon_block_capella_test.go", + "propose_beacon_block_deneb_test.go", "propose_beacon_block_phase0_test.go", "propose_beacon_block_test.go", "propose_exit_test.go", @@ -109,6 +111,7 @@ go_test( "//api/gateway/apimiddleware:go_default_library", "//beacon-chain/rpc/apimiddleware:go_default_library", "//beacon-chain/rpc/eth/shared:go_default_library", + "//beacon-chain/rpc/eth/shared/testing:go_default_library", "//beacon-chain/rpc/eth/validator:go_default_library", "//beacon-chain/rpc/prysm/validator:go_default_library", "//config/params:go_default_library", diff --git a/validator/client/beacon-api/get_beacon_block.go b/validator/client/beacon-api/get_beacon_block.go index c9e723cf6..069e34a71 100644 --- a/validator/client/beacon-api/get_beacon_block.go +++ b/validator/client/beacon-api/get_beacon_block.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) @@ -98,7 +99,16 @@ func (c beaconApiValidatorClient) getBeaconBlock(ctx context.Context, slot primi response.Block = ðpb.GenericBeaconBlock_Capella{ Capella: capellaBlock, } - + case "deneb": + jsonDenebBlockContents := shared.BeaconBlockContentsDeneb{} + if err := decoder.Decode(&jsonDenebBlockContents); err != nil { + return nil, errors.Wrap(err, "failed to decode deneb block response json") + } + genericBlock, err := jsonDenebBlockContents.ToGeneric() + if err != nil { + return nil, errors.Wrap(err, "could not convert deneb block contents to generic block") + } + response = genericBlock default: return nil, errors.Errorf("unsupported consensus version `%s`", produceBlockResponseJson.Version) } diff --git a/validator/client/beacon-api/get_beacon_block_test.go b/validator/client/beacon-api/get_beacon_block_test.go index 839be0241..529bba917 100644 --- a/validator/client/beacon-api/get_beacon_block_test.go +++ b/validator/client/beacon-api/get_beacon_block_test.go @@ -10,6 +10,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/mock/gomock" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v4/testing/assert" @@ -379,3 +381,47 @@ func TestGetBeaconBlock_CapellaValid(t *testing.T) { assert.DeepEqual(t, expectedBeaconBlock, beaconBlock) } + +func TestGetBeaconBlock_DenebValid(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + var blockContents shared.SignedBeaconBlockContentsDeneb + err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents) + require.NoError(t, err) + + denebBeaconBlockBytes, err := json.Marshal(blockContents.ToUnsigned()) + require.NoError(t, err) + ctx := context.Background() + const slot = primitives.Slot(1) + randaoReveal, err := hexutil.Decode("0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505") + require.NoError(t, err) + graffiti, err := hexutil.Decode("0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2") + require.NoError(t, err) + + jsonRestHandler := mock.NewMockjsonRestHandler(ctrl) + jsonRestHandler.EXPECT().GetRestJsonResponse( + ctx, + fmt.Sprintf("/eth/v2/validator/blocks/%d?graffiti=%s&randao_reveal=%s", slot, hexutil.Encode(graffiti), hexutil.Encode(randaoReveal)), + &abstractProduceBlockResponseJson{}, + ).SetArg( + 2, + abstractProduceBlockResponseJson{ + Version: "deneb", + Data: denebBeaconBlockBytes, + }, + ).Return( + nil, + nil, + ).Times(1) + + validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler} + + beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti) + require.NoError(t, err) + + expectedBeaconBlock, err := blockContents.ToUnsigned().ToGeneric() + require.NoError(t, err) + + assert.DeepEqual(t, expectedBeaconBlock, beaconBlock) +} diff --git a/validator/client/beacon-api/propose_beacon_block.go b/validator/client/beacon-api/propose_beacon_block.go index 345423672..14aa1d2a0 100644 --- a/validator/client/beacon-api/propose_beacon_block.go +++ b/validator/client/beacon-api/propose_beacon_block.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) @@ -90,6 +91,35 @@ func (c beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *et if err != nil { return nil, errors.Wrap(err, "failed to marshall blinded capella beacon block") } + case *ethpb.GenericSignedBeaconBlock_Deneb: + consensusVersion = "deneb" + beaconBlockRoot, err = blockType.Deneb.Block.HashTreeRoot() + if err != nil { + return nil, errors.Wrap(err, "failed to compute block root for deneb beacon block") + } + signedBlock, err := shared.SignedBeaconBlockContentsDenebFromConsensus(blockType.Deneb) + if err != nil { + return nil, errors.Wrap(err, "failed to convert deneb beacon block contents") + } + marshalledSignedBeaconBlockJson, err = json.Marshal(signedBlock) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal deneb beacon block contents") + } + case *ethpb.GenericSignedBeaconBlock_BlindedDeneb: + blinded = true + consensusVersion = "deneb" + beaconBlockRoot, err = blockType.BlindedDeneb.Block.HashTreeRoot() + if err != nil { + return nil, errors.Wrap(err, "failed to compute block root for blinded deneb beacon block") + } + signedBlock, err := shared.SignedBlindedBeaconBlockContentsDenebFromConsensus(blockType.BlindedDeneb) + if err != nil { + return nil, errors.Wrap(err, "failed to convert blinded deneb beacon block contents") + } + marshalledSignedBeaconBlockJson, err = json.Marshal(signedBlock) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal blinded deneb beacon block contents") + } default: return nil, errors.Errorf("unsupported block type %T", in.Block) } diff --git a/validator/client/beacon-api/propose_beacon_block_blinded_deneb_test.go b/validator/client/beacon-api/propose_beacon_block_blinded_deneb_test.go new file mode 100644 index 000000000..41cb117a8 --- /dev/null +++ b/validator/client/beacon-api/propose_beacon_block_blinded_deneb_test.go @@ -0,0 +1,50 @@ +package beacon_api + +import ( + "bytes" + "context" + "encoding/json" + "testing" + + "github.com/golang/mock/gomock" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing" + "github.com/prysmaticlabs/prysm/v4/testing/assert" + "github.com/prysmaticlabs/prysm/v4/testing/require" + "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock" +) + +func TestProposeBeaconBlock_BlindedDeneb(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jsonRestHandler := mock.NewMockjsonRestHandler(ctrl) + + var blockContents shared.SignedBlindedBeaconBlockContentsDeneb + err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlockContents), &blockContents) + require.NoError(t, err) + genericSignedBlock, err := blockContents.ToGeneric() + require.NoError(t, err) + + denebBytes, err := json.Marshal(blockContents) + require.NoError(t, err) + // Make sure that what we send in the POST body is the marshalled version of the protobuf block + headers := map[string]string{"Eth-Consensus-Version": "deneb"} + jsonRestHandler.EXPECT().PostRestJson( + context.Background(), + "/eth/v1/beacon/blinded_blocks", + headers, + bytes.NewBuffer(denebBytes), + nil, + ) + + validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler} + proposeResponse, err := validatorClient.proposeBeaconBlock(context.Background(), genericSignedBlock) + assert.NoError(t, err) + require.NotNil(t, proposeResponse) + + expectedBlockRoot, err := genericSignedBlock.GetBlindedDeneb().Block.HashTreeRoot() + require.NoError(t, err) + + // Make sure that the block root is set + assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot) +} diff --git a/validator/client/beacon-api/propose_beacon_block_deneb_test.go b/validator/client/beacon-api/propose_beacon_block_deneb_test.go new file mode 100644 index 000000000..e21e8a5eb --- /dev/null +++ b/validator/client/beacon-api/propose_beacon_block_deneb_test.go @@ -0,0 +1,50 @@ +package beacon_api + +import ( + "bytes" + "context" + "encoding/json" + "testing" + + "github.com/golang/mock/gomock" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing" + "github.com/prysmaticlabs/prysm/v4/testing/assert" + "github.com/prysmaticlabs/prysm/v4/testing/require" + "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock" +) + +func TestProposeBeaconBlock_Deneb(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + jsonRestHandler := mock.NewMockjsonRestHandler(ctrl) + + var blockContents shared.SignedBeaconBlockContentsDeneb + err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents) + require.NoError(t, err) + genericSignedBlock, err := blockContents.ToGeneric() + require.NoError(t, err) + + denebBytes, err := json.Marshal(blockContents) + require.NoError(t, err) + // Make sure that what we send in the POST body is the marshalled version of the protobuf block + headers := map[string]string{"Eth-Consensus-Version": "deneb"} + jsonRestHandler.EXPECT().PostRestJson( + context.Background(), + "/eth/v1/beacon/blocks", + headers, + bytes.NewBuffer(denebBytes), + nil, + ) + + validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler} + proposeResponse, err := validatorClient.proposeBeaconBlock(context.Background(), genericSignedBlock) + assert.NoError(t, err) + require.NotNil(t, proposeResponse) + + expectedBlockRoot, err := genericSignedBlock.GetDeneb().Block.HashTreeRoot() + require.NoError(t, err) + + // Make sure that the block root is set + assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot) +} diff --git a/validator/client/beacon-api/stream_blocks.go b/validator/client/beacon-api/stream_blocks.go index 7da000025..d0b3cd474 100644 --- a/validator/client/beacon-api/stream_blocks.go +++ b/validator/client/beacon-api/stream_blocks.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v4/consensus-types/primitives" ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "google.golang.org/grpc" @@ -183,6 +184,25 @@ func (c beaconApiValidatorClient) getHeadSignedBeaconBlock(ctx context.Context) } slot = capellaBlock.Slot + case "deneb": + jsonDenebBlock := shared.SignedBeaconBlockDeneb{} + if err := decoder.Decode(&jsonDenebBlock); err != nil { + return nil, errors.Wrap(err, "failed to decode signed deneb block response json") + } + + denebBlock, err := jsonDenebBlock.ToConsensus() + if err != nil { + return nil, errors.Wrap(err, "failed to get signed deneb block") + } + + response.Block = ðpb.StreamBlocksResponse_DenebBlock{ + DenebBlock: ðpb.SignedBeaconBlockDeneb{ + Signature: denebBlock.Signature, + Block: denebBlock.Block, + }, + } + + slot = denebBlock.Block.Slot default: return nil, errors.Errorf("unsupported consensus version `%s`", signedBlockResponseJson.Version) diff --git a/validator/client/beacon-api/stream_blocks_test.go b/validator/client/beacon-api/stream_blocks_test.go index ac8f3b5e2..19a71db0b 100644 --- a/validator/client/beacon-api/stream_blocks_test.go +++ b/validator/client/beacon-api/stream_blocks_test.go @@ -7,9 +7,12 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/mock/gomock" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared" + rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing" eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v4/testing/assert" "github.com/prysmaticlabs/prysm/v4/testing/require" @@ -830,3 +833,148 @@ func TestStreamBlocks_CapellaValid(t *testing.T) { }) } } + +func TestStreamBlocks_DenebValid(t *testing.T) { + testCases := []struct { + name string + verifiedOnly bool + }{ + { + name: "verified optional", + verifiedOnly: false, + }, + { + name: "verified only", + verifiedOnly: true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + + signedBlockResponseJson := abstractSignedBlockResponseJson{} + jsonRestHandler := mock.NewMockjsonRestHandler(ctrl) + beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl) + + // For the first call, return a block that satisfies the verifiedOnly condition. This block should be returned by the first Recv(). + // For the second call, return the same block as the previous one. This block shouldn't be returned by the second Recv(). + var blockContents shared.SignedBeaconBlockContentsDeneb + err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents) + require.NoError(t, err) + sig := "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + denebBlock := blockContents.SignedBlock + denebBlock.Message.Slot = "1" + denebBlock.Signature = sig + + marshalledSignedBeaconBlockContainer1, err := json.Marshal(denebBlock) + require.NoError(t, err) + jsonRestHandler.EXPECT().GetRestJsonResponse( + ctx, + "/eth/v2/beacon/blocks/head", + &signedBlockResponseJson, + ).Return( + nil, + nil, + ).SetArg( + 2, + abstractSignedBlockResponseJson{ + Version: "deneb", + ExecutionOptimistic: false, + Data: marshalledSignedBeaconBlockContainer1, + }, + ).Times(2) + + // For the third call, return a block with a different slot than the previous one, but with the verifiedOnly condition not satisfied. + // If verifiedOnly == false, this block will be returned by the second Recv(); otherwise, another block will be requested. + + var blockContents2 shared.SignedBeaconBlockContentsDeneb + err = json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents2) + require.NoError(t, err) + sig2 := "0x2b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + denebBlock2 := blockContents.SignedBlock + denebBlock2.Message.Slot = "2" + denebBlock2.Signature = sig2 + + marshalledSignedBeaconBlockContainer2, err := json.Marshal(denebBlock2) + require.NoError(t, err) + + jsonRestHandler.EXPECT().GetRestJsonResponse( + ctx, + "/eth/v2/beacon/blocks/head", + &signedBlockResponseJson, + ).Return( + nil, + nil, + ).SetArg( + 2, + abstractSignedBlockResponseJson{ + Version: "deneb", + ExecutionOptimistic: true, + Data: marshalledSignedBeaconBlockContainer2, + }, + ).Times(1) + + // The fourth call is only necessary when verifiedOnly == true since the previous block was optimistic + if testCase.verifiedOnly { + jsonRestHandler.EXPECT().GetRestJsonResponse( + ctx, + "/eth/v2/beacon/blocks/head", + &signedBlockResponseJson, + ).Return( + nil, + nil, + ).SetArg( + 2, + abstractSignedBlockResponseJson{ + Version: "deneb", + ExecutionOptimistic: false, + Data: marshalledSignedBeaconBlockContainer2, + }, + ).Times(1) + } + + validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler, beaconBlockConverter: beaconBlockConverter} + streamBlocksClient := validatorClient.streamBlocks(ctx, ð.StreamBlocksRequest{VerifiedOnly: testCase.verifiedOnly}, time.Millisecond*100) + + // Get the first block + streamBlocksResponse1, err := streamBlocksClient.Recv() + require.NoError(t, err) + consensusBlock, err := denebBlock.Message.ToConsensus() + consensusBlock.Slot = 1 + require.NoError(t, err) + sigBytes, err := hexutil.Decode(sig) + require.NoError(t, err) + expectedStreamBlocksResponse1 := ð.StreamBlocksResponse{ + Block: ð.StreamBlocksResponse_DenebBlock{ + DenebBlock: ð.SignedBeaconBlockDeneb{ + Block: consensusBlock, + Signature: sigBytes, + }, + }, + } + + assert.DeepEqual(t, expectedStreamBlocksResponse1, streamBlocksResponse1) + + // Get the second block + streamBlocksResponse2, err := streamBlocksClient.Recv() + require.NoError(t, err) + consensusBlock.Slot = 2 + sig2Bytes, err := hexutil.Decode(sig2) + require.NoError(t, err) + expectedStreamBlocksResponse2 := ð.StreamBlocksResponse{ + Block: ð.StreamBlocksResponse_DenebBlock{ + DenebBlock: ð.SignedBeaconBlockDeneb{ + Block: consensusBlock, + Signature: sig2Bytes, + }, + }, + } + + assert.DeepEqual(t, expectedStreamBlocksResponse2, streamBlocksResponse2) + }) + } +}