@@ -2,16 +2,19 @@ package filesystem
22
33import (
44 "bytes"
5+ "fmt"
56 "math"
67 "os"
78 "path"
89 "sync"
910 "testing"
1011
1112 ssz "github.com/prysmaticlabs/fastssz"
13+ "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/kzg"
1214 "github.com/prysmaticlabs/prysm/v5/beacon-chain/db"
1315 "github.com/prysmaticlabs/prysm/v5/beacon-chain/verification"
1416 "github.com/prysmaticlabs/prysm/v5/config/params"
17+ "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
1518 "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
1619 ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
1720 "github.com/prysmaticlabs/prysm/v5/testing/require"
@@ -56,7 +59,7 @@ func TestBlobStorage_SaveBlobData(t *testing.T) {
5659 require .NoError (t , bs .Save (sc ))
5760 actualSc , err := bs .Get (sc .BlockRoot (), sc .Index )
5861 require .NoError (t , err )
59- expectedIdx := blobIndexMask {false , false , true , false , false , false }
62+ expectedIdx := dataIndexMask {false , false , true , false , false , false }
6063 actualIdx := bs .Summary (actualSc .BlockRoot ()).mask
6164 require .NoError (t , err )
6265 require .DeepEqual (t , expectedIdx , actualIdx )
@@ -221,3 +224,158 @@ func TestLayoutNames(t *testing.T) {
221224 _ , err := newLayout (badLayoutName , nil , nil , nil )
222225 require .ErrorIs (t , err , errInvalidLayoutName )
223226}
227+
228+ func TestBlobStorage_DataColumn_WithAllLayouts (t * testing.T ) {
229+ for _ , layout := range LayoutNames {
230+ t .Run (fmt .Sprintf ("layout=%s" , layout ), func (t * testing.T ) {
231+ sidecars := setupDataColumnTest (t )
232+
233+ t .Run ("no error for duplicate" , func (t * testing.T ) {
234+ fs , bs := NewEphemeralBlobStorageAndFs (t , WithLayout (layout ))
235+ sidecar := sidecars [0 ]
236+
237+ columnPath := bs .layout .sszPath (identForDataColumnSidecar (sidecar ))
238+ data , err := ssz .MarshalSSZ (sidecar )
239+ require .NoError (t , err )
240+
241+ require .NoError (t , bs .SaveDataColumn (sidecar ))
242+ // No error when attempting to write twice.
243+ require .NoError (t , bs .SaveDataColumn (sidecar ))
244+
245+ content , err := afero .ReadFile (fs , columnPath )
246+ require .NoError (t , err )
247+ require .Equal (t , true , bytes .Equal (data , content ))
248+
249+ // Deserialize the DataColumnSidecar from the saved file data.
250+ saved := & ethpb.DataColumnSidecar {}
251+ err = saved .UnmarshalSSZ (content )
252+ require .NoError (t , err )
253+
254+ // Compare the original Sidecar and the saved Sidecar.
255+ require .DeepSSZEqual (t , sidecar .DataColumnSidecar , saved )
256+ })
257+
258+ t .Run ("indices" , func (t * testing.T ) {
259+ bs := NewEphemeralBlobStorage (t , WithLayout (layout ))
260+ sidecar := sidecars [2 ]
261+ require .NoError (t , bs .SaveDataColumn (sidecar ))
262+ actual , err := bs .GetColumn (sidecar .BlockRoot (), sidecar .ColumnIndex )
263+ require .NoError (t , err )
264+ require .DeepEqual (t , sidecar , actual )
265+
266+ expectedIdx := make (dataIndexMask , params .BeaconConfig ().NumberOfColumns )
267+ expectedIdx [2 ] = true
268+ actualIdx := bs .Summary (actual .BlockRoot ()).mask
269+ require .NoError (t , err )
270+ require .DeepEqual (t , expectedIdx , actualIdx )
271+
272+ sidecar = sidecars [10 ]
273+ expectedIdx [10 ] = true
274+ require .NoError (t , bs .SaveDataColumn (sidecar ))
275+ actual , err = bs .GetColumn (sidecar .BlockRoot (), sidecar .ColumnIndex )
276+ require .NoError (t , err )
277+ require .DeepEqual (t , sidecar , actual )
278+ actualIdx = bs .Summary (actual .BlockRoot ()).mask
279+ require .NoError (t , err )
280+ require .DeepEqual (t , expectedIdx , actualIdx )
281+ })
282+
283+ t .Run ("write -> read -> delete" , func (t * testing.T ) {
284+ bs := NewEphemeralBlobStorage (t , WithLayout (layout ))
285+ err := bs .SaveDataColumn (sidecars [0 ])
286+ require .NoError (t , err )
287+
288+ expected := sidecars [0 ]
289+ actual , err := bs .GetColumn (expected .BlockRoot (), expected .ColumnIndex )
290+ require .NoError (t , err )
291+ require .DeepEqual (t , expected , actual )
292+
293+ require .NoError (t , bs .Remove (expected .BlockRoot ()))
294+ for i := range params .BeaconConfig ().NumberOfColumns {
295+ _ , err = bs .GetColumn (expected .BlockRoot (), uint64 (i ))
296+ require .Equal (t , true , db .IsNotFound (err ))
297+ }
298+ })
299+
300+ t .Run ("clear" , func (t * testing.T ) {
301+ bs := NewEphemeralBlobStorage (t , WithLayout (layout ))
302+ err := bs .SaveDataColumn (sidecars [0 ])
303+ require .NoError (t , err )
304+ res , err := bs .GetColumn (sidecars [0 ].BlockRoot (), sidecars [0 ].ColumnIndex )
305+ require .NoError (t , err )
306+ require .NotNil (t , res )
307+ require .NoError (t , bs .Clear ())
308+ // After clearing, the blob should not exist in the db.
309+ _ , err = bs .GetColumn (sidecars [0 ].BlockRoot (), sidecars [0 ].ColumnIndex )
310+ require .ErrorIs (t , err , os .ErrNotExist )
311+ })
312+ })
313+ }
314+ }
315+
316+ func TestBlobStorage_DataColumn_WithMigrationFromFlatToByEpoch (t * testing.T ) {
317+ sidecars := setupDataColumnTest (t )
318+
319+ // Setup flat layout
320+ fs , bs := NewEphemeralBlobStorageAndFs (t , WithLayout (LayoutNameFlat ))
321+ sidecar := sidecars [0 ]
322+ columnPath := bs .layout .sszPath (identForDataColumnSidecar (sidecar ))
323+ data , err := ssz .MarshalSSZ (sidecar )
324+ require .NoError (t , err )
325+ require .NoError (t , bs .SaveDataColumn (sidecar ))
326+ content , err := afero .ReadFile (fs , columnPath )
327+ require .NoError (t , err )
328+ require .Equal (t , true , bytes .Equal (data , content ))
329+
330+ // Setup by-epoch layout
331+ bs = NewWarmedEphemeralBlobStorageUsingFs (t , fs , WithLayout (LayoutNameByEpoch ))
332+
333+ // Verify data is the same
334+ columnPath = bs .layout .sszPath (identForDataColumnSidecar (sidecar ))
335+ content , err = afero .ReadFile (fs , columnPath )
336+ require .NoError (t , err )
337+ require .Equal (t , true , bytes .Equal (data , content ))
338+ }
339+
340+ func TestBlobStorage_DataColumn_WithMigrationFromByEpochToFlat (t * testing.T ) {
341+ sidecars := setupDataColumnTest (t )
342+
343+ // Setup by-epoch layout
344+ fs , bs := NewEphemeralBlobStorageAndFs (t , WithLayout (LayoutNameFlat ))
345+ for _ , sidecar := range sidecars {
346+ require .NoError (t , bs .SaveDataColumn (sidecar ))
347+ }
348+ columnPath := bs .layout .sszPath (identForDataColumnSidecar (sidecars [0 ]))
349+ content , err := afero .ReadFile (fs , columnPath )
350+ require .NoError (t , err )
351+ data , err := ssz .MarshalSSZ (sidecars [0 ])
352+ require .NoError (t , err )
353+ require .Equal (t , true , bytes .Equal (data , content ))
354+
355+ // Setup flat layout
356+ bs = NewWarmedEphemeralBlobStorageUsingFs (t , fs , WithLayout (LayoutNameByEpoch ))
357+
358+ // Verify data is the same
359+ columnPath = bs .layout .sszPath (identForDataColumnSidecar (sidecars [0 ]))
360+ content , err = afero .ReadFile (fs , columnPath )
361+ require .NoError (t , err )
362+ require .Equal (t , true , bytes .Equal (data , content ))
363+ }
364+
365+ func setupDataColumnTest (t * testing.T ) []blocks.VerifiedRODataColumn {
366+ // load trusted setup
367+ err := kzg .Start ()
368+ require .NoError (t , err )
369+
370+ // Setup right fork epoch
371+ params .SetupTestConfigCleanup (t )
372+ cfg := params .BeaconConfig ().Copy ()
373+ cfg .CapellaForkEpoch = 0
374+ cfg .DenebForkEpoch = 0
375+ cfg .ElectraForkEpoch = 0
376+ cfg .FuluForkEpoch = 0
377+ params .OverrideBeaconConfig (cfg )
378+
379+ _ , scs := util .GenerateTestFuluBlockWithSidecar (t , [32 ]byte {}, 0 , 1 )
380+ return verification .FakeVerifyDataColumnSliceForTest (t , scs )
381+ }
0 commit comments