mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 03:30:35 +00:00
34593d34d4
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
352 lines
9.8 KiB
Go
352 lines
9.8 KiB
Go
package sync
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p"
|
|
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
|
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
|
|
types "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
|
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
|
"github.com/prysmaticlabs/prysm/v5/time/slots"
|
|
)
|
|
|
|
func (c *blobsTestCase) defaultOldestSlotByRange(t *testing.T) types.Slot {
|
|
currentEpoch := slots.ToEpoch(c.chain.CurrentSlot())
|
|
oldestEpoch := currentEpoch - params.BeaconConfig().MinEpochsForBlobsSidecarsRequest
|
|
if oldestEpoch < params.BeaconConfig().DenebForkEpoch {
|
|
oldestEpoch = params.BeaconConfig().DenebForkEpoch
|
|
}
|
|
oldestSlot, err := slots.EpochStart(oldestEpoch)
|
|
require.NoError(t, err)
|
|
return oldestSlot
|
|
}
|
|
|
|
func blobRangeRequestFromSidecars(scs []blocks.ROBlob) interface{} {
|
|
maxBlobs := fieldparams.MaxBlobsPerBlock
|
|
count := uint64(len(scs) / maxBlobs)
|
|
return ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: scs[0].Slot(),
|
|
Count: count,
|
|
}
|
|
}
|
|
|
|
func (c *blobsTestCase) filterExpectedByRange(t *testing.T, scs []blocks.ROBlob, req interface{}) []*expectedBlobChunk {
|
|
var expect []*expectedBlobChunk
|
|
blockOffset := 0
|
|
lastRoot := scs[0].BlockRoot()
|
|
rreq, ok := req.(*ethpb.BlobSidecarsByRangeRequest)
|
|
require.Equal(t, true, ok)
|
|
var writes uint64
|
|
for i := range scs {
|
|
sc := scs[i]
|
|
root := sc.BlockRoot()
|
|
if root != lastRoot {
|
|
blockOffset += 1
|
|
}
|
|
lastRoot = root
|
|
|
|
if sc.Slot() < c.oldestSlot(t) {
|
|
continue
|
|
}
|
|
if sc.Slot() < rreq.StartSlot || sc.Slot() > rreq.StartSlot+types.Slot(rreq.Count)-1 {
|
|
continue
|
|
}
|
|
if writes == params.BeaconConfig().MaxRequestBlobSidecars {
|
|
continue
|
|
}
|
|
expect = append(expect, &expectedBlobChunk{
|
|
sidecar: &sc,
|
|
code: responseCodeSuccess,
|
|
message: "",
|
|
})
|
|
writes += 1
|
|
}
|
|
return expect
|
|
}
|
|
|
|
func (c *blobsTestCase) runTestBlobSidecarsByRange(t *testing.T) {
|
|
if c.serverHandle == nil {
|
|
c.serverHandle = func(s *Service) rpcHandler { return s.blobSidecarsByRangeRPCHandler }
|
|
}
|
|
if c.defineExpected == nil {
|
|
c.defineExpected = c.filterExpectedByRange
|
|
}
|
|
if c.requestFromSidecars == nil {
|
|
c.requestFromSidecars = blobRangeRequestFromSidecars
|
|
}
|
|
if c.topic == "" {
|
|
c.topic = p2p.RPCBlobSidecarsByRangeTopicV1
|
|
}
|
|
if c.oldestSlot == nil {
|
|
c.oldestSlot = c.defaultOldestSlotByRange
|
|
}
|
|
if c.streamReader == nil {
|
|
c.streamReader = defaultExpectedRequirer
|
|
}
|
|
c.run(t)
|
|
}
|
|
|
|
func TestBlobByRangeOK(t *testing.T) {
|
|
origNC := params.BeaconConfig()
|
|
// restore network config after test completes
|
|
defer func() {
|
|
params.OverrideBeaconConfig(origNC)
|
|
}()
|
|
// set MaxRequestBlobSidecars to a low-ish value so the test doesn't timeout.
|
|
nc := params.BeaconConfig().Copy()
|
|
nc.MaxRequestBlobSidecars = 100
|
|
params.OverrideBeaconConfig(nc)
|
|
|
|
cases := []*blobsTestCase{
|
|
{
|
|
name: "beginning of window + 10",
|
|
nblocks: 10,
|
|
},
|
|
{
|
|
name: "10 slots before window, 10 slots after, count = 20",
|
|
nblocks: 10,
|
|
requestFromSidecars: func(scs []blocks.ROBlob) interface{} {
|
|
return ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: scs[0].Slot() - 10,
|
|
Count: 20,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "request before window, empty response",
|
|
nblocks: 10,
|
|
requestFromSidecars: func(scs []blocks.ROBlob) interface{} {
|
|
return ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: scs[0].Slot() - 10,
|
|
Count: 10,
|
|
}
|
|
},
|
|
total: func() *int { x := 0; return &x }(),
|
|
},
|
|
{
|
|
name: "10 blocks * 4 blobs = 40",
|
|
nblocks: 10,
|
|
requestFromSidecars: func(scs []blocks.ROBlob) interface{} {
|
|
return ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: scs[0].Slot() - 10,
|
|
Count: 20,
|
|
}
|
|
},
|
|
total: func() *int { x := fieldparams.MaxBlobsPerBlock * 10; return &x }(), // 10 blocks * 4 blobs = 40
|
|
},
|
|
{
|
|
name: "when request count > MAX_REQUEST_BLOCKS_DENEB, MAX_REQUEST_BLOBS_SIDECARS sidecars in response",
|
|
nblocks: int(params.BeaconConfig().MaxRequestBlocksDeneb) + 10,
|
|
requestFromSidecars: func(scs []blocks.ROBlob) interface{} {
|
|
return ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: scs[0].Slot(),
|
|
Count: params.BeaconConfig().MaxRequestBlocksDeneb + 1,
|
|
}
|
|
},
|
|
total: func() *int { x := int(params.BeaconConfig().MaxRequestBlobSidecars); return &x }(),
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
c.runTestBlobSidecarsByRange(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBlobsByRangeValidation(t *testing.T) {
|
|
cfg := params.BeaconConfig()
|
|
repositionFutureEpochs(cfg)
|
|
undo, err := params.SetActiveWithUndo(cfg)
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
require.NoError(t, undo())
|
|
}()
|
|
denebSlot, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
|
|
require.NoError(t, err)
|
|
|
|
minReqEpochs := params.BeaconConfig().MinEpochsForBlobsSidecarsRequest
|
|
minReqSlots, err := slots.EpochStart(minReqEpochs)
|
|
require.NoError(t, err)
|
|
// spec criteria for mix,max bound checking
|
|
/*
|
|
Clients MUST keep a record of signed blobs sidecars seen on the epoch range
|
|
[max(current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]
|
|
where current_epoch is defined by the current wall-clock time,
|
|
and clients MUST support serving requests of blobs on this range.
|
|
*/
|
|
defaultCurrent := denebSlot + 100 + minReqSlots
|
|
defaultMinStart, err := BlobRPCMinValidSlot(defaultCurrent)
|
|
require.NoError(t, err)
|
|
cases := []struct {
|
|
name string
|
|
current types.Slot
|
|
req *ethpb.BlobSidecarsByRangeRequest
|
|
// chain := defaultMockChain(t)
|
|
|
|
start types.Slot
|
|
end types.Slot
|
|
batch uint64
|
|
err error
|
|
}{
|
|
{
|
|
name: "start at current",
|
|
current: denebSlot + 100,
|
|
req: ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: denebSlot + 100,
|
|
Count: 10,
|
|
},
|
|
start: denebSlot + 100,
|
|
end: denebSlot + 100,
|
|
batch: 10,
|
|
},
|
|
{
|
|
name: "start after current",
|
|
current: denebSlot,
|
|
req: ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: denebSlot + 100,
|
|
Count: 10,
|
|
},
|
|
start: denebSlot,
|
|
end: denebSlot,
|
|
batch: 0,
|
|
},
|
|
{
|
|
name: "start before current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS",
|
|
current: defaultCurrent,
|
|
req: ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: defaultMinStart - 100,
|
|
Count: 10,
|
|
},
|
|
start: defaultMinStart,
|
|
end: defaultMinStart,
|
|
batch: 10,
|
|
},
|
|
{
|
|
name: "start before current_epoch - MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS - end still valid",
|
|
current: defaultCurrent,
|
|
req: ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: defaultMinStart - 10,
|
|
Count: 20,
|
|
},
|
|
start: defaultMinStart,
|
|
end: defaultMinStart + 9,
|
|
batch: blobBatchLimit(),
|
|
},
|
|
{
|
|
name: "count > MAX_REQUEST_BLOB_SIDECARS",
|
|
current: defaultCurrent,
|
|
req: ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: defaultMinStart - 10,
|
|
Count: 1000,
|
|
},
|
|
start: defaultMinStart,
|
|
end: defaultMinStart - 10 + 999,
|
|
// a large count is ok, we just limit the amount of actual responses
|
|
batch: blobBatchLimit(),
|
|
},
|
|
{
|
|
name: "start + count > current",
|
|
current: defaultCurrent,
|
|
req: ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: defaultCurrent + 100,
|
|
Count: 100,
|
|
},
|
|
start: defaultCurrent,
|
|
end: defaultCurrent,
|
|
batch: 0,
|
|
},
|
|
{
|
|
name: "start before deneb",
|
|
current: defaultCurrent - minReqSlots + 100,
|
|
req: ðpb.BlobSidecarsByRangeRequest{
|
|
StartSlot: denebSlot - 10,
|
|
Count: 100,
|
|
},
|
|
start: denebSlot,
|
|
end: denebSlot + 89,
|
|
batch: blobBatchLimit(),
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
rp, err := validateBlobsByRange(c.req, c.current)
|
|
if c.err != nil {
|
|
require.ErrorIs(t, err, c.err)
|
|
return
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
require.Equal(t, c.start, rp.start)
|
|
require.Equal(t, c.end, rp.end)
|
|
require.Equal(t, c.batch, rp.size)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBlobRPCMinValidSlot(t *testing.T) {
|
|
denebSlot, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
|
|
require.NoError(t, err)
|
|
cases := []struct {
|
|
name string
|
|
current func(t *testing.T) types.Slot
|
|
expected types.Slot
|
|
err error
|
|
}{
|
|
{
|
|
name: "before deneb",
|
|
current: func(t *testing.T) types.Slot {
|
|
st, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch - 1)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
require.NoError(t, err)
|
|
return st
|
|
},
|
|
expected: denebSlot,
|
|
},
|
|
{
|
|
name: "equal to deneb",
|
|
current: func(t *testing.T) types.Slot {
|
|
st, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
require.NoError(t, err)
|
|
return st
|
|
},
|
|
expected: denebSlot,
|
|
},
|
|
{
|
|
name: "after deneb, before expiry starts",
|
|
current: func(t *testing.T) types.Slot {
|
|
st, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch + params.BeaconConfig().MinEpochsForBlobsSidecarsRequest)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
require.NoError(t, err)
|
|
return st
|
|
},
|
|
expected: denebSlot,
|
|
},
|
|
{
|
|
name: "expiry starts one epoch after deneb + MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS",
|
|
current: func(t *testing.T) types.Slot {
|
|
st, err := slots.EpochStart(params.BeaconConfig().DenebForkEpoch + params.BeaconConfig().MinEpochsForBlobsSidecarsRequest + 1)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
require.NoError(t, err)
|
|
return st
|
|
},
|
|
expected: denebSlot + params.BeaconConfig().SlotsPerEpoch,
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
current := c.current(t)
|
|
got, err := BlobRPCMinValidSlot(current)
|
|
if c.err != nil {
|
|
require.ErrorIs(t, err, c.err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
require.Equal(t, c.expected, got)
|
|
})
|
|
}
|
|
}
|