mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 04:54:05 -05:00
default validator client rest mode to ssz for post block (#15645)
* wip * fixing tests * updating unit tests for coverage * gofmt * changelog * consolidated propose beacon block tests and improved code coverage * gaz * partial review feedback * continuation fixing feedback on functions * adding log * only fall back on 406 * fixing broken tests * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * Update validator/client/beacon-api/propose_beacon_block.go Co-authored-by: Radosław Kapka <rkapka@wp.pl> * radek review feedback * more feedback * radek suggestion * fixing unit tests --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -91,18 +91,6 @@ go_test(
|
||||
"index_test.go",
|
||||
"prepare_beacon_proposer_test.go",
|
||||
"propose_attestation_test.go",
|
||||
"propose_beacon_block_altair_test.go",
|
||||
"propose_beacon_block_bellatrix_test.go",
|
||||
"propose_beacon_block_blinded_bellatrix_test.go",
|
||||
"propose_beacon_block_blinded_capella_test.go",
|
||||
"propose_beacon_block_blinded_deneb_test.go",
|
||||
"propose_beacon_block_blinded_electra_test.go",
|
||||
"propose_beacon_block_blinded_fulu_test.go",
|
||||
"propose_beacon_block_capella_test.go",
|
||||
"propose_beacon_block_deneb_test.go",
|
||||
"propose_beacon_block_electra_test.go",
|
||||
"propose_beacon_block_fulu_test.go",
|
||||
"propose_beacon_block_phase0_test.go",
|
||||
"propose_beacon_block_test.go",
|
||||
"propose_exit_test.go",
|
||||
"prysm_beacon_chain_client_test.go",
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/config/params"
|
||||
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
|
||||
"github.com/OffchainLabs/prysm/v6/network/httputil"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
@@ -136,15 +140,14 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockValid(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
map[string]string{"Eth-Consensus-Version": "phase0"},
|
||||
gomock.Any(),
|
||||
nil,
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil,
|
||||
).Times(2)
|
||||
nil, nil, nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
|
||||
@@ -153,34 +156,38 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockValid(t *testing.T) {
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
)
|
||||
|
||||
resp, err := validatorClient.ProposeBeaconBlock(
|
||||
ctx,
|
||||
ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
)
|
||||
|
||||
assert.DeepEqual(t, expectedErr, err)
|
||||
assert.DeepEqual(t, expectedResp, resp)
|
||||
require.NoError(t, expectedErr)
|
||||
require.NotNil(t, expectedResp)
|
||||
}
|
||||
|
||||
func TestBeaconApiValidatorClient_ProposeBeaconBlockError(t *testing.T) {
|
||||
func TestBeaconApiValidatorClient_ProposeBeaconBlockError_ThenPass(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil, nil, &httputil.DefaultJsonError{
|
||||
Code: http.StatusNotAcceptable,
|
||||
Message: "SSZ not supported",
|
||||
},
|
||||
).Times(1)
|
||||
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
map[string]string{"Eth-Consensus-Version": "phase0"},
|
||||
gomock.Any(),
|
||||
nil,
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
errors.New("foo error"),
|
||||
).Times(2)
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
|
||||
@@ -189,16 +196,351 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockError(t *testing.T) {
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
)
|
||||
require.NoError(t, expectedErr)
|
||||
require.NotNil(t, expectedResp)
|
||||
}
|
||||
|
||||
resp, err := validatorClient.ProposeBeaconBlock(
|
||||
ctx,
|
||||
ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
func TestBeaconApiValidatorClient_ProposeBeaconBlockAllTypes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
block *ethpb.GenericSignedBeaconBlock
|
||||
expectedPath string
|
||||
wantErr bool
|
||||
errorMessage string
|
||||
}{
|
||||
{
|
||||
name: "Phase0 block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
)
|
||||
{
|
||||
name: "Altair block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedAltairBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Bellatrix block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBellatrixBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Capella block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedCapellaBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Bellatrix block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedBellatrixBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Capella block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedCapellaBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Deneb block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedDenebBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Deneb block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedDenebBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Electra block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedElectraBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Electra block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedElectraBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Fulu block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedFuluBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Fulu block",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedFuluBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Unsupported block type",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
errorMessage: "unsupported block type",
|
||||
},
|
||||
}
|
||||
|
||||
assert.ErrorContains(t, expectedErr.Error(), err)
|
||||
assert.DeepEqual(t, expectedResp, resp)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
if !tt.wantErr {
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
tt.expectedPath,
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(nil, nil, nil).Times(1)
|
||||
}
|
||||
|
||||
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
resp, err := validatorClient.proposeBeaconBlock(ctx, tt.block)
|
||||
|
||||
if tt.wantErr {
|
||||
require.ErrorContains(t, tt.errorMessage, err)
|
||||
assert.Equal(t, (*ethpb.ProposeResponse)(nil), resp)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeaconApiValidatorClient_ProposeBeaconBlockHTTPErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sszError error
|
||||
expectJSON bool
|
||||
errorMessage string
|
||||
}{
|
||||
{
|
||||
name: "HTTP 202 Accepted - block broadcast but failed validation",
|
||||
sszError: &httputil.DefaultJsonError{
|
||||
Code: http.StatusAccepted,
|
||||
Message: "block broadcast but failed validation",
|
||||
},
|
||||
expectJSON: false, // No fallback for non-406 errors
|
||||
errorMessage: "failed to submit block ssz",
|
||||
},
|
||||
{
|
||||
name: "Other HTTP error",
|
||||
sszError: &httputil.DefaultJsonError{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: "bad request",
|
||||
},
|
||||
expectJSON: false, // No fallback for non-406 errors
|
||||
errorMessage: "failed to submit block ssz",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(nil, nil, tt.sszError).Times(1)
|
||||
|
||||
if tt.expectJSON {
|
||||
// When SSZ fails, it falls back to JSON
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(tt.sszError).Times(1)
|
||||
}
|
||||
|
||||
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err := validatorClient.proposeBeaconBlock(
|
||||
ctx,
|
||||
ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
)
|
||||
require.ErrorContains(t, tt.errorMessage, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeaconApiValidatorClient_ProposeBeaconBlockJSONFallback(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
block *ethpb.GenericSignedBeaconBlock
|
||||
expectedPath string
|
||||
jsonError error
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Phase0 block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Altair block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedAltairBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Bellatrix block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBellatrixBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Capella block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedCapellaBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Bellatrix block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedBellatrixBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Capella block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedCapellaBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Deneb block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedDenebBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Deneb block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedDenebBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Electra block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedElectraBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Electra block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedElectraBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "Fulu block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedFuluBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
},
|
||||
{
|
||||
name: "Blinded Fulu block JSON fallback success",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedBlindedFuluBlock(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blinded_blocks",
|
||||
},
|
||||
{
|
||||
name: "JSON fallback fails",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
expectedPath: "/eth/v2/beacon/blocks",
|
||||
jsonError: errors.New("json post failed"),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
// SSZ call fails with 406 to trigger JSON fallback
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
tt.expectedPath,
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(nil, nil, &httputil.DefaultJsonError{
|
||||
Code: http.StatusNotAcceptable,
|
||||
Message: "SSZ not supported",
|
||||
}).Times(1)
|
||||
|
||||
// JSON fallback
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
tt.expectedPath,
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(tt.jsonError).Times(1)
|
||||
|
||||
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
resp, err := validatorClient.proposeBeaconBlock(ctx, tt.block)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, (*ethpb.ProposeResponse)(nil), resp)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeaconApiValidatorClient_Host(t *testing.T) {
|
||||
@@ -229,3 +571,88 @@ func TestBeaconApiValidatorClient_Host(t *testing.T) {
|
||||
host = validatorClient.Host()
|
||||
require.Equal(t, hosts[1], host)
|
||||
}
|
||||
|
||||
// Helper functions for generating test blocks for newer consensus versions
|
||||
func generateSignedDenebBlock() *ethpb.GenericSignedBeaconBlock_Deneb {
|
||||
var blockContents structs.SignedBeaconBlockContentsDeneb
|
||||
if err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genericBlock, err := blockContents.ToGeneric()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ðpb.GenericSignedBeaconBlock_Deneb{
|
||||
Deneb: genericBlock.GetDeneb(),
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedBlindedDenebBlock() *ethpb.GenericSignedBeaconBlock_BlindedDeneb {
|
||||
var blindedBlock structs.SignedBlindedBeaconBlockDeneb
|
||||
if err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlock), &blindedBlock); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genericBlock, err := blindedBlock.ToGeneric()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ðpb.GenericSignedBeaconBlock_BlindedDeneb{
|
||||
BlindedDeneb: genericBlock.GetBlindedDeneb(),
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedElectraBlock() *ethpb.GenericSignedBeaconBlock_Electra {
|
||||
var blockContents structs.SignedBeaconBlockContentsElectra
|
||||
if err := json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &blockContents); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genericBlock, err := blockContents.ToGeneric()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ðpb.GenericSignedBeaconBlock_Electra{
|
||||
Electra: genericBlock.GetElectra(),
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedBlindedElectraBlock() *ethpb.GenericSignedBeaconBlock_BlindedElectra {
|
||||
var blindedBlock structs.SignedBlindedBeaconBlockElectra
|
||||
if err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &blindedBlock); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genericBlock, err := blindedBlock.ToGeneric()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ðpb.GenericSignedBeaconBlock_BlindedElectra{
|
||||
BlindedElectra: genericBlock.GetBlindedElectra(),
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedFuluBlock() *ethpb.GenericSignedBeaconBlock_Fulu {
|
||||
var blockContents structs.SignedBeaconBlockContentsFulu
|
||||
if err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blockContents); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genericBlock, err := blockContents.ToGeneric()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ðpb.GenericSignedBeaconBlock_Fulu{
|
||||
Fulu: genericBlock.GetFulu(),
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedBlindedFuluBlock() *ethpb.GenericSignedBeaconBlock_BlindedFulu {
|
||||
var blindedBlock structs.SignedBlindedBeaconBlockFulu
|
||||
if err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &blindedBlock); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genericBlock, err := blindedBlock.ToGeneric()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ðpb.GenericSignedBeaconBlock_BlindedFulu{
|
||||
BlindedFulu: genericBlock.GetBlindedFulu(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,22 @@ func (mr *MockJsonRestHandlerMockRecorder) Post(ctx, endpoint, headers, data, re
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Post", reflect.TypeOf((*MockJsonRestHandler)(nil).Post), ctx, endpoint, headers, data, resp)
|
||||
}
|
||||
|
||||
// Post mocks base method.
|
||||
func (m *MockJsonRestHandler) PostSSZ(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer) ([]byte, http.Header, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PostSSZ", ctx, endpoint, headers, data)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(http.Header)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0,ret1,ret2
|
||||
}
|
||||
|
||||
// Post indicates an expected call of Post.
|
||||
func (mr *MockJsonRestHandlerMockRecorder) PostSSZ(ctx, endpoint, headers, data any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostSSZ", reflect.TypeOf((*MockJsonRestHandler)(nil).PostSSZ), ctx, endpoint, headers, data)
|
||||
}
|
||||
|
||||
// SetHost mocks base method.
|
||||
func (m *MockJsonRestHandler) SetHost(host string) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -15,8 +15,10 @@ import (
|
||||
type blockProcessingResult struct {
|
||||
consensusVersion string
|
||||
beaconBlockRoot [32]byte
|
||||
marshalledJSON []byte
|
||||
marshalledSSZ []byte
|
||||
blinded bool
|
||||
// Function to marshal JSON on demand
|
||||
marshalJSON func() ([]byte, error)
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
|
||||
@@ -62,17 +64,54 @@ func (c *beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *e
|
||||
}
|
||||
|
||||
headers := map[string]string{"Eth-Consensus-Version": res.consensusVersion}
|
||||
err = c.jsonRestHandler.Post(ctx, endpoint, headers, bytes.NewBuffer(res.marshalledJSON), nil)
|
||||
errJson := &httputil.DefaultJsonError{}
|
||||
if err != nil {
|
||||
if !errors.As(err, &errJson) {
|
||||
return nil, err
|
||||
|
||||
// Try PostSSZ first with SSZ data
|
||||
if res.marshalledSSZ != nil {
|
||||
_, _, err = c.jsonRestHandler.PostSSZ(ctx, endpoint, headers, bytes.NewBuffer(res.marshalledSSZ))
|
||||
if err != nil {
|
||||
errJson := &httputil.DefaultJsonError{}
|
||||
// If PostSSZ fails with 406 (Not Acceptable), fall back to JSON
|
||||
if !errors.As(err, &errJson) {
|
||||
return nil, err
|
||||
}
|
||||
if errJson.Code == http.StatusNotAcceptable && res.marshalJSON != nil {
|
||||
log.WithError(err).Warn("Failed to submit block ssz, falling back to JSON")
|
||||
jsonData, jsonErr := res.marshalJSON()
|
||||
if jsonErr != nil {
|
||||
return nil, errors.Wrap(jsonErr, "failed to marshal JSON")
|
||||
}
|
||||
// Reset headers for JSON
|
||||
err = c.jsonRestHandler.Post(ctx, endpoint, headers, bytes.NewBuffer(jsonData), nil)
|
||||
// If JSON also fails, return that error
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to submit block via JSON fallback")
|
||||
}
|
||||
} else {
|
||||
// For non-406 errors or when no JSON fallback is available, return the SSZ error
|
||||
return nil, errors.Wrap(errJson, "failed to submit block ssz")
|
||||
}
|
||||
}
|
||||
// Error 202 means that the block was successfully broadcast, but validation failed
|
||||
if errJson.Code == http.StatusAccepted {
|
||||
return nil, errors.New("block was successfully broadcast but failed validation")
|
||||
} else if res.marshalJSON == nil {
|
||||
return nil, errors.New("no marshalling functions available")
|
||||
} else {
|
||||
// No SSZ data available, marshal and use JSON
|
||||
jsonData, jsonErr := res.marshalJSON()
|
||||
if jsonErr != nil {
|
||||
return nil, errors.Wrap(jsonErr, "failed to marshal JSON")
|
||||
}
|
||||
// Reset headers for JSON
|
||||
err = c.jsonRestHandler.Post(ctx, endpoint, headers, bytes.NewBuffer(jsonData), nil)
|
||||
errJson := &httputil.DefaultJsonError{}
|
||||
if err != nil {
|
||||
if !errors.As(err, &errJson) {
|
||||
return nil, err
|
||||
}
|
||||
// Error 202 means that the block was successfully broadcast, but validation failed
|
||||
if errJson.Code == http.StatusAccepted {
|
||||
return nil, errors.New("block was successfully broadcast but failed validation")
|
||||
}
|
||||
return nil, errJson
|
||||
}
|
||||
return nil, errJson
|
||||
}
|
||||
|
||||
return ðpb.ProposeResponse{BlockRoot: res.beaconBlockRoot[:]}, nil
|
||||
@@ -89,11 +128,19 @@ func handlePhase0Block(block *ethpb.GenericSignedBeaconBlock_Phase0) (*blockProc
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock := structs.SignedBeaconBlockPhase0FromConsensus(block.Phase0)
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Phase0.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall phase0 beacon block to json")
|
||||
return nil, errors.Wrap(err, "failed to serialize block for phase0 beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock := structs.SignedBeaconBlockPhase0FromConsensus(block.Phase0)
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -108,11 +155,19 @@ func handleAltairBlock(block *ethpb.GenericSignedBeaconBlock_Altair) (*blockProc
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock := structs.SignedBeaconBlockAltairFromConsensus(block.Altair)
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Altair.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall altair beacon block to json")
|
||||
return nil, errors.Wrap(err, "failed to serialize block for altair beacon block")
|
||||
}
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock := structs.SignedBeaconBlockAltairFromConsensus(block.Altair)
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -127,13 +182,20 @@ func handleBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_Bellatrix) (*blo
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(block.Bellatrix)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Bellatrix.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall bellatrix beacon block")
|
||||
return nil, errors.Wrap(err, "failed to serialize block for bellatrix beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall bellatrix beacon block to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(block.Bellatrix)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert bellatrix beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -150,13 +212,20 @@ func handleBlindedBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_BlindedBe
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedBellatrix.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall blinded bellatrix beacon block")
|
||||
return nil, errors.Wrap(err, "failed to serialize block for bellatrix beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall blinded bellatrix beacon block to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert blinded bellatrix beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -173,13 +242,20 @@ func handleCapellaBlock(block *ethpb.GenericSignedBeaconBlock_Capella) (*blockPr
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(block.Capella)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Capella.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall capella beacon block")
|
||||
return nil, errors.Wrap(err, "failed to serialize capella beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall capella beacon block to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(block.Capella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert capella beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -196,13 +272,20 @@ func handleBlindedCapellaBlock(block *ethpb.GenericSignedBeaconBlock_BlindedCape
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedCapella.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall blinded capella beacon block")
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded capella beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall blinded capella beacon block to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert blinded capella beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -219,13 +302,20 @@ func handleDenebBlockContents(block *ethpb.GenericSignedBeaconBlock_Deneb) (*blo
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsDenebFromConsensus(block.Deneb)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Deneb.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall deneb beacon block contents")
|
||||
return nil, errors.Wrap(err, "failed to serialize deneb beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall deneb beacon block contents to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsDenebFromConsensus(block.Deneb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert deneb beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -242,13 +332,20 @@ func handleBlindedDenebBlock(block *ethpb.GenericSignedBeaconBlock_BlindedDeneb)
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedDeneb.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall deneb blinded beacon block ")
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded deneb beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall deneb blinded beacon block to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert deneb blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -265,13 +362,20 @@ func handleElectraBlockContents(block *ethpb.GenericSignedBeaconBlock_Electra) (
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Electra.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall electra beacon block contents")
|
||||
return nil, errors.Wrap(err, "failed to serialize electra beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall electra beacon block contents to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert electra beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -288,13 +392,20 @@ func handleBlindedElectraBlock(block *ethpb.GenericSignedBeaconBlock_BlindedElec
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockElectraFromConsensus(block.BlindedElectra)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedElectra.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall electra blinded beacon block")
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded electra beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall electra blinded beacon block to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockElectraFromConsensus(block.BlindedElectra)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert electra blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -311,13 +422,20 @@ func handleFuluBlockContents(block *ethpb.GenericSignedBeaconBlock_Fulu) (*block
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.Fulu.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall fulu beacon block contents")
|
||||
return nil, errors.Wrap(err, "failed to serialize fulu beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall fulu beacon block contents to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert fulu beacon block contents")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
@@ -334,13 +452,20 @@ func handleBlindedFuluBlock(block *ethpb.GenericSignedBeaconBlock_BlindedFulu) (
|
||||
}
|
||||
res.beaconBlockRoot = beaconBlockRoot
|
||||
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockFuluFromConsensus(block.BlindedFulu)
|
||||
// Marshal SSZ
|
||||
ssz, err := block.BlindedFulu.MarshalSSZ()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall fulu blinded beacon block")
|
||||
return nil, errors.Wrap(err, "failed to serialize blinded fulu beacon block")
|
||||
}
|
||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshall fulu blinded beacon block to json")
|
||||
res.marshalledSSZ = ssz
|
||||
|
||||
// Set up JSON marshalling function for fallback
|
||||
res.marshalJSON = func() ([]byte, error) {
|
||||
signedBlock, err := structs.SignedBlindedBeaconBlockFuluFromConsensus(block.BlindedFulu)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert fulu blinded beacon block")
|
||||
}
|
||||
return json.Marshal(signedBlock)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Altair(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
altairBlock := generateSignedAltairBlock()
|
||||
|
||||
genericSignedBlock := ðpb.GenericSignedBeaconBlock{}
|
||||
genericSignedBlock.Block = altairBlock
|
||||
|
||||
jsonAltairBlock := structs.SignedBeaconBlockAltairFromConsensus(altairBlock.Altair)
|
||||
|
||||
marshalledBlock, err := json.Marshal(jsonAltairBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "altair"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(marshalledBlock),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := altairBlock.Altair.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
|
||||
func generateSignedAltairBlock() *ethpb.GenericSignedBeaconBlock_Altair {
|
||||
return ðpb.GenericSignedBeaconBlock_Altair{
|
||||
Altair: ðpb.SignedBeaconBlockAltair{
|
||||
Block: testhelpers.GenerateProtoAltairBeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 112),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Bellatrix(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
bellatrixBlock := generateSignedBellatrixBlock()
|
||||
|
||||
genericSignedBlock := ðpb.GenericSignedBeaconBlock{}
|
||||
genericSignedBlock.Block = bellatrixBlock
|
||||
|
||||
jsonBellatrixBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(bellatrixBlock.Bellatrix)
|
||||
require.NoError(t, err)
|
||||
|
||||
marshalledBlock, err := json.Marshal(jsonBellatrixBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "bellatrix"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(marshalledBlock),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := bellatrixBlock.Bellatrix.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
|
||||
func generateSignedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_Bellatrix {
|
||||
return ðpb.GenericSignedBeaconBlock_Bellatrix{
|
||||
Bellatrix: ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: testhelpers.GenerateProtoBellatrixBeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 127),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_BlindedBellatrix(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
blindedBellatrixBlock := generateSignedBlindedBellatrixBlock()
|
||||
|
||||
genericSignedBlock := ðpb.GenericSignedBeaconBlock{}
|
||||
genericSignedBlock.Block = blindedBellatrixBlock
|
||||
|
||||
jsonBlindedBellatrixBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(blindedBellatrixBlock.BlindedBellatrix)
|
||||
require.NoError(t, err)
|
||||
|
||||
marshalledBlock, err := json.Marshal(jsonBlindedBellatrixBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "bellatrix"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(marshalledBlock),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := blindedBellatrixBlock.BlindedBellatrix.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
|
||||
func generateSignedBlindedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_BlindedBellatrix {
|
||||
return ðpb.GenericSignedBeaconBlock_BlindedBellatrix{
|
||||
BlindedBellatrix: ðpb.SignedBlindedBeaconBlockBellatrix{
|
||||
Block: ðpb.BlindedBeaconBlockBellatrix{
|
||||
Slot: 1,
|
||||
ProposerIndex: 2,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 3),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 4),
|
||||
Body: ðpb.BlindedBeaconBlockBodyBellatrix{
|
||||
RandaoReveal: testhelpers.FillByteSlice(96, 5),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: testhelpers.FillByteSlice(32, 6),
|
||||
DepositCount: 7,
|
||||
BlockHash: testhelpers.FillByteSlice(32, 8),
|
||||
},
|
||||
Graffiti: testhelpers.FillByteSlice(32, 9),
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{
|
||||
{
|
||||
Header_1: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 11,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 12),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 13),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 14),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 15),
|
||||
},
|
||||
Header_2: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 16,
|
||||
ProposerIndex: 17,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 18),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 19),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 20),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 21),
|
||||
},
|
||||
},
|
||||
{
|
||||
Header_1: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 22,
|
||||
ProposerIndex: 23,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 24),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 25),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 26),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 27),
|
||||
},
|
||||
Header_2: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 28,
|
||||
ProposerIndex: 29,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 30),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 31),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 32),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 33),
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*ethpb.AttesterSlashing{
|
||||
{
|
||||
Attestation_1: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{34, 35},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 36,
|
||||
CommitteeIndex: 37,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 39,
|
||||
Root: testhelpers.FillByteSlice(32, 40),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 41,
|
||||
Root: testhelpers.FillByteSlice(32, 42),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 43),
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{44, 45},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 46,
|
||||
CommitteeIndex: 47,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 49,
|
||||
Root: testhelpers.FillByteSlice(32, 50),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 51,
|
||||
Root: testhelpers.FillByteSlice(32, 52),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 53),
|
||||
},
|
||||
},
|
||||
{
|
||||
Attestation_1: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{54, 55},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 56,
|
||||
CommitteeIndex: 57,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 59,
|
||||
Root: testhelpers.FillByteSlice(32, 60),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 61,
|
||||
Root: testhelpers.FillByteSlice(32, 62),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 63),
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{64, 65},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 66,
|
||||
CommitteeIndex: 67,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 69,
|
||||
Root: testhelpers.FillByteSlice(32, 70),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 71,
|
||||
Root: testhelpers.FillByteSlice(32, 72),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 73),
|
||||
},
|
||||
},
|
||||
},
|
||||
Attestations: []*ethpb.Attestation{
|
||||
{
|
||||
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 75,
|
||||
CommitteeIndex: 76,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 78,
|
||||
Root: testhelpers.FillByteSlice(32, 79),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 80,
|
||||
Root: testhelpers.FillByteSlice(32, 81),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 82),
|
||||
},
|
||||
{
|
||||
AggregationBits: testhelpers.FillByteSlice(4, 83),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 84,
|
||||
CommitteeIndex: 85,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 87,
|
||||
Root: testhelpers.FillByteSlice(32, 88),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 89,
|
||||
Root: testhelpers.FillByteSlice(32, 90),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 91),
|
||||
},
|
||||
},
|
||||
Deposits: []*ethpb.Deposit{
|
||||
{
|
||||
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 92)),
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: testhelpers.FillByteSlice(48, 94),
|
||||
WithdrawalCredentials: testhelpers.FillByteSlice(32, 95),
|
||||
Amount: 96,
|
||||
Signature: testhelpers.FillByteSlice(96, 97),
|
||||
},
|
||||
},
|
||||
{
|
||||
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 98)),
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: testhelpers.FillByteSlice(48, 100),
|
||||
WithdrawalCredentials: testhelpers.FillByteSlice(32, 101),
|
||||
Amount: 102,
|
||||
Signature: testhelpers.FillByteSlice(96, 103),
|
||||
},
|
||||
},
|
||||
},
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 104,
|
||||
ValidatorIndex: 105,
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 106),
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 107,
|
||||
ValidatorIndex: 108,
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 109),
|
||||
},
|
||||
},
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: testhelpers.FillByteSlice(64, 110),
|
||||
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 111),
|
||||
},
|
||||
ExecutionPayloadHeader: &enginev1.ExecutionPayloadHeader{
|
||||
ParentHash: testhelpers.FillByteSlice(32, 112),
|
||||
FeeRecipient: testhelpers.FillByteSlice(20, 113),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 114),
|
||||
ReceiptsRoot: testhelpers.FillByteSlice(32, 115),
|
||||
LogsBloom: testhelpers.FillByteSlice(256, 116),
|
||||
PrevRandao: testhelpers.FillByteSlice(32, 117),
|
||||
BlockNumber: 118,
|
||||
GasLimit: 119,
|
||||
GasUsed: 120,
|
||||
Timestamp: 121,
|
||||
ExtraData: testhelpers.FillByteSlice(32, 122),
|
||||
BaseFeePerGas: testhelpers.FillByteSlice(32, 123),
|
||||
BlockHash: testhelpers.FillByteSlice(32, 124),
|
||||
TransactionsRoot: testhelpers.FillByteSlice(32, 125),
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 126),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,310 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_BlindedCapella(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
blindedCapellaBlock := generateSignedBlindedCapellaBlock()
|
||||
|
||||
genericSignedBlock := ðpb.GenericSignedBeaconBlock{}
|
||||
genericSignedBlock.Block = blindedCapellaBlock
|
||||
|
||||
jsonBlindedCapellaBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(blindedCapellaBlock.BlindedCapella)
|
||||
require.NoError(t, err)
|
||||
|
||||
marshalledBlock, err := json.Marshal(jsonBlindedCapellaBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "capella"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(marshalledBlock),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := blindedCapellaBlock.BlindedCapella.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
|
||||
func generateSignedBlindedCapellaBlock() *ethpb.GenericSignedBeaconBlock_BlindedCapella {
|
||||
return ðpb.GenericSignedBeaconBlock_BlindedCapella{
|
||||
BlindedCapella: ðpb.SignedBlindedBeaconBlockCapella{
|
||||
Block: ðpb.BlindedBeaconBlockCapella{
|
||||
Slot: 1,
|
||||
ProposerIndex: 2,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 3),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 4),
|
||||
Body: ðpb.BlindedBeaconBlockBodyCapella{
|
||||
RandaoReveal: testhelpers.FillByteSlice(96, 5),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: testhelpers.FillByteSlice(32, 6),
|
||||
DepositCount: 7,
|
||||
BlockHash: testhelpers.FillByteSlice(32, 8),
|
||||
},
|
||||
Graffiti: testhelpers.FillByteSlice(32, 9),
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{
|
||||
{
|
||||
Header_1: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 11,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 12),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 13),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 14),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 15),
|
||||
},
|
||||
Header_2: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 16,
|
||||
ProposerIndex: 17,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 18),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 19),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 20),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 21),
|
||||
},
|
||||
},
|
||||
{
|
||||
Header_1: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 22,
|
||||
ProposerIndex: 23,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 24),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 25),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 26),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 27),
|
||||
},
|
||||
Header_2: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 28,
|
||||
ProposerIndex: 29,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 30),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 31),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 32),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 33),
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*ethpb.AttesterSlashing{
|
||||
{
|
||||
Attestation_1: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{34, 35},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 36,
|
||||
CommitteeIndex: 37,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 39,
|
||||
Root: testhelpers.FillByteSlice(32, 40),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 41,
|
||||
Root: testhelpers.FillByteSlice(32, 42),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 43),
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{44, 45},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 46,
|
||||
CommitteeIndex: 47,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 49,
|
||||
Root: testhelpers.FillByteSlice(32, 50),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 51,
|
||||
Root: testhelpers.FillByteSlice(32, 52),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 53),
|
||||
},
|
||||
},
|
||||
{
|
||||
Attestation_1: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{54, 55},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 56,
|
||||
CommitteeIndex: 57,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 59,
|
||||
Root: testhelpers.FillByteSlice(32, 60),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 61,
|
||||
Root: testhelpers.FillByteSlice(32, 62),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 63),
|
||||
},
|
||||
Attestation_2: ðpb.IndexedAttestation{
|
||||
AttestingIndices: []uint64{64, 65},
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 66,
|
||||
CommitteeIndex: 67,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 69,
|
||||
Root: testhelpers.FillByteSlice(32, 70),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 71,
|
||||
Root: testhelpers.FillByteSlice(32, 72),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 73),
|
||||
},
|
||||
},
|
||||
},
|
||||
Attestations: []*ethpb.Attestation{
|
||||
{
|
||||
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 75,
|
||||
CommitteeIndex: 76,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 78,
|
||||
Root: testhelpers.FillByteSlice(32, 79),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 80,
|
||||
Root: testhelpers.FillByteSlice(32, 81),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 82),
|
||||
},
|
||||
{
|
||||
AggregationBits: testhelpers.FillByteSlice(4, 83),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 84,
|
||||
CommitteeIndex: 85,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 87,
|
||||
Root: testhelpers.FillByteSlice(32, 88),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 89,
|
||||
Root: testhelpers.FillByteSlice(32, 90),
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 91),
|
||||
},
|
||||
},
|
||||
Deposits: []*ethpb.Deposit{
|
||||
{
|
||||
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 92)),
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: testhelpers.FillByteSlice(48, 94),
|
||||
WithdrawalCredentials: testhelpers.FillByteSlice(32, 95),
|
||||
Amount: 96,
|
||||
Signature: testhelpers.FillByteSlice(96, 97),
|
||||
},
|
||||
},
|
||||
{
|
||||
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 98)),
|
||||
Data: ðpb.Deposit_Data{
|
||||
PublicKey: testhelpers.FillByteSlice(48, 100),
|
||||
WithdrawalCredentials: testhelpers.FillByteSlice(32, 101),
|
||||
Amount: 102,
|
||||
Signature: testhelpers.FillByteSlice(96, 103),
|
||||
},
|
||||
},
|
||||
},
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 104,
|
||||
ValidatorIndex: 105,
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 106),
|
||||
},
|
||||
{
|
||||
Exit: ðpb.VoluntaryExit{
|
||||
Epoch: 107,
|
||||
ValidatorIndex: 108,
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 109),
|
||||
},
|
||||
},
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: testhelpers.FillByteSlice(64, 110),
|
||||
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 111),
|
||||
},
|
||||
ExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: testhelpers.FillByteSlice(32, 112),
|
||||
FeeRecipient: testhelpers.FillByteSlice(20, 113),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 114),
|
||||
ReceiptsRoot: testhelpers.FillByteSlice(32, 115),
|
||||
LogsBloom: testhelpers.FillByteSlice(256, 116),
|
||||
PrevRandao: testhelpers.FillByteSlice(32, 117),
|
||||
BlockNumber: 118,
|
||||
GasLimit: 119,
|
||||
GasUsed: 120,
|
||||
Timestamp: 121,
|
||||
ExtraData: testhelpers.FillByteSlice(32, 122),
|
||||
BaseFeePerGas: testhelpers.FillByteSlice(32, 123),
|
||||
BlockHash: testhelpers.FillByteSlice(32, 124),
|
||||
TransactionsRoot: testhelpers.FillByteSlice(32, 125),
|
||||
WithdrawalsRoot: testhelpers.FillByteSlice(32, 126),
|
||||
},
|
||||
BlsToExecutionChanges: []*ethpb.SignedBLSToExecutionChange{
|
||||
{
|
||||
Message: ðpb.BLSToExecutionChange{
|
||||
ValidatorIndex: 127,
|
||||
FromBlsPubkey: testhelpers.FillByteSlice(48, 128),
|
||||
ToExecutionAddress: testhelpers.FillByteSlice(20, 129),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 130),
|
||||
},
|
||||
{
|
||||
Message: ðpb.BLSToExecutionChange{
|
||||
ValidatorIndex: 131,
|
||||
FromBlsPubkey: testhelpers.FillByteSlice(48, 132),
|
||||
ToExecutionAddress: testhelpers.FillByteSlice(20, 133),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 134),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 135),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_BlindedDeneb(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var block structs.SignedBlindedBeaconBlockDeneb
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlock), &block)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := block.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
denebBytes, err := json.Marshal(block)
|
||||
require.NoError(t, err)
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "deneb"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(denebBytes),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetBlindedDeneb().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_BlindedElectra(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var block structs.SignedBlindedBeaconBlockElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &block)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := block.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
electraBytes, err := json.Marshal(block)
|
||||
require.NoError(t, err)
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "electra"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(electraBytes),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetBlindedElectra().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_BlindedFulu(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var block structs.SignedBlindedBeaconBlockFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &block)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := block.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
fuluBytes, err := json.Marshal(block)
|
||||
require.NoError(t, err)
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "fulu"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(fuluBytes),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetBlindedFulu().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Capella(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
capellaBlock := generateSignedCapellaBlock()
|
||||
|
||||
genericSignedBlock := ðpb.GenericSignedBeaconBlock{}
|
||||
genericSignedBlock.Block = capellaBlock
|
||||
|
||||
jsonCapellaBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(capellaBlock.Capella)
|
||||
require.NoError(t, err)
|
||||
|
||||
marshalledBlock, err := json.Marshal(jsonCapellaBlock)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "capella"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(marshalledBlock),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := capellaBlock.Capella.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
|
||||
func generateSignedCapellaBlock() *ethpb.GenericSignedBeaconBlock_Capella {
|
||||
return ðpb.GenericSignedBeaconBlock_Capella{
|
||||
Capella: ðpb.SignedBeaconBlockCapella{
|
||||
Block: testhelpers.GenerateProtoCapellaBeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 127),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Deneb(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blockContents structs.SignedBeaconBlockContentsDeneb
|
||||
err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blockContents.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
denebBytes, err := json.Marshal(blockContents)
|
||||
require.NoError(t, err)
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "deneb"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(denebBytes),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetDeneb().Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Electra(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blockContents structs.SignedBeaconBlockContentsElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &blockContents)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blockContents.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
electraBytes, err := json.Marshal(blockContents)
|
||||
require.NoError(t, err)
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "electra"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(electraBytes),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetElectra().Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Fulu(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blockContents structs.SignedBeaconBlockContentsFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blockContents)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blockContents.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
fuluBytes, err := json.Marshal(blockContents)
|
||||
require.NoError(t, err)
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "fulu"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(fuluBytes),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetFulu().Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Phase0(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
phase0Block := generateSignedPhase0Block()
|
||||
|
||||
genericSignedBlock := ðpb.GenericSignedBeaconBlock{}
|
||||
genericSignedBlock.Block = phase0Block
|
||||
|
||||
jsonPhase0Block := structs.SignedBeaconBlockPhase0FromConsensus(phase0Block.Phase0)
|
||||
|
||||
marshalledBlock, err := json.Marshal(jsonPhase0Block)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
|
||||
headers := map[string]string{"Eth-Consensus-Version": "phase0"}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
headers,
|
||||
bytes.NewBuffer(marshalledBlock),
|
||||
nil,
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := phase0Block.Phase0.Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Make sure that the block root is set
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
}
|
||||
|
||||
func generateSignedPhase0Block() *ethpb.GenericSignedBeaconBlock_Phase0 {
|
||||
return ðpb.GenericSignedBeaconBlock_Phase0{
|
||||
Phase0: ðpb.SignedBeaconBlock{
|
||||
Block: testhelpers.GenerateProtoPhase0BeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 110),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,42 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
||||
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
|
||||
"github.com/OffchainLabs/prysm/v6/network/httputil"
|
||||
engine "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
|
||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
||||
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestProposeBeaconBlock_Error(t *testing.T) {
|
||||
func TestProposeBeaconBlock_SSZ_Error(t *testing.T) {
|
||||
testSuites := []struct {
|
||||
name string
|
||||
returnedError error
|
||||
expectedErrorMessage string
|
||||
}{
|
||||
{
|
||||
name: "error 202",
|
||||
expectedErrorMessage: "block was successfully broadcast but failed validation",
|
||||
returnedError: &httputil.DefaultJsonError{
|
||||
Code: http.StatusAccepted,
|
||||
Message: "202 error",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error 500",
|
||||
expectedErrorMessage: "HTTP request unsuccessful (500: foo error)",
|
||||
expectedErrorMessage: "failed to submit block ssz",
|
||||
returnedError: &httputil.DefaultJsonError{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: "foo error",
|
||||
Message: "failed to submit block ssz",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "other error",
|
||||
expectedErrorMessage: "foo error",
|
||||
returnedError: errors.New("foo error"),
|
||||
expectedErrorMessage: "failed to submit block ssz",
|
||||
returnedError: errors.New("failed to submit block ssz"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -106,17 +105,21 @@ func TestProposeBeaconBlock_Error(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
headers := map[string]string{"Eth-Consensus-Version": testCase.consensusVersion}
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
// Expect PostSSZ to be called first with SSZ data
|
||||
headers := map[string]string{
|
||||
"Eth-Consensus-Version": testCase.consensusVersion,
|
||||
}
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
testCase.endpoint,
|
||||
headers,
|
||||
gomock.Any(),
|
||||
nil,
|
||||
).Return(
|
||||
testSuite.returnedError,
|
||||
nil, nil, testSuite.returnedError,
|
||||
).Times(1)
|
||||
|
||||
// No JSON fallback expected for non-406 errors
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
|
||||
assert.ErrorContains(t, testSuite.expectedErrorMessage, err)
|
||||
@@ -130,3 +133,546 @@ func TestProposeBeaconBlock_UnsupportedBlockType(t *testing.T) {
|
||||
_, err := validatorClient.proposeBeaconBlock(t.Context(), ðpb.GenericSignedBeaconBlock{})
|
||||
assert.ErrorContains(t, "unsupported block type", err)
|
||||
}
|
||||
|
||||
func TestProposeBeaconBlock_SSZSuccess_NoFallback(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
consensusVersion string
|
||||
endpoint string
|
||||
block *ethpb.GenericSignedBeaconBlock
|
||||
}{
|
||||
{
|
||||
name: "phase0",
|
||||
consensusVersion: "phase0",
|
||||
endpoint: "/eth/v2/beacon/blocks",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "altair",
|
||||
consensusVersion: "altair",
|
||||
endpoint: "/eth/v2/beacon/blocks",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedAltairBlock(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
// Expect PostSSZ to be called and succeed
|
||||
headers := map[string]string{
|
||||
"Eth-Consensus-Version": testCase.consensusVersion,
|
||||
}
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
testCase.endpoint,
|
||||
headers,
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil, nil, nil,
|
||||
).Times(1)
|
||||
|
||||
// Post should NOT be called when PostSSZ succeeds
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Times(0)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposeBeaconBlock_NewerTypes_SSZMarshal(t *testing.T) {
|
||||
t.Run("deneb", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blockContents structs.SignedBeaconBlockContentsDeneb
|
||||
err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blockContents.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
denebBytes, err := genericSignedBlock.GetDeneb().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
bytes.NewBuffer(denebBytes),
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetDeneb().Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
})
|
||||
|
||||
t.Run("blinded_deneb", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blindedBlock structs.SignedBlindedBeaconBlockDeneb
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlock), &blindedBlock)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blindedBlock.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
blindedDenebBytes, err := genericSignedBlock.GetBlindedDeneb().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
gomock.Any(),
|
||||
bytes.NewBuffer(blindedDenebBytes),
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetBlindedDeneb().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
})
|
||||
|
||||
t.Run("electra", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blockContents structs.SignedBeaconBlockContentsElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &blockContents)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blockContents.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
electraBytes, err := genericSignedBlock.GetElectra().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
bytes.NewBuffer(electraBytes),
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetElectra().Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
})
|
||||
|
||||
t.Run("blinded_electra", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blindedBlock structs.SignedBlindedBeaconBlockElectra
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &blindedBlock)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blindedBlock.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
blindedElectraBytes, err := genericSignedBlock.GetBlindedElectra().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
gomock.Any(),
|
||||
bytes.NewBuffer(blindedElectraBytes),
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetBlindedElectra().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
})
|
||||
|
||||
t.Run("fulu", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blockContents structs.SignedBeaconBlockContentsFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blockContents)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blockContents.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
fuluBytes, err := genericSignedBlock.GetFulu().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blocks",
|
||||
gomock.Any(),
|
||||
bytes.NewBuffer(fuluBytes),
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetFulu().Block.HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
})
|
||||
|
||||
t.Run("blinded_fulu", func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
var blindedBlock structs.SignedBlindedBeaconBlockFulu
|
||||
err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &blindedBlock)
|
||||
require.NoError(t, err)
|
||||
genericSignedBlock, err := blindedBlock.ToGeneric()
|
||||
require.NoError(t, err)
|
||||
|
||||
blindedFuluBytes, err := genericSignedBlock.GetBlindedFulu().MarshalSSZ()
|
||||
require.NoError(t, err)
|
||||
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
"/eth/v2/beacon/blinded_blocks",
|
||||
gomock.Any(),
|
||||
bytes.NewBuffer(blindedFuluBytes),
|
||||
)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, proposeResponse)
|
||||
|
||||
expectedBlockRoot, err := genericSignedBlock.GetBlindedFulu().HashTreeRoot()
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
|
||||
})
|
||||
}
|
||||
|
||||
// Generator functions for test blocks
|
||||
func generateSignedPhase0Block() *ethpb.GenericSignedBeaconBlock_Phase0 {
|
||||
return ðpb.GenericSignedBeaconBlock_Phase0{
|
||||
Phase0: ðpb.SignedBeaconBlock{
|
||||
Block: testhelpers.GenerateProtoPhase0BeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 110),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedAltairBlock() *ethpb.GenericSignedBeaconBlock_Altair {
|
||||
return ðpb.GenericSignedBeaconBlock_Altair{
|
||||
Altair: ðpb.SignedBeaconBlockAltair{
|
||||
Block: testhelpers.GenerateProtoAltairBeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 112),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_Bellatrix {
|
||||
return ðpb.GenericSignedBeaconBlock_Bellatrix{
|
||||
Bellatrix: ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: testhelpers.GenerateProtoBellatrixBeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 127),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedBlindedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_BlindedBellatrix {
|
||||
return ðpb.GenericSignedBeaconBlock_BlindedBellatrix{
|
||||
BlindedBellatrix: ðpb.SignedBlindedBeaconBlockBellatrix{
|
||||
Block: ðpb.BlindedBeaconBlockBellatrix{
|
||||
Slot: 1,
|
||||
ProposerIndex: 2,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 3),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 4),
|
||||
Body: ðpb.BlindedBeaconBlockBodyBellatrix{
|
||||
RandaoReveal: testhelpers.FillByteSlice(96, 5),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: testhelpers.FillByteSlice(32, 6),
|
||||
DepositCount: 7,
|
||||
BlockHash: testhelpers.FillByteSlice(32, 8),
|
||||
},
|
||||
Graffiti: testhelpers.FillByteSlice(32, 9),
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{
|
||||
{
|
||||
Header_1: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 11,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 12),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 13),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 14),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 15),
|
||||
},
|
||||
Header_2: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 16,
|
||||
ProposerIndex: 17,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 18),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 19),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 20),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 21),
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*ethpb.AttesterSlashing{},
|
||||
Attestations: []*ethpb.Attestation{},
|
||||
Deposits: []*ethpb.Deposit{},
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{},
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: testhelpers.FillByteSlice(64, 100),
|
||||
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 101),
|
||||
},
|
||||
ExecutionPayloadHeader: &engine.ExecutionPayloadHeader{
|
||||
ParentHash: testhelpers.FillByteSlice(32, 102),
|
||||
FeeRecipient: testhelpers.FillByteSlice(20, 103),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 104),
|
||||
ReceiptsRoot: testhelpers.FillByteSlice(32, 105),
|
||||
LogsBloom: testhelpers.FillByteSlice(256, 106),
|
||||
PrevRandao: testhelpers.FillByteSlice(32, 107),
|
||||
BlockNumber: 108,
|
||||
GasLimit: 109,
|
||||
GasUsed: 110,
|
||||
Timestamp: 111,
|
||||
ExtraData: testhelpers.FillByteSlice(32, 112),
|
||||
BaseFeePerGas: testhelpers.FillByteSlice(32, 113),
|
||||
BlockHash: testhelpers.FillByteSlice(32, 114),
|
||||
TransactionsRoot: testhelpers.FillByteSlice(32, 115),
|
||||
},
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 116),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedCapellaBlock() *ethpb.GenericSignedBeaconBlock_Capella {
|
||||
return ðpb.GenericSignedBeaconBlock_Capella{
|
||||
Capella: ðpb.SignedBeaconBlockCapella{
|
||||
Block: testhelpers.GenerateProtoCapellaBeaconBlock(),
|
||||
Signature: testhelpers.FillByteSlice(96, 127),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignedBlindedCapellaBlock() *ethpb.GenericSignedBeaconBlock_BlindedCapella {
|
||||
return ðpb.GenericSignedBeaconBlock_BlindedCapella{
|
||||
BlindedCapella: ðpb.SignedBlindedBeaconBlockCapella{
|
||||
Block: ðpb.BlindedBeaconBlockCapella{
|
||||
Slot: 1,
|
||||
ProposerIndex: 2,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 3),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 4),
|
||||
Body: ðpb.BlindedBeaconBlockBodyCapella{
|
||||
RandaoReveal: testhelpers.FillByteSlice(96, 5),
|
||||
Eth1Data: ðpb.Eth1Data{
|
||||
DepositRoot: testhelpers.FillByteSlice(32, 6),
|
||||
DepositCount: 7,
|
||||
BlockHash: testhelpers.FillByteSlice(32, 8),
|
||||
},
|
||||
Graffiti: testhelpers.FillByteSlice(32, 9),
|
||||
ProposerSlashings: []*ethpb.ProposerSlashing{
|
||||
{
|
||||
Header_1: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 10,
|
||||
ProposerIndex: 11,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 12),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 13),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 14),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 15),
|
||||
},
|
||||
Header_2: ðpb.SignedBeaconBlockHeader{
|
||||
Header: ðpb.BeaconBlockHeader{
|
||||
Slot: 16,
|
||||
ProposerIndex: 17,
|
||||
ParentRoot: testhelpers.FillByteSlice(32, 18),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 19),
|
||||
BodyRoot: testhelpers.FillByteSlice(32, 20),
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 21),
|
||||
},
|
||||
},
|
||||
},
|
||||
AttesterSlashings: []*ethpb.AttesterSlashing{},
|
||||
Attestations: []*ethpb.Attestation{},
|
||||
Deposits: []*ethpb.Deposit{},
|
||||
VoluntaryExits: []*ethpb.SignedVoluntaryExit{},
|
||||
SyncAggregate: ðpb.SyncAggregate{
|
||||
SyncCommitteeBits: testhelpers.FillByteSlice(64, 37),
|
||||
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 38),
|
||||
},
|
||||
ExecutionPayloadHeader: &engine.ExecutionPayloadHeaderCapella{
|
||||
ParentHash: testhelpers.FillByteSlice(32, 39),
|
||||
FeeRecipient: testhelpers.FillByteSlice(20, 40),
|
||||
StateRoot: testhelpers.FillByteSlice(32, 41),
|
||||
ReceiptsRoot: testhelpers.FillByteSlice(32, 42),
|
||||
LogsBloom: testhelpers.FillByteSlice(256, 43),
|
||||
PrevRandao: testhelpers.FillByteSlice(32, 44),
|
||||
BlockNumber: 45,
|
||||
GasLimit: 46,
|
||||
GasUsed: 47,
|
||||
Timestamp: 48,
|
||||
ExtraData: testhelpers.FillByteSlice(32, 49),
|
||||
BaseFeePerGas: testhelpers.FillByteSlice(32, 50),
|
||||
BlockHash: testhelpers.FillByteSlice(32, 51),
|
||||
TransactionsRoot: testhelpers.FillByteSlice(32, 52),
|
||||
WithdrawalsRoot: testhelpers.FillByteSlice(32, 53),
|
||||
},
|
||||
BlsToExecutionChanges: []*ethpb.SignedBLSToExecutionChange{},
|
||||
},
|
||||
},
|
||||
Signature: testhelpers.FillByteSlice(96, 54),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposeBeaconBlock_SSZFails_406_FallbackToJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
consensusVersion string
|
||||
endpoint string
|
||||
block *ethpb.GenericSignedBeaconBlock
|
||||
}{
|
||||
{
|
||||
name: "phase0",
|
||||
consensusVersion: "phase0",
|
||||
endpoint: "/eth/v2/beacon/blocks",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
// Expect PostSSZ to be called first and fail
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
testCase.endpoint,
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil, nil, &httputil.DefaultJsonError{
|
||||
Code: http.StatusNotAcceptable,
|
||||
Message: "SSZ not supported",
|
||||
},
|
||||
).Times(1)
|
||||
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
testCase.endpoint,
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
nil,
|
||||
).Return(
|
||||
nil,
|
||||
).Times(1)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposeBeaconBlock_SSZFails_Non406_NoFallback(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
consensusVersion string
|
||||
endpoint string
|
||||
block *ethpb.GenericSignedBeaconBlock
|
||||
}{
|
||||
{
|
||||
name: "phase0",
|
||||
consensusVersion: "phase0",
|
||||
endpoint: "/eth/v2/beacon/blocks",
|
||||
block: ðpb.GenericSignedBeaconBlock{
|
||||
Block: generateSignedPhase0Block(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
ctx := t.Context()
|
||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||
|
||||
// Expect PostSSZ to be called first and fail with non-406 error
|
||||
sszHeaders := map[string]string{
|
||||
"Eth-Consensus-Version": testCase.consensusVersion,
|
||||
}
|
||||
jsonRestHandler.EXPECT().PostSSZ(
|
||||
gomock.Any(),
|
||||
testCase.endpoint,
|
||||
sszHeaders,
|
||||
gomock.Any(),
|
||||
).Return(
|
||||
nil, nil, &httputil.DefaultJsonError{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: "Internal server error",
|
||||
},
|
||||
).Times(1)
|
||||
|
||||
// Post should NOT be called for non-406 errors
|
||||
jsonRestHandler.EXPECT().Post(
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
gomock.Any(),
|
||||
).Times(0)
|
||||
|
||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
|
||||
require.ErrorContains(t, "Internal server error", err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ type RestHandler interface {
|
||||
Get(ctx context.Context, endpoint string, resp interface{}) error
|
||||
GetSSZ(ctx context.Context, endpoint string) ([]byte, http.Header, error)
|
||||
Post(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer, resp interface{}) error
|
||||
PostSSZ(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer) ([]byte, http.Header, error)
|
||||
HttpClient() *http.Client
|
||||
Host() string
|
||||
SetHost(host string)
|
||||
@@ -179,6 +180,75 @@ func (c *BeaconApiRestHandler) Post(
|
||||
return decodeResp(httpResp, resp)
|
||||
}
|
||||
|
||||
// PostSSZ sends a POST request and prefers an SSZ (application/octet-stream) response body.
|
||||
func (c *BeaconApiRestHandler) PostSSZ(
|
||||
ctx context.Context,
|
||||
apiEndpoint string,
|
||||
headers map[string]string,
|
||||
data *bytes.Buffer,
|
||||
) ([]byte, http.Header, error) {
|
||||
if data == nil {
|
||||
return nil, nil, errors.New("data is nil")
|
||||
}
|
||||
url := c.host + apiEndpoint
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, data)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to create request for endpoint %s", url)
|
||||
}
|
||||
|
||||
// Accept header: prefer octet-stream (SSZ), fall back to JSON
|
||||
primaryAcceptType := fmt.Sprintf("%s;q=%s", api.OctetStreamMediaType, "0.95")
|
||||
secondaryAcceptType := fmt.Sprintf("%s;q=%s", api.JsonMediaType, "0.9")
|
||||
acceptHeaderString := fmt.Sprintf("%s,%s", primaryAcceptType, secondaryAcceptType)
|
||||
req.Header.Set("Accept", acceptHeaderString)
|
||||
|
||||
// User-supplied headers
|
||||
for headerKey, headerValue := range headers {
|
||||
req.Header.Set(headerKey, headerValue)
|
||||
}
|
||||
|
||||
for _, o := range c.reqOverrides {
|
||||
o(req)
|
||||
}
|
||||
req.Header.Set("Content-Type", api.OctetStreamMediaType)
|
||||
req.Header.Set("User-Agent", version.BuildData())
|
||||
httpResp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to perform request for endpoint %s", url)
|
||||
}
|
||||
defer func() {
|
||||
if err := httpResp.Body.Close(); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
accept := req.Header.Get("Accept")
|
||||
contentType := httpResp.Header.Get("Content-Type")
|
||||
body, err := io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to read response body for %s", httpResp.Request.URL)
|
||||
}
|
||||
|
||||
if !apiutil.PrimaryAcceptMatches(accept, contentType) {
|
||||
log.WithFields(logrus.Fields{
|
||||
"Accept": accept,
|
||||
"Content-Type": contentType,
|
||||
}).Debug("Server responded with non primary accept type")
|
||||
}
|
||||
|
||||
// non-2XX codes are a failure
|
||||
if !strings.HasPrefix(httpResp.Status, "2") {
|
||||
decoder := json.NewDecoder(bytes.NewBuffer(body))
|
||||
errorJson := &httputil.DefaultJsonError{}
|
||||
if err = decoder.Decode(errorJson); err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to decode response body into error json for %s", httpResp.Request.URL)
|
||||
}
|
||||
return nil, nil, errorJson
|
||||
}
|
||||
|
||||
return body, httpResp.Header, nil
|
||||
}
|
||||
|
||||
func decodeResp(httpResp *http.Response, resp interface{}) error {
|
||||
body, err := io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user