package filesystem import ( "testing" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" "github.com/OffchainLabs/prysm/v7/testing/require" ) func TestHasIndex(t *testing.T) { summary := NewDataColumnStorageSummary(0, [fieldparams.NumberOfColumns]bool{false, true}) hasIndex := summary.HasIndex(1_000_000) require.Equal(t, false, hasIndex) hasIndex = summary.HasIndex(0) require.Equal(t, false, hasIndex) hasIndex = summary.HasIndex(1) require.Equal(t, true, hasIndex) } func TestHasAtLeastOneIndex(t *testing.T) { summary := NewDataColumnStorageSummary(0, [fieldparams.NumberOfColumns]bool{false, true}) actual := summary.HasAtLeastOneIndex([]uint64{3, 1, fieldparams.NumberOfColumns, 2}) require.Equal(t, true, actual) actual = summary.HasAtLeastOneIndex([]uint64{3, 4, fieldparams.NumberOfColumns, 2}) require.Equal(t, false, actual) } func TestCount(t *testing.T) { summary := NewDataColumnStorageSummary(0, [fieldparams.NumberOfColumns]bool{false, true, false, true}) count := summary.Count() require.Equal(t, uint64(2), count) } func TestAllAvailableDataColumns(t *testing.T) { const count = uint64(1_000) summary := NewDataColumnStorageSummary(0, [fieldparams.NumberOfColumns]bool{false, true, false, true}) indices := make(map[uint64]bool, count) for i := range count { indices[i] = true } allAvailable := summary.AllAvailable(indices) require.Equal(t, false, allAvailable) indices = map[uint64]bool{1: true, 2: true} allAvailable = summary.AllAvailable(indices) require.Equal(t, false, allAvailable) indices = map[uint64]bool{1: true, 3: true} allAvailable = summary.AllAvailable(indices) require.Equal(t, true, allAvailable) } func TestStored(t *testing.T) { summary := NewDataColumnStorageSummary(0, [fieldparams.NumberOfColumns]bool{false, true, true, false}) expected := map[uint64]bool{1: true, 2: true} actual := summary.Stored() require.Equal(t, len(expected), len(actual)) for k, v := range expected { require.Equal(t, v, actual[k]) } } func TestSummary(t *testing.T) { root := [fieldparams.RootLength]byte{} summaryCache := newDataColumnStorageSummaryCache() expected := NewDataColumnStorageSummary(0, [fieldparams.NumberOfColumns]bool{}) actual := summaryCache.Summary(root) require.DeepEqual(t, expected, actual) summaryCache = newDataColumnStorageSummaryCache() expected = NewDataColumnStorageSummary(0, [fieldparams.NumberOfColumns]bool{true, false, true, false}) summaryCache.cache[root] = expected actual = summaryCache.Summary(root) require.DeepEqual(t, expected, actual) } func TestHighestEpoch(t *testing.T) { root1 := [fieldparams.RootLength]byte{1} root2 := [fieldparams.RootLength]byte{2} root3 := [fieldparams.RootLength]byte{3} summaryCache := newDataColumnStorageSummaryCache() actual := summaryCache.HighestEpoch() require.Equal(t, primitives.Epoch(0), actual) err := summaryCache.set(DataColumnsIdent{Root: root1, Epoch: 42, Indices: []uint64{1, 3}}) require.NoError(t, err) require.Equal(t, primitives.Epoch(42), summaryCache.HighestEpoch()) err = summaryCache.set(DataColumnsIdent{Root: root2, Epoch: 43, Indices: []uint64{1, 3}}) require.NoError(t, err) require.Equal(t, primitives.Epoch(43), summaryCache.HighestEpoch()) err = summaryCache.set(DataColumnsIdent{Root: root3, Epoch: 40, Indices: []uint64{1, 3}}) require.NoError(t, err) require.Equal(t, primitives.Epoch(43), summaryCache.HighestEpoch()) } func TestSet(t *testing.T) { t.Run("Index out of bounds", func(t *testing.T) { summaryCache := newDataColumnStorageSummaryCache() err := summaryCache.set(DataColumnsIdent{Indices: []uint64{1_000_000}}) require.ErrorIs(t, err, errDataColumnIndexOutOfBounds) require.Equal(t, params.BeaconConfig().FarFutureEpoch, summaryCache.lowestCachedEpoch) require.Equal(t, 0, len(summaryCache.cache)) }) t.Run("Nominal", func(t *testing.T) { root1 := [fieldparams.RootLength]byte{1} root2 := [fieldparams.RootLength]byte{2} summaryCache := newDataColumnStorageSummaryCache() err := summaryCache.set(DataColumnsIdent{Root: root1, Epoch: 42, Indices: []uint64{1, 3}}) require.NoError(t, err) require.Equal(t, primitives.Epoch(42), summaryCache.lowestCachedEpoch) require.Equal(t, 1, len(summaryCache.cache)) expected := DataColumnStorageSummary{epoch: 42, mask: [fieldparams.NumberOfColumns]bool{false, true, false, true}} actual := summaryCache.cache[root1] require.DeepEqual(t, expected, actual) err = summaryCache.set(DataColumnsIdent{Root: root1, Epoch: 42, Indices: []uint64{0, 1}}) require.NoError(t, err) require.Equal(t, primitives.Epoch(42), summaryCache.lowestCachedEpoch) require.Equal(t, 1, len(summaryCache.cache)) expected = DataColumnStorageSummary{epoch: 42, mask: [fieldparams.NumberOfColumns]bool{true, true, false, true}} actual = summaryCache.cache[root1] require.DeepEqual(t, expected, actual) err = summaryCache.set(DataColumnsIdent{Root: root2, Epoch: 43, Indices: []uint64{1}}) require.NoError(t, err) require.Equal(t, primitives.Epoch(42), summaryCache.lowestCachedEpoch) // Epoch 42 is still the lowest require.Equal(t, 2, len(summaryCache.cache)) expected = DataColumnStorageSummary{epoch: 43, mask: [fieldparams.NumberOfColumns]bool{false, true}} actual = summaryCache.cache[root2] require.DeepEqual(t, expected, actual) }) } func TestGet(t *testing.T) { t.Run("Not in cache", func(t *testing.T) { summaryCache := newDataColumnStorageSummaryCache() root := [fieldparams.RootLength]byte{} _, ok := summaryCache.get(root) require.Equal(t, false, ok) }) t.Run("In cache", func(t *testing.T) { root := [fieldparams.RootLength]byte{} summaryCache := newDataColumnStorageSummaryCache() summaryCache.cache[root] = NewDataColumnStorageSummary(42, [fieldparams.NumberOfColumns]bool{true, false, true, false}) actual, ok := summaryCache.get(root) require.Equal(t, true, ok) expected := NewDataColumnStorageSummary(42, [fieldparams.NumberOfColumns]bool{true, false, true, false}) require.DeepEqual(t, expected, actual) }) } func TestEvict(t *testing.T) { t.Run("No eviction", func(t *testing.T) { root := [fieldparams.RootLength]byte{} summaryCache := newDataColumnStorageSummaryCache() evicted := summaryCache.evict(root) require.Equal(t, 0, evicted) }) t.Run("Eviction", func(t *testing.T) { root1 := [fieldparams.RootLength]byte{1} root2 := [fieldparams.RootLength]byte{2} summaryCache := newDataColumnStorageSummaryCache() summaryCache.cache[root1] = NewDataColumnStorageSummary(42, [fieldparams.NumberOfColumns]bool{true, false, true, false}) summaryCache.cache[root2] = NewDataColumnStorageSummary(43, [fieldparams.NumberOfColumns]bool{false, true, false, true}) evicted := summaryCache.evict(root1) require.Equal(t, 2, evicted) require.Equal(t, 1, len(summaryCache.cache)) _, ok := summaryCache.cache[root1] require.Equal(t, false, ok) _, ok = summaryCache.cache[root2] require.Equal(t, true, ok) }) } func TestPruneUpTo(t *testing.T) { t.Run("No pruning", func(t *testing.T) { summaryCache := newDataColumnStorageSummaryCache() err := summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{1}, Epoch: 42, Indices: []uint64{1}}) require.NoError(t, err) err = summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{2}, Epoch: 43, Indices: []uint64{2, 4}}) require.NoError(t, err) count := summaryCache.pruneUpTo(41) require.Equal(t, uint64(0), count) require.Equal(t, 2, len(summaryCache.cache)) require.Equal(t, primitives.Epoch(42), summaryCache.lowestCachedEpoch) }) t.Run("Pruning", func(t *testing.T) { summaryCache := newDataColumnStorageSummaryCache() err := summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{1}, Epoch: 42, Indices: []uint64{1}}) require.NoError(t, err) err = summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{2}, Epoch: 44, Indices: []uint64{2, 4}}) require.NoError(t, err) err = summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{3}, Epoch: 45, Indices: []uint64{2, 4}}) require.NoError(t, err) count := summaryCache.pruneUpTo(42) require.Equal(t, uint64(1), count) require.Equal(t, 2, len(summaryCache.cache)) require.Equal(t, primitives.Epoch(44), summaryCache.lowestCachedEpoch) count = summaryCache.pruneUpTo(45) require.Equal(t, uint64(4), count) require.Equal(t, 0, len(summaryCache.cache)) require.Equal(t, params.BeaconConfig().FarFutureEpoch, summaryCache.lowestCachedEpoch) require.Equal(t, primitives.Epoch(0), summaryCache.highestCachedEpoch) }) t.Run("Clear", func(t *testing.T) { summaryCache := newDataColumnStorageSummaryCache() err := summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{1}, Epoch: 42, Indices: []uint64{1}}) require.NoError(t, err) err = summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{2}, Epoch: 44, Indices: []uint64{2, 4}}) require.NoError(t, err) err = summaryCache.set(DataColumnsIdent{Root: [fieldparams.RootLength]byte{3}, Epoch: 45, Indices: []uint64{2, 4}}) require.NoError(t, err) count := summaryCache.clear() require.Equal(t, uint64(5), count) require.Equal(t, 0, len(summaryCache.cache)) require.Equal(t, params.BeaconConfig().FarFutureEpoch, summaryCache.lowestCachedEpoch) require.Equal(t, primitives.Epoch(0), summaryCache.highestCachedEpoch) }) }