mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
336 lines
9.6 KiB
Go
336 lines
9.6 KiB
Go
package sync
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p"
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
|
|
"github.com/OffchainLabs/prysm/v6/config/params"
|
|
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
|
|
types "github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v6/genesis"
|
|
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v6/testing/require"
|
|
"github.com/OffchainLabs/prysm/v6/testing/util"
|
|
)
|
|
|
|
func (c *blobsTestCase) defaultOldestSlotByRange(t *testing.T) types.Slot {
|
|
currentEpoch := c.clock.CurrentEpoch()
|
|
oldestEpoch := currentEpoch - params.BeaconConfig().MinEpochsForBlobsSidecarsRequest
|
|
if oldestEpoch < params.BeaconConfig().DenebForkEpoch {
|
|
oldestEpoch = params.BeaconConfig().DenebForkEpoch
|
|
}
|
|
oldestSlot := util.SlotAtEpoch(t, oldestEpoch)
|
|
return oldestSlot
|
|
}
|
|
|
|
func blobRangeRequestFromSidecars(scs []blocks.ROBlob) interface{} {
|
|
maxBlobs := params.BeaconConfig().MaxBlobsPerBlock(scs[0].Slot())
|
|
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) {
|
|
params.SetupTestConfigCleanup(t)
|
|
ds := util.SlotAtEpoch(t, params.BeaconConfig().DenebForkEpoch)
|
|
params.BeaconConfig().InitializeForkSchedule()
|
|
retainSlots := util.SlotAtEpoch(t, params.BeaconConfig().MinEpochsForBlobsSidecarsRequest)
|
|
current := ds + retainSlots
|
|
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 := params.BeaconConfig().MaxBlobsPerBlock(ds) * 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) + 1,
|
|
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 }(),
|
|
},
|
|
}
|
|
clock := startup.NewClock(genesis.Time(), genesis.ValidatorsRoot(), startup.WithSlotAsNow(current))
|
|
for _, c := range cases {
|
|
c.clock = clock
|
|
t.Run(c.name, func(t *testing.T) {
|
|
c.runTestBlobSidecarsByRange(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBlobsByRangeValidation(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
repositionFutureEpochs(params.BeaconConfig())
|
|
denebSlot := util.SlotAtEpoch(t, params.BeaconConfig().DenebForkEpoch)
|
|
|
|
minReqEpochs := params.BeaconConfig().MinEpochsForBlobsSidecarsRequest
|
|
minReqSlots := util.SlotAtEpoch(t, minReqEpochs)
|
|
// 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
|
|
|
|
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(defaultCurrent),
|
|
},
|
|
{
|
|
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(defaultCurrent),
|
|
},
|
|
{
|
|
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(defaultCurrent - minReqSlots + 100),
|
|
},
|
|
}
|
|
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 := util.SlotAtEpoch(t, params.BeaconConfig().DenebForkEpoch)
|
|
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 := util.SlotAtEpoch(t, params.BeaconConfig().DenebForkEpoch-1)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
return st
|
|
},
|
|
expected: denebSlot,
|
|
},
|
|
{
|
|
name: "equal to deneb",
|
|
current: func(t *testing.T) types.Slot {
|
|
st := util.SlotAtEpoch(t, params.BeaconConfig().DenebForkEpoch)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
return st
|
|
},
|
|
expected: denebSlot,
|
|
},
|
|
{
|
|
name: "after deneb, before expiry starts",
|
|
current: func(t *testing.T) types.Slot {
|
|
st := util.SlotAtEpoch(t, params.BeaconConfig().DenebForkEpoch+params.BeaconConfig().MinEpochsForBlobsSidecarsRequest)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
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 := util.SlotAtEpoch(t, params.BeaconConfig().DenebForkEpoch+params.BeaconConfig().MinEpochsForBlobsSidecarsRequest+1)
|
|
// note: we no longer need to deal with deneb fork epoch being far future
|
|
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)
|
|
})
|
|
}
|
|
}
|