Files
prysm/beacon-chain/rpc/eth/beacon/handlers_test.go
Sammy Rosso 90fb2325db /eth/v1/config/deposit_contract return string instead of uint (#12952)
* Convert uint to string

* Add test

* Rename test
2023-09-25 13:02:47 +00:00

1677 lines
64 KiB
Go

package beacon
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/mock/gomock"
"github.com/gorilla/mux"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/api"
chainMock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
dbTest "github.com/prysmaticlabs/prysm/v4/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v4/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
rpctesting "github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared/testing"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
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"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
"github.com/prysmaticlabs/prysm/v4/proto/migration"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"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"
"github.com/prysmaticlabs/prysm/v4/time/slots"
"github.com/stretchr/testify/mock"
)
func TestPublishBlockV2(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Phase0)
converted, err := shared.BeaconBlockFromConsensus(block.Phase0.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlock
err = json.Unmarshal([]byte(rpctesting.Phase0Block), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.Phase0Block)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Altair)
converted, err := shared.BeaconBlockAltairFromConsensus(block.Altair.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockAltair
err = json.Unmarshal([]byte(rpctesting.AltairBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.AltairBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Bellatrix)
converted, err := shared.BeaconBlockBellatrixFromConsensus(block.Bellatrix.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockBellatrix
err = json.Unmarshal([]byte(rpctesting.BellatrixBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Capella)
converted, err := shared.BeaconBlockCapellaFromConsensus(block.Capella.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockCapella
err = json.Unmarshal([]byte(rpctesting.CapellaBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.CapellaBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Deneb)
converted, err := shared.BeaconBlockDenebFromConsensus(block.Deneb.Block.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockContentsDeneb
err = json.Unmarshal([]byte(rpctesting.DenebBlockContents), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.SignedBlock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.DenebBlockContents)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedBellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "please add the api header"))
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
t.Run("invalid block with version header", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BadCapellaBlock)))
request.Header.Set(api.VersionHeader, version.String(version.Capella))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
body := writer.Body.String()
assert.Equal(t, true, strings.Contains(body, "Body does not represent a valid block type"))
assert.Equal(t, true, strings.Contains(body, fmt.Sprintf("could not decode %s request body into consensus block:", version.String(version.Capella))))
})
t.Run("syncing", func(t *testing.T) {
chainService := &chainMock.ChainService{}
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte("foo")))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusServiceUnavailable, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Beacon node is currently syncing and not serving request on that endpoint"))
})
}
func TestPublishBlockV2SSZ(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Bellatrix)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var bellablock shared.SignedBeaconBlockBellatrix
err := json.Unmarshal([]byte(rpctesting.BellatrixBlock), &bellablock)
require.NoError(t, err)
genericBlock, err := bellablock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetBellatrix().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Capella)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var cblock shared.SignedBeaconBlockCapella
err := json.Unmarshal([]byte(rpctesting.CapellaBlock), &cblock)
require.NoError(t, err)
genericBlock, err := cblock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetCapella().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_Deneb)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var dblock shared.SignedBeaconBlockContentsDeneb
err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &dblock)
require.NoError(t, err)
genericBlock, err := dblock.ToGeneric()
require.NoError(t, err)
v2block, err := migration.V1Alpha1SignedBeaconBlockDenebAndBlobsToV2(genericBlock.GetDeneb())
require.NoError(t, err)
sszvalue, err := v2block.MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedBellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlockV2(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
}
func TestPublishBlindedBlockV2(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Phase 0", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Phase0)
converted, err := shared.BeaconBlockFromConsensus(block.Phase0.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlock
err = json.Unmarshal([]byte(rpctesting.Phase0Block), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.Phase0Block)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Altair", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_Altair)
converted, err := shared.BeaconBlockAltairFromConsensus(block.Altair.Block)
require.NoError(t, err)
var signedblock *shared.SignedBeaconBlockAltair
err = json.Unmarshal([]byte(rpctesting.AltairBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.AltairBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Blinded Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedBellatrix)
converted, err := shared.BlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix.Block)
require.NoError(t, err)
var signedblock *shared.SignedBlindedBeaconBlockBellatrix
err = json.Unmarshal([]byte(rpctesting.BlindedBellatrixBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedBellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Blinded Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedCapella)
converted, err := shared.BlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella.Block)
require.NoError(t, err)
var signedblock *shared.SignedBlindedBeaconBlockCapella
err = json.Unmarshal([]byte(rpctesting.BlindedCapellaBlock), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedCapellaBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Blinded Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
block, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedDeneb)
converted, err := shared.BlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb.SignedBlindedBlock.Message)
require.NoError(t, err)
var signedblock *shared.SignedBlindedBeaconBlockContentsDeneb
err = json.Unmarshal([]byte(rpctesting.BlindedDenebBlockContents), &signedblock)
require.NoError(t, err)
require.DeepEqual(t, converted, signedblock.SignedBlindedBlock.Message)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BlindedDenebBlockContents)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "please add the api header"))
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
t.Run("invalid block with version header", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BadBlindedBellatrixBlock)))
request.Header.Set(api.VersionHeader, version.String(version.Bellatrix))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
body := writer.Body.String()
assert.Equal(t, true, strings.Contains(body, "Body does not represent a valid block type"))
assert.Equal(t, true, strings.Contains(body, fmt.Sprintf("could not decode %s request body into consensus block:", version.String(version.Bellatrix))))
})
t.Run("syncing", func(t *testing.T) {
chainService := &chainMock.ChainService{}
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: true},
HeadFetcher: chainService,
TimeFetcher: chainService,
OptimisticModeFetcher: chainService,
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte("foo")))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusServiceUnavailable, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Beacon node is currently syncing and not serving request on that endpoint"))
})
}
func TestPublishBlindedBlockV2SSZ(t *testing.T) {
ctrl := gomock.NewController(t)
t.Run("Bellatrix", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedBellatrix)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var bellablock shared.SignedBlindedBeaconBlockBellatrix
err := json.Unmarshal([]byte(rpctesting.BlindedBellatrixBlock), &bellablock)
require.NoError(t, err)
genericBlock, err := bellablock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetBlindedBellatrix().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Capella", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedCapella)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var cblock shared.SignedBlindedBeaconBlockCapella
err := json.Unmarshal([]byte(rpctesting.BlindedCapellaBlock), &cblock)
require.NoError(t, err)
genericBlock, err := cblock.ToGeneric()
require.NoError(t, err)
sszvalue, err := genericBlock.GetBlindedCapella().MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("Deneb", func(t *testing.T) {
v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl)
v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool {
_, ok := req.Block.(*eth.GenericSignedBeaconBlock_BlindedDeneb)
return ok
}))
server := &Server{
V1Alpha1ValidatorServer: v1alpha1Server,
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
var cblock shared.SignedBlindedBeaconBlockContentsDeneb
err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlockContents), &cblock)
require.NoError(t, err)
genericBlock, err := cblock.ToGeneric()
require.NoError(t, err)
v1block, err := migration.V1Alpha1SignedBlindedBlockAndBlobsDenebToV2Blinded(genericBlock.GetBlindedDeneb())
require.NoError(t, err)
sszvalue, err := v1block.MarshalSSZ()
require.NoError(t, err)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader(sszvalue))
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
})
t.Run("invalid block", func(t *testing.T) {
server := &Server{
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.BellatrixBlock)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlindedBlockV2(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.Equal(t, true, strings.Contains(writer.Body.String(), "Body does not represent a valid block type"))
})
}
func TestValidateConsensus(t *testing.T) {
ctx := context.Background()
parentState, privs := util.DeterministicGenesisState(t, params.MinimalSpecConfig().MinGenesisActiveValidatorCount)
parentBlock, err := util.GenerateFullBlock(parentState, privs, util.DefaultBlockGenConfig(), parentState.Slot())
require.NoError(t, err)
parentSbb, err := blocks.NewSignedBeaconBlock(parentBlock)
require.NoError(t, err)
st, err := transition.ExecuteStateTransition(ctx, parentState, parentSbb)
require.NoError(t, err)
block, err := util.GenerateFullBlock(st, privs, util.DefaultBlockGenConfig(), st.Slot())
require.NoError(t, err)
sbb, err := blocks.NewSignedBeaconBlock(block)
require.NoError(t, err)
parentRoot, err := parentSbb.Block().HashTreeRoot()
require.NoError(t, err)
server := &Server{
Blocker: &testutil.MockBlocker{RootBlockMap: map[[32]byte]interfaces.ReadOnlySignedBeaconBlock{parentRoot: parentSbb}},
Stater: &testutil.MockStater{StatesByRoot: map[[32]byte]state.BeaconState{bytesutil.ToBytes32(parentBlock.Block.StateRoot): parentState}},
}
require.NoError(t, server.validateConsensus(ctx, sbb))
}
func TestValidateEquivocation(t *testing.T) {
t.Run("ok", func(t *testing.T) {
st, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, st.SetSlot(10))
fc := doublylinkedtree.New()
require.NoError(t, fc.InsertNode(context.Background(), st, bytesutil.ToBytes32([]byte("root"))))
server := &Server{
ForkchoiceFetcher: &chainMock.ChainService{ForkChoiceStore: fc},
}
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
blk.SetSlot(st.Slot() + 1)
require.NoError(t, server.validateEquivocation(blk.Block()))
})
t.Run("block already exists", func(t *testing.T) {
st, err := util.NewBeaconState()
require.NoError(t, err)
require.NoError(t, st.SetSlot(10))
fc := doublylinkedtree.New()
require.NoError(t, fc.InsertNode(context.Background(), st, bytesutil.ToBytes32([]byte("root"))))
server := &Server{
ForkchoiceFetcher: &chainMock.ChainService{ForkChoiceStore: fc},
}
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlock())
require.NoError(t, err)
blk.SetSlot(st.Slot())
assert.ErrorContains(t, "already exists", server.validateEquivocation(blk.Block()))
})
}
func TestServer_GetBlockRoot(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
url := "http://example.com/eth/v1/beacon/blocks/{block_id}/root"
genBlk, blkContainers := fillDBTestBlocks(ctx, t, beaconDB)
headBlock := blkContainers[len(blkContainers)-1]
t.Run("get root", func(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(headBlock.Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block)
require.NoError(t, err)
mockChainFetcher := &chainMock.ChainService{
DB: beaconDB,
Block: wsb,
Root: headBlock.BlockRoot,
FinalizedCheckPoint: &eth.Checkpoint{Root: blkContainers[64].BlockRoot},
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
BeaconDB: beaconDB,
ChainInfoFetcher: mockChainFetcher,
HeadFetcher: mockChainFetcher,
OptimisticModeFetcher: mockChainFetcher,
FinalizationFetcher: mockChainFetcher,
}
root, err := genBlk.Block.HashTreeRoot()
require.NoError(t, err)
tests := []struct {
name string
blockID map[string]string
want string
wantErr string
wantCode int
}{
{
name: "bad formatting",
blockID: map[string]string{"block_id": "3bad0"},
wantErr: "Could not parse block ID",
wantCode: http.StatusBadRequest,
},
{
name: "canonical slot",
blockID: map[string]string{"block_id": "30"},
want: hexutil.Encode(blkContainers[30].BlockRoot),
wantErr: "",
wantCode: http.StatusOK,
},
{
name: "head",
blockID: map[string]string{"block_id": "head"},
want: hexutil.Encode(headBlock.BlockRoot),
wantErr: "",
wantCode: http.StatusOK,
},
{
name: "finalized",
blockID: map[string]string{"block_id": "finalized"},
want: hexutil.Encode(blkContainers[64].BlockRoot),
wantErr: "",
wantCode: http.StatusOK,
},
{
name: "genesis",
blockID: map[string]string{"block_id": "genesis"},
want: hexutil.Encode(root[:]),
wantErr: "",
wantCode: http.StatusOK,
},
{
name: "genesis root",
blockID: map[string]string{"block_id": hexutil.Encode(root[:])},
want: hexutil.Encode(root[:]),
wantErr: "",
wantCode: http.StatusOK,
},
{
name: "root",
blockID: map[string]string{"block_id": hexutil.Encode(blkContainers[20].BlockRoot)},
want: hexutil.Encode(blkContainers[20].BlockRoot),
wantErr: "",
wantCode: http.StatusOK,
},
{
name: "non-existent root",
blockID: map[string]string{"block_id": hexutil.Encode(bytesutil.PadTo([]byte("hi there"), 32))},
wantErr: "Could not find block",
wantCode: http.StatusNotFound,
},
{
name: "slot",
blockID: map[string]string{"block_id": "40"},
want: hexutil.Encode(blkContainers[40].BlockRoot),
wantErr: "",
wantCode: http.StatusOK,
},
{
name: "no block",
blockID: map[string]string{"block_id": "105"},
wantErr: "Could not find any blocks with given slot",
wantCode: http.StatusNotFound,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, tt.blockID)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockRoot(writer, request)
assert.Equal(t, tt.wantCode, writer.Code)
resp := &BlockRootResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
if tt.wantErr != "" {
require.ErrorContains(t, tt.wantErr, errors.New(writer.Body.String()))
return
}
require.NotNil(t, resp)
require.DeepEqual(t, resp.Data.Root, tt.want)
})
}
})
t.Run("execution optimistic", func(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(headBlock.Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block)
require.NoError(t, err)
mockChainFetcher := &chainMock.ChainService{
DB: beaconDB,
Block: wsb,
Root: headBlock.BlockRoot,
FinalizedCheckPoint: &eth.Checkpoint{Root: blkContainers[64].BlockRoot},
Optimistic: true,
FinalizedRoots: map[[32]byte]bool{},
OptimisticRoots: map[[32]byte]bool{
bytesutil.ToBytes32(headBlock.BlockRoot): true,
},
}
bs := &Server{
BeaconDB: beaconDB,
ChainInfoFetcher: mockChainFetcher,
HeadFetcher: mockChainFetcher,
OptimisticModeFetcher: mockChainFetcher,
FinalizationFetcher: mockChainFetcher,
}
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockRoot(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &BlockRootResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.DeepEqual(t, resp.ExecutionOptimistic, true)
})
t.Run("finalized", func(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(headBlock.Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block)
require.NoError(t, err)
mockChainFetcher := &chainMock.ChainService{
DB: beaconDB,
Block: wsb,
Root: headBlock.BlockRoot,
FinalizedCheckPoint: &eth.Checkpoint{Root: blkContainers[64].BlockRoot},
Optimistic: true,
FinalizedRoots: map[[32]byte]bool{
bytesutil.ToBytes32(blkContainers[32].BlockRoot): true,
bytesutil.ToBytes32(blkContainers[64].BlockRoot): false,
},
}
bs := &Server{
BeaconDB: beaconDB,
ChainInfoFetcher: mockChainFetcher,
HeadFetcher: mockChainFetcher,
OptimisticModeFetcher: mockChainFetcher,
FinalizationFetcher: mockChainFetcher,
}
t.Run("true", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "32"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockRoot(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &BlockRootResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.DeepEqual(t, resp.Finalized, true)
})
t.Run("false", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "64"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockRoot(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &BlockRootResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.DeepEqual(t, resp.Finalized, false)
})
})
}
func TestGetStateFork(t *testing.T) {
ctx := context.Background()
request := httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/states/{state_id}/fork", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
request.Header.Set("Accept", "application/octet-stream")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
fillFork := func(state *eth.BeaconState) error {
state.Fork = &eth.Fork{
PreviousVersion: []byte("prev"),
CurrentVersion: []byte("curr"),
Epoch: 123,
}
return nil
}
fakeState, err := util.NewBeaconState(fillFork)
require.NoError(t, err)
db := dbTest.SetupDB(t)
chainService := &chainMock.ChainService{}
server := &Server{
Stater: &testutil.MockStater{
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
server.GetStateFork(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
var stateForkReponse *GetStateForkResponse
err = json.Unmarshal(writer.Body.Bytes(), &stateForkReponse)
require.NoError(t, err)
expectedFork := fakeState.Fork()
assert.Equal(t, fmt.Sprint(expectedFork.Epoch), stateForkReponse.Data.Epoch)
assert.DeepEqual(t, hexutil.Encode(expectedFork.CurrentVersion), stateForkReponse.Data.CurrentVersion)
assert.DeepEqual(t, hexutil.Encode(expectedFork.PreviousVersion), stateForkReponse.Data.PreviousVersion)
t.Run("execution optimistic", func(t *testing.T) {
request = httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/states/{state_id}/fork", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
request.Header.Set("Accept", "application/octet-stream")
writer = httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
chainService = &chainMock.ChainService{Optimistic: true}
server = &Server{
Stater: &testutil.MockStater{
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
server.GetStateFork(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
err = json.Unmarshal(writer.Body.Bytes(), &stateForkReponse)
require.NoError(t, err)
assert.DeepEqual(t, true, stateForkReponse.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
request = httptest.NewRequest(http.MethodGet, "http://foo.example/eth/v1/beacon/states/{state_id}/fork", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
request.Header.Set("Accept", "application/octet-stream")
writer = httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
chainService = &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
}
server = &Server{
Stater: &testutil.MockStater{
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
server.GetStateFork(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
err = json.Unmarshal(writer.Body.Bytes(), &stateForkReponse)
require.NoError(t, err)
assert.DeepEqual(t, true, stateForkReponse.Finalized)
})
}
func TestGetCommittees(t *testing.T) {
db := dbTest.SetupDB(t)
ctx := context.Background()
url := "http://example.com/eth/v1/beacon/states/{state_id}/committees"
var st state.BeaconState
st, _ = util.DeterministicGenesisState(t, 8192)
epoch := slots.ToEpoch(st.Slot())
chainService := &chainMock.ChainService{}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
t.Run("Head all committees", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetCommittees(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetCommitteesResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, int(params.BeaconConfig().SlotsPerEpoch)*2, len(resp.Data))
for _, datum := range resp.Data {
index, err := strconv.ParseUint(datum.Index, 10, 32)
require.NoError(t, err)
slot, err := strconv.ParseUint(datum.Slot, 10, 32)
require.NoError(t, err)
assert.Equal(t, true, index == 0 || index == 1)
assert.Equal(t, epoch, slots.ToEpoch(primitives.Slot(slot)))
}
})
t.Run("Head all committees of epoch 10", func(t *testing.T) {
query := url + "?epoch=10"
request := httptest.NewRequest(http.MethodGet, query, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetCommittees(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetCommitteesResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
for _, datum := range resp.Data {
slot, err := strconv.ParseUint(datum.Slot, 10, 32)
require.NoError(t, err)
assert.Equal(t, true, slot >= 320 && slot <= 351)
}
})
t.Run("Head all committees of slot 4", func(t *testing.T) {
query := url + "?slot=4"
request := httptest.NewRequest(http.MethodGet, query, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetCommittees(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetCommitteesResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, 2, len(resp.Data))
exSlot := uint64(4)
exIndex := uint64(0)
for _, datum := range resp.Data {
slot, err := strconv.ParseUint(datum.Slot, 10, 32)
require.NoError(t, err)
index, err := strconv.ParseUint(datum.Index, 10, 32)
require.NoError(t, err)
assert.Equal(t, epoch, slots.ToEpoch(primitives.Slot(slot)))
assert.Equal(t, exSlot, slot)
assert.Equal(t, exIndex, index)
exIndex++
}
})
t.Run("Head all committees of index 1", func(t *testing.T) {
query := url + "?index=1"
request := httptest.NewRequest(http.MethodGet, query, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetCommittees(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetCommitteesResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, int(params.BeaconConfig().SlotsPerEpoch), len(resp.Data))
exSlot := uint64(0)
exIndex := uint64(1)
for _, datum := range resp.Data {
slot, err := strconv.ParseUint(datum.Slot, 10, 32)
require.NoError(t, err)
index, err := strconv.ParseUint(datum.Index, 10, 32)
require.NoError(t, err)
assert.Equal(t, epoch, slots.ToEpoch(primitives.Slot(slot)))
assert.Equal(t, exSlot, slot)
assert.Equal(t, exIndex, index)
exSlot++
}
})
t.Run("Head all committees of slot 2, index 1", func(t *testing.T) {
query := url + "?slot=2&index=1"
request := httptest.NewRequest(http.MethodGet, query, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetCommittees(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetCommitteesResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, 1, len(resp.Data))
exIndex := uint64(1)
exSlot := uint64(2)
for _, datum := range resp.Data {
index, err := strconv.ParseUint(datum.Index, 10, 32)
require.NoError(t, err)
slot, err := strconv.ParseUint(datum.Slot, 10, 32)
require.NoError(t, err)
assert.Equal(t, epoch, slots.ToEpoch(primitives.Slot(slot)))
assert.Equal(t, exSlot, slot)
assert.Equal(t, exIndex, index)
}
})
t.Run("Execution optimistic", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
chainService = &chainMock.ChainService{Optimistic: true}
s = &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetCommittees(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetCommitteesResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("Finalized", func(t *testing.T) {
parentRoot := [32]byte{'a'}
blk := util.NewBeaconBlock()
blk.Block.ParentRoot = parentRoot[:]
root, err := blk.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, db, blk)
require.NoError(t, db.SaveGenesisBlockRoot(ctx, root))
headerRoot, err := st.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
chainService = &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
}
s = &Server{
Stater: &testutil.MockStater{
BeaconState: st,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
BeaconDB: db,
}
request := httptest.NewRequest(http.MethodGet, url, nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetCommittees(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetCommitteesResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.NoError(t, err)
assert.Equal(t, true, resp.Finalized)
})
}
func TestGetBlockHeaders(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
ctx := context.Background()
_, blkContainers := fillDBTestBlocks(ctx, t, beaconDB)
headBlock := blkContainers[len(blkContainers)-1]
b1 := util.NewBeaconBlock()
b1.Block.Slot = 30
b1.Block.ParentRoot = bytesutil.PadTo([]byte{1}, 32)
util.SaveBlock(t, ctx, beaconDB, b1)
b2 := util.NewBeaconBlock()
b2.Block.Slot = 30
b2.Block.ParentRoot = bytesutil.PadTo([]byte{4}, 32)
util.SaveBlock(t, ctx, beaconDB, b2)
b3 := util.NewBeaconBlock()
b3.Block.Slot = 31
b3.Block.ParentRoot = bytesutil.PadTo([]byte{1}, 32)
util.SaveBlock(t, ctx, beaconDB, b3)
b4 := util.NewBeaconBlock()
b4.Block.Slot = 28
b4.Block.ParentRoot = bytesutil.PadTo([]byte{1}, 32)
util.SaveBlock(t, ctx, beaconDB, b4)
url := "http://example.com/eth/v1/beacon/headers"
t.Run("list headers", func(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(headBlock.Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block)
require.NoError(t, err)
mockChainFetcher := &chainMock.ChainService{
DB: beaconDB,
Block: wsb,
Root: headBlock.BlockRoot,
FinalizedCheckPoint: &eth.Checkpoint{Root: blkContainers[64].BlockRoot},
FinalizedRoots: map[[32]byte]bool{},
}
bs := &Server{
BeaconDB: beaconDB,
ChainInfoFetcher: mockChainFetcher,
OptimisticModeFetcher: mockChainFetcher,
FinalizationFetcher: mockChainFetcher,
}
tests := []struct {
name string
slot primitives.Slot
parentRoot string
want []*eth.SignedBeaconBlock
wantErr bool
}{
{
name: "slot",
slot: primitives.Slot(30),
parentRoot: "",
want: []*eth.SignedBeaconBlock{
blkContainers[30].Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block,
b1,
b2,
},
},
{
name: "parent root",
parentRoot: hexutil.Encode(b1.Block.ParentRoot),
want: []*eth.SignedBeaconBlock{
blkContainers[1].Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block,
b1,
b3,
b4,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
urlWithParams := fmt.Sprintf("%s?slot=%d&parent_root=%s", url, tt.slot, tt.parentRoot)
request := httptest.NewRequest(http.MethodGet, urlWithParams, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockHeaders(writer, request)
resp := &GetBlockHeadersResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.Equal(t, len(tt.want), len(resp.Data))
for i, blk := range tt.want {
expectedBodyRoot, err := blk.Block.Body.HashTreeRoot()
require.NoError(t, err)
expectedHeader := &eth.BeaconBlockHeader{
Slot: blk.Block.Slot,
ProposerIndex: blk.Block.ProposerIndex,
ParentRoot: blk.Block.ParentRoot,
StateRoot: make([]byte, 32),
BodyRoot: expectedBodyRoot[:],
}
expectedHeaderRoot, err := expectedHeader.HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, hexutil.Encode(expectedHeaderRoot[:]), resp.Data[i].Root)
assert.DeepEqual(t, shared.BeaconBlockHeaderFromConsensus(expectedHeader), resp.Data[i].Header.Message)
}
})
}
})
t.Run("execution optimistic", func(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(headBlock.Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block)
require.NoError(t, err)
mockChainFetcher := &chainMock.ChainService{
DB: beaconDB,
Block: wsb,
Root: headBlock.BlockRoot,
FinalizedCheckPoint: &eth.Checkpoint{Root: blkContainers[64].BlockRoot},
Optimistic: true,
FinalizedRoots: map[[32]byte]bool{},
OptimisticRoots: map[[32]byte]bool{
bytesutil.ToBytes32(blkContainers[30].BlockRoot): true,
},
}
bs := &Server{
BeaconDB: beaconDB,
ChainInfoFetcher: mockChainFetcher,
OptimisticModeFetcher: mockChainFetcher,
FinalizationFetcher: mockChainFetcher,
}
slot := primitives.Slot(30)
urlWithParams := fmt.Sprintf("%s?slot=%d", url, slot)
request := httptest.NewRequest(http.MethodGet, urlWithParams, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockHeaders(writer, request)
resp := &GetBlockHeadersResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
wsb, err := blocks.NewSignedBeaconBlock(headBlock.Block.(*eth.BeaconBlockContainer_Phase0Block).Phase0Block)
require.NoError(t, err)
child1 := util.NewBeaconBlock()
child1.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
child1.Block.Slot = 999
util.SaveBlock(t, ctx, beaconDB, child1)
child2 := util.NewBeaconBlock()
child2.Block.ParentRoot = bytesutil.PadTo([]byte("parent"), 32)
child2.Block.Slot = 1000
util.SaveBlock(t, ctx, beaconDB, child2)
child1Root, err := child1.Block.HashTreeRoot()
require.NoError(t, err)
child2Root, err := child2.Block.HashTreeRoot()
require.NoError(t, err)
mockChainFetcher := &chainMock.ChainService{
DB: beaconDB,
Block: wsb,
Root: headBlock.BlockRoot,
FinalizedCheckPoint: &eth.Checkpoint{Root: blkContainers[64].BlockRoot},
FinalizedRoots: map[[32]byte]bool{child1Root: true, child2Root: false},
}
bs := &Server{
BeaconDB: beaconDB,
ChainInfoFetcher: mockChainFetcher,
OptimisticModeFetcher: mockChainFetcher,
FinalizationFetcher: mockChainFetcher,
}
t.Run("true", func(t *testing.T) {
slot := primitives.Slot(999)
urlWithParams := fmt.Sprintf("%s?slot=%d", url, slot)
request := httptest.NewRequest(http.MethodGet, urlWithParams, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockHeaders(writer, request)
resp := &GetBlockHeadersResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Finalized)
})
t.Run("false", func(t *testing.T) {
slot := primitives.Slot(1000)
urlWithParams := fmt.Sprintf("%s?slot=%d", url, slot)
request := httptest.NewRequest(http.MethodGet, urlWithParams, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockHeaders(writer, request)
resp := &GetBlockHeadersResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, false, resp.Finalized)
})
t.Run("false when at least one not finalized", func(t *testing.T) {
urlWithParams := fmt.Sprintf("%s?parent_root=%s", url, hexutil.Encode(child1.Block.ParentRoot))
request := httptest.NewRequest(http.MethodGet, urlWithParams, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
bs.GetBlockHeaders(writer, request)
resp := &GetBlockHeadersResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, false, resp.Finalized)
})
})
}
func TestServer_GetBlockHeader(t *testing.T) {
b := util.NewBeaconBlock()
b.Block.Slot = 123
b.Block.ProposerIndex = 123
b.Block.StateRoot = bytesutil.PadTo([]byte("stateroot"), 32)
b.Block.ParentRoot = bytesutil.PadTo([]byte("parentroot"), 32)
b.Block.Body.Graffiti = bytesutil.PadTo([]byte("graffiti"), 32)
sb, err := blocks.NewSignedBeaconBlock(b)
sb.SetSignature(bytesutil.PadTo([]byte("sig"), 96))
require.NoError(t, err)
mockBlockFetcher := &testutil.MockBlocker{BlockToReturn: sb}
mockChainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
ChainInfoFetcher: mockChainService,
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
t.Run("ok", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/headers/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockHeader(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockHeaderResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Data.Canonical)
assert.Equal(t, "0xd7d92f6206707f2c9c4e7e82320617d5abac2b6461a65ea5bb1a154b5b5ea2fa", resp.Data.Root)
assert.Equal(t, "0x736967000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", resp.Data.Header.Signature)
assert.Equal(t, "123", resp.Data.Header.Message.Slot)
assert.Equal(t, "0x706172656e74726f6f7400000000000000000000000000000000000000000000", resp.Data.Header.Message.ParentRoot)
assert.Equal(t, "123", resp.Data.Header.Message.ProposerIndex)
assert.Equal(t, "0xdd32cbaa01c6c0ef399b293f86884ce6a15b532d34682edb16a48fa70ea5bc79", resp.Data.Header.Message.BodyRoot)
assert.Equal(t, "0x7374617465726f6f740000000000000000000000000000000000000000000000", resp.Data.Header.Message.StateRoot)
})
t.Run("missing block_id", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/headers/{block_id}", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockHeader(writer, request)
require.Equal(t, http.StatusBadRequest, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusBadRequest, e.Code)
assert.StringContains(t, "block_id is required in URL params", e.Message)
})
t.Run("execution optimistic", func(t *testing.T) {
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
mockChainService := &chainMock.ChainService{
OptimisticRoots: map[[32]byte]bool{r: true},
FinalizedRoots: map[[32]byte]bool{},
}
s := &Server{
ChainInfoFetcher: mockChainService,
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/headers/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockHeader(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockHeaderResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
r, err := sb.Block().HashTreeRoot()
require.NoError(t, err)
t.Run("true", func(t *testing.T) {
mockChainService := &chainMock.ChainService{FinalizedRoots: map[[32]byte]bool{r: true}}
s := &Server{
ChainInfoFetcher: mockChainService,
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/headers/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": hexutil.Encode(r[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockHeader(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockHeaderResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Finalized)
})
t.Run("false", func(t *testing.T) {
mockChainService := &chainMock.ChainService{FinalizedRoots: map[[32]byte]bool{r: false}}
s := &Server{
ChainInfoFetcher: mockChainService,
OptimisticModeFetcher: mockChainService,
FinalizationFetcher: mockChainService,
Blocker: mockBlockFetcher,
}
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/beacon/headers/{block_id}", nil)
request = mux.SetURLVars(request, map[string]string{"block_id": hexutil.Encode(r[:])})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetBlockHeader(writer, request)
require.Equal(t, http.StatusOK, writer.Code)
resp := &GetBlockHeaderResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, false, resp.Finalized)
})
})
}
func TestGetFinalityCheckpoints(t *testing.T) {
fillCheckpoints := func(state *eth.BeaconState) error {
state.PreviousJustifiedCheckpoint = &eth.Checkpoint{
Root: bytesutil.PadTo([]byte("previous"), 32),
Epoch: 113,
}
state.CurrentJustifiedCheckpoint = &eth.Checkpoint{
Root: bytesutil.PadTo([]byte("current"), 32),
Epoch: 123,
}
state.FinalizedCheckpoint = &eth.Checkpoint{
Root: bytesutil.PadTo([]byte("finalized"), 32),
Epoch: 103,
}
return nil
}
fakeState, err := util.NewBeaconState(fillCheckpoints)
require.NoError(t, err)
chainService := &chainMock.ChainService{}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
}
t.Run("ok", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/states/{state_id}/finality_checkpoints", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetFinalityCheckpoints(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetFinalityCheckpointsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.NotNil(t, resp.Data)
assert.Equal(t, strconv.FormatUint(uint64(fakeState.FinalizedCheckpoint().Epoch), 10), resp.Data.Finalized.Epoch)
assert.DeepEqual(t, hexutil.Encode(fakeState.FinalizedCheckpoint().Root), resp.Data.Finalized.Root)
assert.Equal(t, strconv.FormatUint(uint64(fakeState.CurrentJustifiedCheckpoint().Epoch), 10), resp.Data.CurrentJustified.Epoch)
assert.DeepEqual(t, hexutil.Encode(fakeState.CurrentJustifiedCheckpoint().Root), resp.Data.CurrentJustified.Root)
assert.Equal(t, strconv.FormatUint(uint64(fakeState.PreviousJustifiedCheckpoint().Epoch), 10), resp.Data.PreviousJustified.Epoch)
assert.DeepEqual(t, hexutil.Encode(fakeState.PreviousJustifiedCheckpoint().Root), resp.Data.PreviousJustified.Root)
})
t.Run("no state_id", func(t *testing.T) {
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/states/{state_id}/finality_checkpoints", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetFinalityCheckpoints(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusBadRequest, e.Code)
assert.StringContains(t, "state_id is required in URL params", e.Message)
})
t.Run("execution optimistic", func(t *testing.T) {
chainService := &chainMock.ChainService{Optimistic: true}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/states/{state_id}/finality_checkpoints", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetFinalityCheckpoints(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetFinalityCheckpointsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
})
t.Run("finalized", func(t *testing.T) {
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
require.NoError(t, err)
chainService := &chainMock.ChainService{
FinalizedRoots: map[[32]byte]bool{
headerRoot: true,
},
}
s := &Server{
Stater: &testutil.MockStater{
BeaconState: fakeState,
},
HeadFetcher: chainService,
OptimisticModeFetcher: chainService,
FinalizationFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/states/{state_id}/finality_checkpoints", nil)
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetFinalityCheckpoints(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetFinalityCheckpointsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.Finalized)
})
}
func TestGetGenesis(t *testing.T) {
params.SetupTestConfigCleanup(t)
config := params.BeaconConfig().Copy()
config.GenesisForkVersion = []byte("genesis")
params.OverrideBeaconConfig(config)
genesis := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
validatorsRoot := [32]byte{1, 2, 3, 4, 5, 6}
t.Run("ok", func(t *testing.T) {
chainService := &chainMock.ChainService{
Genesis: genesis,
ValidatorsRoot: validatorsRoot,
}
s := Server{
GenesisTimeFetcher: chainService,
ChainInfoFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/genesis", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetGenesis(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &GetGenesisResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
require.NotNil(t, resp.Data)
assert.Equal(t, strconv.FormatInt(genesis.Unix(), 10), resp.Data.GenesisTime)
assert.DeepEqual(t, hexutil.Encode(validatorsRoot[:]), resp.Data.GenesisValidatorsRoot)
assert.DeepEqual(t, hexutil.Encode([]byte("genesis")), resp.Data.GenesisForkVersion)
})
t.Run("no genesis time", func(t *testing.T) {
chainService := &chainMock.ChainService{
Genesis: time.Time{},
ValidatorsRoot: validatorsRoot,
}
s := Server{
GenesisTimeFetcher: chainService,
ChainInfoFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/genesis", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetGenesis(writer, request)
assert.Equal(t, http.StatusNotFound, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusNotFound, e.Code)
assert.StringContains(t, "Chain genesis info is not yet known", e.Message)
})
t.Run("no genesis validators root", func(t *testing.T) {
chainService := &chainMock.ChainService{
Genesis: genesis,
ValidatorsRoot: [32]byte{},
}
s := Server{
GenesisTimeFetcher: chainService,
ChainInfoFetcher: chainService,
}
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/genesis", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.GetGenesis(writer, request)
assert.Equal(t, http.StatusNotFound, writer.Code)
e := &http2.DefaultErrorJson{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusNotFound, e.Code)
assert.StringContains(t, "Chain genesis info is not yet known", e.Message)
})
}
func TestGetDepositContract(t *testing.T) {
params.SetupTestConfigCleanup(t)
config := params.BeaconConfig().Copy()
config.DepositChainID = uint64(10)
config.DepositContractAddress = "0x4242424242424242424242424242424242424242"
params.OverrideBeaconConfig(config)
request := httptest.NewRequest(http.MethodGet, "/eth/v1/beacon/states/{state_id}/finality_checkpoints", nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s := &Server{}
s.GetDepositContract(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
response := DepositContractResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &response))
assert.Equal(t, "10", response.Data.ChainId)
assert.Equal(t, "0x4242424242424242424242424242424242424242", response.Data.Address)
}