mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 03:30:35 +00:00
Add REST implementation for Validator's StreamBlocksAltair
(#11974)
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
parent
63186c8b0f
commit
791110f795
@ -76,6 +76,7 @@ beacon_api_mocks=(
|
||||
"$beacon_api_mock_path/duties_mock.go duties.go"
|
||||
"$beacon_api_mock_path/json_rest_handler_mock.go json_rest_handler.go"
|
||||
"$beacon_api_mock_path/state_validators_mock.go state_validators.go"
|
||||
"$beacon_api_mock_path/beacon_block_converter_mock.go beacon_block_converter.go"
|
||||
)
|
||||
|
||||
for ((i = 0; i < ${#beacon_api_mocks[@]}; i++)); do
|
||||
|
4
testing/mock/validator_client_mock.go
generated
4
testing/mock/validator_client_mock.go
generated
@ -9,7 +9,7 @@ import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
primitives "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@ -353,7 +353,7 @@ func (mr *MockValidatorClientMockRecorder) SubmitValidatorRegistrations(arg0, ar
|
||||
}
|
||||
|
||||
// SubscribeCommitteeSubnets mocks base method.
|
||||
func (m *MockValidatorClient) SubscribeCommitteeSubnets(arg0 context.Context, arg1 *eth.CommitteeSubnetsSubscribeRequest, arg2 []types.ValidatorIndex) (*emptypb.Empty, error) {
|
||||
func (m *MockValidatorClient) SubscribeCommitteeSubnets(arg0 context.Context, arg1 *eth.CommitteeSubnetsSubscribeRequest, arg2 []primitives.ValidatorIndex) (*emptypb.Empty, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SubscribeCommitteeSubnets", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(*emptypb.Empty)
|
||||
|
@ -7,6 +7,7 @@ go_library(
|
||||
"attestation_data.go",
|
||||
"beacon_api_helpers.go",
|
||||
"beacon_api_validator_client.go",
|
||||
"beacon_block_converter.go",
|
||||
"beacon_block_json_helpers.go",
|
||||
"beacon_block_proto_helpers.go",
|
||||
"domain_data.go",
|
||||
@ -24,6 +25,7 @@ go_library(
|
||||
"registration.go",
|
||||
"state_validators.go",
|
||||
"status.go",
|
||||
"stream_blocks.go",
|
||||
"submit_signed_aggregate_proof.go",
|
||||
"submit_signed_contribution_and_proof.go",
|
||||
"subscribe_committee_subnets.go",
|
||||
@ -60,16 +62,13 @@ go_test(
|
||||
"attestation_data_test.go",
|
||||
"beacon_api_helpers_test.go",
|
||||
"beacon_api_validator_client_test.go",
|
||||
"beacon_block_converter_test.go",
|
||||
"beacon_block_json_helpers_test.go",
|
||||
"beacon_block_proto_helpers_test.go",
|
||||
"domain_data_test.go",
|
||||
"doppelganger_test.go",
|
||||
"duties_test.go",
|
||||
"genesis_test.go",
|
||||
"get_beacon_block_altair_test.go",
|
||||
"get_beacon_block_bellatrix_test.go",
|
||||
"get_beacon_block_capella_test.go",
|
||||
"get_beacon_block_phase0_test.go",
|
||||
"get_beacon_block_test.go",
|
||||
"index_test.go",
|
||||
"json_rest_handler_test.go",
|
||||
@ -86,6 +85,7 @@ go_test(
|
||||
"registration_test.go",
|
||||
"state_validators_test.go",
|
||||
"status_test.go",
|
||||
"stream_blocks_test.go",
|
||||
"submit_signed_aggregate_proof_test.go",
|
||||
"submit_signed_contribution_and_proof_test.go",
|
||||
"subscribe_committee_subnets_test.go",
|
||||
|
@ -20,6 +20,7 @@ type beaconApiValidatorClient struct {
|
||||
stateValidatorsProvider stateValidatorsProvider
|
||||
jsonRestHandler jsonRestHandler
|
||||
fallbackClient iface.ValidatorClient
|
||||
beaconBlockConverter beaconBlockConverter
|
||||
}
|
||||
|
||||
func NewBeaconApiValidatorClientWithFallback(host string, timeout time.Duration, fallbackClient iface.ValidatorClient) iface.ValidatorClient {
|
||||
@ -33,6 +34,7 @@ func NewBeaconApiValidatorClientWithFallback(host string, timeout time.Duration,
|
||||
dutiesProvider: beaconApiDutiesProvider{jsonRestHandler: jsonRestHandler},
|
||||
stateValidatorsProvider: beaconApiStateValidatorsProvider{jsonRestHandler: jsonRestHandler},
|
||||
jsonRestHandler: jsonRestHandler,
|
||||
beaconBlockConverter: beaconApiBeaconBlockConverter{},
|
||||
fallbackClient: fallbackClient,
|
||||
}
|
||||
}
|
||||
@ -108,12 +110,7 @@ func (c *beaconApiValidatorClient) ProposeExit(ctx context.Context, in *ethpb.Si
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) StreamBlocksAltair(ctx context.Context, in *ethpb.StreamBlocksRequest) (ethpb.BeaconNodeValidator_StreamBlocksAltairClient, error) {
|
||||
if c.fallbackClient != nil {
|
||||
return c.fallbackClient.StreamBlocksAltair(ctx, in)
|
||||
}
|
||||
|
||||
// TODO: Implement me
|
||||
panic("beaconApiValidatorClient.StreamBlocksAltair is not implemented. To use a fallback client, create this validator with NewBeaconApiValidatorClientWithFallback instead.")
|
||||
return c.streamBlocks(ctx, in, time.Second), nil
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) StreamDuties(ctx context.Context, in *ethpb.DutiesRequest) (ethpb.BeaconNodeValidator_StreamDutiesClient, error) {
|
||||
|
423
validator/client/beacon-api/beacon_block_converter.go
Normal file
423
validator/client/beacon-api/beacon_block_converter.go
Normal file
@ -0,0 +1,423 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
type beaconBlockConverter interface {
|
||||
ConvertRESTPhase0BlockToProto(block *apimiddleware.BeaconBlockJson) (*ethpb.BeaconBlock, error)
|
||||
ConvertRESTAltairBlockToProto(block *apimiddleware.BeaconBlockAltairJson) (*ethpb.BeaconBlockAltair, error)
|
||||
ConvertRESTBellatrixBlockToProto(block *apimiddleware.BeaconBlockBellatrixJson) (*ethpb.BeaconBlockBellatrix, error)
|
||||
ConvertRESTCapellaBlockToProto(block *apimiddleware.BeaconBlockCapellaJson) (*ethpb.BeaconBlockCapella, error)
|
||||
}
|
||||
|
||||
type beaconApiBeaconBlockConverter struct{}
|
||||
|
||||
// ConvertRESTPhase0BlockToProto converts a Phase0 JSON beacon block to its protobuf equivalent
|
||||
func (c beaconApiBeaconBlockConverter) ConvertRESTPhase0BlockToProto(block *apimiddleware.BeaconBlockJson) (*ethpb.BeaconBlock, error) {
|
||||
blockSlot, err := strconv.ParseUint(block.Slot, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse slot `%s`", block.Slot)
|
||||
}
|
||||
|
||||
blockProposerIndex, err := strconv.ParseUint(block.ProposerIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse proposer index `%s`", block.ProposerIndex)
|
||||
}
|
||||
|
||||
parentRoot, err := hexutil.Decode(block.ParentRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode parent root `%s`", block.ParentRoot)
|
||||
}
|
||||
|
||||
stateRoot, err := hexutil.Decode(block.StateRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode state root `%s`", block.StateRoot)
|
||||
}
|
||||
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
randaoReveal, err := hexutil.Decode(block.Body.RandaoReveal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode randao reveal `%s`", block.Body.RandaoReveal)
|
||||
}
|
||||
|
||||
if block.Body.Eth1Data == nil {
|
||||
return nil, errors.New("eth1 data is nil")
|
||||
}
|
||||
|
||||
depositRoot, err := hexutil.Decode(block.Body.Eth1Data.DepositRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode deposit root `%s`", block.Body.Eth1Data.DepositRoot)
|
||||
}
|
||||
|
||||
depositCount, err := strconv.ParseUint(block.Body.Eth1Data.DepositCount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse deposit count `%s`", block.Body.Eth1Data.DepositCount)
|
||||
}
|
||||
|
||||
blockHash, err := hexutil.Decode(block.Body.Eth1Data.BlockHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode block hash `%s`", block.Body.Eth1Data.BlockHash)
|
||||
}
|
||||
|
||||
graffiti, err := hexutil.Decode(block.Body.Graffiti)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode graffiti `%s`", block.Body.Graffiti)
|
||||
}
|
||||
|
||||
proposerSlashings, err := convertProposerSlashingsToProto(block.Body.ProposerSlashings)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get proposer slashings")
|
||||
}
|
||||
|
||||
attesterSlashings, err := convertAttesterSlashingsToProto(block.Body.AttesterSlashings)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get attester slashings")
|
||||
}
|
||||
|
||||
attestations, err := convertAttestationsToProto(block.Body.Attestations)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get attestations")
|
||||
}
|
||||
|
||||
deposits, err := convertDepositsToProto(block.Body.Deposits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get deposits")
|
||||
}
|
||||
|
||||
voluntaryExits, err := convertVoluntaryExitsToProto(block.Body.VoluntaryExits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get voluntary exits")
|
||||
}
|
||||
|
||||
return ðpb.BeaconBlock{
|
||||
Slot: primitives.Slot(blockSlot),
|
||||
ProposerIndex: primitives.ValidatorIndex(blockProposerIndex),
|
||||
ParentRoot: parentRoot,
|
||||
StateRoot: stateRoot,
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: randaoReveal,
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: depositRoot,
|
||||
DepositCount: depositCount,
|
||||
BlockHash: blockHash,
|
||||
},
|
||||
Graffiti: graffiti,
|
||||
ProposerSlashings: proposerSlashings,
|
||||
AttesterSlashings: attesterSlashings,
|
||||
Attestations: attestations,
|
||||
Deposits: deposits,
|
||||
VoluntaryExits: voluntaryExits,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ConvertRESTAltairBlockToProto converts an Altair JSON beacon block to its protobuf equivalent
|
||||
func (c beaconApiBeaconBlockConverter) ConvertRESTAltairBlockToProto(block *apimiddleware.BeaconBlockAltairJson) (*ethpb.BeaconBlockAltair, error) {
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
// Call convertRESTPhase0BlockToProto to set the phase0 fields because all the error handling and the heavy lifting
|
||||
// has already been done
|
||||
phase0Block, err := c.ConvertRESTPhase0BlockToProto(&apimiddleware.BeaconBlockJson{
|
||||
Slot: block.Slot,
|
||||
ProposerIndex: block.ProposerIndex,
|
||||
ParentRoot: block.ParentRoot,
|
||||
StateRoot: block.StateRoot,
|
||||
Body: &apimiddleware.BeaconBlockBodyJson{
|
||||
RandaoReveal: block.Body.RandaoReveal,
|
||||
Eth1Data: block.Body.Eth1Data,
|
||||
Graffiti: block.Body.Graffiti,
|
||||
ProposerSlashings: block.Body.ProposerSlashings,
|
||||
AttesterSlashings: block.Body.AttesterSlashings,
|
||||
Attestations: block.Body.Attestations,
|
||||
Deposits: block.Body.Deposits,
|
||||
VoluntaryExits: block.Body.VoluntaryExits,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get the phase0 fields of the altair block")
|
||||
}
|
||||
|
||||
if block.Body.SyncAggregate == nil {
|
||||
return nil, errors.New("sync aggregate is nil")
|
||||
}
|
||||
|
||||
syncCommitteeBits, err := hexutil.Decode(block.Body.SyncAggregate.SyncCommitteeBits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode sync committee bits `%s`", block.Body.SyncAggregate.SyncCommitteeBits)
|
||||
}
|
||||
|
||||
syncCommitteeSignature, err := hexutil.Decode(block.Body.SyncAggregate.SyncCommitteeSignature)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode sync committee signature `%s`", block.Body.SyncAggregate.SyncCommitteeSignature)
|
||||
}
|
||||
|
||||
return ðpb.BeaconBlockAltair{
|
||||
Slot: phase0Block.Slot,
|
||||
ProposerIndex: phase0Block.ProposerIndex,
|
||||
ParentRoot: phase0Block.ParentRoot,
|
||||
StateRoot: phase0Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
RandaoReveal: phase0Block.Body.RandaoReveal,
|
||||
Eth1Data: phase0Block.Body.Eth1Data,
|
||||
Graffiti: phase0Block.Body.Graffiti,
|
||||
ProposerSlashings: phase0Block.Body.ProposerSlashings,
|
||||
AttesterSlashings: phase0Block.Body.AttesterSlashings,
|
||||
Attestations: phase0Block.Body.Attestations,
|
||||
Deposits: phase0Block.Body.Deposits,
|
||||
VoluntaryExits: phase0Block.Body.VoluntaryExits,
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: syncCommitteeBits,
|
||||
SyncCommitteeSignature: syncCommitteeSignature,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ConvertRESTBellatrixBlockToProto converts a Bellatrix JSON beacon block to its protobuf equivalent
|
||||
func (c beaconApiBeaconBlockConverter) ConvertRESTBellatrixBlockToProto(block *apimiddleware.BeaconBlockBellatrixJson) (*ethpb.BeaconBlockBellatrix, error) {
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
// Call convertRESTAltairBlockToProto to set the altair fields because all the error handling and the heavy lifting
|
||||
// has already been done
|
||||
altairBlock, err := c.ConvertRESTAltairBlockToProto(&apimiddleware.BeaconBlockAltairJson{
|
||||
Slot: block.Slot,
|
||||
ProposerIndex: block.ProposerIndex,
|
||||
ParentRoot: block.ParentRoot,
|
||||
StateRoot: block.StateRoot,
|
||||
Body: &apimiddleware.BeaconBlockBodyAltairJson{
|
||||
RandaoReveal: block.Body.RandaoReveal,
|
||||
Eth1Data: block.Body.Eth1Data,
|
||||
Graffiti: block.Body.Graffiti,
|
||||
ProposerSlashings: block.Body.ProposerSlashings,
|
||||
AttesterSlashings: block.Body.AttesterSlashings,
|
||||
Attestations: block.Body.Attestations,
|
||||
Deposits: block.Body.Deposits,
|
||||
VoluntaryExits: block.Body.VoluntaryExits,
|
||||
SyncAggregate: block.Body.SyncAggregate,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get the altair fields of the bellatrix block")
|
||||
}
|
||||
|
||||
if block.Body.ExecutionPayload == nil {
|
||||
return nil, errors.New("execution payload is nil")
|
||||
}
|
||||
|
||||
parentHash, err := hexutil.Decode(block.Body.ExecutionPayload.ParentHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload parent hash `%s`", block.Body.ExecutionPayload.ParentHash)
|
||||
}
|
||||
|
||||
feeRecipient, err := hexutil.Decode(block.Body.ExecutionPayload.FeeRecipient)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload fee recipient `%s`", block.Body.ExecutionPayload.FeeRecipient)
|
||||
}
|
||||
|
||||
stateRoot, err := hexutil.Decode(block.Body.ExecutionPayload.StateRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload state root `%s`", block.Body.ExecutionPayload.StateRoot)
|
||||
}
|
||||
|
||||
receiptsRoot, err := hexutil.Decode(block.Body.ExecutionPayload.ReceiptsRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload receipts root `%s`", block.Body.ExecutionPayload.ReceiptsRoot)
|
||||
}
|
||||
|
||||
logsBloom, err := hexutil.Decode(block.Body.ExecutionPayload.LogsBloom)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload logs bloom `%s`", block.Body.ExecutionPayload.LogsBloom)
|
||||
}
|
||||
|
||||
prevRandao, err := hexutil.Decode(block.Body.ExecutionPayload.PrevRandao)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload prev randao `%s`", block.Body.ExecutionPayload.PrevRandao)
|
||||
}
|
||||
|
||||
blockNumber, err := strconv.ParseUint(block.Body.ExecutionPayload.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload block number `%s`", block.Body.ExecutionPayload.BlockNumber)
|
||||
}
|
||||
|
||||
gasLimit, err := strconv.ParseUint(block.Body.ExecutionPayload.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload gas limit `%s`", block.Body.ExecutionPayload.GasLimit)
|
||||
}
|
||||
|
||||
gasUsed, err := strconv.ParseUint(block.Body.ExecutionPayload.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload gas used `%s`", block.Body.ExecutionPayload.GasUsed)
|
||||
}
|
||||
|
||||
timestamp, err := strconv.ParseUint(block.Body.ExecutionPayload.TimeStamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload timestamp `%s`", block.Body.ExecutionPayload.TimeStamp)
|
||||
}
|
||||
|
||||
extraData, err := hexutil.Decode(block.Body.ExecutionPayload.ExtraData)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload extra data `%s`", block.Body.ExecutionPayload.ExtraData)
|
||||
}
|
||||
|
||||
baseFeePerGas := new(big.Int)
|
||||
if _, ok := baseFeePerGas.SetString(block.Body.ExecutionPayload.BaseFeePerGas, 10); !ok {
|
||||
return nil, errors.Errorf("failed to parse execution payload base fee per gas `%s`", block.Body.ExecutionPayload.BaseFeePerGas)
|
||||
}
|
||||
|
||||
blockHash, err := hexutil.Decode(block.Body.ExecutionPayload.BlockHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload block hash `%s`", block.Body.ExecutionPayload.BlockHash)
|
||||
}
|
||||
|
||||
transactions, err := convertTransactionsToProto(block.Body.ExecutionPayload.Transactions)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get execution payload transactions")
|
||||
}
|
||||
|
||||
return ðpb.BeaconBlockBellatrix{
|
||||
Slot: altairBlock.Slot,
|
||||
ProposerIndex: altairBlock.ProposerIndex,
|
||||
ParentRoot: altairBlock.ParentRoot,
|
||||
StateRoot: altairBlock.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: altairBlock.Body.RandaoReveal,
|
||||
Eth1Data: altairBlock.Body.Eth1Data,
|
||||
Graffiti: altairBlock.Body.Graffiti,
|
||||
ProposerSlashings: altairBlock.Body.ProposerSlashings,
|
||||
AttesterSlashings: altairBlock.Body.AttesterSlashings,
|
||||
Attestations: altairBlock.Body.Attestations,
|
||||
Deposits: altairBlock.Body.Deposits,
|
||||
VoluntaryExits: altairBlock.Body.VoluntaryExits,
|
||||
SyncAggregate: altairBlock.Body.SyncAggregate,
|
||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||
ParentHash: parentHash,
|
||||
FeeRecipient: feeRecipient,
|
||||
StateRoot: stateRoot,
|
||||
ReceiptsRoot: receiptsRoot,
|
||||
LogsBloom: logsBloom,
|
||||
PrevRandao: prevRandao,
|
||||
BlockNumber: blockNumber,
|
||||
GasLimit: gasLimit,
|
||||
GasUsed: gasUsed,
|
||||
Timestamp: timestamp,
|
||||
ExtraData: extraData,
|
||||
BaseFeePerGas: bytesutil.PadTo(bytesutil.BigIntToLittleEndianBytes(baseFeePerGas), 32),
|
||||
BlockHash: blockHash,
|
||||
Transactions: transactions,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ConvertRESTCapellaBlockToProto converts a Capella JSON beacon block to its protobuf equivalent
|
||||
func (c beaconApiBeaconBlockConverter) ConvertRESTCapellaBlockToProto(block *apimiddleware.BeaconBlockCapellaJson) (*ethpb.BeaconBlockCapella, error) {
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
if block.Body.ExecutionPayload == nil {
|
||||
return nil, errors.New("execution payload is nil")
|
||||
}
|
||||
|
||||
// Call convertRESTBellatrixBlockToProto to set the bellatrix fields because all the error handling and the heavy
|
||||
// lifting has already been done
|
||||
bellatrixBlock, err := c.ConvertRESTBellatrixBlockToProto(&apimiddleware.BeaconBlockBellatrixJson{
|
||||
Slot: block.Slot,
|
||||
ProposerIndex: block.ProposerIndex,
|
||||
ParentRoot: block.ParentRoot,
|
||||
StateRoot: block.StateRoot,
|
||||
Body: &apimiddleware.BeaconBlockBodyBellatrixJson{
|
||||
RandaoReveal: block.Body.RandaoReveal,
|
||||
Eth1Data: block.Body.Eth1Data,
|
||||
Graffiti: block.Body.Graffiti,
|
||||
ProposerSlashings: block.Body.ProposerSlashings,
|
||||
AttesterSlashings: block.Body.AttesterSlashings,
|
||||
Attestations: block.Body.Attestations,
|
||||
Deposits: block.Body.Deposits,
|
||||
VoluntaryExits: block.Body.VoluntaryExits,
|
||||
SyncAggregate: block.Body.SyncAggregate,
|
||||
ExecutionPayload: &apimiddleware.ExecutionPayloadJson{
|
||||
ParentHash: block.Body.ExecutionPayload.ParentHash,
|
||||
FeeRecipient: block.Body.ExecutionPayload.FeeRecipient,
|
||||
StateRoot: block.Body.ExecutionPayload.StateRoot,
|
||||
ReceiptsRoot: block.Body.ExecutionPayload.ReceiptsRoot,
|
||||
LogsBloom: block.Body.ExecutionPayload.LogsBloom,
|
||||
PrevRandao: block.Body.ExecutionPayload.PrevRandao,
|
||||
BlockNumber: block.Body.ExecutionPayload.BlockNumber,
|
||||
GasLimit: block.Body.ExecutionPayload.GasLimit,
|
||||
GasUsed: block.Body.ExecutionPayload.GasUsed,
|
||||
TimeStamp: block.Body.ExecutionPayload.TimeStamp,
|
||||
ExtraData: block.Body.ExecutionPayload.ExtraData,
|
||||
BaseFeePerGas: block.Body.ExecutionPayload.BaseFeePerGas,
|
||||
BlockHash: block.Body.ExecutionPayload.BlockHash,
|
||||
Transactions: block.Body.ExecutionPayload.Transactions,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get the bellatrix fields of the capella block")
|
||||
}
|
||||
|
||||
withdrawals, err := convertWithdrawalsToProto(block.Body.ExecutionPayload.Withdrawals)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get withdrawals")
|
||||
}
|
||||
|
||||
blsToExecutionChanges, err := convertBlsToExecutionChangesToProto(block.Body.BLSToExecutionChanges)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get bls to execution changes")
|
||||
}
|
||||
|
||||
return ðpb.BeaconBlockCapella{
|
||||
Slot: bellatrixBlock.Slot,
|
||||
ProposerIndex: bellatrixBlock.ProposerIndex,
|
||||
ParentRoot: bellatrixBlock.ParentRoot,
|
||||
StateRoot: bellatrixBlock.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyCapella{
|
||||
RandaoReveal: bellatrixBlock.Body.RandaoReveal,
|
||||
Eth1Data: bellatrixBlock.Body.Eth1Data,
|
||||
Graffiti: bellatrixBlock.Body.Graffiti,
|
||||
ProposerSlashings: bellatrixBlock.Body.ProposerSlashings,
|
||||
AttesterSlashings: bellatrixBlock.Body.AttesterSlashings,
|
||||
Attestations: bellatrixBlock.Body.Attestations,
|
||||
Deposits: bellatrixBlock.Body.Deposits,
|
||||
VoluntaryExits: bellatrixBlock.Body.VoluntaryExits,
|
||||
SyncAggregate: bellatrixBlock.Body.SyncAggregate,
|
||||
ExecutionPayload: &enginev1.ExecutionPayloadCapella{
|
||||
ParentHash: bellatrixBlock.Body.ExecutionPayload.ParentHash,
|
||||
FeeRecipient: bellatrixBlock.Body.ExecutionPayload.FeeRecipient,
|
||||
StateRoot: bellatrixBlock.Body.ExecutionPayload.StateRoot,
|
||||
ReceiptsRoot: bellatrixBlock.Body.ExecutionPayload.ReceiptsRoot,
|
||||
LogsBloom: bellatrixBlock.Body.ExecutionPayload.LogsBloom,
|
||||
PrevRandao: bellatrixBlock.Body.ExecutionPayload.PrevRandao,
|
||||
BlockNumber: bellatrixBlock.Body.ExecutionPayload.BlockNumber,
|
||||
GasLimit: bellatrixBlock.Body.ExecutionPayload.GasLimit,
|
||||
GasUsed: bellatrixBlock.Body.ExecutionPayload.GasUsed,
|
||||
Timestamp: bellatrixBlock.Body.ExecutionPayload.Timestamp,
|
||||
ExtraData: bellatrixBlock.Body.ExecutionPayload.ExtraData,
|
||||
BaseFeePerGas: bellatrixBlock.Body.ExecutionPayload.BaseFeePerGas,
|
||||
BlockHash: bellatrixBlock.Body.ExecutionPayload.BlockHash,
|
||||
Transactions: bellatrixBlock.Body.ExecutionPayload.Transactions,
|
||||
Withdrawals: withdrawals,
|
||||
},
|
||||
BlsToExecutionChanges: blsToExecutionChanges,
|
||||
},
|
||||
}, nil
|
||||
}
|
505
validator/client/beacon-api/beacon_block_converter_test.go
Normal file
505
validator/client/beacon-api/beacon_block_converter_test.go
Normal file
@ -0,0 +1,505 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/test-helpers"
|
||||
)
|
||||
|
||||
func TestGetBeaconBlockConverter_Phase0Valid(t *testing.T) {
|
||||
expectedBeaconBlock := test_helpers.GenerateProtoPhase0BeaconBlock()
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
beaconBlock, err := beaconBlockConverter.ConvertRESTPhase0BlockToProto(test_helpers.GenerateJsonPhase0BeaconBlock())
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlockConverter_Phase0Error(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil eth1 data",
|
||||
expectedErrorMessage: "eth1 data is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad slot",
|
||||
expectedErrorMessage: "failed to parse slot `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Slot = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad proposer index",
|
||||
expectedErrorMessage: "failed to parse proposer index `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.ProposerIndex = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad parent root",
|
||||
expectedErrorMessage: "failed to decode parent root `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.ParentRoot = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad state root",
|
||||
expectedErrorMessage: "failed to decode state root `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.StateRoot = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad randao reveal",
|
||||
expectedErrorMessage: "failed to decode randao reveal `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.RandaoReveal = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad deposit root",
|
||||
expectedErrorMessage: "failed to decode deposit root `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data.DepositRoot = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad deposit count",
|
||||
expectedErrorMessage: "failed to parse deposit count `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data.DepositCount = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad block hash",
|
||||
expectedErrorMessage: "failed to decode block hash `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data.BlockHash = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad graffiti",
|
||||
expectedErrorMessage: "failed to decode graffiti `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Graffiti = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad proposer slashings",
|
||||
expectedErrorMessage: "failed to get proposer slashings",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.ProposerSlashings[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad attester slashings",
|
||||
expectedErrorMessage: "failed to get attester slashings",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.AttesterSlashings[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad attestations",
|
||||
expectedErrorMessage: "failed to get attestations",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Attestations[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad deposits",
|
||||
expectedErrorMessage: "failed to get deposits",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Deposits[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad voluntary exits",
|
||||
expectedErrorMessage: "failed to get voluntary exits",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.VoluntaryExits[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
beaconBlockJson := testCase.generateData()
|
||||
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
_, err := beaconBlockConverter.ConvertRESTPhase0BlockToProto(beaconBlockJson)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBeaconBlockConverter_AltairValid(t *testing.T) {
|
||||
expectedBeaconBlock := test_helpers.GenerateProtoAltairBeaconBlock()
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
beaconBlock, err := beaconBlockConverter.ConvertRESTAltairBlockToProto(test_helpers.GenerateJsonAltairBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlockConverter_AltairError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockAltairJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil sync aggregate",
|
||||
expectedErrorMessage: "sync aggregate is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.SyncAggregate = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad phase0 fields",
|
||||
expectedErrorMessage: "failed to get the phase0 fields of the altair block",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad sync committee bits",
|
||||
expectedErrorMessage: "failed to decode sync committee bits `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.SyncAggregate.SyncCommitteeBits = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad sync committee signature",
|
||||
expectedErrorMessage: "failed to decode sync committee signature `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.SyncAggregate.SyncCommitteeSignature = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
beaconBlockJson := testCase.generateData()
|
||||
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
_, err := beaconBlockConverter.ConvertRESTAltairBlockToProto(beaconBlockJson)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBeaconBlockConverter_BellatrixValid(t *testing.T) {
|
||||
expectedBeaconBlock := test_helpers.GenerateProtoBellatrixBeaconBlock()
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
beaconBlock, err := beaconBlockConverter.ConvertRESTBellatrixBlockToProto(test_helpers.GenerateJsonBellatrixBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlockConverter_BellatrixError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockBellatrixJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil execution payload",
|
||||
expectedErrorMessage: "execution payload is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad altair fields",
|
||||
expectedErrorMessage: "failed to get the altair fields of the bellatrix block",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad parent hash",
|
||||
expectedErrorMessage: "failed to decode execution payload parent hash `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.ParentHash = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad fee recipient",
|
||||
expectedErrorMessage: "failed to decode execution payload fee recipient `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.FeeRecipient = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad state root",
|
||||
expectedErrorMessage: "failed to decode execution payload state root `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.StateRoot = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad receipts root",
|
||||
expectedErrorMessage: "failed to decode execution payload receipts root `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.ReceiptsRoot = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad logs bloom",
|
||||
expectedErrorMessage: "failed to decode execution payload logs bloom `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.LogsBloom = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad prev randao",
|
||||
expectedErrorMessage: "failed to decode execution payload prev randao `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.PrevRandao = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad block number",
|
||||
expectedErrorMessage: "failed to parse execution payload block number `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.BlockNumber = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad gas limit",
|
||||
expectedErrorMessage: "failed to parse execution payload gas limit `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.GasLimit = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad gas used",
|
||||
expectedErrorMessage: "failed to parse execution payload gas used `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.GasUsed = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad timestamp",
|
||||
expectedErrorMessage: "failed to parse execution payload timestamp `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.TimeStamp = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad extra data",
|
||||
expectedErrorMessage: "failed to decode execution payload extra data `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.ExtraData = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad base fee per gas",
|
||||
expectedErrorMessage: "failed to parse execution payload base fee per gas `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.BaseFeePerGas = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad block hash",
|
||||
expectedErrorMessage: "failed to decode execution payload block hash `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.BlockHash = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad transactions",
|
||||
expectedErrorMessage: "failed to get execution payload transactions",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.Transactions[0] = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
beaconBlockJson := testCase.generateData()
|
||||
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
_, err := beaconBlockConverter.ConvertRESTBellatrixBlockToProto(beaconBlockJson)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBeaconBlockConverter_CapellaValid(t *testing.T) {
|
||||
expectedBeaconBlock := test_helpers.GenerateProtoCapellaBeaconBlock()
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
beaconBlock, err := beaconBlockConverter.ConvertRESTCapellaBlockToProto(test_helpers.GenerateJsonCapellaBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlockConverter_CapellaError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockCapellaJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil execution payload",
|
||||
expectedErrorMessage: "execution payload is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad bellatrix fields",
|
||||
expectedErrorMessage: "failed to get the bellatrix fields of the capella block",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad withdrawals",
|
||||
expectedErrorMessage: "failed to get withdrawals",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.Withdrawals[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad bls execution changes",
|
||||
expectedErrorMessage: "failed to get bls to execution changes",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.BLSToExecutionChanges[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
beaconBlockJson := testCase.generateData()
|
||||
|
||||
beaconBlockConverter := &beaconApiBeaconBlockConverter{}
|
||||
_, err := beaconBlockConverter.ConvertRESTCapellaBlockToProto(beaconBlockJson)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
@ -5,16 +5,12 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
neturl "net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
@ -53,11 +49,13 @@ func (c beaconApiValidatorClient) getBeaconBlock(ctx context.Context, slot primi
|
||||
return nil, errors.Wrap(err, "failed to decode phase0 block response json")
|
||||
}
|
||||
|
||||
phase0Block, err := convertRESTPhase0BlockToProto(&jsonPhase0Block)
|
||||
phase0Block, err := c.beaconBlockConverter.ConvertRESTPhase0BlockToProto(&jsonPhase0Block)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get phase0 block")
|
||||
}
|
||||
response.Block = phase0Block
|
||||
response.Block = ðpb.GenericBeaconBlock_Phase0{
|
||||
Phase0: phase0Block,
|
||||
}
|
||||
|
||||
case "altair":
|
||||
jsonAltairBlock := apimiddleware.BeaconBlockAltairJson{}
|
||||
@ -65,11 +63,13 @@ func (c beaconApiValidatorClient) getBeaconBlock(ctx context.Context, slot primi
|
||||
return nil, errors.Wrap(err, "failed to decode altair block response json")
|
||||
}
|
||||
|
||||
altairBlock, err := convertRESTAltairBlockToProto(&jsonAltairBlock)
|
||||
altairBlock, err := c.beaconBlockConverter.ConvertRESTAltairBlockToProto(&jsonAltairBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get altair block")
|
||||
}
|
||||
response.Block = altairBlock
|
||||
response.Block = ðpb.GenericBeaconBlock_Altair{
|
||||
Altair: altairBlock,
|
||||
}
|
||||
|
||||
case "bellatrix":
|
||||
jsonBellatrixBlock := apimiddleware.BeaconBlockBellatrixJson{}
|
||||
@ -77,11 +77,13 @@ func (c beaconApiValidatorClient) getBeaconBlock(ctx context.Context, slot primi
|
||||
return nil, errors.Wrap(err, "failed to decode bellatrix block response json")
|
||||
}
|
||||
|
||||
bellatrixBlock, err := convertRESTBellatrixBlockToProto(&jsonBellatrixBlock)
|
||||
bellatrixBlock, err := c.beaconBlockConverter.ConvertRESTBellatrixBlockToProto(&jsonBellatrixBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get bellatrix block")
|
||||
}
|
||||
response.Block = bellatrixBlock
|
||||
response.Block = ðpb.GenericBeaconBlock_Bellatrix{
|
||||
Bellatrix: bellatrixBlock,
|
||||
}
|
||||
|
||||
case "capella":
|
||||
jsonCapellaBlock := apimiddleware.BeaconBlockCapellaJson{}
|
||||
@ -89,418 +91,16 @@ func (c beaconApiValidatorClient) getBeaconBlock(ctx context.Context, slot primi
|
||||
return nil, errors.Wrap(err, "failed to decode capella block response json")
|
||||
}
|
||||
|
||||
capellaBlock, err := convertRESTCapellaBlockToProto(&jsonCapellaBlock)
|
||||
capellaBlock, err := c.beaconBlockConverter.ConvertRESTCapellaBlockToProto(&jsonCapellaBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get capella block")
|
||||
}
|
||||
response.Block = capellaBlock
|
||||
response.Block = ðpb.GenericBeaconBlock_Capella{
|
||||
Capella: capellaBlock,
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported consensus version `%s`", produceBlockResponseJson.Version)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func convertRESTPhase0BlockToProto(block *apimiddleware.BeaconBlockJson) (*ethpb.GenericBeaconBlock_Phase0, error) {
|
||||
blockSlot, err := strconv.ParseUint(block.Slot, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse slot `%s`", block.Slot)
|
||||
}
|
||||
|
||||
blockProposerIndex, err := strconv.ParseUint(block.ProposerIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse proposer index `%s`", block.ProposerIndex)
|
||||
}
|
||||
|
||||
parentRoot, err := hexutil.Decode(block.ParentRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode parent root `%s`", block.ParentRoot)
|
||||
}
|
||||
|
||||
stateRoot, err := hexutil.Decode(block.StateRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode state root `%s`", block.StateRoot)
|
||||
}
|
||||
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
randaoReveal, err := hexutil.Decode(block.Body.RandaoReveal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode randao reveal `%s`", block.Body.RandaoReveal)
|
||||
}
|
||||
|
||||
if block.Body.Eth1Data == nil {
|
||||
return nil, errors.New("eth1 data is nil")
|
||||
}
|
||||
|
||||
depositRoot, err := hexutil.Decode(block.Body.Eth1Data.DepositRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode deposit root `%s`", block.Body.Eth1Data.DepositRoot)
|
||||
}
|
||||
|
||||
depositCount, err := strconv.ParseUint(block.Body.Eth1Data.DepositCount, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse deposit count `%s`", block.Body.Eth1Data.DepositCount)
|
||||
}
|
||||
|
||||
blockHash, err := hexutil.Decode(block.Body.Eth1Data.BlockHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode block hash `%s`", block.Body.Eth1Data.BlockHash)
|
||||
}
|
||||
|
||||
graffiti, err := hexutil.Decode(block.Body.Graffiti)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode graffiti `%s`", block.Body.Graffiti)
|
||||
}
|
||||
|
||||
proposerSlashings, err := convertProposerSlashingsToProto(block.Body.ProposerSlashings)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get proposer slashings")
|
||||
}
|
||||
|
||||
attesterSlashings, err := convertAttesterSlashingsToProto(block.Body.AttesterSlashings)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get attester slashings")
|
||||
}
|
||||
|
||||
attestations, err := convertAttestationsToProto(block.Body.Attestations)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get attestations")
|
||||
}
|
||||
|
||||
deposits, err := convertDepositsToProto(block.Body.Deposits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get deposits")
|
||||
}
|
||||
|
||||
voluntaryExits, err := convertVoluntaryExitsToProto(block.Body.VoluntaryExits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get voluntary exits")
|
||||
}
|
||||
|
||||
return ðpb.GenericBeaconBlock_Phase0{
|
||||
Phase0: ðpb.BeaconBlock{
|
||||
Slot: primitives.Slot(blockSlot),
|
||||
ProposerIndex: primitives.ValidatorIndex(blockProposerIndex),
|
||||
ParentRoot: parentRoot,
|
||||
StateRoot: stateRoot,
|
||||
Body: ðpb.BeaconBlockBody{
|
||||
RandaoReveal: randaoReveal,
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: depositRoot,
|
||||
DepositCount: depositCount,
|
||||
BlockHash: blockHash,
|
||||
},
|
||||
Graffiti: graffiti,
|
||||
ProposerSlashings: proposerSlashings,
|
||||
AttesterSlashings: attesterSlashings,
|
||||
Attestations: attestations,
|
||||
Deposits: deposits,
|
||||
VoluntaryExits: voluntaryExits,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertRESTAltairBlockToProto(block *apimiddleware.BeaconBlockAltairJson) (*ethpb.GenericBeaconBlock_Altair, error) {
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
// Call convertRESTPhase0BlockToProto to set the phase0 fields because all the error handling and the heavy lifting
|
||||
// has already been done
|
||||
phase0Block, err := convertRESTPhase0BlockToProto(&apimiddleware.BeaconBlockJson{
|
||||
Slot: block.Slot,
|
||||
ProposerIndex: block.ProposerIndex,
|
||||
ParentRoot: block.ParentRoot,
|
||||
StateRoot: block.StateRoot,
|
||||
Body: &apimiddleware.BeaconBlockBodyJson{
|
||||
RandaoReveal: block.Body.RandaoReveal,
|
||||
Eth1Data: block.Body.Eth1Data,
|
||||
Graffiti: block.Body.Graffiti,
|
||||
ProposerSlashings: block.Body.ProposerSlashings,
|
||||
AttesterSlashings: block.Body.AttesterSlashings,
|
||||
Attestations: block.Body.Attestations,
|
||||
Deposits: block.Body.Deposits,
|
||||
VoluntaryExits: block.Body.VoluntaryExits,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get the phase0 fields of the altair block")
|
||||
}
|
||||
|
||||
if block.Body.SyncAggregate == nil {
|
||||
return nil, errors.New("sync aggregate is nil")
|
||||
}
|
||||
|
||||
syncCommitteeBits, err := hexutil.Decode(block.Body.SyncAggregate.SyncCommitteeBits)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode sync committee bits `%s`", block.Body.SyncAggregate.SyncCommitteeBits)
|
||||
}
|
||||
|
||||
syncCommitteeSignature, err := hexutil.Decode(block.Body.SyncAggregate.SyncCommitteeSignature)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode sync committee signature `%s`", block.Body.SyncAggregate.SyncCommitteeSignature)
|
||||
}
|
||||
|
||||
return ðpb.GenericBeaconBlock_Altair{
|
||||
Altair: ðpb.BeaconBlockAltair{
|
||||
Slot: phase0Block.Phase0.Slot,
|
||||
ProposerIndex: phase0Block.Phase0.ProposerIndex,
|
||||
ParentRoot: phase0Block.Phase0.ParentRoot,
|
||||
StateRoot: phase0Block.Phase0.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyAltair{
|
||||
RandaoReveal: phase0Block.Phase0.Body.RandaoReveal,
|
||||
Eth1Data: phase0Block.Phase0.Body.Eth1Data,
|
||||
Graffiti: phase0Block.Phase0.Body.Graffiti,
|
||||
ProposerSlashings: phase0Block.Phase0.Body.ProposerSlashings,
|
||||
AttesterSlashings: phase0Block.Phase0.Body.AttesterSlashings,
|
||||
Attestations: phase0Block.Phase0.Body.Attestations,
|
||||
Deposits: phase0Block.Phase0.Body.Deposits,
|
||||
VoluntaryExits: phase0Block.Phase0.Body.VoluntaryExits,
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: syncCommitteeBits,
|
||||
SyncCommitteeSignature: syncCommitteeSignature,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertRESTBellatrixBlockToProto(block *apimiddleware.BeaconBlockBellatrixJson) (*ethpb.GenericBeaconBlock_Bellatrix, error) {
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
// Call convertRESTAltairBlockToProto to set the altair fields because all the error handling and the heavy lifting
|
||||
// has already been done
|
||||
altairBlock, err := convertRESTAltairBlockToProto(&apimiddleware.BeaconBlockAltairJson{
|
||||
Slot: block.Slot,
|
||||
ProposerIndex: block.ProposerIndex,
|
||||
ParentRoot: block.ParentRoot,
|
||||
StateRoot: block.StateRoot,
|
||||
Body: &apimiddleware.BeaconBlockBodyAltairJson{
|
||||
RandaoReveal: block.Body.RandaoReveal,
|
||||
Eth1Data: block.Body.Eth1Data,
|
||||
Graffiti: block.Body.Graffiti,
|
||||
ProposerSlashings: block.Body.ProposerSlashings,
|
||||
AttesterSlashings: block.Body.AttesterSlashings,
|
||||
Attestations: block.Body.Attestations,
|
||||
Deposits: block.Body.Deposits,
|
||||
VoluntaryExits: block.Body.VoluntaryExits,
|
||||
SyncAggregate: block.Body.SyncAggregate,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get the altair fields of the bellatrix block")
|
||||
}
|
||||
|
||||
if block.Body.ExecutionPayload == nil {
|
||||
return nil, errors.New("execution payload is nil")
|
||||
}
|
||||
|
||||
parentHash, err := hexutil.Decode(block.Body.ExecutionPayload.ParentHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload parent hash `%s`", block.Body.ExecutionPayload.ParentHash)
|
||||
}
|
||||
|
||||
feeRecipient, err := hexutil.Decode(block.Body.ExecutionPayload.FeeRecipient)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload fee recipient `%s`", block.Body.ExecutionPayload.FeeRecipient)
|
||||
}
|
||||
|
||||
stateRoot, err := hexutil.Decode(block.Body.ExecutionPayload.StateRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload state root `%s`", block.Body.ExecutionPayload.StateRoot)
|
||||
}
|
||||
|
||||
receiptsRoot, err := hexutil.Decode(block.Body.ExecutionPayload.ReceiptsRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload receipts root `%s`", block.Body.ExecutionPayload.ReceiptsRoot)
|
||||
}
|
||||
|
||||
logsBloom, err := hexutil.Decode(block.Body.ExecutionPayload.LogsBloom)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload logs bloom `%s`", block.Body.ExecutionPayload.LogsBloom)
|
||||
}
|
||||
|
||||
prevRandao, err := hexutil.Decode(block.Body.ExecutionPayload.PrevRandao)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload prev randao `%s`", block.Body.ExecutionPayload.PrevRandao)
|
||||
}
|
||||
|
||||
blockNumber, err := strconv.ParseUint(block.Body.ExecutionPayload.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload block number `%s`", block.Body.ExecutionPayload.BlockNumber)
|
||||
}
|
||||
|
||||
gasLimit, err := strconv.ParseUint(block.Body.ExecutionPayload.GasLimit, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload gas limit `%s`", block.Body.ExecutionPayload.GasLimit)
|
||||
}
|
||||
|
||||
gasUsed, err := strconv.ParseUint(block.Body.ExecutionPayload.GasUsed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload gas used `%s`", block.Body.ExecutionPayload.GasUsed)
|
||||
}
|
||||
|
||||
timestamp, err := strconv.ParseUint(block.Body.ExecutionPayload.TimeStamp, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse execution payload timestamp `%s`", block.Body.ExecutionPayload.TimeStamp)
|
||||
}
|
||||
|
||||
extraData, err := hexutil.Decode(block.Body.ExecutionPayload.ExtraData)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload extra data `%s`", block.Body.ExecutionPayload.ExtraData)
|
||||
}
|
||||
|
||||
baseFeePerGas := new(big.Int)
|
||||
if _, ok := baseFeePerGas.SetString(block.Body.ExecutionPayload.BaseFeePerGas, 10); !ok {
|
||||
return nil, errors.Errorf("failed to parse execution payload base fee per gas `%s`", block.Body.ExecutionPayload.BaseFeePerGas)
|
||||
}
|
||||
|
||||
blockHash, err := hexutil.Decode(block.Body.ExecutionPayload.BlockHash)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode execution payload block hash `%s`", block.Body.ExecutionPayload.BlockHash)
|
||||
}
|
||||
|
||||
transactions, err := convertTransactionsToProto(block.Body.ExecutionPayload.Transactions)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get execution payload transactions")
|
||||
}
|
||||
|
||||
return ðpb.GenericBeaconBlock_Bellatrix{
|
||||
Bellatrix: ðpb.BeaconBlockBellatrix{
|
||||
Slot: altairBlock.Altair.Slot,
|
||||
ProposerIndex: altairBlock.Altair.ProposerIndex,
|
||||
ParentRoot: altairBlock.Altair.ParentRoot,
|
||||
StateRoot: altairBlock.Altair.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: altairBlock.Altair.Body.RandaoReveal,
|
||||
Eth1Data: altairBlock.Altair.Body.Eth1Data,
|
||||
Graffiti: altairBlock.Altair.Body.Graffiti,
|
||||
ProposerSlashings: altairBlock.Altair.Body.ProposerSlashings,
|
||||
AttesterSlashings: altairBlock.Altair.Body.AttesterSlashings,
|
||||
Attestations: altairBlock.Altair.Body.Attestations,
|
||||
Deposits: altairBlock.Altair.Body.Deposits,
|
||||
VoluntaryExits: altairBlock.Altair.Body.VoluntaryExits,
|
||||
SyncAggregate: altairBlock.Altair.Body.SyncAggregate,
|
||||
ExecutionPayload: &enginev1.ExecutionPayload{
|
||||
ParentHash: parentHash,
|
||||
FeeRecipient: feeRecipient,
|
||||
StateRoot: stateRoot,
|
||||
ReceiptsRoot: receiptsRoot,
|
||||
LogsBloom: logsBloom,
|
||||
PrevRandao: prevRandao,
|
||||
BlockNumber: blockNumber,
|
||||
GasLimit: gasLimit,
|
||||
GasUsed: gasUsed,
|
||||
Timestamp: timestamp,
|
||||
ExtraData: extraData,
|
||||
BaseFeePerGas: bytesutil.PadTo(bytesutil.BigIntToLittleEndianBytes(baseFeePerGas), 32),
|
||||
BlockHash: blockHash,
|
||||
Transactions: transactions,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertRESTCapellaBlockToProto(block *apimiddleware.BeaconBlockCapellaJson) (*ethpb.GenericBeaconBlock_Capella, error) {
|
||||
if block.Body == nil {
|
||||
return nil, errors.New("block body is nil")
|
||||
}
|
||||
|
||||
if block.Body.ExecutionPayload == nil {
|
||||
return nil, errors.New("execution payload is nil")
|
||||
}
|
||||
|
||||
// Call convertRESTBellatrixBlockToProto to set the bellatrix fields because all the error handling and the heavy
|
||||
// lifting has already been done
|
||||
bellatrixBlock, err := convertRESTBellatrixBlockToProto(&apimiddleware.BeaconBlockBellatrixJson{
|
||||
Slot: block.Slot,
|
||||
ProposerIndex: block.ProposerIndex,
|
||||
ParentRoot: block.ParentRoot,
|
||||
StateRoot: block.StateRoot,
|
||||
Body: &apimiddleware.BeaconBlockBodyBellatrixJson{
|
||||
RandaoReveal: block.Body.RandaoReveal,
|
||||
Eth1Data: block.Body.Eth1Data,
|
||||
Graffiti: block.Body.Graffiti,
|
||||
ProposerSlashings: block.Body.ProposerSlashings,
|
||||
AttesterSlashings: block.Body.AttesterSlashings,
|
||||
Attestations: block.Body.Attestations,
|
||||
Deposits: block.Body.Deposits,
|
||||
VoluntaryExits: block.Body.VoluntaryExits,
|
||||
SyncAggregate: block.Body.SyncAggregate,
|
||||
ExecutionPayload: &apimiddleware.ExecutionPayloadJson{
|
||||
ParentHash: block.Body.ExecutionPayload.ParentHash,
|
||||
FeeRecipient: block.Body.ExecutionPayload.FeeRecipient,
|
||||
StateRoot: block.Body.ExecutionPayload.StateRoot,
|
||||
ReceiptsRoot: block.Body.ExecutionPayload.ReceiptsRoot,
|
||||
LogsBloom: block.Body.ExecutionPayload.LogsBloom,
|
||||
PrevRandao: block.Body.ExecutionPayload.PrevRandao,
|
||||
BlockNumber: block.Body.ExecutionPayload.BlockNumber,
|
||||
GasLimit: block.Body.ExecutionPayload.GasLimit,
|
||||
GasUsed: block.Body.ExecutionPayload.GasUsed,
|
||||
TimeStamp: block.Body.ExecutionPayload.TimeStamp,
|
||||
ExtraData: block.Body.ExecutionPayload.ExtraData,
|
||||
BaseFeePerGas: block.Body.ExecutionPayload.BaseFeePerGas,
|
||||
BlockHash: block.Body.ExecutionPayload.BlockHash,
|
||||
Transactions: block.Body.ExecutionPayload.Transactions,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get the bellatrix fields of the capella block")
|
||||
}
|
||||
|
||||
withdrawals, err := convertWithdrawalsToProto(block.Body.ExecutionPayload.Withdrawals)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get withdrawals")
|
||||
}
|
||||
|
||||
blsToExecutionChanges, err := convertBlsToExecutionChangesToProto(block.Body.BLSToExecutionChanges)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get bls to execution changes")
|
||||
}
|
||||
|
||||
return ðpb.GenericBeaconBlock_Capella{
|
||||
Capella: ðpb.BeaconBlockCapella{
|
||||
Slot: bellatrixBlock.Bellatrix.Slot,
|
||||
ProposerIndex: bellatrixBlock.Bellatrix.ProposerIndex,
|
||||
ParentRoot: bellatrixBlock.Bellatrix.ParentRoot,
|
||||
StateRoot: bellatrixBlock.Bellatrix.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyCapella{
|
||||
RandaoReveal: bellatrixBlock.Bellatrix.Body.RandaoReveal,
|
||||
Eth1Data: bellatrixBlock.Bellatrix.Body.Eth1Data,
|
||||
Graffiti: bellatrixBlock.Bellatrix.Body.Graffiti,
|
||||
ProposerSlashings: bellatrixBlock.Bellatrix.Body.ProposerSlashings,
|
||||
AttesterSlashings: bellatrixBlock.Bellatrix.Body.AttesterSlashings,
|
||||
Attestations: bellatrixBlock.Bellatrix.Body.Attestations,
|
||||
Deposits: bellatrixBlock.Bellatrix.Body.Deposits,
|
||||
VoluntaryExits: bellatrixBlock.Bellatrix.Body.VoluntaryExits,
|
||||
SyncAggregate: bellatrixBlock.Bellatrix.Body.SyncAggregate,
|
||||
ExecutionPayload: &enginev1.ExecutionPayloadCapella{
|
||||
ParentHash: bellatrixBlock.Bellatrix.Body.ExecutionPayload.ParentHash,
|
||||
FeeRecipient: bellatrixBlock.Bellatrix.Body.ExecutionPayload.FeeRecipient,
|
||||
StateRoot: bellatrixBlock.Bellatrix.Body.ExecutionPayload.StateRoot,
|
||||
ReceiptsRoot: bellatrixBlock.Bellatrix.Body.ExecutionPayload.ReceiptsRoot,
|
||||
LogsBloom: bellatrixBlock.Bellatrix.Body.ExecutionPayload.LogsBloom,
|
||||
PrevRandao: bellatrixBlock.Bellatrix.Body.ExecutionPayload.PrevRandao,
|
||||
BlockNumber: bellatrixBlock.Bellatrix.Body.ExecutionPayload.BlockNumber,
|
||||
GasLimit: bellatrixBlock.Bellatrix.Body.ExecutionPayload.GasLimit,
|
||||
GasUsed: bellatrixBlock.Bellatrix.Body.ExecutionPayload.GasUsed,
|
||||
Timestamp: bellatrixBlock.Bellatrix.Body.ExecutionPayload.Timestamp,
|
||||
ExtraData: bellatrixBlock.Bellatrix.Body.ExecutionPayload.ExtraData,
|
||||
BaseFeePerGas: bellatrixBlock.Bellatrix.Body.ExecutionPayload.BaseFeePerGas,
|
||||
BlockHash: bellatrixBlock.Bellatrix.Body.ExecutionPayload.BlockHash,
|
||||
Transactions: bellatrixBlock.Bellatrix.Body.ExecutionPayload.Transactions,
|
||||
Withdrawals: withdrawals,
|
||||
},
|
||||
BlsToExecutionChanges: blsToExecutionChanges,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -1,151 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/test-helpers"
|
||||
)
|
||||
|
||||
func TestGetBeaconBlock_AltairValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
altairProtoBeaconBlock := test_helpers.GenerateProtoAltairBeaconBlock()
|
||||
altairBeaconBlockBytes, err := json.Marshal(test_helpers.GenerateJsonAltairBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
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: "altair",
|
||||
Data: altairBeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
expectedBeaconBlock := generateProtoAltairBlock(altairProtoBeaconBlock)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_AltairError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockAltairJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil sync aggregate",
|
||||
expectedErrorMessage: "sync aggregate is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.SyncAggregate = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad phase0 fields",
|
||||
expectedErrorMessage: "failed to get the phase0 fields of the altair block",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad sync committee bits",
|
||||
expectedErrorMessage: "failed to decode sync committee bits `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.SyncAggregate.SyncCommitteeBits = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad sync committee signature",
|
||||
expectedErrorMessage: "failed to decode sync committee signature `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockAltairJson {
|
||||
beaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
beaconBlock.Body.SyncAggregate.SyncCommitteeSignature = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dataBytes, err := json.Marshal(testCase.generateData())
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
||||
ctx,
|
||||
gomock.Any(),
|
||||
&abstractProduceBlockResponseJson{},
|
||||
).SetArg(
|
||||
2,
|
||||
abstractProduceBlockResponseJson{
|
||||
Version: "altair",
|
||||
Data: dataBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err = validatorClient.getBeaconBlock(ctx, 1, []byte{1}, []byte{2})
|
||||
assert.ErrorContains(t, "failed to get altair block", err)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateProtoAltairBlock(altairProtoBeaconBlock *ethpb.BeaconBlockAltair) *ethpb.GenericBeaconBlock {
|
||||
return ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Altair{
|
||||
Altair: altairProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/test-helpers"
|
||||
)
|
||||
|
||||
func TestGetBeaconBlock_BellatrixValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
bellatrixProtoBeaconBlock := test_helpers.GenerateProtoBellatrixBeaconBlock()
|
||||
bellatrixBeaconBlockBytes, err := json.Marshal(test_helpers.GenerateJsonBellatrixBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
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: "bellatrix",
|
||||
Data: bellatrixBeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
expectedBeaconBlock := generateProtoBellatrixBlock(bellatrixProtoBeaconBlock)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_BellatrixError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockBellatrixJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil execution payload",
|
||||
expectedErrorMessage: "execution payload is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad altair fields",
|
||||
expectedErrorMessage: "failed to get the altair fields of the bellatrix block",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad parent hash",
|
||||
expectedErrorMessage: "failed to decode execution payload parent hash `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.ParentHash = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad fee recipient",
|
||||
expectedErrorMessage: "failed to decode execution payload fee recipient `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.FeeRecipient = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad state root",
|
||||
expectedErrorMessage: "failed to decode execution payload state root `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.StateRoot = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad receipts root",
|
||||
expectedErrorMessage: "failed to decode execution payload receipts root `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.ReceiptsRoot = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad logs bloom",
|
||||
expectedErrorMessage: "failed to decode execution payload logs bloom `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.LogsBloom = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad prev randao",
|
||||
expectedErrorMessage: "failed to decode execution payload prev randao `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.PrevRandao = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad block number",
|
||||
expectedErrorMessage: "failed to parse execution payload block number `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.BlockNumber = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad gas limit",
|
||||
expectedErrorMessage: "failed to parse execution payload gas limit `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.GasLimit = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad gas used",
|
||||
expectedErrorMessage: "failed to parse execution payload gas used `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.GasUsed = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad timestamp",
|
||||
expectedErrorMessage: "failed to parse execution payload timestamp `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.TimeStamp = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad extra data",
|
||||
expectedErrorMessage: "failed to decode execution payload extra data `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.ExtraData = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad base fee per gas",
|
||||
expectedErrorMessage: "failed to parse execution payload base fee per gas `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.BaseFeePerGas = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad block hash",
|
||||
expectedErrorMessage: "failed to decode execution payload block hash `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.BlockHash = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad transactions",
|
||||
expectedErrorMessage: "failed to get execution payload transactions",
|
||||
generateData: func() *apimiddleware.BeaconBlockBellatrixJson {
|
||||
beaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.Transactions[0] = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
dataBytes, err := json.Marshal(testCase.generateData())
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
||||
ctx,
|
||||
gomock.Any(),
|
||||
&abstractProduceBlockResponseJson{},
|
||||
).SetArg(
|
||||
2,
|
||||
abstractProduceBlockResponseJson{
|
||||
Version: "bellatrix",
|
||||
Data: dataBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err = validatorClient.getBeaconBlock(ctx, 1, []byte{1}, []byte{2})
|
||||
assert.ErrorContains(t, "failed to get bellatrix block", err)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateProtoBellatrixBlock(bellatrixProtoBeaconBlock *ethpb.BeaconBlockBellatrix) *ethpb.GenericBeaconBlock {
|
||||
return ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Bellatrix{
|
||||
Bellatrix: bellatrixProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/test-helpers"
|
||||
)
|
||||
|
||||
func TestGetBeaconBlock_CapellaValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
capellaProtoBeaconBlock := test_helpers.GenerateProtoCapellaBeaconBlock()
|
||||
capellaBeaconBlockBytes, err := json.Marshal(test_helpers.GenerateJsonCapellaBeaconBlock())
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
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: "capella",
|
||||
Data: capellaBeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
expectedBeaconBlock := generateProtoCapellaBlock(capellaProtoBeaconBlock)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_CapellaError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockCapellaJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil execution payload",
|
||||
expectedErrorMessage: "execution payload is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad bellatrix fields",
|
||||
expectedErrorMessage: "failed to get the bellatrix fields of the capella block",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad withdrawals",
|
||||
expectedErrorMessage: "failed to get withdrawals",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.ExecutionPayload.Withdrawals[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad bls execution changes",
|
||||
expectedErrorMessage: "failed to get bls to execution changes",
|
||||
generateData: func() *apimiddleware.BeaconBlockCapellaJson {
|
||||
beaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
beaconBlock.Body.BLSToExecutionChanges[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dataBytes, err := json.Marshal(testCase.generateData())
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
||||
context.Background(),
|
||||
gomock.Any(),
|
||||
&abstractProduceBlockResponseJson{},
|
||||
).SetArg(
|
||||
2,
|
||||
abstractProduceBlockResponseJson{
|
||||
Version: "capella",
|
||||
Data: dataBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err = validatorClient.getBeaconBlock(context.Background(), 1, []byte{1}, []byte{2})
|
||||
assert.ErrorContains(t, "failed to get capella block", err)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateProtoCapellaBlock(capellaProtoBeaconBlock *ethpb.BeaconBlockCapella) *ethpb.GenericBeaconBlock {
|
||||
return ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Capella{
|
||||
Capella: capellaProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/test-helpers"
|
||||
)
|
||||
|
||||
func TestGetBeaconBlock_Phase0Valid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
phase0ProtoBeaconBlock := test_helpers.GenerateProtoPhase0BeaconBlock()
|
||||
phase0BeaconBlockBytes, err := json.Marshal(test_helpers.GenerateJsonPhase0BeaconBlock())
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
ctx := context.Background()
|
||||
|
||||
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: "phase0",
|
||||
Data: phase0BeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
expectedBeaconBlock := generateProtoPhase0Block(phase0ProtoBeaconBlock)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_Phase0Error(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expectedErrorMessage string
|
||||
generateData func() *apimiddleware.BeaconBlockJson
|
||||
}{
|
||||
{
|
||||
name: "nil body",
|
||||
expectedErrorMessage: "block body is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil eth1 data",
|
||||
expectedErrorMessage: "eth1 data is nil",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad slot",
|
||||
expectedErrorMessage: "failed to parse slot `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Slot = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad proposer index",
|
||||
expectedErrorMessage: "failed to parse proposer index `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.ProposerIndex = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad parent root",
|
||||
expectedErrorMessage: "failed to decode parent root `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.ParentRoot = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad state root",
|
||||
expectedErrorMessage: "failed to decode state root `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.StateRoot = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad randao reveal",
|
||||
expectedErrorMessage: "failed to decode randao reveal `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.RandaoReveal = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad deposit root",
|
||||
expectedErrorMessage: "failed to decode deposit root `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data.DepositRoot = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad deposit count",
|
||||
expectedErrorMessage: "failed to parse deposit count `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data.DepositCount = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad block hash",
|
||||
expectedErrorMessage: "failed to decode block hash `bar`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Eth1Data.BlockHash = "bar"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad graffiti",
|
||||
expectedErrorMessage: "failed to decode graffiti `foo`",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Graffiti = "foo"
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad proposer slashings",
|
||||
expectedErrorMessage: "failed to get proposer slashings",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.ProposerSlashings[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad attester slashings",
|
||||
expectedErrorMessage: "failed to get attester slashings",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.AttesterSlashings[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad attestations",
|
||||
expectedErrorMessage: "failed to get attestations",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Attestations[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad deposits",
|
||||
expectedErrorMessage: "failed to get deposits",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.Deposits[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bad voluntary exits",
|
||||
expectedErrorMessage: "failed to get voluntary exits",
|
||||
generateData: func() *apimiddleware.BeaconBlockJson {
|
||||
beaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
beaconBlock.Body.VoluntaryExits[0] = nil
|
||||
return beaconBlock
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
dataBytes, err := json.Marshal(testCase.generateData())
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().GetRestJsonResponse(
|
||||
ctx,
|
||||
gomock.Any(),
|
||||
&abstractProduceBlockResponseJson{},
|
||||
).SetArg(
|
||||
2,
|
||||
abstractProduceBlockResponseJson{
|
||||
Version: "phase0",
|
||||
Data: dataBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err = validatorClient.getBeaconBlock(ctx, 1, []byte{1}, []byte{2})
|
||||
assert.ErrorContains(t, "failed to get phase0 block", err)
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateProtoPhase0Block(phase0ProtoBeaconBlock *ethpb.BeaconBlock) *ethpb.GenericBeaconBlock {
|
||||
return ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Phase0{
|
||||
Phase0: phase0ProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
}
|
@ -4,13 +4,18 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/test-helpers"
|
||||
)
|
||||
|
||||
func TestGetBeaconBlock_RequestFailed(t *testing.T) {
|
||||
@ -132,9 +137,245 @@ func TestGetBeaconBlock_Error(t *testing.T) {
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
||||
beaconBlockConverter.EXPECT().ConvertRESTPhase0BlockToProto(
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil,
|
||||
errors.New(testCase.expectedErrorMessage),
|
||||
).AnyTimes()
|
||||
|
||||
beaconBlockConverter.EXPECT().ConvertRESTAltairBlockToProto(
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil,
|
||||
errors.New(testCase.expectedErrorMessage),
|
||||
).AnyTimes()
|
||||
|
||||
beaconBlockConverter.EXPECT().ConvertRESTBellatrixBlockToProto(
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil,
|
||||
errors.New(testCase.expectedErrorMessage),
|
||||
).AnyTimes()
|
||||
|
||||
beaconBlockConverter.EXPECT().ConvertRESTCapellaBlockToProto(
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil,
|
||||
errors.New(testCase.expectedErrorMessage),
|
||||
).AnyTimes()
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler, beaconBlockConverter: beaconBlockConverter}
|
||||
_, err := validatorClient.getBeaconBlock(ctx, 1, []byte{1}, []byte{2})
|
||||
assert.ErrorContains(t, testCase.expectedErrorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_Phase0Valid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
phase0ProtoBeaconBlock := test_helpers.GenerateProtoPhase0BeaconBlock()
|
||||
phase0BeaconBlock := test_helpers.GenerateJsonPhase0BeaconBlock()
|
||||
phase0BeaconBlockBytes, err := json.Marshal(phase0BeaconBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
ctx := context.Background()
|
||||
|
||||
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: "phase0",
|
||||
Data: phase0BeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
||||
beaconBlockConverter.EXPECT().ConvertRESTPhase0BlockToProto(
|
||||
phase0BeaconBlock,
|
||||
).Return(
|
||||
phase0ProtoBeaconBlock,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler, beaconBlockConverter: beaconBlockConverter}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Phase0{
|
||||
Phase0: phase0ProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_AltairValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
altairProtoBeaconBlock := test_helpers.GenerateProtoAltairBeaconBlock()
|
||||
altairBeaconBlock := test_helpers.GenerateJsonAltairBeaconBlock()
|
||||
altairBeaconBlockBytes, err := json.Marshal(altairBeaconBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
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: "altair",
|
||||
Data: altairBeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
||||
beaconBlockConverter.EXPECT().ConvertRESTAltairBlockToProto(
|
||||
altairBeaconBlock,
|
||||
).Return(
|
||||
altairProtoBeaconBlock,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler, beaconBlockConverter: beaconBlockConverter}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Altair{
|
||||
Altair: altairProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_BellatrixValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
bellatrixProtoBeaconBlock := test_helpers.GenerateProtoBellatrixBeaconBlock()
|
||||
bellatrixBeaconBlock := test_helpers.GenerateJsonBellatrixBeaconBlock()
|
||||
bellatrixBeaconBlockBytes, err := json.Marshal(bellatrixBeaconBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
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: "bellatrix",
|
||||
Data: bellatrixBeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
||||
beaconBlockConverter.EXPECT().ConvertRESTBellatrixBlockToProto(
|
||||
bellatrixBeaconBlock,
|
||||
).Return(
|
||||
bellatrixProtoBeaconBlock,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler, beaconBlockConverter: beaconBlockConverter}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Bellatrix{
|
||||
Bellatrix: bellatrixProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
||||
func TestGetBeaconBlock_CapellaValid(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
capellaProtoBeaconBlock := test_helpers.GenerateProtoCapellaBeaconBlock()
|
||||
capellaBeaconBlock := test_helpers.GenerateJsonCapellaBeaconBlock()
|
||||
capellaBeaconBlockBytes, err := json.Marshal(capellaBeaconBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
const slot = primitives.Slot(1)
|
||||
randaoReveal := []byte{2}
|
||||
graffiti := []byte{3}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
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: "capella",
|
||||
Data: capellaBeaconBlockBytes,
|
||||
},
|
||||
).Return(
|
||||
nil,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
beaconBlockConverter := mock.NewMockbeaconBlockConverter(ctrl)
|
||||
beaconBlockConverter.EXPECT().ConvertRESTCapellaBlockToProto(
|
||||
capellaBeaconBlock,
|
||||
).Return(
|
||||
capellaProtoBeaconBlock,
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler, beaconBlockConverter: beaconBlockConverter}
|
||||
beaconBlock, err := validatorClient.getBeaconBlock(ctx, slot, randaoReveal, graffiti)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedBeaconBlock := ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Capella{
|
||||
Capella: capellaProtoBeaconBlock,
|
||||
},
|
||||
}
|
||||
|
||||
assert.DeepEqual(t, expectedBeaconBlock, beaconBlock)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"beacon_block_converter_mock.go",
|
||||
"duties_mock.go",
|
||||
"genesis_mock.go",
|
||||
"json_rest_handler_mock.go",
|
||||
@ -14,6 +15,7 @@ go_library(
|
||||
"//api/gateway/apimiddleware:go_default_library",
|
||||
"//beacon-chain/rpc/apimiddleware:go_default_library",
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_golang_mock//gomock:go_default_library",
|
||||
],
|
||||
)
|
||||
|
96
validator/client/beacon-api/mock/beacon_block_converter_mock.go
generated
Normal file
96
validator/client/beacon-api/mock/beacon_block_converter_mock.go
generated
Normal file
@ -0,0 +1,96 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: validator/client/beacon-api/beacon_block_converter.go
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
apimiddleware "github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// MockbeaconBlockConverter is a mock of beaconBlockConverter interface.
|
||||
type MockbeaconBlockConverter struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockbeaconBlockConverterMockRecorder
|
||||
}
|
||||
|
||||
// MockbeaconBlockConverterMockRecorder is the mock recorder for MockbeaconBlockConverter.
|
||||
type MockbeaconBlockConverterMockRecorder struct {
|
||||
mock *MockbeaconBlockConverter
|
||||
}
|
||||
|
||||
// NewMockbeaconBlockConverter creates a new mock instance.
|
||||
func NewMockbeaconBlockConverter(ctrl *gomock.Controller) *MockbeaconBlockConverter {
|
||||
mock := &MockbeaconBlockConverter{ctrl: ctrl}
|
||||
mock.recorder = &MockbeaconBlockConverterMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockbeaconBlockConverter) EXPECT() *MockbeaconBlockConverterMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// ConvertRESTAltairBlockToProto mocks base method.
|
||||
func (m *MockbeaconBlockConverter) ConvertRESTAltairBlockToProto(block *apimiddleware.BeaconBlockAltairJson) (*eth.BeaconBlockAltair, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ConvertRESTAltairBlockToProto", block)
|
||||
ret0, _ := ret[0].(*eth.BeaconBlockAltair)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ConvertRESTAltairBlockToProto indicates an expected call of ConvertRESTAltairBlockToProto.
|
||||
func (mr *MockbeaconBlockConverterMockRecorder) ConvertRESTAltairBlockToProto(block interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConvertRESTAltairBlockToProto", reflect.TypeOf((*MockbeaconBlockConverter)(nil).ConvertRESTAltairBlockToProto), block)
|
||||
}
|
||||
|
||||
// ConvertRESTBellatrixBlockToProto mocks base method.
|
||||
func (m *MockbeaconBlockConverter) ConvertRESTBellatrixBlockToProto(block *apimiddleware.BeaconBlockBellatrixJson) (*eth.BeaconBlockBellatrix, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ConvertRESTBellatrixBlockToProto", block)
|
||||
ret0, _ := ret[0].(*eth.BeaconBlockBellatrix)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ConvertRESTBellatrixBlockToProto indicates an expected call of ConvertRESTBellatrixBlockToProto.
|
||||
func (mr *MockbeaconBlockConverterMockRecorder) ConvertRESTBellatrixBlockToProto(block interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConvertRESTBellatrixBlockToProto", reflect.TypeOf((*MockbeaconBlockConverter)(nil).ConvertRESTBellatrixBlockToProto), block)
|
||||
}
|
||||
|
||||
// ConvertRESTCapellaBlockToProto mocks base method.
|
||||
func (m *MockbeaconBlockConverter) ConvertRESTCapellaBlockToProto(block *apimiddleware.BeaconBlockCapellaJson) (*eth.BeaconBlockCapella, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ConvertRESTCapellaBlockToProto", block)
|
||||
ret0, _ := ret[0].(*eth.BeaconBlockCapella)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ConvertRESTCapellaBlockToProto indicates an expected call of ConvertRESTCapellaBlockToProto.
|
||||
func (mr *MockbeaconBlockConverterMockRecorder) ConvertRESTCapellaBlockToProto(block interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConvertRESTCapellaBlockToProto", reflect.TypeOf((*MockbeaconBlockConverter)(nil).ConvertRESTCapellaBlockToProto), block)
|
||||
}
|
||||
|
||||
// ConvertRESTPhase0BlockToProto mocks base method.
|
||||
func (m *MockbeaconBlockConverter) ConvertRESTPhase0BlockToProto(block *apimiddleware.BeaconBlockJson) (*eth.BeaconBlock, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ConvertRESTPhase0BlockToProto", block)
|
||||
ret0, _ := ret[0].(*eth.BeaconBlock)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ConvertRESTPhase0BlockToProto indicates an expected call of ConvertRESTPhase0BlockToProto.
|
||||
func (mr *MockbeaconBlockConverterMockRecorder) ConvertRESTPhase0BlockToProto(block interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConvertRESTPhase0BlockToProto", reflect.TypeOf((*MockbeaconBlockConverter)(nil).ConvertRESTPhase0BlockToProto), block)
|
||||
}
|
10
validator/client/beacon-api/mock/duties_mock.go
generated
10
validator/client/beacon-api/mock/duties_mock.go
generated
@ -10,7 +10,7 @@ import (
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
apimiddleware "github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
primitives "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
)
|
||||
|
||||
// MockdutiesProvider is a mock of dutiesProvider interface.
|
||||
@ -37,7 +37,7 @@ func (m *MockdutiesProvider) EXPECT() *MockdutiesProviderMockRecorder {
|
||||
}
|
||||
|
||||
// GetAttesterDuties mocks base method.
|
||||
func (m *MockdutiesProvider) GetAttesterDuties(ctx context.Context, epoch types.Epoch, validatorIndices []types.ValidatorIndex) ([]*apimiddleware.AttesterDutyJson, error) {
|
||||
func (m *MockdutiesProvider) GetAttesterDuties(ctx context.Context, epoch primitives.Epoch, validatorIndices []primitives.ValidatorIndex) ([]*apimiddleware.AttesterDutyJson, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAttesterDuties", ctx, epoch, validatorIndices)
|
||||
ret0, _ := ret[0].([]*apimiddleware.AttesterDutyJson)
|
||||
@ -52,7 +52,7 @@ func (mr *MockdutiesProviderMockRecorder) GetAttesterDuties(ctx, epoch, validato
|
||||
}
|
||||
|
||||
// GetCommittees mocks base method.
|
||||
func (m *MockdutiesProvider) GetCommittees(ctx context.Context, epoch types.Epoch) ([]*apimiddleware.CommitteeJson, error) {
|
||||
func (m *MockdutiesProvider) GetCommittees(ctx context.Context, epoch primitives.Epoch) ([]*apimiddleware.CommitteeJson, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCommittees", ctx, epoch)
|
||||
ret0, _ := ret[0].([]*apimiddleware.CommitteeJson)
|
||||
@ -67,7 +67,7 @@ func (mr *MockdutiesProviderMockRecorder) GetCommittees(ctx, epoch interface{})
|
||||
}
|
||||
|
||||
// GetProposerDuties mocks base method.
|
||||
func (m *MockdutiesProvider) GetProposerDuties(ctx context.Context, epoch types.Epoch) ([]*apimiddleware.ProposerDutyJson, error) {
|
||||
func (m *MockdutiesProvider) GetProposerDuties(ctx context.Context, epoch primitives.Epoch) ([]*apimiddleware.ProposerDutyJson, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetProposerDuties", ctx, epoch)
|
||||
ret0, _ := ret[0].([]*apimiddleware.ProposerDutyJson)
|
||||
@ -82,7 +82,7 @@ func (mr *MockdutiesProviderMockRecorder) GetProposerDuties(ctx, epoch interface
|
||||
}
|
||||
|
||||
// GetSyncDuties mocks base method.
|
||||
func (m *MockdutiesProvider) GetSyncDuties(ctx context.Context, epoch types.Epoch, validatorIndices []types.ValidatorIndex) ([]*apimiddleware.SyncCommitteeDuty, error) {
|
||||
func (m *MockdutiesProvider) GetSyncDuties(ctx context.Context, epoch primitives.Epoch, validatorIndices []primitives.ValidatorIndex) ([]*apimiddleware.SyncCommitteeDuty, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetSyncDuties", ctx, epoch, validatorIndices)
|
||||
ret0, _ := ret[0].([]*apimiddleware.SyncCommitteeDuty)
|
||||
|
196
validator/client/beacon-api/stream_blocks.go
Normal file
196
validator/client/beacon-api/stream_blocks.go
Normal file
@ -0,0 +1,196 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type abstractSignedBlockResponseJson struct {
|
||||
Version string `json:"version" enum:"true"`
|
||||
ExecutionOptimistic bool `json:"execution_optimistic"`
|
||||
Finalized bool `json:"finalized"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
type streamBlocksAltairClient struct {
|
||||
grpc.ClientStream
|
||||
ctx context.Context
|
||||
beaconApiClient beaconApiValidatorClient
|
||||
streamBlocksRequest *ethpb.StreamBlocksRequest
|
||||
prevBlockSlot primitives.Slot
|
||||
pingDelay time.Duration
|
||||
}
|
||||
|
||||
type headSignedBeaconBlockResult struct {
|
||||
streamBlocksResponse *ethpb.StreamBlocksResponse
|
||||
executionOptimistic bool
|
||||
slot primitives.Slot
|
||||
}
|
||||
|
||||
func (c beaconApiValidatorClient) streamBlocks(ctx context.Context, in *ethpb.StreamBlocksRequest, pingDelay time.Duration) ethpb.BeaconNodeValidator_StreamBlocksAltairClient {
|
||||
return &streamBlocksAltairClient{
|
||||
ctx: ctx,
|
||||
beaconApiClient: c,
|
||||
streamBlocksRequest: in,
|
||||
pingDelay: pingDelay,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *streamBlocksAltairClient) Recv() (*ethpb.StreamBlocksResponse, error) {
|
||||
result, err := c.beaconApiClient.getHeadSignedBeaconBlock(c.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get latest signed block")
|
||||
}
|
||||
|
||||
// We keep querying the beacon chain for the latest block until we receive a new slot
|
||||
for (c.streamBlocksRequest.VerifiedOnly && result.executionOptimistic) || c.prevBlockSlot == result.slot {
|
||||
select {
|
||||
case <-time.After(c.pingDelay):
|
||||
result, err = c.beaconApiClient.getHeadSignedBeaconBlock(c.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get latest signed block")
|
||||
}
|
||||
case <-c.ctx.Done():
|
||||
return nil, errors.New("context canceled")
|
||||
}
|
||||
}
|
||||
|
||||
c.prevBlockSlot = result.slot
|
||||
return result.streamBlocksResponse, nil
|
||||
}
|
||||
|
||||
func (c beaconApiValidatorClient) getHeadSignedBeaconBlock(ctx context.Context) (*headSignedBeaconBlockResult, error) {
|
||||
// Since we don't know yet what the json looks like, we unmarshal into an abstract structure that has only a version
|
||||
// and a blob of data
|
||||
signedBlockResponseJson := abstractSignedBlockResponseJson{}
|
||||
if _, err := c.jsonRestHandler.GetRestJsonResponse(ctx, "/eth/v2/beacon/blocks/head", &signedBlockResponseJson); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to query GET REST endpoint")
|
||||
}
|
||||
|
||||
// Once we know what the consensus version is, we can go ahead and unmarshal into the specific structs unique to each version
|
||||
decoder := json.NewDecoder(bytes.NewReader(signedBlockResponseJson.Data))
|
||||
decoder.DisallowUnknownFields()
|
||||
|
||||
response := ðpb.StreamBlocksResponse{}
|
||||
var slot primitives.Slot
|
||||
|
||||
switch signedBlockResponseJson.Version {
|
||||
case "phase0":
|
||||
jsonPhase0Block := apimiddleware.SignedBeaconBlockContainerJson{}
|
||||
if err := decoder.Decode(&jsonPhase0Block); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode signed phase0 block response json")
|
||||
}
|
||||
|
||||
phase0Block, err := c.beaconBlockConverter.ConvertRESTPhase0BlockToProto(jsonPhase0Block.Message)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get signed phase0 block")
|
||||
}
|
||||
|
||||
decodedSignature, err := hexutil.Decode(jsonPhase0Block.Signature)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode phase0 block signature `%s`", jsonPhase0Block.Signature)
|
||||
}
|
||||
|
||||
response.Block = ðpb.StreamBlocksResponse_Phase0Block{
|
||||
Phase0Block: ðpb.SignedBeaconBlock{
|
||||
Signature: decodedSignature,
|
||||
Block: phase0Block,
|
||||
},
|
||||
}
|
||||
|
||||
slot = phase0Block.Slot
|
||||
|
||||
case "altair":
|
||||
jsonAltairBlock := apimiddleware.SignedBeaconBlockAltairContainerJson{}
|
||||
if err := decoder.Decode(&jsonAltairBlock); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode signed altair block response json")
|
||||
}
|
||||
|
||||
altairBlock, err := c.beaconBlockConverter.ConvertRESTAltairBlockToProto(jsonAltairBlock.Message)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get signed altair block")
|
||||
}
|
||||
|
||||
decodedSignature, err := hexutil.Decode(jsonAltairBlock.Signature)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode altair block signature `%s`", jsonAltairBlock.Signature)
|
||||
}
|
||||
|
||||
response.Block = ðpb.StreamBlocksResponse_AltairBlock{
|
||||
AltairBlock: ðpb.SignedBeaconBlockAltair{
|
||||
Signature: decodedSignature,
|
||||
Block: altairBlock,
|
||||
},
|
||||
}
|
||||
|
||||
slot = altairBlock.Slot
|
||||
|
||||
case "bellatrix":
|
||||
jsonBellatrixBlock := apimiddleware.SignedBeaconBlockBellatrixContainerJson{}
|
||||
if err := decoder.Decode(&jsonBellatrixBlock); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode signed bellatrix block response json")
|
||||
}
|
||||
|
||||
bellatrixBlock, err := c.beaconBlockConverter.ConvertRESTBellatrixBlockToProto(jsonBellatrixBlock.Message)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get signed bellatrix block")
|
||||
}
|
||||
|
||||
decodedSignature, err := hexutil.Decode(jsonBellatrixBlock.Signature)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode bellatrix block signature `%s`", jsonBellatrixBlock.Signature)
|
||||
}
|
||||
|
||||
response.Block = ðpb.StreamBlocksResponse_BellatrixBlock{
|
||||
BellatrixBlock: ðpb.SignedBeaconBlockBellatrix{
|
||||
Signature: decodedSignature,
|
||||
Block: bellatrixBlock,
|
||||
},
|
||||
}
|
||||
|
||||
slot = bellatrixBlock.Slot
|
||||
|
||||
case "capella":
|
||||
jsonCapellaBlock := apimiddleware.SignedBeaconBlockCapellaContainerJson{}
|
||||
if err := decoder.Decode(&jsonCapellaBlock); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode signed capella block response json")
|
||||
}
|
||||
|
||||
capellaBlock, err := c.beaconBlockConverter.ConvertRESTCapellaBlockToProto(jsonCapellaBlock.Message)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get signed capella block")
|
||||
}
|
||||
|
||||
decodedSignature, err := hexutil.Decode(jsonCapellaBlock.Signature)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to decode capella block signature `%s`", jsonCapellaBlock.Signature)
|
||||
}
|
||||
|
||||
response.Block = ðpb.StreamBlocksResponse_CapellaBlock{
|
||||
CapellaBlock: ðpb.SignedBeaconBlockCapella{
|
||||
Signature: decodedSignature,
|
||||
Block: capellaBlock,
|
||||
},
|
||||
}
|
||||
|
||||
slot = capellaBlock.Slot
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported consensus version `%s`", signedBlockResponseJson.Version)
|
||||
}
|
||||
|
||||
return &headSignedBeaconBlockResult{
|
||||
streamBlocksResponse: response,
|
||||
executionOptimistic: signedBlockResponseJson.ExecutionOptimistic,
|
||||
slot: slot,
|
||||
}, nil
|
||||
}
|
832
validator/client/beacon-api/stream_blocks_test.go
Normal file
832
validator/client/beacon-api/stream_blocks_test.go
Normal file
@ -0,0 +1,832 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/rpc/apimiddleware"
|
||||
eth "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/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)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user