mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-14 14:08:19 +00:00
d17996f8b0
* Update V3 from V4 * Fix build v3 -> v4 * Update ssz * Update beacon_chain.pb.go * Fix formatter import * Update update-mockgen.sh comment to v4 * Fix conflicts. Pass build and tests * Fix test
833 lines
25 KiB
Go
833 lines
25 KiB
Go
package beacon_api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/pkg/errors"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/apimiddleware"
|
|
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
|
"github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/mock"
|
|
test_helpers "github.com/prysmaticlabs/prysm/v4/validator/client/beacon-api/test-helpers"
|
|
)
|
|
|
|
func TestStreamBlocks_UnsupportedConsensusVersion(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := context.Background()
|
|
|
|
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
gomock.Any(),
|
|
&abstractSignedBlockResponseJson{},
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{Version: "foo"},
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).Times(1)
|
|
|
|
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
|
streamBlocksClient := validatorClient.streamBlocks(ctx, ð.StreamBlocksRequest{}, time.Millisecond*100)
|
|
_, err := streamBlocksClient.Recv()
|
|
assert.ErrorContains(t, "unsupported consensus version `foo`", err)
|
|
}
|
|
|
|
func TestStreamBlocks_Error(t *testing.T) {
|
|
testSuites := []struct {
|
|
consensusVersion string
|
|
generateBeaconBlockConverter func(ctrl *gomock.Controller, conversionError error) *mock.MockbeaconBlockConverter
|
|
}{
|
|
{
|
|
consensusVersion: "phase0",
|
|
generateBeaconBlockConverter: func(ctrl *gomock.Controller, conversionError error) *mock.MockbeaconBlockConverter {
|
|
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
|
beaconBlockConverter.EXPECT().ConvertRESTPhase0BlockToProto(
|
|
gomock.Any(),
|
|
).Return(
|
|
nil,
|
|
conversionError,
|
|
).AnyTimes()
|
|
|
|
return beaconBlockConverter
|
|
},
|
|
},
|
|
{
|
|
consensusVersion: "altair",
|
|
generateBeaconBlockConverter: func(ctrl *gomock.Controller, conversionError error) *mock.MockbeaconBlockConverter {
|
|
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
|
beaconBlockConverter.EXPECT().ConvertRESTAltairBlockToProto(
|
|
gomock.Any(),
|
|
).Return(
|
|
nil,
|
|
conversionError,
|
|
).AnyTimes()
|
|
|
|
return beaconBlockConverter
|
|
},
|
|
},
|
|
{
|
|
consensusVersion: "bellatrix",
|
|
generateBeaconBlockConverter: func(ctrl *gomock.Controller, conversionError error) *mock.MockbeaconBlockConverter {
|
|
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
|
beaconBlockConverter.EXPECT().ConvertRESTBellatrixBlockToProto(
|
|
gomock.Any(),
|
|
).Return(
|
|
nil,
|
|
conversionError,
|
|
).AnyTimes()
|
|
|
|
return beaconBlockConverter
|
|
},
|
|
},
|
|
{
|
|
consensusVersion: "capella",
|
|
generateBeaconBlockConverter: func(ctrl *gomock.Controller, conversionError error) *mock.MockbeaconBlockConverter {
|
|
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
|
beaconBlockConverter.EXPECT().ConvertRESTCapellaBlockToProto(
|
|
gomock.Any(),
|
|
).Return(
|
|
nil,
|
|
conversionError,
|
|
).AnyTimes()
|
|
|
|
return beaconBlockConverter
|
|
},
|
|
},
|
|
}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
expectedErrorMessage string
|
|
conversionError error
|
|
generateData func(consensusVersion string) []byte
|
|
}{
|
|
{
|
|
name: "block decoding failed",
|
|
expectedErrorMessage: "failed to decode signed %s block response json",
|
|
generateData: func(consensusVersion string) []byte { return []byte{} },
|
|
},
|
|
{
|
|
name: "block conversion failed",
|
|
expectedErrorMessage: "failed to get signed %s block",
|
|
conversionError: errors.New("foo"),
|
|
generateData: func(consensusVersion string) []byte {
|
|
blockBytes, err := json.Marshal(apimiddleware.SignedBeaconBlockContainerJson{Signature: "0x01"})
|
|
require.NoError(t, err)
|
|
return blockBytes
|
|
},
|
|
},
|
|
{
|
|
name: "signature decoding failed",
|
|
expectedErrorMessage: "failed to decode %s block signature `foo`",
|
|
generateData: func(consensusVersion string) []byte {
|
|
blockBytes, err := json.Marshal(apimiddleware.SignedBeaconBlockContainerJson{Signature: "foo"})
|
|
require.NoError(t, err)
|
|
return blockBytes
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testSuite := range testSuites {
|
|
t.Run(testSuite.consensusVersion, func(t *testing.T) {
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
ctx := context.Background()
|
|
|
|
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
gomock.Any(),
|
|
&abstractSignedBlockResponseJson{},
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: testSuite.consensusVersion,
|
|
Data: testCase.generateData(testSuite.consensusVersion),
|
|
},
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).Times(1)
|
|
|
|
beaconBlockConverter := testSuite.generateBeaconBlockConverter(ctrl, testCase.conversionError)
|
|
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler, beaconBlockConverter: beaconBlockConverter}
|
|
streamBlocksClient := validatorClient.streamBlocks(ctx, ð.StreamBlocksRequest{}, time.Millisecond*100)
|
|
|
|
_, err := streamBlocksClient.Recv()
|
|
assert.ErrorContains(t, fmt.Sprintf(testCase.expectedErrorMessage, testSuite.consensusVersion), err)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestStreamBlocks_Phase0Valid(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().
|
|
phase0BeaconBlock1 := test_helpers.GenerateJsonPhase0BeaconBlock()
|
|
phase0BeaconBlock1.Slot = "1"
|
|
signedBeaconBlockContainer1 := apimiddleware.SignedBeaconBlockContainerJson{
|
|
Message: phase0BeaconBlock1,
|
|
Signature: "0x01",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer1, err := json.Marshal(signedBeaconBlockContainer1)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "phase0",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer1,
|
|
},
|
|
).Times(2)
|
|
|
|
phase0ProtoBeaconBlock1 := test_helpers.GenerateProtoPhase0BeaconBlock()
|
|
phase0ProtoBeaconBlock1.Slot = 1
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTPhase0BlockToProto(
|
|
phase0BeaconBlock1,
|
|
).Return(
|
|
phase0ProtoBeaconBlock1,
|
|
nil,
|
|
).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.
|
|
phase0BeaconBlock2 := test_helpers.GenerateJsonPhase0BeaconBlock()
|
|
phase0BeaconBlock2.Slot = "2"
|
|
signedBeaconBlockContainer2 := apimiddleware.SignedBeaconBlockContainerJson{
|
|
Message: phase0BeaconBlock2,
|
|
Signature: "0x02",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer2, err := json.Marshal(signedBeaconBlockContainer2)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "phase0",
|
|
ExecutionOptimistic: true,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
phase0ProtoBeaconBlock2 := test_helpers.GenerateProtoPhase0BeaconBlock()
|
|
phase0ProtoBeaconBlock2.Slot = 2
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTPhase0BlockToProto(
|
|
phase0BeaconBlock2,
|
|
).Return(
|
|
phase0ProtoBeaconBlock2,
|
|
nil,
|
|
).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: "phase0",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTPhase0BlockToProto(
|
|
phase0BeaconBlock2,
|
|
).Return(
|
|
phase0ProtoBeaconBlock2,
|
|
nil,
|
|
).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)
|
|
|
|
expectedStreamBlocksResponse1 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_Phase0Block{
|
|
Phase0Block: ð.SignedBeaconBlock{
|
|
Block: phase0ProtoBeaconBlock1,
|
|
Signature: []byte{1},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse1, streamBlocksResponse1)
|
|
|
|
// Get the second block
|
|
streamBlocksResponse2, err := streamBlocksClient.Recv()
|
|
require.NoError(t, err)
|
|
|
|
expectedStreamBlocksResponse2 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_Phase0Block{
|
|
Phase0Block: ð.SignedBeaconBlock{
|
|
Block: phase0ProtoBeaconBlock2,
|
|
Signature: []byte{2},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse2, streamBlocksResponse2)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStreamBlocks_AltairValid(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().
|
|
altairBeaconBlock1 := test_helpers.GenerateJsonAltairBeaconBlock()
|
|
altairBeaconBlock1.Slot = "1"
|
|
signedBeaconBlockContainer1 := apimiddleware.SignedBeaconBlockAltairContainerJson{
|
|
Message: altairBeaconBlock1,
|
|
Signature: "0x01",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer1, err := json.Marshal(signedBeaconBlockContainer1)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "altair",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer1,
|
|
},
|
|
).Times(2)
|
|
|
|
altairProtoBeaconBlock1 := test_helpers.GenerateProtoAltairBeaconBlock()
|
|
altairProtoBeaconBlock1.Slot = 1
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTAltairBlockToProto(
|
|
altairBeaconBlock1,
|
|
).Return(
|
|
altairProtoBeaconBlock1,
|
|
nil,
|
|
).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.
|
|
altairBeaconBlock2 := test_helpers.GenerateJsonAltairBeaconBlock()
|
|
altairBeaconBlock2.Slot = "2"
|
|
signedBeaconBlockContainer2 := apimiddleware.SignedBeaconBlockAltairContainerJson{
|
|
Message: altairBeaconBlock2,
|
|
Signature: "0x02",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer2, err := json.Marshal(signedBeaconBlockContainer2)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "altair",
|
|
ExecutionOptimistic: true,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
altairProtoBeaconBlock2 := test_helpers.GenerateProtoAltairBeaconBlock()
|
|
altairProtoBeaconBlock2.Slot = 2
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTAltairBlockToProto(
|
|
altairBeaconBlock2,
|
|
).Return(
|
|
altairProtoBeaconBlock2,
|
|
nil,
|
|
).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: "altair",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTAltairBlockToProto(
|
|
altairBeaconBlock2,
|
|
).Return(
|
|
altairProtoBeaconBlock2,
|
|
nil,
|
|
).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)
|
|
|
|
expectedStreamBlocksResponse1 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_AltairBlock{
|
|
AltairBlock: ð.SignedBeaconBlockAltair{
|
|
Block: altairProtoBeaconBlock1,
|
|
Signature: []byte{1},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse1, streamBlocksResponse1)
|
|
|
|
// Get the second block
|
|
streamBlocksResponse2, err := streamBlocksClient.Recv()
|
|
require.NoError(t, err)
|
|
|
|
expectedStreamBlocksResponse2 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_AltairBlock{
|
|
AltairBlock: ð.SignedBeaconBlockAltair{
|
|
Block: altairProtoBeaconBlock2,
|
|
Signature: []byte{2},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse2, streamBlocksResponse2)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStreamBlocks_BellatrixValid(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().
|
|
bellatrixBeaconBlock1 := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
|
bellatrixBeaconBlock1.Slot = "1"
|
|
signedBeaconBlockContainer1 := apimiddleware.SignedBeaconBlockBellatrixContainerJson{
|
|
Message: bellatrixBeaconBlock1,
|
|
Signature: "0x01",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer1, err := json.Marshal(signedBeaconBlockContainer1)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "bellatrix",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer1,
|
|
},
|
|
).Times(2)
|
|
|
|
bellatrixProtoBeaconBlock1 := test_helpers.GenerateProtoBellatrixBeaconBlock()
|
|
bellatrixProtoBeaconBlock1.Slot = 1
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTBellatrixBlockToProto(
|
|
bellatrixBeaconBlock1,
|
|
).Return(
|
|
bellatrixProtoBeaconBlock1,
|
|
nil,
|
|
).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.
|
|
bellatrixBeaconBlock2 := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
|
bellatrixBeaconBlock2.Slot = "2"
|
|
signedBeaconBlockContainer2 := apimiddleware.SignedBeaconBlockBellatrixContainerJson{
|
|
Message: bellatrixBeaconBlock2,
|
|
Signature: "0x02",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer2, err := json.Marshal(signedBeaconBlockContainer2)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "bellatrix",
|
|
ExecutionOptimistic: true,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
bellatrixProtoBeaconBlock2 := test_helpers.GenerateProtoBellatrixBeaconBlock()
|
|
bellatrixProtoBeaconBlock2.Slot = 2
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTBellatrixBlockToProto(
|
|
bellatrixBeaconBlock2,
|
|
).Return(
|
|
bellatrixProtoBeaconBlock2,
|
|
nil,
|
|
).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: "bellatrix",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTBellatrixBlockToProto(
|
|
bellatrixBeaconBlock2,
|
|
).Return(
|
|
bellatrixProtoBeaconBlock2,
|
|
nil,
|
|
).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)
|
|
|
|
expectedStreamBlocksResponse1 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_BellatrixBlock{
|
|
BellatrixBlock: ð.SignedBeaconBlockBellatrix{
|
|
Block: bellatrixProtoBeaconBlock1,
|
|
Signature: []byte{1},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse1, streamBlocksResponse1)
|
|
|
|
// Get the second block
|
|
streamBlocksResponse2, err := streamBlocksClient.Recv()
|
|
require.NoError(t, err)
|
|
|
|
expectedStreamBlocksResponse2 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_BellatrixBlock{
|
|
BellatrixBlock: ð.SignedBeaconBlockBellatrix{
|
|
Block: bellatrixProtoBeaconBlock2,
|
|
Signature: []byte{2},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse2, streamBlocksResponse2)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStreamBlocks_CapellaValid(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().
|
|
capellaBeaconBlock1 := test_helpers.GenerateJsonCapellaBeaconBlock()
|
|
capellaBeaconBlock1.Slot = "1"
|
|
signedBeaconBlockContainer1 := apimiddleware.SignedBeaconBlockCapellaContainerJson{
|
|
Message: capellaBeaconBlock1,
|
|
Signature: "0x01",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer1, err := json.Marshal(signedBeaconBlockContainer1)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "capella",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer1,
|
|
},
|
|
).Times(2)
|
|
|
|
capellaProtoBeaconBlock1 := test_helpers.GenerateProtoCapellaBeaconBlock()
|
|
capellaProtoBeaconBlock1.Slot = 1
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTCapellaBlockToProto(
|
|
capellaBeaconBlock1,
|
|
).Return(
|
|
capellaProtoBeaconBlock1,
|
|
nil,
|
|
).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.
|
|
capellaBeaconBlock2 := test_helpers.GenerateJsonCapellaBeaconBlock()
|
|
capellaBeaconBlock2.Slot = "2"
|
|
signedBeaconBlockContainer2 := apimiddleware.SignedBeaconBlockCapellaContainerJson{
|
|
Message: capellaBeaconBlock2,
|
|
Signature: "0x02",
|
|
}
|
|
|
|
marshalledSignedBeaconBlockContainer2, err := json.Marshal(signedBeaconBlockContainer2)
|
|
require.NoError(t, err)
|
|
|
|
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
|
ctx,
|
|
"/eth/v2/beacon/blocks/head",
|
|
&signedBlockResponseJson,
|
|
).Return(
|
|
nil,
|
|
nil,
|
|
).SetArg(
|
|
2,
|
|
abstractSignedBlockResponseJson{
|
|
Version: "capella",
|
|
ExecutionOptimistic: true,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
capellaProtoBeaconBlock2 := test_helpers.GenerateProtoCapellaBeaconBlock()
|
|
capellaProtoBeaconBlock2.Slot = 2
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTCapellaBlockToProto(
|
|
capellaBeaconBlock2,
|
|
).Return(
|
|
capellaProtoBeaconBlock2,
|
|
nil,
|
|
).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: "capella",
|
|
ExecutionOptimistic: false,
|
|
Data: marshalledSignedBeaconBlockContainer2,
|
|
},
|
|
).Times(1)
|
|
|
|
beaconBlockConverter.EXPECT().ConvertRESTCapellaBlockToProto(
|
|
capellaBeaconBlock2,
|
|
).Return(
|
|
capellaProtoBeaconBlock2,
|
|
nil,
|
|
).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)
|
|
|
|
expectedStreamBlocksResponse1 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_CapellaBlock{
|
|
CapellaBlock: ð.SignedBeaconBlockCapella{
|
|
Block: capellaProtoBeaconBlock1,
|
|
Signature: []byte{1},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse1, streamBlocksResponse1)
|
|
|
|
// Get the second block
|
|
streamBlocksResponse2, err := streamBlocksClient.Recv()
|
|
require.NoError(t, err)
|
|
|
|
expectedStreamBlocksResponse2 := ð.StreamBlocksResponse{
|
|
Block: ð.StreamBlocksResponse_CapellaBlock{
|
|
CapellaBlock: ð.SignedBeaconBlockCapella{
|
|
Block: capellaProtoBeaconBlock2,
|
|
Signature: []byte{2},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.DeepEqual(t, expectedStreamBlocksResponse2, streamBlocksResponse2)
|
|
})
|
|
}
|
|
}
|