From 47e31ca163494ad31be19962fcd5a22fc8abce05 Mon Sep 17 00:00:00 2001 From: Adrian Dobrita Date: Thu, 9 Jul 2026 11:32:31 +0300 Subject: [PATCH 1/2] cleanup wrong type records, add tests --- integrationTests/testProcessorNode.go | 6 +- process/block/metablockProposal_test.go | 63 ++++++++++++++------ process/block/shardblockProposal_test.go | 42 +++++++++++++ process/track/baseBlockTrack.go | 34 ++++++----- process/track/baseBlockTrack_test.go | 75 +++++++++++++++++++++++- process/track/export_test.go | 8 +++ 6 files changed, 193 insertions(+), 35 deletions(-) diff --git a/integrationTests/testProcessorNode.go b/integrationTests/testProcessorNode.go index 4db620e0d6..b579fced8d 100644 --- a/integrationTests/testProcessorNode.go +++ b/integrationTests/testProcessorNode.go @@ -31,11 +31,12 @@ import ( ed25519SingleSig "github.com/multiversx/mx-chain-crypto-go/signing/ed25519/singlesig" "github.com/multiversx/mx-chain-crypto-go/signing/mcl" mclsig "github.com/multiversx/mx-chain-crypto-go/signing/mcl/singlesig" - "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" vmcommon "github.com/multiversx/mx-chain-vm-common-go" "github.com/multiversx/mx-chain-vm-common-go/parsers" wasmConfig "github.com/multiversx/mx-chain-vm-go/config" + "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" + "github.com/multiversx/mx-chain-go/process/aotSelection" "github.com/multiversx/mx-chain-go/process/asyncExecution" "github.com/multiversx/mx-chain-go/process/asyncExecution/executionManager" @@ -698,6 +699,9 @@ func (tpn *TestProcessorNode) Close() { _ = tpn.FullArchiveMessenger.Close() _ = tpn.VMContainer.Close() _ = tpn.ExecutionManager.Close() + if !check.IfNil(tpn.BlockTracker) { + _ = tpn.BlockTracker.Close() + } } func (tpn *TestProcessorNode) initAccountDBsWithPruningStorer() { diff --git a/process/block/metablockProposal_test.go b/process/block/metablockProposal_test.go index e49e3b08db..7f5d605f5a 100644 --- a/process/block/metablockProposal_test.go +++ b/process/block/metablockProposal_test.go @@ -1833,7 +1833,7 @@ func Test_checkShardHeadersValidityAndFinalityProposal(t *testing.T) { dataPoolMock.SetProofsPool(proofsPool) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -1859,7 +1859,7 @@ func Test_checkShardHeadersValidityAndFinalityProposal(t *testing.T) { }, "dataPool": dataPoolMock, "proofsPool": proofsPool, - "store": storage, + "store": st, }) require.Nil(t, err) @@ -1893,7 +1893,7 @@ func Test_checkShardHeadersValidityAndFinalityProposal(t *testing.T) { dataPoolMock.SetProofsPool(proofsPool) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -1929,7 +1929,7 @@ func Test_checkShardHeadersValidityAndFinalityProposal(t *testing.T) { }, }, "proofsPool": proofsPool, - "store": storage, + "store": st, }) require.Nil(t, err) @@ -3041,7 +3041,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { dataPoolMock.SetHeadersPool(headersPoolMock) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -3062,7 +3062,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { }, }, "marshalizer": &marshal.GogoProtoMarshalizer{}, - "store": storage, + "store": st, "epochStartTrigger": &testscommon.EpochStartTriggerStub{ EpochCalled: func() uint32 { return 1 @@ -3101,7 +3101,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { dataPoolMock.SetHeadersPool(headersPoolMock) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -3123,7 +3123,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { return 1 }}, "marshalizer": marshaller, - "store": storage, + "store": st, }) require.Nil(t, err) @@ -3164,7 +3164,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { dataPoolMock.SetHeadersPool(headersPoolMock) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -3188,7 +3188,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { return 1 }}, "marshalizer": marshaller, - "store": storage, + "store": st, }) require.Nil(t, err) @@ -3227,7 +3227,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { dataPoolMock.SetHeadersPool(headersPoolMock) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -3250,7 +3250,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { "epochStartTrigger": &testscommon.EpochStartTriggerStub{EpochCalled: func() uint32 { return 1 }}, - "store": storage, + "store": st, "marshalizer": marshaller, }) require.Nil(t, err) @@ -3293,7 +3293,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { dataPoolMock.SetHeadersPool(headersPoolMock) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -3317,7 +3317,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { return 1 }}, "marshalizer": &marshal.GogoProtoMarshalizer{}, - "store": storage, + "store": st, }) require.Nil(t, err) mp.SetEpochStartData(&blproc.EpochStartDataWrapper{ @@ -3363,7 +3363,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { dataPoolMock.SetHeadersPool(headersPoolMock) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -3386,7 +3386,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { "epochStartTrigger": &testscommon.EpochStartTriggerStub{EpochCalled: func() uint32 { return 1 }}, - "store": storage, + "store": st, "marshalizer": &marshal.GogoProtoMarshalizer{}, }) mp.SetEpochStartData(&blproc.EpochStartDataWrapper{ @@ -3428,7 +3428,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { dataPoolMock.SetHeadersPool(headersPoolMock) marshaller := &marshal.GogoProtoMarshalizer{} - storage := &storageStubs.ChainStorerStub{ + st := &storageStubs.ChainStorerStub{ GetStorerCalled: func(unitType dataRetriever.UnitType) (storage.Storer, error) { return &storageStubs.StorerStub{ GetCalled: func(key []byte) ([]byte, error) { @@ -3452,7 +3452,7 @@ func TestMetaProcessor_checkEpochCorrectnessV3(t *testing.T) { "epochStartTrigger": &testscommon.EpochStartTriggerStub{EpochCalled: func() uint32 { return 1 }}, - "store": storage, + "store": st, }) mp.SetEpochStartData(&blproc.EpochStartDataWrapper{ Epoch: 2, @@ -3595,6 +3595,33 @@ func TestMetaProcessor_checkHeadersSequenceCorrectness(t *testing.T) { require.Equal(t, expectedErr, err) }) + t.Run("should return error for quarantined header", func(t *testing.T) { + t.Parallel() + + quarantinedHash := []byte("quarantined hash") + mp, err := blproc.ConstructPartialMetaBlockProcessorForTest(map[string]interface{}{ + "headerValidator": &processMocks.HeaderValidatorMock{ + IsHeaderConstructionValidCalled: func(currHdr, prevHdr data.HeaderHandler) error { + return nil + }, + }, + "blockTracker": &integrationTestsMock.BlockTrackerStub{ + IsHeaderQuarantinedCalled: func(hash []byte) bool { + return bytes.Equal(hash, quarantinedHash) + }, + }, + }) + require.Nil(t, err) + + err = mp.CheckHeadersSequenceCorrectness([]blproc.ShardHeaderInfo{ + { + Header: &block.Header{Nonce: 2}, + Hash: quarantinedHash, + }, + }, blproc.ShardHeaderInfo{}) + require.ErrorContains(t, err, "included quarantined header") + }) + t.Run("should work", func(t *testing.T) { t.Parallel() diff --git a/process/block/shardblockProposal_test.go b/process/block/shardblockProposal_test.go index da136c6304..8db03867c6 100644 --- a/process/block/shardblockProposal_test.go +++ b/process/block/shardblockProposal_test.go @@ -2740,6 +2740,48 @@ func TestShardProcessor_CheckMetaHeadersValidityAndFinalityProposal(t *testing.T require.Equal(t, expectedError, err) }) + t.Run("quarantined meta header should error", func(t *testing.T) { + t.Parallel() + + coreComponents, dataComponents, bootstrapComponents, statusComponents := createComponentHolderMocks() + arguments := CreateMockArguments(coreComponents, dataComponents, bootstrapComponents, statusComponents) + + metaHeader := &block.MetaBlockV3{} + arguments.BlockTracker = &mock.BlockTrackerMock{ + GetLastCrossNotarizedHeaderCalled: func(shardID uint32) (data.HeaderHandler, []byte, error) { + return metaHeader, []byte("h"), nil + }, + IsHeaderQuarantinedCalled: func(hash []byte) bool { + return true + }, + } + arguments.HeaderValidator = &processMocks.HeaderValidatorMock{ + IsHeaderConstructionValidCalled: func(currHdr, prevHdr data.HeaderHandler) error { + require.Fail(t, "should not have been called for a quarantined header") + return nil + }, + } + + dataPool, ok := dataComponents.Datapool().(*dataRetriever.PoolsHolderStub) + require.True(t, ok) + + dataPool.HeadersCalled = func() retriever.HeadersPool { + return &pool.HeadersPoolStub{ + GetHeaderByHashCalled: func(hash []byte) (data.HeaderHandler, error) { + return &block.Header{}, nil + }, + } + } + + sp, _ := blproc.NewShardProcessor(arguments) + + header := &block.HeaderV3{ + MetaBlockHashes: [][]byte{[]byte("hh")}, + } + err := sp.CheckMetaHeadersValidityAndFinalityProposal(header) + require.ErrorContains(t, err, "included quarantined header") + }) + t.Run("invalid header should error", func(t *testing.T) { t.Parallel() diff --git a/process/track/baseBlockTrack.go b/process/track/baseBlockTrack.go index f7a33ff597..a6d05e4be6 100644 --- a/process/track/baseBlockTrack.go +++ b/process/track/baseBlockTrack.go @@ -205,21 +205,27 @@ func (bbt *baseBlockTrack) sweepQuarantinedHeaders(ctx context.Context) { case <-ctx.Done(): return case <-ticker.C: - currentRound := bbt.roundHandler.Index() - for _, key := range bbt.quarantinedHeaders.Keys() { - val, _ := bbt.quarantinedHeaders.Get(key) - headerRound, ok := val.(uint64) - if !ok { - continue - } - - if currentRound-int64(headerRound) < maxQuarantineRoundDelta { - continue - } - - bbt.quarantinedHeaders.Remove(key) - } + bbt.sweepExpiredQuarantinedHeaders() + } + } +} + +func (bbt *baseBlockTrack) sweepExpiredQuarantinedHeaders() { + currentRound := bbt.roundHandler.Index() + for _, key := range bbt.quarantinedHeaders.Keys() { + val, _ := bbt.quarantinedHeaders.Get(key) + headerRound, ok := val.(uint64) + if !ok { + log.Warn("sweepExpiredQuarantinedHeaders: unexpected value type, removing entry", "hash", key) + bbt.quarantinedHeaders.Remove(key) + continue } + + if currentRound-int64(headerRound) < maxQuarantineRoundDelta { + continue + } + + bbt.quarantinedHeaders.Remove(key) } } diff --git a/process/track/baseBlockTrack_test.go b/process/track/baseBlockTrack_test.go index 762ea05157..aef79a9f52 100644 --- a/process/track/baseBlockTrack_test.go +++ b/process/track/baseBlockTrack_test.go @@ -10,12 +10,13 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data" "github.com/multiversx/mx-chain-core-go/data/block" - "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" - "github.com/multiversx/mx-chain-go/testscommon/pool" logger "github.com/multiversx/mx-chain-logger-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/multiversx/mx-chain-go/testscommon/epochNotifier" + "github.com/multiversx/mx-chain-go/testscommon/pool" + "github.com/multiversx/mx-chain-go/common/configs" "github.com/multiversx/mx-chain-go/common/graceperiod" "github.com/multiversx/mx-chain-go/config" @@ -2543,6 +2544,76 @@ func TestBaseBlockTrack_QuarantineIfLateProof(t *testing.T) { }) } +func TestBaseBlockTrack_SweepExpiredQuarantinedHeaders(t *testing.T) { + t.Parallel() + + const currentRound = int64(50) + + newTracker := func() (interface { + SweepExpiredQuarantinedHeaders() + }, storage.Cacher) { + quarantinedHeaders, err := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: 100, Shards: 1}) + require.NoError(t, err) + + bbt := track.NewBaseBlockTrack() + bbt.SetRoundHandler(&mock.RoundHandlerMock{RoundIndex: currentRound}) + bbt.SetQuarantinedHeaders(quarantinedHeaders) + + return bbt, quarantinedHeaders + } + + t.Run("recent entries are kept", func(t *testing.T) { + t.Parallel() + + bbt, quarantinedHeaders := newTracker() + hash := []byte("recent hash") + quarantinedHeaders.Put(hash, uint64(currentRound-track.MaxQuarantineRoundDelta+1), 8) + + bbt.SweepExpiredQuarantinedHeaders() + + require.True(t, quarantinedHeaders.Has(hash)) + }) + + t.Run("entries at the round delta threshold are removed", func(t *testing.T) { + t.Parallel() + + bbt, quarantinedHeaders := newTracker() + hash := []byte("expired hash") + quarantinedHeaders.Put(hash, uint64(currentRound-track.MaxQuarantineRoundDelta), 8) + + bbt.SweepExpiredQuarantinedHeaders() + + require.False(t, quarantinedHeaders.Has(hash)) + }) + + t.Run("entries with unexpected value type are removed", func(t *testing.T) { + t.Parallel() + + bbt, quarantinedHeaders := newTracker() + hash := []byte("bad value hash") + quarantinedHeaders.Put(hash, int64(currentRound), 8) + + bbt.SweepExpiredQuarantinedHeaders() + + require.False(t, quarantinedHeaders.Has(hash)) + }) + + t.Run("only expired entries are removed", func(t *testing.T) { + t.Parallel() + + bbt, quarantinedHeaders := newTracker() + expiredHash := []byte("expired hash") + recentHash := []byte("recent hash") + quarantinedHeaders.Put(expiredHash, uint64(currentRound-track.MaxQuarantineRoundDelta-1), 8) + quarantinedHeaders.Put(recentHash, uint64(currentRound-1), 8) + + bbt.SweepExpiredQuarantinedHeaders() + + require.False(t, quarantinedHeaders.Has(expiredHash)) + require.True(t, quarantinedHeaders.Has(recentHash)) + }) +} + func TestBaseBlockTrack_CheckProofAgainstRoundHandler(t *testing.T) { t.Parallel() diff --git a/process/track/export_test.go b/process/track/export_test.go index 7dbd0e44a4..45eb3b87d9 100644 --- a/process/track/export_test.go +++ b/process/track/export_test.go @@ -185,6 +185,14 @@ func (bbt *baseBlockTrack) QuarantineIfLateProof(proof data.HeaderProofHandler) bbt.quarantineIfLateProof(proof) } +// SweepExpiredQuarantinedHeaders - +func (bbt *baseBlockTrack) SweepExpiredQuarantinedHeaders() { + bbt.sweepExpiredQuarantinedHeaders() +} + +// MaxQuarantineRoundDelta - +const MaxQuarantineRoundDelta = maxQuarantineRoundDelta + // blockNotifier // GetNotarizedHeadersHandlers - From 1c3c522a86521c2baab38b8940ac47b282258cb4 Mon Sep 17 00:00:00 2001 From: Adrian Dobrita Date: Thu, 9 Jul 2026 11:51:41 +0300 Subject: [PATCH 2/2] fixes after review --- process/track/baseBlockTrack.go | 6 +- process/track/baseBlockTrack_test.go | 152 ++++++++++++++++----------- 2 files changed, 93 insertions(+), 65 deletions(-) diff --git a/process/track/baseBlockTrack.go b/process/track/baseBlockTrack.go index a6d05e4be6..353c6ad10f 100644 --- a/process/track/baseBlockTrack.go +++ b/process/track/baseBlockTrack.go @@ -213,7 +213,11 @@ func (bbt *baseBlockTrack) sweepQuarantinedHeaders(ctx context.Context) { func (bbt *baseBlockTrack) sweepExpiredQuarantinedHeaders() { currentRound := bbt.roundHandler.Index() for _, key := range bbt.quarantinedHeaders.Keys() { - val, _ := bbt.quarantinedHeaders.Get(key) + val, found := bbt.quarantinedHeaders.Get(key) + if !found { + continue + } + headerRound, ok := val.(uint64) if !ok { log.Warn("sweepExpiredQuarantinedHeaders: unexpected value type, removing entry", "hash", key) diff --git a/process/track/baseBlockTrack_test.go b/process/track/baseBlockTrack_test.go index aef79a9f52..2f72872b12 100644 --- a/process/track/baseBlockTrack_test.go +++ b/process/track/baseBlockTrack_test.go @@ -30,6 +30,7 @@ import ( "github.com/multiversx/mx-chain-go/storage/database" "github.com/multiversx/mx-chain-go/storage/storageunit" "github.com/multiversx/mx-chain-go/testscommon" + "github.com/multiversx/mx-chain-go/testscommon/cache" dataRetrieverMock "github.com/multiversx/mx-chain-go/testscommon/dataRetriever" "github.com/multiversx/mx-chain-go/testscommon/economicsmocks" "github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock" @@ -108,8 +109,8 @@ func generateStorageUnit() storage.Storer { } func generateTestCache() storage.Cacher { - cache, _ := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: 1000, Shards: 1, SizeInBytes: 0}) - return cache + testCache, _ := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: 1000, Shards: 1, SizeInBytes: 0}) + return testCache } func CreateShardTrackerMockArguments() track.ArgShardTracker { @@ -2466,7 +2467,7 @@ func TestBaseBlockTrack_QuarantineIfLateProof(t *testing.T) { const currentRound = int64(50) - newTracker := func() (interface { + newTracker := func(t *testing.T) (interface { QuarantineIfLateProof(proof data.HeaderProofHandler) }, storage.Cacher) { quarantinedHeaders, err := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: 100, Shards: 1}) @@ -2492,7 +2493,7 @@ func TestBaseBlockTrack_QuarantineIfLateProof(t *testing.T) { t.Run("one round late intra shard proof should not be quarantined", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) hash := []byte("late proof header hash") bbt.QuarantineIfLateProof(&block.HeaderProof{ HeaderHash: hash, @@ -2506,7 +2507,7 @@ func TestBaseBlockTrack_QuarantineIfLateProof(t *testing.T) { t.Run("same round proof should not be quarantined", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) hash := []byte("same round proof header hash") bbt.QuarantineIfLateProof(&block.HeaderProof{ HeaderHash: hash, @@ -2519,7 +2520,7 @@ func TestBaseBlockTrack_QuarantineIfLateProof(t *testing.T) { t.Run("higher round proof should error and not be quarantined", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) hash := []byte("higher round proof header hash") bbt.QuarantineIfLateProof(&block.HeaderProof{ HeaderHash: hash, @@ -2532,7 +2533,7 @@ func TestBaseBlockTrack_QuarantineIfLateProof(t *testing.T) { t.Run("one round late proof should be quarantined", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) hash := []byte("late proof header hash") bbt.QuarantineIfLateProof(&block.HeaderProof{ HeaderHash: hash, @@ -2549,7 +2550,7 @@ func TestBaseBlockTrack_SweepExpiredQuarantinedHeaders(t *testing.T) { const currentRound = int64(50) - newTracker := func() (interface { + newTracker := func(t *testing.T) (interface { SweepExpiredQuarantinedHeaders() }, storage.Cacher) { quarantinedHeaders, err := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: 100, Shards: 1}) @@ -2565,7 +2566,7 @@ func TestBaseBlockTrack_SweepExpiredQuarantinedHeaders(t *testing.T) { t.Run("recent entries are kept", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) hash := []byte("recent hash") quarantinedHeaders.Put(hash, uint64(currentRound-track.MaxQuarantineRoundDelta+1), 8) @@ -2577,7 +2578,7 @@ func TestBaseBlockTrack_SweepExpiredQuarantinedHeaders(t *testing.T) { t.Run("entries at the round delta threshold are removed", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) hash := []byte("expired hash") quarantinedHeaders.Put(hash, uint64(currentRound-track.MaxQuarantineRoundDelta), 8) @@ -2586,10 +2587,33 @@ func TestBaseBlockTrack_SweepExpiredQuarantinedHeaders(t *testing.T) { require.False(t, quarantinedHeaders.Has(hash)) }) + t.Run("entries removed concurrently are skipped", func(t *testing.T) { + t.Parallel() + + removeCalled := false + bbt := track.NewBaseBlockTrack() + bbt.SetRoundHandler(&mock.RoundHandlerMock{RoundIndex: currentRound}) + bbt.SetQuarantinedHeaders(&cache.CacherStub{ + KeysCalled: func() [][]byte { + return [][]byte{[]byte("removed hash")} + }, + GetCalled: func(key []byte) (interface{}, bool) { + return nil, false + }, + RemoveCalled: func(key []byte) { + removeCalled = true + }, + }) + + bbt.SweepExpiredQuarantinedHeaders() + + require.False(t, removeCalled) + }) + t.Run("entries with unexpected value type are removed", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) hash := []byte("bad value hash") quarantinedHeaders.Put(hash, int64(currentRound), 8) @@ -2601,7 +2625,7 @@ func TestBaseBlockTrack_SweepExpiredQuarantinedHeaders(t *testing.T) { t.Run("only expired entries are removed", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) expiredHash := []byte("expired hash") recentHash := []byte("recent hash") quarantinedHeaders.Put(expiredHash, uint64(currentRound-track.MaxQuarantineRoundDelta-1), 8) @@ -2619,7 +2643,7 @@ func TestBaseBlockTrack_CheckProofAgainstRoundHandler(t *testing.T) { const currentRound = int64(50) - newTracker := func() (interface { + newTracker := func(t *testing.T) (interface { CheckProofAgainstRoundHandler(proof data.HeaderProofHandler) error }, storage.Cacher) { quarantinedHeaders, err := storageunit.NewCache(storageunit.CacheConfig{Type: storageunit.LRUCache, Capacity: 100, Shards: 1}) @@ -2640,7 +2664,7 @@ func TestBaseBlockTrack_CheckProofAgainstRoundHandler(t *testing.T) { t.Run("nil proof should err", func(t *testing.T) { t.Parallel() - bbt, quarantinedHeaders := newTracker() + bbt, quarantinedHeaders := newTracker(t) err := bbt.CheckProofAgainstRoundHandler(nil) require.Equal(t, process.ErrNilHeaderProof, err) @@ -2891,7 +2915,7 @@ func TestBaseBlockTrack_CheckBlockAgainstFinalShouldWork(t *testing.T) { func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededMetaShouldReturn(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} metaArguments := CreateMetaTrackerMockArguments() @@ -2899,7 +2923,7 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededMetaShouldReturn(t *test AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -2916,7 +2940,7 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededMetaShouldReturn(t *test mbt.DoWhitelistWithMetaBlockIfNeeded(metaHdr) - _, ok := cache[string(metaHdr.MiniBlockHeaders[0].Hash)] + _, ok := testCache[string(metaHdr.MiniBlockHeaders[0].Hash)] assert.False(t, ok) } @@ -2926,14 +2950,14 @@ func TestBaseBlockTrack_DoWhitelistWithMetaHeaderIfNeededV3ExecutionResults(t *t t.Run("execution results should whitelist cross miniblocks", func(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} shardArguments := CreateShardTrackerMockArguments() shardArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -2964,22 +2988,22 @@ func TestBaseBlockTrack_DoWhitelistWithMetaHeaderIfNeededV3ExecutionResults(t *t sbt.DoWhitelistWithMetaHeaderIfNeeded(metaHdr) - _, ok := cache[string(execMiniBlockHash)] + _, ok := testCache[string(execMiniBlockHash)] assert.True(t, ok) - assert.Len(t, cache, 1) + assert.Len(t, testCache, 1) }) t.Run("execution results and proposed miniblocks should whitelist both sources", func(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} shardArguments := CreateShardTrackerMockArguments() shardArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3014,22 +3038,22 @@ func TestBaseBlockTrack_DoWhitelistWithMetaHeaderIfNeededV3ExecutionResults(t *t sbt.DoWhitelistWithMetaHeaderIfNeeded(metaHdr) - _, ok := cache[string(execMiniBlockHash)] + _, ok := testCache[string(execMiniBlockHash)] assert.True(t, ok) - assert.Len(t, cache, 1) + assert.Len(t, testCache, 1) }) t.Run("non-cross proposed miniblocks should not be whitelisted", func(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} shardArguments := CreateShardTrackerMockArguments() shardArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3064,11 +3088,11 @@ func TestBaseBlockTrack_DoWhitelistWithMetaHeaderIfNeededV3ExecutionResults(t *t sbt.DoWhitelistWithMetaHeaderIfNeeded(metaHdr) - _, ok := cache[string(proposedMiniBlockHash)] + _, ok := testCache[string(proposedMiniBlockHash)] assert.False(t, ok) - _, ok = cache[string(execMiniBlockHash)] + _, ok = testCache[string(execMiniBlockHash)] assert.True(t, ok) - assert.Len(t, cache, 1) + assert.Len(t, testCache, 1) }) } @@ -3078,14 +3102,14 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededV3ExecutionResults(t * t.Run("execution results should whitelist cross miniblocks", func(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} metaArguments := CreateMetaTrackerMockArguments() metaArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3116,22 +3140,22 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededV3ExecutionResults(t * mbt.DoWhitelistWithShardHeaderIfNeeded(shardHdr) - _, ok := cache[string(execMiniBlockHash)] + _, ok := testCache[string(execMiniBlockHash)] assert.True(t, ok) - assert.Len(t, cache, 1) + assert.Len(t, testCache, 1) }) t.Run("execution results and proposed miniblocks should whitelist both sources", func(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} metaArguments := CreateMetaTrackerMockArguments() metaArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3166,22 +3190,22 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededV3ExecutionResults(t * mbt.DoWhitelistWithShardHeaderIfNeeded(shardHdr) - _, ok := cache[string(execMiniBlockHash)] + _, ok := testCache[string(execMiniBlockHash)] assert.True(t, ok) - assert.Len(t, cache, 1) + assert.Len(t, testCache, 1) }) t.Run("non-cross proposed miniblocks should not be whitelisted", func(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} metaArguments := CreateMetaTrackerMockArguments() metaArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3216,17 +3240,17 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededV3ExecutionResults(t * mbt.DoWhitelistWithShardHeaderIfNeeded(shardHdr) - _, ok := cache[string(proposedMiniBlockHash)] + _, ok := testCache[string(proposedMiniBlockHash)] assert.False(t, ok) - _, ok = cache[string(execMiniBlockHash)] + _, ok = testCache[string(execMiniBlockHash)] assert.True(t, ok) - assert.Len(t, cache, 1) + assert.Len(t, testCache, 1) }) } func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededShardShouldReturn(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} shardArguments := CreateShardTrackerMockArguments() @@ -3234,7 +3258,7 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededShardShouldReturn(t *t AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3251,14 +3275,14 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededShardShouldReturn(t *t sbt.DoWhitelistWithShardHeaderIfNeeded(shardHdr) - _, ok := cache[string(shardHdr.MiniBlockHeaders[0].Hash)] + _, ok := testCache[string(shardHdr.MiniBlockHeaders[0].Hash)] assert.False(t, ok) } func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededNilMetaShouldReturnAndNotPanic(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} defer func() { @@ -3271,7 +3295,7 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededNilMetaShouldReturnAndNo AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3279,13 +3303,13 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededNilMetaShouldReturnAndNo sbt, _ := track.NewShardBlockTrack(shardArguments) sbt.DoWhitelistWithMetaBlockIfNeeded(nil) - assert.Equal(t, 0, len(cache)) + assert.Equal(t, 0, len(testCache)) } func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededNilShardShouldReturnAndNotPanic(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} defer func() { @@ -3298,7 +3322,7 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededNilShardShouldReturnAn AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3306,20 +3330,20 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededNilShardShouldReturnAn mbt, _ := track.NewMetaBlockTrack(metaArguments) mbt.DoWhitelistWithShardHeaderIfNeeded(nil) - assert.Equal(t, 0, len(cache)) + assert.Equal(t, 0, len(testCache)) } func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededIsHeaderOutOfRangeShouldReturn(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} shardArguments := CreateShardTrackerMockArguments() shardArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3335,7 +3359,7 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededIsHeaderOutOfRangeShould } sbt.DoWhitelistWithMetaBlockIfNeeded(metaHdr) - _, ok := cache[string(metaHdr.MiniBlockHeaders[0].Hash)] + _, ok := testCache[string(metaHdr.MiniBlockHeaders[0].Hash)] assert.False(t, ok) } @@ -3343,14 +3367,14 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededIsHeaderOutOfRangeShould func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededIsHeaderOutOfRangeShouldReturn(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} metaArguments := CreateMetaTrackerMockArguments() metaArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3366,7 +3390,7 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededIsHeaderOutOfRangeShou } mbt.DoWhitelistWithShardHeaderIfNeeded(shardHdr) - _, ok := cache[string(shardHdr.MiniBlockHeaders[0].Hash)] + _, ok := testCache[string(shardHdr.MiniBlockHeaders[0].Hash)] assert.False(t, ok) } @@ -3374,14 +3398,14 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededIsHeaderOutOfRangeShou func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededShardShouldWhitelistCrossMiniblocks(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} shardArguments := CreateShardTrackerMockArguments() shardArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3397,7 +3421,7 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededShardShouldWhitelistCros } sbt.DoWhitelistWithMetaBlockIfNeeded(metaHdr) - _, ok := cache[string(metaHdr.MiniBlockHeaders[0].Hash)] + _, ok := testCache[string(metaHdr.MiniBlockHeaders[0].Hash)] assert.True(t, ok) } @@ -3405,14 +3429,14 @@ func TestBaseBlockTrack_DoWhitelistWithMetaBlockIfNeededShardShouldWhitelistCros func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededMetaShouldWhitelistCrossMiniblocks(t *testing.T) { t.Parallel() - cache := make(map[string]struct{}) + testCache := make(map[string]struct{}) mutCache := sync.Mutex{} metaArguments := CreateMetaTrackerMockArguments() metaArguments.WhitelistHandler = &testscommon.WhiteListHandlerStub{ AddCalled: func(keys [][]byte) { mutCache.Lock() for _, key := range keys { - cache[string(key)] = struct{}{} + testCache[string(key)] = struct{}{} } mutCache.Unlock() }, @@ -3428,7 +3452,7 @@ func TestBaseBlockTrack_DoWhitelistWithShardHeaderIfNeededMetaShouldWhitelistCro } mbt.DoWhitelistWithShardHeaderIfNeeded(shardHdr) - _, ok := cache[string(shardHdr.MiniBlockHeaders[0].Hash)] + _, ok := testCache[string(shardHdr.MiniBlockHeaders[0].Hash)] assert.True(t, ok) }