mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -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:
3
changelog/james-prysm_post-block-ssz.md
Normal file
3
changelog/james-prysm_post-block-ssz.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
### Changed
|
||||||
|
|
||||||
|
- Switching default of validator client rest call for submit block from JSON to SSZ. Fallback json will be attempted.
|
||||||
@@ -91,18 +91,6 @@ go_test(
|
|||||||
"index_test.go",
|
"index_test.go",
|
||||||
"prepare_beacon_proposer_test.go",
|
"prepare_beacon_proposer_test.go",
|
||||||
"propose_attestation_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_beacon_block_test.go",
|
||||||
"propose_exit_test.go",
|
"propose_exit_test.go",
|
||||||
"prysm_beacon_chain_client_test.go",
|
"prysm_beacon_chain_client_test.go",
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package beacon_api
|
package beacon_api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/OffchainLabs/prysm/v6/api/server/structs"
|
"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/config/params"
|
||||||
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
||||||
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
|
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
|
||||||
|
"github.com/OffchainLabs/prysm/v6/network/httputil"
|
||||||
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||||
@@ -136,15 +140,14 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockValid(t *testing.T) {
|
|||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||||
jsonRestHandler.EXPECT().Post(
|
jsonRestHandler.EXPECT().PostSSZ(
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
"/eth/v2/beacon/blocks",
|
"/eth/v2/beacon/blocks",
|
||||||
map[string]string{"Eth-Consensus-Version": "phase0"},
|
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
nil,
|
gomock.Any(),
|
||||||
).Return(
|
).Return(
|
||||||
nil,
|
nil, nil, nil,
|
||||||
).Times(2)
|
).Times(1)
|
||||||
|
|
||||||
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||||
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
|
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
|
||||||
@@ -153,34 +156,38 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockValid(t *testing.T) {
|
|||||||
Block: generateSignedPhase0Block(),
|
Block: generateSignedPhase0Block(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
require.NoError(t, expectedErr)
|
||||||
resp, err := validatorClient.ProposeBeaconBlock(
|
require.NotNil(t, expectedResp)
|
||||||
ctx,
|
|
||||||
ðpb.GenericSignedBeaconBlock{
|
|
||||||
Block: generateSignedPhase0Block(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.DeepEqual(t, expectedErr, err)
|
|
||||||
assert.DeepEqual(t, expectedResp, resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBeaconApiValidatorClient_ProposeBeaconBlockError(t *testing.T) {
|
func TestBeaconApiValidatorClient_ProposeBeaconBlockError_ThenPass(t *testing.T) {
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
|
|
||||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
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(
|
jsonRestHandler.EXPECT().Post(
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
"/eth/v2/beacon/blocks",
|
"/eth/v2/beacon/blocks",
|
||||||
map[string]string{"Eth-Consensus-Version": "phase0"},
|
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
nil,
|
gomock.Any(),
|
||||||
|
gomock.Any(),
|
||||||
).Return(
|
).Return(
|
||||||
errors.New("foo error"),
|
nil,
|
||||||
).Times(2)
|
).Times(1)
|
||||||
|
|
||||||
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||||
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
|
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
|
||||||
@@ -189,16 +196,351 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockError(t *testing.T) {
|
|||||||
Block: generateSignedPhase0Block(),
|
Block: generateSignedPhase0Block(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
require.NoError(t, expectedErr)
|
||||||
|
require.NotNil(t, expectedResp)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := validatorClient.ProposeBeaconBlock(
|
func TestBeaconApiValidatorClient_ProposeBeaconBlockAllTypes(t *testing.T) {
|
||||||
ctx,
|
tests := []struct {
|
||||||
ðpb.GenericSignedBeaconBlock{
|
name string
|
||||||
Block: generateSignedPhase0Block(),
|
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)
|
for _, tt := range tests {
|
||||||
assert.DeepEqual(t, expectedResp, resp)
|
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) {
|
func TestBeaconApiValidatorClient_Host(t *testing.T) {
|
||||||
@@ -229,3 +571,88 @@ func TestBeaconApiValidatorClient_Host(t *testing.T) {
|
|||||||
host = validatorClient.Host()
|
host = validatorClient.Host()
|
||||||
require.Equal(t, hosts[1], 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)
|
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.
|
// SetHost mocks base method.
|
||||||
func (m *MockJsonRestHandler) SetHost(host string) {
|
func (m *MockJsonRestHandler) SetHost(host string) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@@ -15,8 +15,10 @@ import (
|
|||||||
type blockProcessingResult struct {
|
type blockProcessingResult struct {
|
||||||
consensusVersion string
|
consensusVersion string
|
||||||
beaconBlockRoot [32]byte
|
beaconBlockRoot [32]byte
|
||||||
marshalledJSON []byte
|
marshalledSSZ []byte
|
||||||
blinded bool
|
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) {
|
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}
|
headers := map[string]string{"Eth-Consensus-Version": res.consensusVersion}
|
||||||
err = c.jsonRestHandler.Post(ctx, endpoint, headers, bytes.NewBuffer(res.marshalledJSON), nil)
|
|
||||||
errJson := &httputil.DefaultJsonError{}
|
// Try PostSSZ first with SSZ data
|
||||||
if err != nil {
|
if res.marshalledSSZ != nil {
|
||||||
if !errors.As(err, &errJson) {
|
_, _, err = c.jsonRestHandler.PostSSZ(ctx, endpoint, headers, bytes.NewBuffer(res.marshalledSSZ))
|
||||||
return nil, err
|
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
|
} else if res.marshalJSON == nil {
|
||||||
if errJson.Code == http.StatusAccepted {
|
return nil, errors.New("no marshalling functions available")
|
||||||
return nil, errors.New("block was successfully broadcast but failed validation")
|
} 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
|
return ðpb.ProposeResponse{BlockRoot: res.beaconBlockRoot[:]}, nil
|
||||||
@@ -89,11 +128,19 @@ func handlePhase0Block(block *ethpb.GenericSignedBeaconBlock_Phase0) (*blockProc
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock := structs.SignedBeaconBlockPhase0FromConsensus(block.Phase0)
|
// Marshal SSZ
|
||||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
ssz, err := block.Phase0.MarshalSSZ()
|
||||||
if err != nil {
|
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
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +155,19 @@ func handleAltairBlock(block *ethpb.GenericSignedBeaconBlock_Altair) (*blockProc
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock := structs.SignedBeaconBlockAltairFromConsensus(block.Altair)
|
// Marshal SSZ
|
||||||
res.marshalledJSON, err = json.Marshal(signedBlock)
|
ssz, err := block.Altair.MarshalSSZ()
|
||||||
if err != nil {
|
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
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,13 +182,20 @@ func handleBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_Bellatrix) (*blo
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(block.Bellatrix)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.Bellatrix.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall bellatrix beacon block to json")
|
// 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
|
return &res, nil
|
||||||
@@ -150,13 +212,20 @@ func handleBlindedBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_BlindedBe
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.BlindedBellatrix.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall blinded bellatrix beacon block to json")
|
// 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
|
return &res, nil
|
||||||
@@ -173,13 +242,20 @@ func handleCapellaBlock(block *ethpb.GenericSignedBeaconBlock_Capella) (*blockPr
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(block.Capella)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.Capella.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall capella beacon block to json")
|
// 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
|
return &res, nil
|
||||||
@@ -196,13 +272,20 @@ func handleBlindedCapellaBlock(block *ethpb.GenericSignedBeaconBlock_BlindedCape
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.BlindedCapella.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall blinded capella beacon block to json")
|
// 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
|
return &res, nil
|
||||||
@@ -219,13 +302,20 @@ func handleDenebBlockContents(block *ethpb.GenericSignedBeaconBlock_Deneb) (*blo
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBeaconBlockContentsDenebFromConsensus(block.Deneb)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.Deneb.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall deneb beacon block contents to json")
|
// 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
|
return &res, nil
|
||||||
@@ -242,13 +332,20 @@ func handleBlindedDenebBlock(block *ethpb.GenericSignedBeaconBlock_BlindedDeneb)
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.BlindedDeneb.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall deneb blinded beacon block to json")
|
// 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
|
return &res, nil
|
||||||
@@ -265,13 +362,20 @@ func handleElectraBlockContents(block *ethpb.GenericSignedBeaconBlock_Electra) (
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.Electra.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall electra beacon block contents to json")
|
// 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
|
return &res, nil
|
||||||
@@ -288,13 +392,20 @@ func handleBlindedElectraBlock(block *ethpb.GenericSignedBeaconBlock_BlindedElec
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBlindedBeaconBlockElectraFromConsensus(block.BlindedElectra)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.BlindedElectra.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall electra blinded beacon block to json")
|
// 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
|
return &res, nil
|
||||||
@@ -311,13 +422,20 @@ func handleFuluBlockContents(block *ethpb.GenericSignedBeaconBlock_Fulu) (*block
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.Fulu.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall fulu beacon block contents to json")
|
// 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
|
return &res, nil
|
||||||
@@ -334,13 +452,20 @@ func handleBlindedFuluBlock(block *ethpb.GenericSignedBeaconBlock_BlindedFulu) (
|
|||||||
}
|
}
|
||||||
res.beaconBlockRoot = beaconBlockRoot
|
res.beaconBlockRoot = beaconBlockRoot
|
||||||
|
|
||||||
signedBlock, err := structs.SignedBlindedBeaconBlockFuluFromConsensus(block.BlindedFulu)
|
// Marshal SSZ
|
||||||
|
ssz, err := block.BlindedFulu.MarshalSSZ()
|
||||||
if err != nil {
|
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)
|
res.marshalledSSZ = ssz
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshall fulu blinded beacon block to json")
|
// 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
|
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
|
package beacon_api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"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"
|
"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"
|
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
||||||
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
||||||
|
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||||
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
"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"
|
"go.uber.org/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProposeBeaconBlock_Error(t *testing.T) {
|
func TestProposeBeaconBlock_SSZ_Error(t *testing.T) {
|
||||||
testSuites := []struct {
|
testSuites := []struct {
|
||||||
name string
|
name string
|
||||||
returnedError error
|
returnedError error
|
||||||
expectedErrorMessage string
|
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",
|
name: "error 500",
|
||||||
expectedErrorMessage: "HTTP request unsuccessful (500: foo error)",
|
expectedErrorMessage: "failed to submit block ssz",
|
||||||
returnedError: &httputil.DefaultJsonError{
|
returnedError: &httputil.DefaultJsonError{
|
||||||
Code: http.StatusInternalServerError,
|
Code: http.StatusInternalServerError,
|
||||||
Message: "foo error",
|
Message: "failed to submit block ssz",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "other error",
|
name: "other error",
|
||||||
expectedErrorMessage: "foo error",
|
expectedErrorMessage: "failed to submit block ssz",
|
||||||
returnedError: errors.New("foo error"),
|
returnedError: errors.New("failed to submit block ssz"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,17 +105,21 @@ func TestProposeBeaconBlock_Error(t *testing.T) {
|
|||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
||||||
|
|
||||||
headers := map[string]string{"Eth-Consensus-Version": testCase.consensusVersion}
|
// Expect PostSSZ to be called first with SSZ data
|
||||||
jsonRestHandler.EXPECT().Post(
|
headers := map[string]string{
|
||||||
|
"Eth-Consensus-Version": testCase.consensusVersion,
|
||||||
|
}
|
||||||
|
jsonRestHandler.EXPECT().PostSSZ(
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
testCase.endpoint,
|
testCase.endpoint,
|
||||||
headers,
|
headers,
|
||||||
gomock.Any(),
|
gomock.Any(),
|
||||||
nil,
|
|
||||||
).Return(
|
).Return(
|
||||||
testSuite.returnedError,
|
nil, nil, testSuite.returnedError,
|
||||||
).Times(1)
|
).Times(1)
|
||||||
|
|
||||||
|
// No JSON fallback expected for non-406 errors
|
||||||
|
|
||||||
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
||||||
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
|
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
|
||||||
assert.ErrorContains(t, testSuite.expectedErrorMessage, err)
|
assert.ErrorContains(t, testSuite.expectedErrorMessage, err)
|
||||||
@@ -130,3 +133,546 @@ func TestProposeBeaconBlock_UnsupportedBlockType(t *testing.T) {
|
|||||||
_, err := validatorClient.proposeBeaconBlock(t.Context(), ðpb.GenericSignedBeaconBlock{})
|
_, err := validatorClient.proposeBeaconBlock(t.Context(), ðpb.GenericSignedBeaconBlock{})
|
||||||
assert.ErrorContains(t, "unsupported block type", err)
|
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
|
Get(ctx context.Context, endpoint string, resp interface{}) error
|
||||||
GetSSZ(ctx context.Context, endpoint string) ([]byte, http.Header, 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
|
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
|
HttpClient() *http.Client
|
||||||
Host() string
|
Host() string
|
||||||
SetHost(host string)
|
SetHost(host string)
|
||||||
@@ -179,6 +180,75 @@ func (c *BeaconApiRestHandler) Post(
|
|||||||
return decodeResp(httpResp, resp)
|
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 {
|
func decodeResp(httpResp *http.Response, resp interface{}) error {
|
||||||
body, err := io.ReadAll(httpResp.Body)
|
body, err := io.ReadAll(httpResp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user