diff --git a/beacon-chain/db/kv/blob.go b/beacon-chain/db/kv/blob.go index 65d9e7ed6630..71d1749f1a98 100644 --- a/beacon-chain/db/kv/blob.go +++ b/beacon-chain/db/kv/blob.go @@ -54,7 +54,7 @@ func (s *Store) SaveBlobSidecar(ctx context.Context, scs []*ethpb.BlobSidecar) e // If there is no element stored at blob.slot % MAX_SLOTS_TO_PERSIST_BLOBS, then we simply // store the blob by key and exit early. if len(replacingKey) != 0 { - slotBytes := replacingKey[:8] + slotBytes := replacingKey[8:16] oldSlot := bytesutil.BytesToSlotBigEndian(slotBytes) oldEpoch := slots.ToEpoch(oldSlot) // The blob we are replacing is too old, so we delete it. diff --git a/beacon-chain/db/kv/blob_test.go b/beacon-chain/db/kv/blob_test.go index c7e0de4a790b..f1fc70e7d5c9 100644 --- a/beacon-chain/db/kv/blob_test.go +++ b/beacon-chain/db/kv/blob_test.go @@ -62,11 +62,22 @@ func TestStore_BlobSidecars(t *testing.T) { require.NoError(t, err) require.NoError(t, equalBlobSlices(scs, got)) }) - t.Run("save and retrieve by root (max)", func(t *testing.T) { + t.Run("save and retrieve by root (max), per batch", func(t *testing.T) { db := setupDB(t) scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) require.NoError(t, db.SaveBlobSidecar(ctx, scs)) - require.Equal(t, int(fieldparams.MaxBlobsPerBlock), len(scs)) + require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs)) + got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + }) + t.Run("save and retrieve by root, max and individually", func(t *testing.T) { + db := setupDB(t) + scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + for _, sc := range scs { + require.NoError(t, db.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{sc})) + } + require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs)) got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)) require.NoError(t, err) require.NoError(t, equalBlobSlices(scs, got)) @@ -116,6 +127,17 @@ func TestStore_BlobSidecars(t *testing.T) { require.NoError(t, err) require.NoError(t, equalBlobSlices(scs, got)) }) + t.Run("save and retrieve by slot, max and individually", func(t *testing.T) { + db := setupDB(t) + scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + for _, sc := range scs { + require.NoError(t, db.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{sc})) + } + require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs)) + got, err := db.BlobSidecarsBySlot(ctx, scs[0].Slot) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + }) t.Run("save and retrieve valid subset by slot", func(t *testing.T) { db := setupDB(t) scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) @@ -161,18 +183,17 @@ func TestStore_BlobSidecars(t *testing.T) { db := setupDB(t) scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) - scs0 := scs[0:2] - scs1 := scs[2:4] - scs2 := scs[4:6] - require.NoError(t, db.SaveBlobSidecar(ctx, scs0)) - require.NoError(t, db.SaveBlobSidecar(ctx, scs1)) - require.NoError(t, db.SaveBlobSidecar(ctx, scs2)) - - saved, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs0[0].BlockRoot)) - require.NoError(t, err) - require.NoError(t, equalBlobSlices(saved, scs)) + for i := 0; i < fieldparams.MaxBlobsPerBlock; i++ { + scs[i].Slot = primitives.Slot(i) + scs[i].BlockRoot = bytesutil.PadTo([]byte{byte(i)}, 32) + require.NoError(t, db.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{scs[i]})) + br := bytesutil.ToBytes32(scs[i].BlockRoot) + saved, err := db.BlobSidecarsByRoot(ctx, br) + require.NoError(t, err) + require.NoError(t, equalBlobSlices([]*ethpb.BlobSidecar{scs[i]}, saved)) + } }) - t.Run("saving a new blob for rotation", func(t *testing.T) { + t.Run("saving a new blob for rotation (batch)", func(t *testing.T) { db := setupDB(t) scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) require.NoError(t, db.SaveBlobSidecar(ctx, scs)) @@ -196,6 +217,82 @@ func TestStore_BlobSidecars(t *testing.T) { require.NoError(t, err) require.NoError(t, equalBlobSlices(newScs, got)) }) + t.Run("save multiple blobs after new rotation (individually)", func(t *testing.T) { + db := setupDB(t) + scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + for _, sc := range scs { + require.NoError(t, db.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{sc})) + } + got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + + scs = generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + newRetentionSlot := primitives.Slot(params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest.Mul(uint64(params.BeaconConfig().SlotsPerEpoch))) + for _, sc := range scs { + sc.Slot = sc.Slot + newRetentionSlot + } + for _, sc := range scs { + require.NoError(t, db.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{sc})) + } + + _, err = db.BlobSidecarsBySlot(ctx, 100) + require.ErrorIs(t, ErrNotFound, err) + + got, err = db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + }) + t.Run("save multiple blobs after new rotation (batch then individually)", func(t *testing.T) { + db := setupDB(t) + scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + require.NoError(t, db.SaveBlobSidecar(ctx, scs)) + require.Equal(t, fieldparams.MaxBlobsPerBlock, len(scs)) + oldBlockRoot := scs[0].BlockRoot + got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(oldBlockRoot)) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + + scs = generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + newRetentionSlot := primitives.Slot(params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest.Mul(uint64(params.BeaconConfig().SlotsPerEpoch))) + for _, sc := range scs { + sc.Slot = sc.Slot + newRetentionSlot + } + for _, sc := range scs { + require.NoError(t, db.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{sc})) + } + + _, err = db.BlobSidecarsBySlot(ctx, 100) + require.ErrorIs(t, ErrNotFound, err) + + got, err = db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + }) + t.Run("save multiple blobs after new rotation (individually then batch)", func(t *testing.T) { + db := setupDB(t) + scs := generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + for _, sc := range scs { + require.NoError(t, db.SaveBlobSidecar(ctx, []*ethpb.BlobSidecar{sc})) + } + got, err := db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + + scs = generateBlobSidecars(t, fieldparams.MaxBlobsPerBlock) + newRetentionSlot := primitives.Slot(params.BeaconNetworkConfig().MinEpochsForBlobsSidecarsRequest.Mul(uint64(params.BeaconConfig().SlotsPerEpoch))) + for _, sc := range scs { + sc.Slot = sc.Slot + newRetentionSlot + } + require.NoError(t, db.SaveBlobSidecar(ctx, scs)) + + _, err = db.BlobSidecarsBySlot(ctx, 100) + require.ErrorIs(t, ErrNotFound, err) + + got, err = db.BlobSidecarsByRoot(ctx, bytesutil.ToBytes32(scs[0].BlockRoot)) + require.NoError(t, err) + require.NoError(t, equalBlobSlices(scs, got)) + }) } func generateBlobSidecars(t *testing.T, n uint64) []*ethpb.BlobSidecar {