github.com/koko1123/flow-go-1@v0.29.6/module/mempool/consensus/exec_fork_suppressor_test.go (about)

     1  package consensus
     2  
     3  import (
     4  	"os"
     5  	"testing"
     6  
     7  	"github.com/dgraph-io/badger/v3"
     8  	"github.com/rs/zerolog"
     9  	"github.com/stretchr/testify/mock"
    10  	"github.com/stretchr/testify/require"
    11  	"go.uber.org/atomic"
    12  
    13  	"github.com/koko1123/flow-go-1/engine"
    14  	"github.com/koko1123/flow-go-1/model/flow"
    15  	actormock "github.com/koko1123/flow-go-1/module/mempool/consensus/mock"
    16  	poolmock "github.com/koko1123/flow-go-1/module/mempool/mock"
    17  	"github.com/koko1123/flow-go-1/module/mempool/stdmap"
    18  	mockstorage "github.com/koko1123/flow-go-1/storage/mock"
    19  	"github.com/koko1123/flow-go-1/utils/unittest"
    20  )
    21  
    22  // Test_Construction verifies correctness of the initial size and limit values
    23  func Test_Construction(t *testing.T) {
    24  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
    25  		wrappedMempool.On("Size").Return(uint(0)).Once()
    26  		require.Equal(t, uint(0), wrapper.Size())
    27  		wrappedMempool.On("Limit").Return(uint(0)).Once()
    28  		require.Equal(t, uint(0), wrapper.Limit())
    29  		wrappedMempool.AssertExpectations(t)
    30  	})
    31  }
    32  
    33  // Test_Size checks that ExecForkSuppressor is reporting the size of the wrapped mempool
    34  func Test_Size(t *testing.T) {
    35  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
    36  		wrappedMempool.On("Size").Return(uint(139)).Once()
    37  		require.Equal(t, uint(139), wrapper.Size())
    38  		wrappedMempool.AssertExpectations(t)
    39  	})
    40  }
    41  
    42  // Test_Limit checks that ExecForkSuppressor is reporting the capacity limit of the wrapped mempool
    43  func Test_Limit(t *testing.T) {
    44  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
    45  		wrappedMempool.On("Limit").Return(uint(227)).Once()
    46  		require.Equal(t, uint(227), wrapper.Limit())
    47  		wrappedMempool.AssertExpectations(t)
    48  	})
    49  }
    50  
    51  // Test_Clear checks that, when clearing the ExecForkSuppressor:
    52  //   - the wrapper also clears the wrapped mempool;
    53  //   - the reported mempool size, _after_ clearing should be zero
    54  func Test_Clear(t *testing.T) {
    55  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
    56  		wrappedMempool.On("Clear").Return().Once()
    57  
    58  		wrapper.Clear()
    59  		wrappedMempool.On("Size").Return(uint(0))
    60  		require.Equal(t, uint(0), wrapper.Size())
    61  		wrappedMempool.AssertExpectations(t)
    62  	})
    63  }
    64  
    65  // Test_All checks that ExecForkSuppressor.All() is returning the elements of the wrapped mempool
    66  func Test_All(t *testing.T) {
    67  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
    68  		expectedSeals := unittest.IncorporatedResultSeal.Fixtures(7)
    69  		wrappedMempool.On("All").Return(expectedSeals)
    70  		retrievedSeals := wrapper.All()
    71  		require.ElementsMatch(t, expectedSeals, retrievedSeals)
    72  	})
    73  }
    74  
    75  // Test_Add adds IncorporatedResultSeals for
    76  //   - 2 different blocks
    77  //   - for each block, we generate one specific result,
    78  //     for which we add 3 IncorporatedResultSeals
    79  //     o IncorporatedResultSeal (1):
    80  //     incorporated in block B1
    81  //     o IncorporatedResultSeal (2):
    82  //     incorporated in block B2
    83  //     o IncorporatedResultSeal (3):
    84  //     same result as (1) and incorporated in same block B1;
    85  //     should be automatically de-duplicated (irrespective of approvals on the seal).
    86  func Test_Add(t *testing.T) {
    87  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
    88  		for _, block := range unittest.BlockFixtures(2) {
    89  			result := unittest.ExecutionResultFixture(unittest.WithBlock(block))
    90  
    91  			// IncorporatedResultSeal (1):
    92  			irSeal1 := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(result))
    93  			wrappedMempool.On("Add", irSeal1).Return(true, nil).Once()
    94  			added, err := wrapper.Add(irSeal1)
    95  			require.NoError(t, err)
    96  			require.True(t, added)
    97  			wrappedMempool.AssertExpectations(t)
    98  
    99  			// IncorporatedResultSeal (2):
   100  			// the value for IncorporatedResultSeal.IncorporatedResult.IncorporatedBlockID is randomly
   101  			// generated and therefore, will be different from for irSeal1
   102  			irSeal2 := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(result))
   103  			require.False(t, irSeal1.ID() == irSeal2.ID()) // incorporated in different block => different seal ID expected
   104  			wrappedMempool.On("Add", irSeal2).Return(true, nil).Once()
   105  			added, err = wrapper.Add(irSeal2)
   106  			require.NoError(t, err)
   107  			require.True(t, added)
   108  			wrappedMempool.AssertExpectations(t)
   109  
   110  			// IncorporatedResultSeal (3):
   111  			irSeal3 := unittest.IncorporatedResultSeal.Fixture(
   112  				unittest.IncorporatedResultSeal.WithResult(result),
   113  				unittest.IncorporatedResultSeal.WithIncorporatedBlockID(irSeal1.IncorporatedResult.IncorporatedBlockID),
   114  			)
   115  			require.True(t, irSeal1.ID() == irSeal3.ID())               // same result incorporated same block as (1) => identical ID expected
   116  			wrappedMempool.On("Add", irSeal3).Return(false, nil).Once() // deduplicate
   117  			added, err = wrapper.Add(irSeal3)
   118  			require.NoError(t, err)
   119  			require.False(t, added)
   120  			wrappedMempool.AssertExpectations(t)
   121  		}
   122  	})
   123  }
   124  
   125  // Test_Remove checks that ExecForkSuppressor.Remove()
   126  //   - delegates the call to the underlying mempool
   127  func Test_Remove(t *testing.T) {
   128  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
   129  		// element is in wrapped mempool: Remove should be called
   130  		seal := unittest.IncorporatedResultSeal.Fixture()
   131  		wrappedMempool.On("Add", seal).Return(true, nil).Once()
   132  		wrappedMempool.On("ByID", seal.ID()).Return(seal, true)
   133  		added, err := wrapper.Add(seal)
   134  		require.NoError(t, err)
   135  		require.True(t, added)
   136  
   137  		wrappedMempool.On("ByID", seal.ID()).Return(seal, true)
   138  		wrappedMempool.On("Remove", seal.ID()).Return(true).Once()
   139  		removed := wrapper.Remove(seal.ID())
   140  		require.True(t, removed)
   141  		wrappedMempool.AssertExpectations(t)
   142  
   143  		// element _not_ in wrapped mempool: Remove might be called
   144  		seal = unittest.IncorporatedResultSeal.Fixture()
   145  		wrappedMempool.On("ByID", seal.ID()).Return(seal, false)
   146  		wrappedMempool.On("Remove", seal.ID()).Return(false).Maybe()
   147  		removed = wrapper.Remove(seal.ID())
   148  		require.False(t, removed)
   149  		wrappedMempool.AssertExpectations(t)
   150  	})
   151  }
   152  
   153  // Test_RejectInvalidSeals verifies that ExecForkSuppressor rejects seals whose
   154  // which don't have a chunk (i.e. their start and end state of the result cannot be determined)
   155  func Test_RejectInvalidSeals(t *testing.T) {
   156  	WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
   157  		irSeal := unittest.IncorporatedResultSeal.Fixture()
   158  		irSeal.IncorporatedResult.Result.Chunks = make(flow.ChunkList, 0)
   159  		irSeal.Seal.FinalState = flow.DummyStateCommitment
   160  
   161  		added, err := wrapper.Add(irSeal)
   162  		require.Error(t, err)
   163  		require.True(t, engine.IsInvalidInputError(err))
   164  		require.False(t, added)
   165  	})
   166  }
   167  
   168  // Test_ConflictingResults verifies that ExecForkSuppressor detects a fork in the execution chain.
   169  // The expected behaviour is:
   170  //   - clear the wrapped mempool
   171  //   - reject addition of all further entities (even valid seals)
   172  //
   173  // This logic has to be executed for all queries(`ByID`, `All`)
   174  func Test_ConflictingResults(t *testing.T) {
   175  	assertConflictingResult := func(t *testing.T, action func(irSeals []*flow.IncorporatedResultSeal, conflictingSeal *flow.IncorporatedResultSeal, wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals)) {
   176  		WithExecStateForkSuppressor(t, func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock) {
   177  			// add 3 random irSeals
   178  			irSeals := unittest.IncorporatedResultSeal.Fixtures(3)
   179  			for _, s := range irSeals {
   180  				wrappedMempool.On("Add", s).Return(true, nil).Once()
   181  				added, err := wrapper.Add(s)
   182  				require.NoError(t, err)
   183  				require.True(t, added)
   184  			}
   185  
   186  			// add seal for result that is _conflicting_ with irSeals[1]
   187  			result := unittest.ExecutionResultFixture()
   188  			result.BlockID = irSeals[1].Seal.BlockID
   189  			for _, c := range result.Chunks {
   190  				c.BlockID = result.BlockID
   191  			}
   192  			conflictingSeal := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(result))
   193  
   194  			wrappedMempool.On("Clear").Return().Once()
   195  			wrappedMempool.On("Add", conflictingSeal).Return(true, nil).Once()
   196  			// add conflicting seal
   197  			added, err := wrapper.Add(conflictingSeal)
   198  			require.NoError(t, err)
   199  			require.True(t, added)
   200  
   201  			execForkActor.On("OnExecFork", mock.Anything).Run(func(args mock.Arguments) {
   202  				conflictingSeals := args.Get(0).([]*flow.IncorporatedResultSeal)
   203  				require.ElementsMatch(t, []*flow.IncorporatedResultSeal{irSeals[1], conflictingSeal}, conflictingSeals)
   204  			}).Return().Once()
   205  			action(irSeals, conflictingSeal, wrapper, wrappedMempool)
   206  
   207  			wrappedMempool.On("ByID", conflictingSeal.ID()).Return(nil, false).Once()
   208  			byID, found := wrapper.ByID(conflictingSeal.ID())
   209  			require.False(t, found)
   210  			require.Nil(t, byID)
   211  
   212  			// mempool should be cleared
   213  			wrappedMempool.On("Size").Return(uint(0)) // we asserted that Clear was called on wrappedMempool
   214  			require.Equal(t, uint(0), wrapper.Size())
   215  
   216  			// additional seals should not be accepted anymore
   217  			added, err = wrapper.Add(unittest.IncorporatedResultSeal.Fixture())
   218  			require.NoError(t, err)
   219  			require.False(t, added)
   220  			require.Equal(t, uint(0), wrapper.Size())
   221  
   222  			wrappedMempool.AssertExpectations(t)
   223  			execForkActor.AssertExpectations(t)
   224  		})
   225  	}
   226  
   227  	t.Run("all-query", func(t *testing.T) {
   228  		assertConflictingResult(t, func(irSeals []*flow.IncorporatedResultSeal, conflictingSeal *flow.IncorporatedResultSeal, wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals) {
   229  			wrappedMempool.On("All").Return(append(irSeals, conflictingSeal)).Once()
   230  			allSeals := wrapper.All()
   231  			require.Len(t, allSeals, 0)
   232  		})
   233  	})
   234  	t.Run("by-id-query", func(t *testing.T) {
   235  		assertConflictingResult(t, func(irSeals []*flow.IncorporatedResultSeal, conflictingSeal *flow.IncorporatedResultSeal, wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals) {
   236  			wrappedMempool.On("ByID", conflictingSeal.ID()).Return(conflictingSeal, true).Once()
   237  			byID, found := wrapper.ByID(conflictingSeal.ID())
   238  			require.False(t, found)
   239  			require.Nil(t, byID)
   240  		})
   241  	})
   242  
   243  }
   244  
   245  // Test_ForkDetectionPersisted verifies that, when ExecForkSuppressor detects a fork, this information is
   246  // persisted in the data base
   247  func Test_ForkDetectionPersisted(t *testing.T) {
   248  	unittest.RunWithTempDir(t, func(dir string) {
   249  		db := unittest.BadgerDB(t, dir)
   250  		defer db.Close()
   251  
   252  		// initialize ExecForkSuppressor
   253  		wrappedMempool := &poolmock.IncorporatedResultSeals{}
   254  		execForkActor := &actormock.ExecForkActorMock{}
   255  		wrapper, _ := NewExecStateForkSuppressor(wrappedMempool, execForkActor.OnExecFork, db, zerolog.New(os.Stderr))
   256  
   257  		// add seal
   258  		block := unittest.BlockFixture()
   259  		sealA := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(unittest.ExecutionResultFixture(unittest.WithBlock(&block))))
   260  		wrappedMempool.On("Add", sealA).Return(true, nil).Once()
   261  		_, _ = wrapper.Add(sealA)
   262  
   263  		// add conflicting seal
   264  		sealB := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(unittest.ExecutionResultFixture(unittest.WithBlock(&block))))
   265  		wrappedMempool.On("Add", sealB).Return(true, nil).Once()
   266  		added, _ := wrapper.Add(sealB) // should be rejected because it is conflicting with sealA
   267  		require.True(t, added)
   268  
   269  		wrappedMempool.On("ByID", sealA.ID()).Return(sealA, true).Once()
   270  		execForkActor.On("OnExecFork", mock.Anything).Run(func(args mock.Arguments) {
   271  			conflictingSeals := args.Get(0).([]*flow.IncorporatedResultSeal)
   272  			require.ElementsMatch(t, []*flow.IncorporatedResultSeal{sealA, sealB}, conflictingSeals)
   273  		}).Return().Once()
   274  		wrappedMempool.On("Clear").Return().Once()
   275  		// try to query, at this point we will detect a conflicting seal
   276  		wrapper.ByID(sealA.ID())
   277  
   278  		wrappedMempool.AssertExpectations(t)
   279  		execForkActor.AssertExpectations(t)
   280  
   281  		// crash => re-initialization
   282  		db.Close()
   283  		db2 := unittest.BadgerDB(t, dir)
   284  		wrappedMempool2 := &poolmock.IncorporatedResultSeals{}
   285  		execForkActor2 := &actormock.ExecForkActorMock{}
   286  		execForkActor2.On("OnExecFork", mock.Anything).
   287  			Run(func(args mock.Arguments) {
   288  				conflictingSeals := args.Get(0).([]*flow.IncorporatedResultSeal)
   289  				require.ElementsMatch(t, []*flow.IncorporatedResultSeal{sealA, sealB}, conflictingSeals)
   290  			}).Return().Once()
   291  		wrapper2, _ := NewExecStateForkSuppressor(wrappedMempool2, execForkActor2.OnExecFork, db2, zerolog.New(os.Stderr))
   292  
   293  		// add another (non-conflicting) seal to ExecForkSuppressor
   294  		// fail test if seal is added to wrapped mempool
   295  		wrappedMempool2.On("Add", mock.Anything).
   296  			Run(func(args mock.Arguments) { require.Fail(t, "seal was added to wrapped mempool") }).
   297  			Return(true, nil).Maybe()
   298  		added, _ = wrapper2.Add(unittest.IncorporatedResultSeal.Fixture())
   299  		require.False(t, added)
   300  		wrappedMempool2.On("Size").Return(uint(0)) // we asserted that Clear was called on wrappedMempool
   301  		require.Equal(t, uint(0), wrapper2.Size())
   302  
   303  		wrappedMempool2.AssertExpectations(t)
   304  		execForkActor2.AssertExpectations(t)
   305  	})
   306  }
   307  
   308  // Test_AddRemove_SmokeTest tests a real system of stdmap.IncorporatedResultSeals mempool
   309  // which is wrapped in an ExecForkSuppressor.
   310  // We add and remove lots of different seals.
   311  func Test_AddRemove_SmokeTest(t *testing.T) {
   312  	onExecFork := func([]*flow.IncorporatedResultSeal) {
   313  		require.Fail(t, "no call to onExecFork expected ")
   314  	}
   315  	unittest.RunWithBadgerDB(t, func(db *badger.DB) {
   316  		wrappedMempool := stdmap.NewIncorporatedResultSeals(100)
   317  		wrapper, err := NewExecStateForkSuppressor(wrappedMempool, onExecFork, db, zerolog.New(os.Stderr))
   318  		require.NoError(t, err)
   319  		require.NotNil(t, wrapper)
   320  
   321  		// Run 100 experiments of the following kind:
   322  		//  * add 10 seals to mempool, which should eject 7 seals
   323  		//  * test that ejected seals are not in mempool anymore
   324  		//  * remove remaining seals
   325  		for i := 0; i < 100; i++ {
   326  			seals := unittest.IncorporatedResultSeal.Fixtures(10)
   327  			for j, s := range seals {
   328  				// fix height for each seal
   329  				s.Header.Height = uint64(i*100 + j)
   330  				added, err := wrapper.Add(s)
   331  				require.NoError(t, err)
   332  				require.True(t, added)
   333  			}
   334  
   335  			require.Equal(t, uint(10), wrapper.seals.Size())
   336  			require.Equal(t, uint(10), wrapper.Size())
   337  
   338  			err := wrapper.PruneUpToHeight(uint64((i + 1) * 100))
   339  			require.NoError(t, err)
   340  
   341  			require.Equal(t, uint(0), wrapper.seals.Size())
   342  			require.Equal(t, uint(0), wrapper.Size())
   343  			require.Len(t, wrapper.sealsForBlock, 0)
   344  		}
   345  	})
   346  }
   347  
   348  // Test_ConflictingSeal_SmokeTest tests a real system where we combine stdmap.IncorporatedResultSeals, consensus.IncorporatedResultSeals and
   349  // ExecForkSuppressor. We wrap stdmap.IncorporatedResultSeals with consensus.IncorporatedResultSeals which is wrapped with ExecForkSuppressor.
   350  // Test adding conflicting seals with different number of matching receipts.
   351  func Test_ConflictingSeal_SmokeTest(t *testing.T) {
   352  	unittest.RunWithBadgerDB(t, func(db *badger.DB) {
   353  		executingForkDetected := atomic.NewBool(false)
   354  		onExecFork := func([]*flow.IncorporatedResultSeal) {
   355  			executingForkDetected.Store(true)
   356  		}
   357  
   358  		rawMempool := stdmap.NewIncorporatedResultSeals(100)
   359  		receiptsDB := mockstorage.NewExecutionReceipts(t)
   360  		wrappedMempool := NewIncorporatedResultSeals(rawMempool, receiptsDB)
   361  		wrapper, err := NewExecStateForkSuppressor(wrappedMempool, onExecFork, db, zerolog.New(os.Stderr))
   362  		require.NoError(t, err)
   363  		require.NotNil(t, wrapper)
   364  
   365  		// add three seals
   366  		// two of them are non-conflicting but for same block and one is conflicting.
   367  
   368  		block := unittest.BlockFixture()
   369  		sealA := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(unittest.ExecutionResultFixture(unittest.WithBlock(&block))))
   370  		_, _ = wrapper.Add(sealA)
   371  
   372  		// different seal but for same result
   373  		sealB := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(sealA.IncorporatedResult.Result))
   374  		_, _ = wrapper.Add(sealB)
   375  
   376  		receiptsDB.On("ByBlockID", block.ID()).Return(nil, nil).Twice()
   377  
   378  		// two seals, but they are not confirmed with receipts
   379  		seals := wrapper.All()
   380  		require.Empty(t, seals)
   381  
   382  		receipts := flow.ExecutionReceiptList{
   383  			unittest.ExecutionReceiptFixture(unittest.WithResult(sealA.IncorporatedResult.Result)),
   384  			unittest.ExecutionReceiptFixture(unittest.WithResult(sealB.IncorporatedResult.Result)),
   385  		}
   386  		receiptsDB.On("ByBlockID", block.ID()).Return(receipts, nil).Twice()
   387  
   388  		// at this point we have two seals, confirmed by two receipts but no execution fork
   389  		seals = wrapper.All()
   390  		require.ElementsMatch(t, []*flow.IncorporatedResultSeal{sealA, sealB}, seals)
   391  
   392  		// add conflicting seal, which doesn't have any receipts yet
   393  		conflictingSeal := unittest.IncorporatedResultSeal.Fixture(unittest.IncorporatedResultSeal.WithResult(unittest.ExecutionResultFixture(unittest.WithBlock(&block))))
   394  		_, _ = wrapper.Add(conflictingSeal)
   395  
   396  		// conflicting seal doesn't have any receipts yet
   397  		receiptsDB.On("ByBlockID", block.ID()).Return(receipts, nil).Times(3)
   398  
   399  		seals = wrapper.All()
   400  		require.ElementsMatch(t, []*flow.IncorporatedResultSeal{sealA, sealB}, seals)
   401  
   402  		// add two receipts for conflicting result
   403  		receipts = append(receipts,
   404  			unittest.ExecutionReceiptFixture(unittest.WithResult(conflictingSeal.IncorporatedResult.Result)),
   405  			unittest.ExecutionReceiptFixture(unittest.WithResult(conflictingSeal.IncorporatedResult.Result)),
   406  		)
   407  
   408  		receiptsDB.On("ByBlockID", block.ID()).Return(receipts, nil).Times(3)
   409  
   410  		// querying should detect execution fork
   411  		seals = wrapper.All()
   412  		require.Empty(t, seals)
   413  		require.True(t, executingForkDetected.Load())
   414  	})
   415  }
   416  
   417  // WithExecStateForkSuppressor
   418  //  1. constructs a mock (aka `wrappedMempool`) of an IncorporatedResultSeals mempool
   419  //  2. wraps `wrappedMempool` in a ExecForkSuppressor
   420  //  3. ensures that initializing the wrapper did not error
   421  //  4. executes the `testLogic`
   422  func WithExecStateForkSuppressor(t testing.TB, testLogic func(wrapper *ExecForkSuppressor, wrappedMempool *poolmock.IncorporatedResultSeals, execForkActor *actormock.ExecForkActorMock)) {
   423  	unittest.RunWithBadgerDB(t, func(db *badger.DB) {
   424  		wrappedMempool := &poolmock.IncorporatedResultSeals{}
   425  		execForkActor := &actormock.ExecForkActorMock{}
   426  		wrapper, err := NewExecStateForkSuppressor(wrappedMempool, execForkActor.OnExecFork, db, zerolog.New(os.Stderr))
   427  		require.NoError(t, err)
   428  		require.NotNil(t, wrapper)
   429  		testLogic(wrapper, wrappedMempool, execForkActor)
   430  	})
   431  }