diff --git a/beacon-chain/sync/rpc_send_request.go b/beacon-chain/sync/rpc_send_request.go index ea00e6f3a35e..b56c184bbc40 100644 --- a/beacon-chain/sync/rpc_send_request.go +++ b/beacon-chain/sync/rpc_send_request.go @@ -147,7 +147,7 @@ func SendBlobsByRangeRequest(ctx context.Context, ci blockchain.ForkFetcher, p2p } defer closeStream(stream, log) - return readChunkEncodedBlobs(stream, p2pApi.Encoding(), ctxMap) + return readChunkEncodedBlobs(stream, p2pApi.Encoding(), ctxMap, blobValidatorFromRangeReq(req)) } func SendBlobSidecarByRoot( @@ -175,6 +175,7 @@ func SendBlobSidecarByRoot( var ErrBlobChunkedReadFailure = errors.New("failed to read stream of chunk-encoded blobs") var ErrBlobUnmarshal = errors.New("Could not unmarshal chunk-encoded blob") var ErrUnrequestedRoot = errors.New("Received BlobSidecar in response that was not requested") +var ErrBlobResponseOutOfBounds = errors.New("received BlobSidecar with slot outside BlobSidecarsByRangeRequest bounds") type blobResponseValidation func(*pb.BlobSidecar) error @@ -191,6 +192,16 @@ func blobValidatorFromRootReq(req *p2ptypes.BlobSidecarsByRootReq) blobResponseV } } +func blobValidatorFromRangeReq(req *pb.BlobSidecarsByRangeRequest) blobResponseValidation { + end := req.StartSlot + primitives.Slot(req.Count) + return func(sc *pb.BlobSidecar) error { + if sc.Slot < req.StartSlot || sc.Slot >= end { + return errors.Wrapf(ErrBlobResponseOutOfBounds, "req start,end:%d,%d, resp:%d", req.StartSlot, end, sc.Slot) + } + return nil + } +} + func readChunkEncodedBlobs(stream network.Stream, encoding encoder.NetworkEncoding, ctxMap ContextByteVersions, vf blobResponseValidation) ([]*pb.BlobSidecar, error) { decode := encoding.DecodeWithMaxLength max := int(params.BeaconNetworkConfig().MaxRequestBlobSidecars) diff --git a/beacon-chain/sync/rpc_send_request_test.go b/beacon-chain/sync/rpc_send_request_test.go index 59366436c0c5..ca4aadb6f371 100644 --- a/beacon-chain/sync/rpc_send_request_test.go +++ b/beacon-chain/sync/rpc_send_request_test.go @@ -513,3 +513,78 @@ func TestBlobValidatorFromRootReq(t *testing.T) { }) } } + +func TestBlobValidatorFromRangeReq(t *testing.T) { + cases := []struct { + name string + req *ethpb.BlobSidecarsByRangeRequest + response []*ethpb.BlobSidecar + err error + }{ + { + name: "valid - count multi", + req: ðpb.BlobSidecarsByRangeRequest{ + StartSlot: 10, + Count: 10, + }, + response: []*ethpb.BlobSidecar{{Slot: 14}}, + }, + { + name: "valid - count 1", + req: ðpb.BlobSidecarsByRangeRequest{ + StartSlot: 10, + Count: 1, + }, + response: []*ethpb.BlobSidecar{{Slot: 10}}, + }, + { + name: "invalid - before", + req: ðpb.BlobSidecarsByRangeRequest{ + StartSlot: 10, + Count: 1, + }, + response: []*ethpb.BlobSidecar{{Slot: 9}}, + err: ErrBlobResponseOutOfBounds, + }, + { + name: "invalid - after, count 1", + req: ðpb.BlobSidecarsByRangeRequest{ + StartSlot: 10, + Count: 1, + }, + response: []*ethpb.BlobSidecar{{Slot: 11}}, + err: ErrBlobResponseOutOfBounds, + }, + { + name: "invalid - after, multi", + req: ðpb.BlobSidecarsByRangeRequest{ + StartSlot: 10, + Count: 10, + }, + response: []*ethpb.BlobSidecar{{Slot: 23}}, + err: ErrBlobResponseOutOfBounds, + }, + { + name: "invalid - after, at boundary, multi", + req: ðpb.BlobSidecarsByRangeRequest{ + StartSlot: 10, + Count: 10, + }, + response: []*ethpb.BlobSidecar{{Slot: 20}}, + err: ErrBlobResponseOutOfBounds, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + vf := blobValidatorFromRangeReq(c.req) + for _, sc := range c.response { + err := vf(sc) + if c.err != nil { + require.ErrorIs(t, err, c.err) + return + } + require.NoError(t, err) + } + }) + } +}