mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
**What type of PR is this?** Other **What does this PR do? Why is it needed?** This pull request removes `NUMBER_OF_COLUMNS` and `MAX_CELLS_IN_EXTENDED_MATRIX` configuration. **Other notes for review** Please read commit by commit, with commit messages. **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description to this PR with sufficient context for reviewers to understand this PR.
799 lines
26 KiB
Go
799 lines
26 KiB
Go
package debug
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"math"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/api"
|
|
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
|
blockchainmock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
|
|
dbtest "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
|
|
doublylinkedtree "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/doubly-linked-tree"
|
|
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/core"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/testutil"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
|
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
|
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v7/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v7/testing/require"
|
|
"github.com/OffchainLabs/prysm/v7/testing/util"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
)
|
|
|
|
func TestGetBeaconStateV2(t *testing.T) {
|
|
ctx := t.Context()
|
|
db := dbtest.SetupDB(t)
|
|
|
|
t.Run("phase0", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, version.String(version.Phase0), resp.Version)
|
|
st := &structs.BeaconState{}
|
|
require.NoError(t, json.Unmarshal(resp.Data, st))
|
|
assert.Equal(t, "123", st.Slot)
|
|
})
|
|
t.Run("Altair", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateAltair()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, version.String(version.Altair), resp.Version)
|
|
st := &structs.BeaconStateAltair{}
|
|
require.NoError(t, json.Unmarshal(resp.Data, st))
|
|
assert.Equal(t, "123", st.Slot)
|
|
})
|
|
t.Run("Bellatrix", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateBellatrix()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, version.String(version.Bellatrix), resp.Version)
|
|
st := &structs.BeaconStateBellatrix{}
|
|
require.NoError(t, json.Unmarshal(resp.Data, st))
|
|
assert.Equal(t, "123", st.Slot)
|
|
})
|
|
t.Run("Capella", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateCapella()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, version.String(version.Capella), resp.Version)
|
|
st := &structs.BeaconStateCapella{}
|
|
require.NoError(t, json.Unmarshal(resp.Data, st))
|
|
assert.Equal(t, "123", st.Slot)
|
|
})
|
|
t.Run("Deneb", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateDeneb()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, version.String(version.Deneb), resp.Version)
|
|
st := &structs.BeaconStateDeneb{}
|
|
require.NoError(t, json.Unmarshal(resp.Data, st))
|
|
assert.Equal(t, "123", st.Slot)
|
|
})
|
|
t.Run("Electra", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateElectra()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, version.String(version.Electra), resp.Version)
|
|
st := &structs.BeaconStateElectra{}
|
|
require.NoError(t, json.Unmarshal(resp.Data, st))
|
|
assert.Equal(t, "123", st.Slot)
|
|
})
|
|
t.Run("Fulu", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateFulu()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, version.String(version.Fulu), resp.Version)
|
|
st := &structs.BeaconStateFulu{}
|
|
require.NoError(t, json.Unmarshal(resp.Data, st))
|
|
assert.Equal(t, "123", st.Slot)
|
|
assert.Equal(t, int(params.BeaconConfig().MinSeedLookahead+1)*int(params.BeaconConfig().SlotsPerEpoch), len(st.ProposerLookahead))
|
|
})
|
|
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))
|
|
|
|
fakeState, err := util.NewBeaconStateBellatrix()
|
|
require.NoError(t, err)
|
|
chainService := &blockchainmock.ChainService{Optimistic: true}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
BeaconDB: db,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
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))
|
|
|
|
fakeState, err := util.NewBeaconStateBellatrix()
|
|
require.NoError(t, err)
|
|
headerRoot, err := fakeState.LatestBlockHeader().HashTreeRoot()
|
|
require.NoError(t, err)
|
|
chainService := &blockchainmock.ChainService{
|
|
FinalizedRoots: map[[32]byte]bool{
|
|
headerRoot: true,
|
|
},
|
|
}
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
BeaconDB: db,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetBeaconStateV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, true, resp.Finalized)
|
|
})
|
|
}
|
|
|
|
func TestGetBeaconStateSSZV2(t *testing.T) {
|
|
t.Run("Phase 0", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
request.Header.Set("Accept", api.OctetStreamMediaType)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, version.String(version.Phase0), writer.Header().Get(api.VersionHeader))
|
|
sszExpected, err := fakeState.MarshalSSZ()
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
|
|
})
|
|
t.Run("Altair", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateAltair()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
request.Header.Set("Accept", api.OctetStreamMediaType)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, version.String(version.Altair), writer.Header().Get(api.VersionHeader))
|
|
sszExpected, err := fakeState.MarshalSSZ()
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
|
|
})
|
|
t.Run("Bellatrix", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateBellatrix()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
request.Header.Set("Accept", api.OctetStreamMediaType)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, version.String(version.Bellatrix), writer.Header().Get(api.VersionHeader))
|
|
sszExpected, err := fakeState.MarshalSSZ()
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
|
|
})
|
|
t.Run("Capella", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateCapella()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
request.Header.Set("Accept", api.OctetStreamMediaType)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, version.String(version.Capella), writer.Header().Get(api.VersionHeader))
|
|
sszExpected, err := fakeState.MarshalSSZ()
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
|
|
})
|
|
t.Run("Deneb", func(t *testing.T) {
|
|
fakeState, err := util.NewBeaconStateDeneb()
|
|
require.NoError(t, err)
|
|
require.NoError(t, fakeState.SetSlot(123))
|
|
|
|
s := &Server{
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: fakeState,
|
|
},
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/states/{state_id}", nil)
|
|
request.SetPathValue("state_id", "head")
|
|
request.Header.Set("Accept", api.OctetStreamMediaType)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetBeaconStateV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
assert.Equal(t, version.String(version.Deneb), writer.Header().Get(api.VersionHeader))
|
|
sszExpected, err := fakeState.MarshalSSZ()
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, sszExpected, writer.Body.Bytes())
|
|
})
|
|
}
|
|
|
|
func TestGetForkChoiceHeadsV2(t *testing.T) {
|
|
expectedSlotsAndRoots := []struct {
|
|
Slot string
|
|
Root string
|
|
}{{
|
|
Slot: "0",
|
|
Root: hexutil.Encode(bytesutil.PadTo([]byte("foo"), 32)),
|
|
}, {
|
|
Slot: "1",
|
|
Root: hexutil.Encode(bytesutil.PadTo([]byte("bar"), 32)),
|
|
}}
|
|
|
|
chainService := &blockchainmock.ChainService{}
|
|
s := &Server{
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/heads", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetForkChoiceHeadsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetForkChoiceHeadsV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, 2, len(resp.Data))
|
|
for _, sr := range expectedSlotsAndRoots {
|
|
found := false
|
|
for _, h := range resp.Data {
|
|
if h.Slot == sr.Slot {
|
|
found = true
|
|
assert.Equal(t, sr.Root, h.Root)
|
|
}
|
|
assert.Equal(t, false, h.ExecutionOptimistic)
|
|
}
|
|
assert.Equal(t, true, found, "Expected head not found")
|
|
}
|
|
|
|
t.Run("optimistic head", func(t *testing.T) {
|
|
chainService := &blockchainmock.ChainService{
|
|
Optimistic: true,
|
|
OptimisticRoots: make(map[[32]byte]bool),
|
|
}
|
|
for _, sr := range expectedSlotsAndRoots {
|
|
b, err := hexutil.Decode(sr.Root)
|
|
require.NoError(t, err)
|
|
chainService.OptimisticRoots[bytesutil.ToBytes32(b)] = true
|
|
}
|
|
s := &Server{
|
|
HeadFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v2/debug/beacon/heads", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetForkChoiceHeadsV2(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetForkChoiceHeadsV2Response{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, 2, len(resp.Data))
|
|
for _, sr := range expectedSlotsAndRoots {
|
|
found := false
|
|
for _, h := range resp.Data {
|
|
if h.Slot == sr.Slot {
|
|
found = true
|
|
assert.Equal(t, sr.Root, h.Root)
|
|
}
|
|
assert.Equal(t, true, h.ExecutionOptimistic)
|
|
}
|
|
assert.Equal(t, true, found, "Expected head not found")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetForkChoice(t *testing.T) {
|
|
store := doublylinkedtree.New()
|
|
fRoot := [32]byte{'a'}
|
|
fc := &forkchoicetypes.Checkpoint{Epoch: 2, Root: fRoot}
|
|
require.NoError(t, store.UpdateFinalizedCheckpoint(fc))
|
|
s := &Server{ForkchoiceFetcher: &blockchainmock.ChainService{ForkChoiceStore: store}}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/debug/fork_choice", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.GetForkChoice(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &structs.GetForkChoiceDumpResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.Equal(t, "2", resp.FinalizedCheckpoint.Epoch)
|
|
}
|
|
|
|
func TestDataColumnSidecars(t *testing.T) {
|
|
t.Run("Fulu fork not configured", func(t *testing.T) {
|
|
// Save the original config
|
|
originalConfig := params.BeaconConfig()
|
|
defer func() { params.OverrideBeaconConfig(originalConfig) }()
|
|
|
|
// Set Fulu fork epoch to MaxUint64 (unconfigured)
|
|
config := params.BeaconConfig().Copy()
|
|
config.FuluForkEpoch = math.MaxUint64
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{}
|
|
|
|
// Create a mock blocker to avoid nil pointer
|
|
mockBlocker := &testutil.MockBlocker{}
|
|
|
|
s := &Server{
|
|
GenesisTimeFetcher: chainService,
|
|
Blocker: mockBlocker,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/debug/beacon/data_column_sidecars/head", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.DataColumnSidecars(writer, request)
|
|
require.Equal(t, http.StatusBadRequest, writer.Code)
|
|
assert.StringContains(t, "Data columns are not supported - Fulu fork not configured", writer.Body.String())
|
|
})
|
|
|
|
t.Run("Before Fulu fork", func(t *testing.T) {
|
|
// Save the original config
|
|
originalConfig := params.BeaconConfig()
|
|
defer func() { params.OverrideBeaconConfig(originalConfig) }()
|
|
|
|
// Set Fulu fork epoch to 100
|
|
config := params.BeaconConfig().Copy()
|
|
config.FuluForkEpoch = 100
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{}
|
|
currentSlot := primitives.Slot(0) // Current slot 0 (epoch 0, before epoch 100)
|
|
chainService.Slot = ¤tSlot
|
|
|
|
// Create a mock blocker to avoid nil pointer
|
|
mockBlocker := &testutil.MockBlocker{
|
|
DataColumnsFunc: func(ctx context.Context, id string, indices []int) ([]blocks.VerifiedRODataColumn, *core.RpcError) {
|
|
return nil, &core.RpcError{Err: errors.New("before Fulu fork"), Reason: core.BadRequest}
|
|
},
|
|
}
|
|
|
|
s := &Server{
|
|
GenesisTimeFetcher: chainService,
|
|
Blocker: mockBlocker,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/debug/beacon/data_column_sidecars/head", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.DataColumnSidecars(writer, request)
|
|
require.Equal(t, http.StatusBadRequest, writer.Code)
|
|
assert.StringContains(t, "Data columns are not supported - before Fulu fork", writer.Body.String())
|
|
})
|
|
|
|
t.Run("Invalid indices", func(t *testing.T) {
|
|
// Save the original config
|
|
originalConfig := params.BeaconConfig()
|
|
defer func() { params.OverrideBeaconConfig(originalConfig) }()
|
|
|
|
// Set Fulu fork epoch to 0 (already activated)
|
|
config := params.BeaconConfig().Copy()
|
|
config.FuluForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{}
|
|
currentSlot := primitives.Slot(0) // Current slot 0 (epoch 0, at fork)
|
|
chainService.Slot = ¤tSlot
|
|
|
|
// Create a mock blocker to avoid nil pointer
|
|
mockBlocker := &testutil.MockBlocker{
|
|
DataColumnsFunc: func(ctx context.Context, id string, indices []int) ([]blocks.VerifiedRODataColumn, *core.RpcError) {
|
|
return nil, &core.RpcError{Err: errors.New("invalid index"), Reason: core.BadRequest}
|
|
},
|
|
}
|
|
|
|
s := &Server{
|
|
GenesisTimeFetcher: chainService,
|
|
Blocker: mockBlocker,
|
|
}
|
|
|
|
// Test with invalid index (out of range)
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/debug/beacon/data_column_sidecars/head?indices=9999", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.DataColumnSidecars(writer, request)
|
|
require.Equal(t, http.StatusBadRequest, writer.Code)
|
|
assert.StringContains(t, "requested data column indices [9999] are invalid", writer.Body.String())
|
|
})
|
|
|
|
t.Run("Block not found", func(t *testing.T) {
|
|
// Save the original config
|
|
originalConfig := params.BeaconConfig()
|
|
defer func() { params.OverrideBeaconConfig(originalConfig) }()
|
|
|
|
// Set Fulu fork epoch to 0 (already activated)
|
|
config := params.BeaconConfig().Copy()
|
|
config.FuluForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
chainService := &blockchainmock.ChainService{}
|
|
currentSlot := primitives.Slot(0) // Current slot 0
|
|
chainService.Slot = ¤tSlot
|
|
|
|
// Create a mock blocker that returns block not found
|
|
mockBlocker := &testutil.MockBlocker{
|
|
DataColumnsFunc: func(ctx context.Context, id string, indices []int) ([]blocks.VerifiedRODataColumn, *core.RpcError) {
|
|
return nil, &core.RpcError{Err: errors.New("block not found"), Reason: core.NotFound}
|
|
},
|
|
BlockToReturn: nil, // Block not found
|
|
}
|
|
|
|
s := &Server{
|
|
GenesisTimeFetcher: chainService,
|
|
Blocker: mockBlocker,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/debug/beacon/data_column_sidecars/head", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.DataColumnSidecars(writer, request)
|
|
require.Equal(t, http.StatusNotFound, writer.Code)
|
|
})
|
|
|
|
t.Run("Empty data columns", func(t *testing.T) {
|
|
// Save the original config
|
|
originalConfig := params.BeaconConfig()
|
|
defer func() { params.OverrideBeaconConfig(originalConfig) }()
|
|
|
|
// Set Fulu fork epoch to 0
|
|
config := params.BeaconConfig().Copy()
|
|
config.FuluForkEpoch = 0
|
|
params.OverrideBeaconConfig(config)
|
|
|
|
// Create a simple test block
|
|
signedTestBlock := util.NewBeaconBlock()
|
|
roBlock, err := blocks.NewSignedBeaconBlock(signedTestBlock)
|
|
require.NoError(t, err)
|
|
|
|
chainService := &blockchainmock.ChainService{}
|
|
currentSlot := primitives.Slot(0) // Current slot 0
|
|
chainService.Slot = ¤tSlot
|
|
chainService.OptimisticRoots = make(map[[32]byte]bool)
|
|
chainService.FinalizedRoots = make(map[[32]byte]bool)
|
|
|
|
mockBlocker := &testutil.MockBlocker{
|
|
DataColumnsFunc: func(ctx context.Context, id string, indices []int) ([]blocks.VerifiedRODataColumn, *core.RpcError) {
|
|
return []blocks.VerifiedRODataColumn{}, nil // Empty data columns
|
|
},
|
|
BlockToReturn: roBlock,
|
|
}
|
|
|
|
s := &Server{
|
|
GenesisTimeFetcher: chainService,
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
Blocker: mockBlocker,
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "http://example.com/eth/v1/debug/beacon/data_column_sidecars/head", nil)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.DataColumnSidecars(writer, request)
|
|
require.Equal(t, http.StatusOK, writer.Code)
|
|
|
|
resp := &structs.GetDebugDataColumnSidecarsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
require.Equal(t, 0, len(resp.Data))
|
|
})
|
|
}
|
|
|
|
func TestParseDataColumnIndices(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
queryParams map[string][]string
|
|
expected []int
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "no indices",
|
|
queryParams: map[string][]string{},
|
|
expected: []int{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid indices",
|
|
queryParams: map[string][]string{"indices": {"0", "1", "127"}},
|
|
expected: []int{0, 1, 127},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "duplicate indices",
|
|
queryParams: map[string][]string{"indices": {"0", "1", "0"}},
|
|
expected: []int{0, 1},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "invalid string index",
|
|
queryParams: map[string][]string{"indices": {"abc"}},
|
|
expected: nil,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "negative index",
|
|
queryParams: map[string][]string{"indices": {"-1"}},
|
|
expected: nil,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "index too large",
|
|
queryParams: map[string][]string{"indices": {"128"}}, // 128 >= NumberOfColumns (128)
|
|
expected: nil,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "mixed valid and invalid",
|
|
queryParams: map[string][]string{"indices": {"0", "abc", "1"}},
|
|
expected: nil,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
u, err := url.Parse("http://example.com/test")
|
|
require.NoError(t, err)
|
|
|
|
q := u.Query()
|
|
for key, values := range tt.queryParams {
|
|
for _, value := range values {
|
|
q.Add(key, value)
|
|
}
|
|
}
|
|
u.RawQuery = q.Encode()
|
|
|
|
result, err := parseDataColumnIndices(u)
|
|
|
|
if tt.expectError {
|
|
assert.NotNil(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBuildDataColumnSidecarsSSZResponse(t *testing.T) {
|
|
t.Run("empty data columns", func(t *testing.T) {
|
|
result, err := buildDataColumnSidecarsSSZResponse([]blocks.VerifiedRODataColumn{})
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, []byte{}, result)
|
|
})
|
|
|
|
t.Run("get SSZ size", func(t *testing.T) {
|
|
size := (ðpb.DataColumnSidecar{}).SizeSSZ()
|
|
assert.Equal(t, true, size > 0)
|
|
})
|
|
}
|