From af8de05799d4502f1a840a9cfa43d480b09ab555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Thu, 4 May 2023 19:52:41 +0200 Subject: [PATCH] Use v1alpha1 server in block production (#12336) Co-authored-by: Raul Jordan # Conflicts: # beacon-chain/rpc/eth/beacon/BUILD.bazel # beacon-chain/rpc/eth/beacon/blinded_blocks_test.go # beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go --- beacon-chain/rpc/eth/beacon/BUILD.bazel | 6 +- beacon-chain/rpc/eth/beacon/blinded_blocks.go | 111 +++- .../rpc/eth/beacon/blinded_blocks_test.go | 559 +++++------------- beacon-chain/rpc/eth/beacon/blocks.go | 202 ++++--- beacon-chain/rpc/eth/beacon/blocks_test.go | 404 +++++-------- beacon-chain/rpc/eth/beacon/server.go | 5 +- .../rpc/eth/validator/validator_test.go | 24 +- .../rpc/prysm/v1alpha1/validator/proposer.go | 12 +- beacon-chain/rpc/service.go | 1 + 9 files changed, 549 insertions(+), 775 deletions(-) diff --git a/beacon-chain/rpc/eth/beacon/BUILD.bazel b/beacon-chain/rpc/eth/beacon/BUILD.bazel index f67b8525a3cd..3e3b1af2ba5f 100644 --- a/beacon-chain/rpc/eth/beacon/BUILD.bazel +++ b/beacon-chain/rpc/eth/beacon/BUILD.bazel @@ -41,7 +41,6 @@ go_library( "//beacon-chain/state/stategen:go_default_library", "//beacon-chain/sync:go_default_library", "//config/features:go_default_library", - "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types:go_default_library", "//consensus-types/blocks:go_default_library", @@ -87,7 +86,6 @@ go_test( deps = [ "//api/grpc:go_default_library", "//beacon-chain/blockchain/testing:go_default_library", - "//beacon-chain/builder/testing:go_default_library", "//beacon-chain/core/signing:go_default_library", "//beacon-chain/core/time:go_default_library", "//beacon-chain/core/transition:go_default_library", @@ -106,6 +104,7 @@ go_test( "//beacon-chain/rpc/testutil:go_default_library", "//beacon-chain/state:go_default_library", "//beacon-chain/state/state-native:go_default_library", + "//beacon-chain/sync/initial-sync/testing:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", @@ -117,17 +116,18 @@ go_test( "//encoding/bytesutil:go_default_library", "//encoding/ssz:go_default_library", "//network/forks:go_default_library", - "//proto/engine/v1:go_default_library", "//proto/eth/service:go_default_library", "//proto/eth/v1:go_default_library", "//proto/eth/v2:go_default_library", "//proto/migration:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//testing/assert:go_default_library", + "//testing/mock:go_default_library", "//testing/require:go_default_library", "//testing/util:go_default_library", "//time/slots:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", + "@com_github_golang_mock//gomock:go_default_library", "@com_github_grpc_ecosystem_grpc_gateway_v2//runtime:go_default_library", "@com_github_prysmaticlabs_go_bitfield//:go_default_library", "@com_github_wealdtech_go_bytesutil//:go_default_library", diff --git a/beacon-chain/rpc/eth/beacon/blinded_blocks.go b/beacon-chain/rpc/eth/beacon/blinded_blocks.go index 7e37ca0eef1a..762184a2043d 100644 --- a/beacon-chain/rpc/eth/beacon/blinded_blocks.go +++ b/beacon-chain/rpc/eth/beacon/blinded_blocks.go @@ -2,11 +2,15 @@ package beacon import ( "context" + "strings" "github.com/pkg/errors" + rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/validator" "github.com/prysmaticlabs/prysm/v4/config/params" consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" + "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v4/encoding/ssz/detect" "github.com/prysmaticlabs/prysm/v4/network/forks" ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1" @@ -160,6 +164,11 @@ func (bs *Server) SubmitBlindedBlock(ctx context.Context, req *ethpbv2.SignedBli ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlindedBlock") defer span.End() + if err := rpchelpers.ValidateSync(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil { + // We simply return the error because it's already a gRPC error. + return nil, err + } + switch blkContainer := req.Message.(type) { case *ethpbv2.SignedBlindedBeaconBlockContentsContainer_DenebContents: if err := bs.submitBlindedDenebContents(ctx, blkContainer.DenebContents); err != nil { @@ -202,6 +211,11 @@ func (bs *Server) SubmitBlindedBlockSSZ(ctx context.Context, req *ethpbv2.SSZCon ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlindedBlockSSZ") defer span.End() + if err := rpchelpers.ValidateSync(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil { + // We simply return the error because it's already a gRPC error. + return nil, err + } + md, ok := metadata.FromIncomingContext(ctx) if !ok { return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read"+versionHeader+" header") @@ -224,31 +238,84 @@ func (bs *Server) SubmitBlindedBlockSSZ(ctx context.Context, req *ethpbv2.SSZCon return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err) } - if block.IsBlinded() { + switch forkVer { + case bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion): + if !block.IsBlinded() { + return nil, status.Error(codes.InvalidArgument, "Submitted block is not blinded") + } + b, err := block.PbBlindedCapellaBlock() + if err != nil { + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) + } + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_BlindedCapella{ + BlindedCapella: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) + } + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) + } + return &emptypb.Empty{}, nil + case bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion): + if !block.IsBlinded() { + return nil, status.Error(codes.InvalidArgument, "Submitted block is not blinded") + } b, err := block.PbBlindedBellatrixBlock() if err != nil { - b, err := block.PbBlindedCapellaBlock() - if err != nil { - return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get blinded block: %v", err) + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) + } + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_BlindedBellatrix{ + BlindedBellatrix: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) } - bb, err := migration.V1Alpha1BeaconBlockBlindedCapellaToV2Blinded(b.Block) - if err != nil { - return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not migrate block: %v", err) + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) + } + return &emptypb.Empty{}, nil + case bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion): + b, err := block.PbAltairBlock() + if err != nil { + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) + } + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Altair{ + Altair: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) } - return &emptypb.Empty{}, bs.submitBlindedCapellaBlock(ctx, bb, b.Signature) + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) } - bb, err := migration.V1Alpha1BeaconBlockBlindedBellatrixToV2Blinded(b.Block) + return &emptypb.Empty{}, nil + case bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion): + b, err := block.PbPhase0Block() if err != nil { - return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not migrate block: %v", err) + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) } - return &emptypb.Empty{}, bs.submitBlindedBellatrixBlock(ctx, bb, b.Signature) - } - - root, err := block.Block().HashTreeRoot() - if err != nil { - return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not compute block's hash tree root: %v", err) + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Phase0{ + Phase0: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) + } + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) + } + return &emptypb.Empty{}, nil + default: + return &emptypb.Empty{}, status.Errorf(codes.InvalidArgument, "Unsupported fork %s", string(forkVer[:])) } - return &emptypb.Empty{}, bs.submitBlock(ctx, root, block) } func getBlindedBlockPhase0(blk interfaces.ReadOnlySignedBeaconBlock) (*ethpbv2.BlindedBlockResponse, error) { @@ -737,7 +804,7 @@ func (bs *Server) submitBlindedBellatrixBlock(ctx context.Context, blindedBellat Signature: sig, }) if err != nil { - return status.Errorf(codes.Internal, "Could not get blinded block: %v", err) + return status.Errorf(codes.Internal, "Could not convert block: %v", err) } _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ Block: ð.GenericSignedBeaconBlock_BlindedBellatrix{ @@ -745,6 +812,9 @@ func (bs *Server) submitBlindedBellatrixBlock(ctx context.Context, blindedBellat }, }) if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return status.Error(codes.InvalidArgument, err.Error()) + } return status.Errorf(codes.Internal, "Could not propose blinded block: %v", err) } return nil @@ -756,7 +826,7 @@ func (bs *Server) submitBlindedCapellaBlock(ctx context.Context, blindedCapellaB Signature: sig, }) if err != nil { - return status.Errorf(codes.Internal, "Could not get blinded block: %v", err) + return status.Errorf(codes.Internal, "Could not convert block: %v", err) } _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ Block: ð.GenericSignedBeaconBlock_BlindedCapella{ @@ -764,6 +834,9 @@ func (bs *Server) submitBlindedCapellaBlock(ctx context.Context, blindedCapellaB }, }) if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return status.Error(codes.InvalidArgument, err.Error()) + } return status.Errorf(codes.Internal, "Could not propose blinded block: %v", err) } return nil diff --git a/beacon-chain/rpc/eth/beacon/blinded_blocks_test.go b/beacon-chain/rpc/eth/beacon/blinded_blocks_test.go index c3b908d1090a..ecdf5ba6aea2 100644 --- a/beacon-chain/rpc/eth/beacon/blinded_blocks_test.go +++ b/beacon-chain/rpc/eth/beacon/blinded_blocks_test.go @@ -4,23 +4,18 @@ import ( "context" "testing" + "github.com/golang/mock/gomock" mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing" - builderTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/builder/testing" - dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/synccommittee" - mockp2p "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/testing" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/validator" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil" + mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing" fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" "github.com/prysmaticlabs/prysm/v4/config/params" "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks" - "github.com/prysmaticlabs/prysm/v4/encoding/bytesutil" - "github.com/prysmaticlabs/prysm/v4/encoding/ssz" - enginev1 "github.com/prysmaticlabs/prysm/v4/proto/engine/v1" ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1" ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2" "github.com/prysmaticlabs/prysm/v4/proto/migration" "github.com/prysmaticlabs/prysm/v4/testing/assert" + mock2 "github.com/prysmaticlabs/prysm/v4/testing/mock" "github.com/prysmaticlabs/prysm/v4/testing/require" "github.com/prysmaticlabs/prysm/v4/testing/util" "google.golang.org/grpc/metadata" @@ -349,468 +344,212 @@ func TestServer_GetBlindedBlockSSZ(t *testing.T) { }) } -func TestServer_SubmitBlindedBlockSSZ_OK(t *testing.T) { - t.Run("Phase 0", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlock() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") +func TestServer_SubmitBlindedBlockSSZ(t *testing.T) { + ctrl := gomock.NewController(t) + ctx := context.Background() - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + t.Run("Phase 0", func(t *testing.T) { + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlock() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + + b := util.NewBeaconBlock() + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "phase0") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlindedBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) }) - t.Run("Altair", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockAltair() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlockAltair() - req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().AltairForkEpoch)) - req.Block.ParentRoot = bsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + + b := util.NewBeaconBlockAltair() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().AltairForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "altair") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlindedBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) }) - t.Run("Bellatrix", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockBellatrix() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - alphaServer := &validator.Server{ - SyncCommitteePool: synccommittee.NewStore(), - P2P: &mockp2p.MockBroadcaster{}, - BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, Payload: emptyPayload()}, - BlockReceiver: c, - BlockNotifier: &mock.MockBlockNotifier{}, - } - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, - V1Alpha1ValidatorServer: alphaServer, - } - req := util.NewBlindedBeaconBlockBellatrix() - req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch)) - req.Block.ParentRoot = bsRoot[:] - transactionsRoot, err := ssz.TransactionsRoot([][]byte{}) - require.NoError(t, err) - req.Block.Body.ExecutionPayloadHeader.TransactionsRoot = transactionsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } + + b := util.NewBlindedBeaconBlockBellatrix() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "bellatrix") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlindedBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) }) + t.Run("Bellatrix full", func(t *testing.T) { + server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } + b := util.NewBeaconBlockBellatrix() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch)) + ssz, err := b.MarshalSSZ() + require.NoError(t, err) + blockReq := ðpbv2.SSZContainer{ + Data: ssz, + } + md := metadata.MD{} + md.Set(versionHeader, "bellatrix") + sszCtx := metadata.NewIncomingContext(ctx, md) + _, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq) + assert.NotNil(t, err) + }) t.Run("Capella", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockCapella() - util.SaveBlock(t, context.Background(), beaconDB, genesis) + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() + b := util.NewBlindedBeaconBlockCapella() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - alphaServer := &validator.Server{ - SyncCommitteePool: synccommittee.NewStore(), - P2P: &mockp2p.MockBroadcaster{}, - BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, PayloadCapella: emptyPayloadCapella()}, - BlockReceiver: c, - BlockNotifier: &mock.MockBlockNotifier{}, + blockReq := ðpbv2.SSZContainer{ + Data: ssz, } - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, - V1Alpha1ValidatorServer: alphaServer, + md := metadata.MD{} + md.Set(versionHeader, "capella") + sszCtx := metadata.NewIncomingContext(ctx, md) + _, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) + }) + t.Run("Capella full", func(t *testing.T) { + server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBlindedBeaconBlockCapella() - req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch)) - req.Block.ParentRoot = bsRoot[:] - transactionsRoot, err := ssz.TransactionsRoot([][]byte{}) - require.NoError(t, err) - req.Block.Body.ExecutionPayloadHeader.TransactionsRoot = transactionsRoot[:] - withdrawalsRoot, err := ssz.WithdrawalSliceRoot([]*enginev1.Withdrawal{}, fieldparams.MaxWithdrawalsPerPayload) - require.NoError(t, err) - req.Block.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + + b := util.NewBeaconBlockCapella() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "capella") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlindedBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlindedBlockSSZ(sszCtx, blockReq) + assert.NotNil(t, err) + }) + t.Run("sync not ready", func(t *testing.T) { + chainService := &mock.ChainService{} + v1Server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: true}, + HeadFetcher: chainService, + TimeFetcher: chainService, + OptimisticModeFetcher: chainService, + } + _, err := v1Server.SubmitBlindedBlockSSZ(context.Background(), nil) + require.ErrorContains(t, "Syncing to latest head", err) }) } func TestSubmitBlindedBlock(t *testing.T) { - t.Run("Phase 0", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlock() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") + ctrl := gomock.NewController(t) - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), + t.Run("Phase 0", func(t *testing.T) { + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlock() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - v1Block, err := migration.V1Alpha1ToV1SignedBlock(req) - require.NoError(t, err) - util.SaveBlock(t, ctx, beaconDB, req) - blockReq := ðpbv2.SignedBlindedBeaconBlockContentsContainer{ - Message: ðpbv2.SignedBlindedBeaconBlockContentsContainer_Phase0Block{Phase0Block: v1Block}, + + blockReq := ðpbv2.SignedBlindedBeaconBlockContainer{ + Message: ðpbv2.SignedBlindedBeaconBlockContainer_Phase0Block{Phase0Block: ðpbv1.BeaconBlock{}}, + Signature: []byte("sig"), } - _, err = beaconChainServer.SubmitBlindedBlock(context.Background(), blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err := server.SubmitBlindedBlock(context.Background(), blockReq) + assert.NoError(t, err) }) - t.Run("Altair", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockAltair() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlockAltair() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - v2Block, err := migration.V1Alpha1BeaconBlockAltairToV2Signed(req) - require.NoError(t, err) - util.SaveBlock(t, ctx, beaconDB, req) - blockReq := ðpbv2.SignedBlindedBeaconBlockContentsContainer{ - Message: ðpbv2.SignedBlindedBeaconBlockContentsContainer_AltairBlock{AltairBlock: v2Block}, + + blockReq := ðpbv2.SignedBlindedBeaconBlockContainer{ + Message: ðpbv2.SignedBlindedBeaconBlockContainer_AltairBlock{AltairBlock: ðpbv2.BeaconBlockAltair{}}, + Signature: []byte("sig"), } - _, err = beaconChainServer.SubmitBlindedBlock(context.Background(), blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err := server.SubmitBlindedBlock(context.Background(), blockReq) + assert.NoError(t, err) }) - t.Run("Bellatrix", func(t *testing.T) { - transactions := [][]byte{[]byte("transaction1"), []byte("transaction2")} - transactionsRoot, err := ssz.TransactionsRoot(transactions) - require.NoError(t, err) - - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockBellatrix() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - p := emptyPayload() - p.Transactions = transactions - alphaServer := &validator.Server{ - SyncCommitteePool: synccommittee.NewStore(), - P2P: &mockp2p.MockBroadcaster{}, - BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, Payload: p}, - BlockReceiver: c, - BlockNotifier: &mock.MockBlockNotifier{}, - } - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - V1Alpha1ValidatorServer: alphaServer, + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) } - blk := util.NewBeaconBlockBellatrix() - blk.Block.Slot = 5 - blk.Block.ParentRoot = bsRoot[:] - blk.Block.Body.ExecutionPayload.Transactions = transactions - blindedBlk := util.NewBlindedBeaconBlockBellatrixV2() - blindedBlk.Message.Slot = 5 - blindedBlk.Message.ParentRoot = bsRoot[:] - blindedBlk.Message.Body.ExecutionPayloadHeader.TransactionsRoot = transactionsRoot[:] - util.SaveBlock(t, ctx, beaconDB, blk) - - blockReq := ðpbv2.SignedBlindedBeaconBlockContentsContainer{ - Message: ðpbv2.SignedBlindedBeaconBlockContentsContainer_BellatrixBlock{BellatrixBlock: blindedBlk}, + blockReq := ðpbv2.SignedBlindedBeaconBlockContainer{ + Message: ðpbv2.SignedBlindedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: ðpbv2.BlindedBeaconBlockBellatrix{}}, + Signature: []byte("sig"), } - _, err = beaconChainServer.SubmitBlindedBlock(context.Background(), blockReq) + _, err := server.SubmitBlindedBlock(context.Background(), blockReq) assert.NoError(t, err) }) - t.Run("Capella", func(t *testing.T) { - transactions := [][]byte{[]byte("transaction1"), []byte("transaction2")} - transactionsRoot, err := ssz.TransactionsRoot(transactions) - require.NoError(t, err) - - withdrawals := []*enginev1.Withdrawal{ - { - Index: 1, - ValidatorIndex: 1, - Address: bytesutil.PadTo([]byte("address1"), 20), - Amount: 1, - }, - { - Index: 2, - ValidatorIndex: 2, - Address: bytesutil.PadTo([]byte("address2"), 20), - Amount: 2, - }, - } - withdrawalsRoot, err := ssz.WithdrawalSliceRoot(withdrawals, 16) - require.NoError(t, err) - - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockCapella() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - p := emptyPayloadCapella() - p.Transactions = transactions - p.Withdrawals = withdrawals - alphaServer := &validator.Server{ - SyncCommitteePool: synccommittee.NewStore(), - P2P: &mockp2p.MockBroadcaster{}, - BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, PayloadCapella: p}, - BlockReceiver: c, - BlockNotifier: &mock.MockBlockNotifier{}, - } - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - V1Alpha1ValidatorServer: alphaServer, - } - - blk := util.NewBeaconBlockCapella() - blk.Block.Slot = 5 - blk.Block.ParentRoot = bsRoot[:] - blk.Block.Body.ExecutionPayload.Transactions = transactions - blk.Block.Body.ExecutionPayload.Withdrawals = withdrawals - blindedBlk := util.NewBlindedBeaconBlockCapellaV2() - blindedBlk.Message.Slot = 5 - blindedBlk.Message.ParentRoot = bsRoot[:] - blindedBlk.Message.Body.ExecutionPayloadHeader.TransactionsRoot = transactionsRoot[:] - blindedBlk.Message.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:] - util.SaveBlock(t, ctx, beaconDB, blk) - - blockReq := ðpbv2.SignedBlindedBeaconBlockContentsContainer{ - Message: ðpbv2.SignedBlindedBeaconBlockContentsContainer_CapellaBlock{CapellaBlock: blindedBlk}, - } - _, err = beaconChainServer.SubmitBlindedBlock(context.Background(), blockReq) - assert.NoError(t, err) - }) + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } - t.Run("Deneb", func(t *testing.T) { - t.Skip("TODO: Skipping this test because we don't have a way to unblind blobs yet.") - transactions := [][]byte{[]byte("transaction1"), []byte("transaction2")} - transactionsRoot, err := ssz.TransactionsRoot(transactions) - require.NoError(t, err) - - withdrawals := []*enginev1.Withdrawal{ - { - Index: 1, - ValidatorIndex: 1, - Address: bytesutil.PadTo([]byte("address1"), 20), - Amount: 1, - }, - { - Index: 2, - ValidatorIndex: 2, - Address: bytesutil.PadTo([]byte("address2"), 20), - Amount: 2, - }, - } - withdrawalsRoot, err := ssz.WithdrawalSliceRoot(withdrawals, 16) - require.NoError(t, err) - - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := util.NewBeaconBlockDeneb().Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - p := emptyPayloadDeneb() - p.Transactions = transactions - p.Withdrawals = withdrawals - alphaServer := &validator.Server{ - SyncCommitteePool: synccommittee.NewStore(), - P2P: &mockp2p.MockBroadcaster{}, - BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, PayloadDeneb: p}, - BlockReceiver: c, - BlockNotifier: &mock.MockBlockNotifier{}, - } - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - V1Alpha1ValidatorServer: alphaServer, - } - - blindedBlk := util.NewBlindedBeaconBlockDenebV2() - blindedBlk.Message.Slot = 5 - blindedBlk.Message.ParentRoot = bsRoot[:] - blindedBlk.Message.Body.ExecutionPayloadHeader.TransactionsRoot = transactionsRoot[:] - blindedBlk.Message.Body.ExecutionPayloadHeader.WithdrawalsRoot = withdrawalsRoot[:] - sidecar := util.NewSignedBlindedBlobSidecarV2() - sidecar.Message.Slot = 5 - - blockReq := ðpbv2.SignedBlindedBeaconBlockContentsContainer{ - Message: ðpbv2.SignedBlindedBeaconBlockContentsContainer_DenebContents{ - DenebContents: ðpbv2.SignedBlindedBeaconBlockContentsDeneb{ - SignedBlindedBlock: blindedBlk, - SignedBlindedBlobSidecars: []*ethpbv2.SignedBlindedBlobSidecar{sidecar}, - }}, - } - _, err = beaconChainServer.SubmitBlindedBlock(context.Background(), blockReq) + blockReq := ðpbv2.SignedBlindedBeaconBlockContainer{ + Message: ðpbv2.SignedBlindedBeaconBlockContainer_CapellaBlock{CapellaBlock: ðpbv2.BlindedBeaconBlockCapella{}}, + Signature: []byte("sig"), + } + _, err := server.SubmitBlindedBlock(context.Background(), blockReq) assert.NoError(t, err) }) + t.Run("sync not ready", func(t *testing.T) { + chainService := &mock.ChainService{} + v1Server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: true}, + HeadFetcher: chainService, + TimeFetcher: chainService, + OptimisticModeFetcher: chainService, + } + _, err := v1Server.SubmitBlindedBlock(context.Background(), nil) + require.ErrorContains(t, "Syncing to latest head", err) + }) } func emptyPayload() *enginev1.ExecutionPayload { diff --git a/beacon-chain/rpc/eth/beacon/blocks.go b/beacon-chain/rpc/eth/beacon/blocks.go index 2eea8bb6fd09..b75d949613f3 100644 --- a/beacon-chain/rpc/eth/beacon/blocks.go +++ b/beacon-chain/rpc/eth/beacon/blocks.go @@ -4,16 +4,15 @@ import ( "context" "fmt" "strconv" + "strings" "github.com/golang/protobuf/ptypes/empty" "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed" - blockfeed "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/block" "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/filters" rpchelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup" - fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams" + "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/validator" "github.com/prysmaticlabs/prysm/v4/config/params" consensus_types "github.com/prysmaticlabs/prysm/v4/consensus-types" "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks" @@ -25,6 +24,7 @@ import ( ethpbv1 "github.com/prysmaticlabs/prysm/v4/proto/eth/v1" ethpbv2 "github.com/prysmaticlabs/prysm/v4/proto/eth/v2" "github.com/prysmaticlabs/prysm/v4/proto/migration" + eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v4/time/slots" "go.opencensus.io/trace" "google.golang.org/grpc/codes" @@ -206,6 +206,11 @@ func (bs *Server) SubmitBlock(ctx context.Context, req *ethpbv2.SignedBeaconBloc ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlock") defer span.End() + if err := rpchelpers.ValidateSync(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil { + // We simply return the error because it's already a gRPC error. + return nil, err + } + switch blkContainer := req.Message.(type) { case *ethpbv2.SignedBeaconBlockContainer_Phase0Block: if err := bs.submitPhase0Block(ctx, blkContainer.Phase0Block, req.Signature); err != nil { @@ -241,6 +246,11 @@ func (bs *Server) SubmitBlockSSZ(ctx context.Context, req *ethpbv2.SSZContainer) ctx, span := trace.StartSpan(ctx, "beacon.SubmitBlockSSZ") defer span.End() + if err := rpchelpers.ValidateSync(ctx, bs.SyncChecker, bs.HeadFetcher, bs.TimeFetcher, bs.OptimisticModeFetcher); err != nil { + // We simply return the error because it's already a gRPC error. + return nil, err + } + md, ok := metadata.FromIncomingContext(ctx) if !ok { return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not read "+versionHeader+" header") @@ -262,11 +272,85 @@ func (bs *Server) SubmitBlockSSZ(ctx context.Context, req *ethpbv2.SSZContainer) if err != nil { return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not unmarshal request data into block: %v", err) } - root, err := block.Block().HashTreeRoot() - if err != nil { - return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not compute block's hash tree root: %v", err) + + switch forkVer { + case bytesutil.ToBytes4(params.BeaconConfig().CapellaForkVersion): + if block.IsBlinded() { + return nil, status.Error(codes.InvalidArgument, "Submitted block is blinded") + } + b, err := block.PbCapellaBlock() + if err != nil { + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) + } + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Capella{ + Capella: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) + } + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) + } + return &emptypb.Empty{}, nil + case bytesutil.ToBytes4(params.BeaconConfig().BellatrixForkVersion): + if block.IsBlinded() { + return nil, status.Error(codes.InvalidArgument, "Submitted block is blinded") + } + b, err := block.PbBellatrixBlock() + if err != nil { + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) + } + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Bellatrix{ + Bellatrix: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) + } + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) + } + return &emptypb.Empty{}, nil + case bytesutil.ToBytes4(params.BeaconConfig().AltairForkVersion): + b, err := block.PbAltairBlock() + if err != nil { + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) + } + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Altair{ + Altair: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) + } + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) + } + return &emptypb.Empty{}, nil + case bytesutil.ToBytes4(params.BeaconConfig().GenesisForkVersion): + b, err := block.PbPhase0Block() + if err != nil { + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not get proto block: %v", err) + } + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Phase0{ + Phase0: b, + }, + }) + if err != nil { + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return &emptypb.Empty{}, status.Error(codes.InvalidArgument, err.Error()) + } + return &emptypb.Empty{}, status.Errorf(codes.Internal, "Could not propose block: %v", err) + } + return &emptypb.Empty{}, nil + default: + return &emptypb.Empty{}, status.Errorf(codes.InvalidArgument, "Unsupported fork %s", string(forkVer[:])) } - return &emptypb.Empty{}, bs.submitBlock(ctx, root, block) } // GetBlock retrieves block details for given block ID. @@ -1102,36 +1186,39 @@ func (bs *Server) getSSZBlockDeneb(ctx context.Context, blk interfaces.ReadOnlyS func (bs *Server) submitPhase0Block(ctx context.Context, phase0Blk *ethpbv1.BeaconBlock, sig []byte) error { v1alpha1Blk, err := migration.V1ToV1Alpha1SignedBlock(ðpbv1.SignedBeaconBlock{Block: phase0Blk, Signature: sig}) if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block") - } - wrappedPhase0Blk, err := blocks.NewSignedBeaconBlock(v1alpha1Blk) - if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not prepare block") + return status.Errorf(codes.InvalidArgument, "Could not convert block: %v", err) } - root, err := phase0Blk.HashTreeRoot() + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Phase0{ + Phase0: v1alpha1Blk, + }, + }) if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err) + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return status.Error(codes.InvalidArgument, err.Error()) + } + return status.Errorf(codes.Internal, "Could not propose block: %v", err) } - - return bs.submitBlock(ctx, root, wrappedPhase0Blk) + return nil } func (bs *Server) submitAltairBlock(ctx context.Context, altairBlk *ethpbv2.BeaconBlockAltair, sig []byte) error { v1alpha1Blk, err := migration.AltairToV1Alpha1SignedBlock(ðpbv2.SignedBeaconBlockAltair{Message: altairBlk, Signature: sig}) if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block") - } - wrappedAltairBlk, err := blocks.NewSignedBeaconBlock(v1alpha1Blk) - if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not prepare block") + return status.Errorf(codes.InvalidArgument, "Could not convert block %v", err) } - - root, err := altairBlk.HashTreeRoot() + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Altair{ + Altair: v1alpha1Blk, + }, + }) if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err) + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return status.Error(codes.InvalidArgument, err.Error()) + } + return status.Errorf(codes.Internal, "Could not propose block: %v", err) } - - return bs.submitBlock(ctx, root, wrappedAltairBlk) + return nil } func (bs *Server) submitBellatrixBlock(ctx context.Context, bellatrixBlk *ethpbv2.BeaconBlockBellatrix, sig []byte) error { @@ -1139,17 +1226,18 @@ func (bs *Server) submitBellatrixBlock(ctx context.Context, bellatrixBlk *ethpbv if err != nil { return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block") } - wrappedBellatrixBlk, err := blocks.NewSignedBeaconBlock(v1alpha1Blk) - if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not prepare block") - } - - root, err := bellatrixBlk.HashTreeRoot() + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Bellatrix{ + Bellatrix: v1alpha1Blk, + }, + }) if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err) + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return status.Error(codes.InvalidArgument, err.Error()) + } + return status.Errorf(codes.Internal, "Could not propose block: %v", err) } - - return bs.submitBlock(ctx, root, wrappedBellatrixBlk) + return nil } func (bs *Server) submitCapellaBlock(ctx context.Context, capellaBlk *ethpbv2.BeaconBlockCapella, sig []byte) error { @@ -1157,42 +1245,16 @@ func (bs *Server) submitCapellaBlock(ctx context.Context, capellaBlk *ethpbv2.Be if err != nil { return status.Errorf(codes.InvalidArgument, "Could not convert block to v1 block") } - wrappedCapellaBlk, err := blocks.NewSignedBeaconBlock(v1alpha1Blk) - if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not prepare block") - } - - root, err := capellaBlk.HashTreeRoot() - if err != nil { - return status.Errorf(codes.InvalidArgument, "Could not tree hash block: %v", err) - } - - return bs.submitBlock(ctx, root, wrappedCapellaBlk) -} - -func (bs *Server) submitBlock(ctx context.Context, blockRoot [fieldparams.RootLength]byte, block interfaces.ReadOnlySignedBeaconBlock) error { - // Do not block proposal critical path with debug logging or block feed updates. - defer func() { - log.WithField("blockRoot", fmt.Sprintf("%#x", bytesutil.Trunc(blockRoot[:]))).Debugf( - "Block proposal received via RPC") - bs.BlockNotifier.BlockFeed().Send(&feed.Event{ - Type: blockfeed.ReceivedBlock, - Data: &blockfeed.ReceivedBlockData{SignedBlock: block}, - }) - }() - - // Broadcast the new block to the network. - blockPb, err := block.Proto() + _, err = bs.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, ð.GenericSignedBeaconBlock{ + Block: ð.GenericSignedBeaconBlock_Capella{ + Capella: v1alpha1Blk, + }, + }) if err != nil { - return errors.Wrap(err, "could not get protobuf block") - } - if err := bs.Broadcaster.Broadcast(ctx, blockPb); err != nil { - return status.Errorf(codes.Internal, "Could not broadcast block: %v", err) - } - - if err := bs.BlockReceiver.ReceiveBlock(ctx, block, blockRoot); err != nil { - return status.Errorf(codes.Internal, "Could not process beacon block: %v", err) + if strings.Contains(err.Error(), validator.CouldNotDecodeBlock) { + return status.Error(codes.InvalidArgument, err.Error()) + } + return status.Errorf(codes.Internal, "Could not propose block: %v", err) } - return nil } diff --git a/beacon-chain/rpc/eth/beacon/blocks_test.go b/beacon-chain/rpc/eth/beacon/blocks_test.go index 0fa61714bd3d..391a4569ab1f 100644 --- a/beacon-chain/rpc/eth/beacon/blocks_test.go +++ b/beacon-chain/rpc/eth/beacon/blocks_test.go @@ -4,12 +4,13 @@ import ( "context" "testing" + "github.com/golang/mock/gomock" "github.com/prysmaticlabs/go-bitfield" mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing" "github.com/prysmaticlabs/prysm/v4/beacon-chain/db" dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing" - mockp2p "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p/testing" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil" + mockSync "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync/initial-sync/testing" "github.com/prysmaticlabs/prysm/v4/config/params" "github.com/prysmaticlabs/prysm/v4/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces" @@ -20,6 +21,7 @@ import ( "github.com/prysmaticlabs/prysm/v4/proto/migration" ethpbalpha "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v4/testing/assert" + mock2 "github.com/prysmaticlabs/prysm/v4/testing/mock" "github.com/prysmaticlabs/prysm/v4/testing/require" "github.com/prysmaticlabs/prysm/v4/testing/util" "google.golang.org/grpc/metadata" @@ -347,319 +349,215 @@ func TestServer_ListBlockHeaders(t *testing.T) { }) } -func TestServer_SubmitBlock_OK(t *testing.T) { - t.Run("Phase 0", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlock() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") +func TestServer_SubmitBlock(t *testing.T) { + ctrl := gomock.NewController(t) - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + t.Run("Phase 0", func(t *testing.T) { + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlock() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - v1Block, err := migration.V1Alpha1ToV1SignedBlock(req) - require.NoError(t, err) - util.SaveBlock(t, ctx, beaconDB, req) + blockReq := ðpbv2.SignedBeaconBlockContainer{ - Message: ðpbv2.SignedBeaconBlockContainer_Phase0Block{Phase0Block: v1Block.Block}, - Signature: v1Block.Signature, + Message: ðpbv2.SignedBeaconBlockContainer_Phase0Block{Phase0Block: ðpbv1.BeaconBlock{}}, + Signature: []byte("sig"), } - _, err = beaconChainServer.SubmitBlock(context.Background(), blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err := server.SubmitBlock(context.Background(), blockReq) + assert.NoError(t, err) }) - t.Run("Altair", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockAltair() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlockAltair() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - v2Block, err := migration.V1Alpha1BeaconBlockAltairToV2(req.Block) - require.NoError(t, err) - util.SaveBlock(t, ctx, beaconDB, req) + blockReq := ðpbv2.SignedBeaconBlockContainer{ - Message: ðpbv2.SignedBeaconBlockContainer_AltairBlock{AltairBlock: v2Block}, - Signature: req.Signature, + Message: ðpbv2.SignedBeaconBlockContainer_AltairBlock{AltairBlock: ðpbv2.BeaconBlockAltair{}}, + Signature: []byte("sig"), } - _, err = beaconChainServer.SubmitBlock(context.Background(), blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err := server.SubmitBlock(context.Background(), blockReq) + assert.NoError(t, err) }) - t.Run("Bellatrix", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockBellatrix() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlockBellatrix() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - v2Block, err := migration.V1Alpha1BeaconBlockBellatrixToV2(req.Block) - require.NoError(t, err) - util.SaveBlock(t, ctx, beaconDB, req) + blockReq := ðpbv2.SignedBeaconBlockContainer{ - Message: ðpbv2.SignedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: v2Block}, - Signature: req.Signature, + Message: ðpbv2.SignedBeaconBlockContainer_BellatrixBlock{BellatrixBlock: ðpbv2.BeaconBlockBellatrix{}}, + Signature: []byte("sig"), } - _, err = beaconChainServer.SubmitBlock(context.Background(), blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err := server.SubmitBlock(context.Background(), blockReq) + assert.NoError(t, err) }) - t.Run("Capella", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockCapella() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlockCapella() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - v2Block, err := migration.V1Alpha1BeaconBlockCapellaToV2(req.Block) - require.NoError(t, err) - util.SaveBlock(t, ctx, beaconDB, req) + blockReq := ðpbv2.SignedBeaconBlockContainer{ - Message: ðpbv2.SignedBeaconBlockContainer_CapellaBlock{CapellaBlock: v2Block}, - Signature: req.Signature, + Message: ðpbv2.SignedBeaconBlockContainer_CapellaBlock{CapellaBlock: ðpbv2.BeaconBlockCapella{}}, + Signature: []byte("sig"), } - _, err = beaconChainServer.SubmitBlock(context.Background(), blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err := server.SubmitBlock(context.Background(), blockReq) + assert.NoError(t, err) + }) + t.Run("sync not ready", func(t *testing.T) { + chainService := &mock.ChainService{} + v1Server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: true}, + HeadFetcher: chainService, + TimeFetcher: chainService, + OptimisticModeFetcher: chainService, + } + _, err := v1Server.SubmitBlock(context.Background(), nil) + require.ErrorContains(t, "Syncing to latest head", err) }) } -func TestServer_SubmitBlockSSZ_OK(t *testing.T) { - t.Run("Phase 0", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlock() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") +func TestServer_SubmitBlockSSZ(t *testing.T) { + ctrl := gomock.NewController(t) + ctx := context.Background() - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + t.Run("Phase 0", func(t *testing.T) { + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlock() - req.Block.Slot = 5 - req.Block.ParentRoot = bsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + + b := util.NewBeaconBlock() + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "phase0") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) }) - t.Run("Altair", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockAltair() - util.SaveBlock(t, context.Background(), beaconDB, genesis) - - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() - require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlockAltair() - req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().AltairForkEpoch)) - req.Block.ParentRoot = bsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + + b := util.NewBeaconBlockAltair() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().AltairForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "altair") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) }) - t.Run("Bellatrix", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockBellatrix() - util.SaveBlock(t, context.Background(), beaconDB, genesis) + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() + b := util.NewBeaconBlockBellatrix() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + blockReq := ðpbv2.SSZContainer{ + Data: ssz, + } + md := metadata.MD{} + md.Set(versionHeader, "bellatrix") + sszCtx := metadata.NewIncomingContext(ctx, md) + _, err = server.SubmitBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) + }) + t.Run("Bellatrix blinded", func(t *testing.T) { + server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: false}, } - req := util.NewBeaconBlockBellatrix() - req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch)) - req.Block.ParentRoot = bsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + + b := util.NewBlindedBeaconBlockBellatrix() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().BellatrixForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "bellatrix") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlockSSZ(sszCtx, blockReq) + assert.NotNil(t, err) }) - t.Run("Capella", func(t *testing.T) { - beaconDB := dbTest.SetupDB(t) - ctx := context.Background() - - genesis := util.NewBeaconBlockCapella() - util.SaveBlock(t, context.Background(), beaconDB, genesis) + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.Any()) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } - numDeposits := uint64(64) - beaconState, _ := util.DeterministicGenesisState(t, numDeposits) - bsRoot, err := beaconState.HashTreeRoot(ctx) - require.NoError(t, err) - genesisRoot, err := genesis.Block.HashTreeRoot() + b := util.NewBeaconBlockCapella() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) - require.NoError(t, beaconDB.SaveState(ctx, beaconState, genesisRoot), "Could not save genesis state") - - c := &mock.ChainService{Root: bsRoot[:], State: beaconState} - beaconChainServer := &Server{ - BeaconDB: beaconDB, - BlockReceiver: c, - ChainInfoFetcher: c, - BlockNotifier: c.BlockNotifier(), - Broadcaster: mockp2p.NewTestP2P(t), - HeadFetcher: c, + blockReq := ðpbv2.SSZContainer{ + Data: ssz, } - req := util.NewBeaconBlockCapella() - req.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch)) - req.Block.ParentRoot = bsRoot[:] - util.SaveBlock(t, ctx, beaconDB, req) - blockSsz, err := req.MarshalSSZ() + md := metadata.MD{} + md.Set(versionHeader, "capella") + sszCtx := metadata.NewIncomingContext(ctx, md) + _, err = server.SubmitBlockSSZ(sszCtx, blockReq) + assert.NoError(t, err) + }) + t.Run("Capella blinded", func(t *testing.T) { + server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } + + b := util.NewBlindedBeaconBlockCapella() + b.Block.Slot = params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().CapellaForkEpoch)) + ssz, err := b.MarshalSSZ() require.NoError(t, err) blockReq := ðpbv2.SSZContainer{ - Data: blockSsz, + Data: ssz, } md := metadata.MD{} md.Set(versionHeader, "capella") sszCtx := metadata.NewIncomingContext(ctx, md) - _, err = beaconChainServer.SubmitBlockSSZ(sszCtx, blockReq) - assert.NoError(t, err, "Could not propose block correctly") + _, err = server.SubmitBlockSSZ(sszCtx, blockReq) + assert.NotNil(t, err) + }) + t.Run("sync not ready", func(t *testing.T) { + chainService := &mock.ChainService{} + v1Server := &Server{ + SyncChecker: &mockSync.Sync{IsSyncing: true}, + HeadFetcher: chainService, + TimeFetcher: chainService, + OptimisticModeFetcher: chainService, + } + _, err := v1Server.SubmitBlockSSZ(context.Background(), nil) + require.ErrorContains(t, "Syncing to latest head", err) }) } diff --git a/beacon-chain/rpc/eth/beacon/server.go b/beacon-chain/rpc/eth/beacon/server.go index def50b84b38d..366c137a9f9d 100644 --- a/beacon-chain/rpc/eth/beacon/server.go +++ b/beacon-chain/rpc/eth/beacon/server.go @@ -15,9 +15,9 @@ import ( "github.com/prysmaticlabs/prysm/v4/beacon-chain/operations/voluntaryexits" "github.com/prysmaticlabs/prysm/v4/beacon-chain/p2p" "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/lookup" - v1alpha1validator "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/prysm/v1alpha1/validator" "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/stategen" "github.com/prysmaticlabs/prysm/v4/beacon-chain/sync" + eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1" ) // Server defines a server implementation of the gRPC Beacon Chain service, @@ -37,8 +37,9 @@ type Server struct { Stater lookup.Stater Blocker lookup.Blocker HeadFetcher blockchain.HeadFetcher + TimeFetcher blockchain.TimeFetcher OptimisticModeFetcher blockchain.OptimisticModeFetcher - V1Alpha1ValidatorServer *v1alpha1validator.Server + V1Alpha1ValidatorServer eth.BeaconNodeValidatorServer SyncChecker sync.Checker CanonicalHistory *stategen.CanonicalHistory ExecutionPayloadReconstructor execution.ExecutionPayloadReconstructor diff --git a/beacon-chain/rpc/eth/validator/validator_test.go b/beacon-chain/rpc/eth/validator/validator_test.go index 05a0ff5819be..dc553c03c595 100644 --- a/beacon-chain/rpc/eth/validator/validator_test.go +++ b/beacon-chain/rpc/eth/validator/validator_test.go @@ -810,16 +810,14 @@ func TestProduceBlockV2(t *testing.T) { require.ErrorContains(t, "The node is currently optimistic and cannot serve validators", err) }) t.Run("sync not ready", func(t *testing.T) { - st, err := util.NewBeaconState() - require.NoError(t, err) - chainService := &mockChain.ChainService{State: st} + chainService := &mockChain.ChainService{} v1Server := &Server{ SyncChecker: &mockSync.Sync{IsSyncing: true}, HeadFetcher: chainService, TimeFetcher: chainService, OptimisticModeFetcher: chainService, } - _, err = v1Server.ProduceBlockV2(context.Background(), nil) + _, err := v1Server.ProduceBlockV2(context.Background(), nil) require.ErrorContains(t, "Syncing to latest head", err) }) } @@ -939,16 +937,14 @@ func TestProduceBlockV2SSZ(t *testing.T) { require.ErrorContains(t, "The node is currently optimistic and cannot serve validators", err) }) t.Run("sync not ready", func(t *testing.T) { - st, err := util.NewBeaconState() - require.NoError(t, err) - chainService := &mockChain.ChainService{State: st} + chainService := &mockChain.ChainService{} v1Server := &Server{ SyncChecker: &mockSync.Sync{IsSyncing: true}, HeadFetcher: chainService, TimeFetcher: chainService, OptimisticModeFetcher: chainService, } - _, err = v1Server.ProduceBlockV2SSZ(context.Background(), nil) + _, err := v1Server.ProduceBlockV2SSZ(context.Background(), nil) require.ErrorContains(t, "Syncing to latest head", err) }) } @@ -1081,9 +1077,7 @@ func TestProduceBlindedBlock(t *testing.T) { require.ErrorContains(t, "Block builder not configured", err) }) t.Run("sync not ready", func(t *testing.T) { - st, err := util.NewBeaconState() - require.NoError(t, err) - chainService := &mockChain.ChainService{State: st} + chainService := &mockChain.ChainService{} v1Server := &Server{ SyncChecker: &mockSync.Sync{IsSyncing: true}, HeadFetcher: chainService, @@ -1091,7 +1085,7 @@ func TestProduceBlindedBlock(t *testing.T) { OptimisticModeFetcher: chainService, BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true}, } - _, err = v1Server.ProduceBlindedBlock(context.Background(), nil) + _, err := v1Server.ProduceBlindedBlock(context.Background(), nil) require.ErrorContains(t, "Syncing to latest head", err) }) } @@ -1224,9 +1218,7 @@ func TestProduceBlindedBlockSSZ(t *testing.T) { require.ErrorContains(t, "Block builder not configured", err) }) t.Run("sync not ready", func(t *testing.T) { - st, err := util.NewBeaconState() - require.NoError(t, err) - chainService := &mockChain.ChainService{State: st} + chainService := &mockChain.ChainService{} v1Server := &Server{ SyncChecker: &mockSync.Sync{IsSyncing: true}, HeadFetcher: chainService, @@ -1234,7 +1226,7 @@ func TestProduceBlindedBlockSSZ(t *testing.T) { OptimisticModeFetcher: chainService, BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true}, } - _, err = v1Server.ProduceBlindedBlockSSZ(context.Background(), nil) + _, err := v1Server.ProduceBlindedBlockSSZ(context.Background(), nil) require.ErrorContains(t, "Syncing to latest head", err) }) } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index d426b955c07f..199e60136a75 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -37,7 +37,11 @@ import ( // eth1DataNotification is a latch to stop flooding logs with the same warning. var eth1DataNotification bool -const eth1dataTimeout = 2 * time.Second +const ( + // CouldNotDecodeBlock means that a signed beacon block couldn't be created from the block present in the request. + CouldNotDecodeBlock = "Could not decode block" + eth1dataTimeout = 2 * time.Second +) // GetBeaconBlock is called by a proposer during its assigned slot to request a block to sign // by passing in the slot and the signed randao reveal of the slot. @@ -205,7 +209,11 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) ( func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) { ctx, span := trace.StartSpan(ctx, "ProposerServer.ProposeBeaconBlock") defer span.End() - return vs.proposeGenericBeaconBlock(ctx, req) + blk, err := blocks.NewSignedBeaconBlock(req.Block) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%s: %v", CouldNotDecodeBlock, err) + } + return vs.proposeGenericBeaconBlock(ctx, blk) } // PrepareBeaconProposer caches and updates the fee recipient for the given proposer. diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 78ba2bb820ee..b19d0393043a 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -337,6 +337,7 @@ func (s *Service) Start() { Blocker: blocker, OptimisticModeFetcher: s.cfg.OptimisticModeFetcher, HeadFetcher: s.cfg.HeadFetcher, + TimeFetcher: s.cfg.GenesisTimeFetcher, VoluntaryExitsPool: s.cfg.ExitPool, V1Alpha1ValidatorServer: validatorServer, SyncChecker: s.cfg.SyncService,