github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/storehouse/register_store_test.go (about)

     1  package storehouse_test
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/onflow/flow-go/engine/execution"
    11  	"github.com/onflow/flow-go/engine/execution/storehouse"
    12  	"github.com/onflow/flow-go/engine/execution/testutil"
    13  	"github.com/onflow/flow-go/model/flow"
    14  	"github.com/onflow/flow-go/storage/pebble"
    15  	"github.com/onflow/flow-go/utils/unittest"
    16  )
    17  
    18  type notifier struct {
    19  	height uint64
    20  }
    21  
    22  func (n *notifier) OnFinalizedAndExecutedHeightUpdated(height uint64) {
    23  	n.height = height
    24  }
    25  
    26  func withRegisterStore(t *testing.T, fn func(
    27  	t *testing.T,
    28  	rs *storehouse.RegisterStore,
    29  	diskStore execution.OnDiskRegisterStore,
    30  	finalized *testutil.MockFinalizedReader,
    31  	rootHeight uint64,
    32  	endHeight uint64,
    33  	headers map[uint64]*flow.Header,
    34  	n *notifier,
    35  )) {
    36  	pebble.RunWithRegistersStorageAtInitialHeights(t, 10, 10, func(diskStore *pebble.Registers) {
    37  		log := unittest.Logger()
    38  		var wal execution.ExecutedFinalizedWAL
    39  		finalized, headerByHeight, highest := testutil.NewMockFinalizedReader(10, 100)
    40  		n := &notifier{height: 10}
    41  		rs, err := storehouse.NewRegisterStore(diskStore, wal, finalized, log, n)
    42  		require.NoError(t, err)
    43  		fn(t, rs, diskStore, finalized, 10, highest, headerByHeight, n)
    44  	})
    45  }
    46  
    47  // GetRegister should fail for
    48  // 1. unknown blockID
    49  // 2. height lower than OnDiskRegisterStore's root height
    50  // 3. height too high
    51  // 4. known block, but unknown register
    52  func TestRegisterStoreGetRegisterFail(t *testing.T) {
    53  	t.Parallel()
    54  	withRegisterStore(t, func(
    55  		t *testing.T,
    56  		rs *storehouse.RegisterStore,
    57  		diskStore execution.OnDiskRegisterStore,
    58  		finalized *testutil.MockFinalizedReader,
    59  		rootHeight uint64,
    60  		endHeight uint64,
    61  		headerByHeight map[uint64]*flow.Header,
    62  		n *notifier,
    63  	) {
    64  		// unknown block
    65  		_, err := rs.GetRegister(rootHeight+1, unknownBlock, unknownReg.Key)
    66  		require.Error(t, err)
    67  		require.ErrorIs(t, err, storehouse.ErrNotExecuted)
    68  
    69  		// too high
    70  		block11 := headerByHeight[rootHeight+1]
    71  		_, err = rs.GetRegister(rootHeight+1, block11.ID(), unknownReg.Key)
    72  		require.Error(t, err)
    73  		require.ErrorIs(t, err, storehouse.ErrNotExecuted)
    74  
    75  		// lower than root height
    76  		_, err = rs.GetRegister(rootHeight-1, unknownBlock, unknownReg.Key)
    77  		require.Error(t, err)
    78  		// TODO: enable it once implemented
    79  		// require.ErrorIs(t, err, storehouse.ErrPruned)
    80  
    81  		// known block, unknown register
    82  		rootBlock := headerByHeight[rootHeight]
    83  		val, err := rs.GetRegister(rootHeight, rootBlock.ID(), unknownReg.Key)
    84  		require.NoError(t, err)
    85  		require.Nil(t, val)
    86  	})
    87  }
    88  
    89  // SaveRegisters should fail for
    90  // 1. mismatching parent
    91  // 2. saved block
    92  func TestRegisterStoreSaveRegistersShouldFail(t *testing.T) {
    93  	t.Parallel()
    94  	withRegisterStore(t, func(
    95  		t *testing.T,
    96  		rs *storehouse.RegisterStore,
    97  		diskStore execution.OnDiskRegisterStore,
    98  		finalized *testutil.MockFinalizedReader,
    99  		rootHeight uint64,
   100  		endHeight uint64,
   101  		headerByHeight map[uint64]*flow.Header,
   102  		n *notifier,
   103  	) {
   104  		wrongParent := unittest.BlockHeaderFixture(unittest.WithHeaderHeight(rootHeight + 1))
   105  		err := rs.SaveRegisters(wrongParent, flow.RegisterEntries{})
   106  		require.Error(t, err)
   107  		require.Contains(t, err.Error(), "parent")
   108  
   109  		err = rs.SaveRegisters(headerByHeight[rootHeight], flow.RegisterEntries{})
   110  		require.Error(t, err)
   111  		require.Contains(t, err.Error(), "pruned")
   112  	})
   113  }
   114  
   115  // SaveRegisters should ok, and
   116  // 1. GetRegister can get saved registers,
   117  // 2. IsBlockExecuted should return true
   118  //
   119  // if SaveRegisters with empty register, then
   120  // 1. LastFinalizedAndExecutedHeight should be updated
   121  // 2. IsBlockExecuted should return true
   122  func TestRegisterStoreSaveRegistersShouldOK(t *testing.T) {
   123  	t.Parallel()
   124  	withRegisterStore(t, func(
   125  		t *testing.T,
   126  		rs *storehouse.RegisterStore,
   127  		diskStore execution.OnDiskRegisterStore,
   128  		finalized *testutil.MockFinalizedReader,
   129  		rootHeight uint64,
   130  		endHeight uint64,
   131  		headerByHeight map[uint64]*flow.Header,
   132  		n *notifier,
   133  	) {
   134  		// not executed
   135  		executed, err := rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+1].ID())
   136  		require.NoError(t, err)
   137  		require.False(t, executed)
   138  
   139  		// save block 11
   140  		reg := makeReg("X", "1")
   141  		err = rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{reg})
   142  		require.NoError(t, err)
   143  
   144  		// should get value
   145  		val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), reg.Key)
   146  		require.NoError(t, err)
   147  		require.Equal(t, reg.Value, val)
   148  
   149  		// should become executed
   150  		executed, err = rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+1].ID())
   151  		require.NoError(t, err)
   152  		require.True(t, executed)
   153  
   154  		// block 12 is empty
   155  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{})
   156  		require.NoError(t, err)
   157  
   158  		// should get same value
   159  		val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+2].ID(), reg.Key)
   160  		require.NoError(t, err)
   161  		require.Equal(t, reg.Value, val)
   162  
   163  		// should become executed
   164  		executed, err = rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+2].ID())
   165  		require.NoError(t, err)
   166  		require.True(t, executed)
   167  	})
   168  }
   169  
   170  // if 11 is latest finalized, then
   171  // 1. IsBlockExecuted should return true for finalized block 10
   172  // 2. IsBlockExecuted should return false for conflicting block 10
   173  // 4. IsBlockExecuted should return true for executed and unfinalized block 12
   174  // 3. IsBlockExecuted should return false for unexecuted block 13
   175  func TestRegisterStoreIsBlockExecuted(t *testing.T) {
   176  	t.Parallel()
   177  	withRegisterStore(t, func(
   178  		t *testing.T,
   179  		rs *storehouse.RegisterStore,
   180  		diskStore execution.OnDiskRegisterStore,
   181  		finalized *testutil.MockFinalizedReader,
   182  		rootHeight uint64,
   183  		endHeight uint64,
   184  		headerByHeight map[uint64]*flow.Header,
   185  		n *notifier,
   186  	) {
   187  		// save block 11
   188  		reg := makeReg("X", "1")
   189  		err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{reg})
   190  		require.NoError(t, err)
   191  
   192  		// save block 12
   193  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")})
   194  		require.NoError(t, err)
   195  
   196  		require.NoError(t, finalized.MockFinal(rootHeight+1))
   197  
   198  		require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized
   199  
   200  		require.Equal(t, rootHeight+1, rs.LastFinalizedAndExecutedHeight())
   201  
   202  		executed, err := rs.IsBlockExecuted(rootHeight, headerByHeight[rootHeight].ID())
   203  		require.NoError(t, err)
   204  		require.True(t, executed)
   205  
   206  		executed, err = rs.IsBlockExecuted(rootHeight+1, headerByHeight[rootHeight+1].ID())
   207  		require.NoError(t, err)
   208  		require.True(t, executed)
   209  
   210  		executed, err = rs.IsBlockExecuted(rootHeight+2, headerByHeight[rootHeight+2].ID())
   211  		require.NoError(t, err)
   212  		require.True(t, executed)
   213  
   214  		executed, err = rs.IsBlockExecuted(rootHeight+3, headerByHeight[rootHeight+3].ID())
   215  		require.NoError(t, err)
   216  		require.False(t, executed)
   217  	})
   218  }
   219  
   220  // Test reading registers from finalized block
   221  func TestRegisterStoreReadingFromDisk(t *testing.T) {
   222  	t.Parallel()
   223  	withRegisterStore(t, func(
   224  		t *testing.T,
   225  		rs *storehouse.RegisterStore,
   226  		diskStore execution.OnDiskRegisterStore,
   227  		finalized *testutil.MockFinalizedReader,
   228  		rootHeight uint64,
   229  		endHeight uint64,
   230  		headerByHeight map[uint64]*flow.Header,
   231  		n *notifier,
   232  	) {
   233  
   234  		// R <- 11 (X: 1, Y: 2) <- 12 (Y: 3) <- 13 (X: 4)
   235  		// save block 11
   236  		err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1"), makeReg("Y", "2")})
   237  		require.NoError(t, err)
   238  
   239  		// save block 12
   240  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("Y", "3")})
   241  		require.NoError(t, err)
   242  
   243  		// save block 13
   244  		err = rs.SaveRegisters(headerByHeight[rootHeight+3], flow.RegisterEntries{makeReg("X", "4")})
   245  		require.NoError(t, err)
   246  
   247  		require.Equal(t, rootHeight, n.height)
   248  
   249  		require.NoError(t, finalized.MockFinal(rootHeight+2))
   250  		require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized
   251  
   252  		require.Equal(t, rootHeight+2, n.height)
   253  
   254  		val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("Y", "2").Key)
   255  		require.NoError(t, err)
   256  		// value at block 11 is now stored in OnDiskRegisterStore, which is 2
   257  		require.Equal(t, makeReg("Y", "2").Value, val)
   258  
   259  		val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("X", "1").Key)
   260  		require.NoError(t, err)
   261  		// value at block 12 is now stored in OnDiskRegisterStore, which is 1
   262  		require.Equal(t, makeReg("X", "1").Value, val)
   263  
   264  		val, err = rs.GetRegister(rootHeight+3, headerByHeight[rootHeight+3].ID(), makeReg("Y", "3").Key)
   265  		require.NoError(t, err)
   266  		// value at block 13 was stored in OnDiskRegisterStore at block 12, which is 3
   267  		require.Equal(t, makeReg("Y", "3").Value, val)
   268  
   269  		_, err = rs.GetRegister(rootHeight+4, headerByHeight[rootHeight+4].ID(), makeReg("Y", "3").Key)
   270  		require.Error(t, err)
   271  	})
   272  }
   273  
   274  func TestRegisterStoreReadingFromInMemStore(t *testing.T) {
   275  	t.Parallel()
   276  	withRegisterStore(t, func(
   277  		t *testing.T,
   278  		rs *storehouse.RegisterStore,
   279  		diskStore execution.OnDiskRegisterStore,
   280  		finalized *testutil.MockFinalizedReader,
   281  		rootHeight uint64,
   282  		endHeight uint64,
   283  		headerByHeight map[uint64]*flow.Header,
   284  		n *notifier,
   285  	) {
   286  
   287  		// R <- 11 (X: 1, Y: 2) <- 12 (Y: 3)
   288  		//   ^- 11 (X: 4)
   289  
   290  		// save block 11
   291  		err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1"), makeReg("Y", "2")})
   292  		require.NoError(t, err)
   293  
   294  		// save block 12
   295  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("Y", "3")})
   296  		require.NoError(t, err)
   297  
   298  		// save block 11 fork
   299  		block11Fork := unittest.BlockWithParentFixture(headerByHeight[rootHeight]).Header
   300  		err = rs.SaveRegisters(block11Fork, flow.RegisterEntries{makeReg("X", "4")})
   301  		require.NoError(t, err)
   302  
   303  		val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "1").Key)
   304  		require.NoError(t, err)
   305  		require.Equal(t, makeReg("X", "1").Value, val)
   306  
   307  		val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("Y", "2").Key)
   308  		require.NoError(t, err)
   309  		require.Equal(t, makeReg("Y", "2").Value, val)
   310  
   311  		val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("X", "1").Key)
   312  		require.NoError(t, err)
   313  		require.Equal(t, makeReg("X", "1").Value, val)
   314  
   315  		val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("Y", "3").Key)
   316  		require.NoError(t, err)
   317  		require.Equal(t, makeReg("Y", "3").Value, val)
   318  
   319  		val, err = rs.GetRegister(rootHeight+1, block11Fork.ID(), makeReg("X", "4").Key)
   320  		require.NoError(t, err)
   321  		require.Equal(t, makeReg("X", "4").Value, val)
   322  
   323  		// finalizing 11 should prune block 11 fork, and won't be able to read register from block 11 fork
   324  		require.NoError(t, finalized.MockFinal(rootHeight+1))
   325  		require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized
   326  
   327  		val, err = rs.GetRegister(rootHeight+1, block11Fork.ID(), makeReg("X", "4").Key)
   328  		require.Error(t, err, fmt.Sprintf("%v", val))
   329  		// pruned conflicting forks are considered not executed
   330  		require.ErrorIs(t, err, storehouse.ErrNotExecuted)
   331  	})
   332  }
   333  
   334  func TestRegisterStoreReadRegisterAtPrunedHeight(t *testing.T) {
   335  	t.Parallel()
   336  	withRegisterStore(t, func(
   337  		t *testing.T,
   338  		rs *storehouse.RegisterStore,
   339  		diskStore execution.OnDiskRegisterStore,
   340  		finalized *testutil.MockFinalizedReader,
   341  		rootHeight uint64,
   342  		endHeight uint64,
   343  		headerByHeight map[uint64]*flow.Header,
   344  		n *notifier,
   345  	) {
   346  
   347  		// R <- 11 (X: 1)
   348  
   349  		// if execute first then finalize later, should be able to read register at pruned height
   350  		// save block 11
   351  		err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")})
   352  		require.NoError(t, err)
   353  		require.Equal(t, 2, finalized.FinalizedCalled()) // called by SaveRegisters with height 11
   354  
   355  		// finalize block 11
   356  		require.NoError(t, finalized.MockFinal(rootHeight+1))
   357  		require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized
   358  
   359  		val, err := rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "").Key)
   360  		require.NoError(t, err)
   361  		require.Equal(t, makeReg("X", "1").Value, val)
   362  
   363  		// R <- 11 (X: 1) <- 12 (X: 2)
   364  		// if finalize first then execute later, should not be able to read register at pruned height
   365  		// finalize block 12
   366  		require.NoError(t, finalized.MockFinal(rootHeight+2))
   367  		require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized
   368  
   369  		// save block 12
   370  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")})
   371  		require.NoError(t, err)
   372  
   373  		val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "").Key)
   374  		require.NoError(t, err)
   375  		require.Equal(t, makeReg("X", "1").Value, val)
   376  
   377  		val, err = rs.GetRegister(rootHeight+2, headerByHeight[rootHeight+2].ID(), makeReg("X", "").Key)
   378  		require.NoError(t, err)
   379  		require.Equal(t, makeReg("X", "2").Value, val)
   380  	})
   381  }
   382  
   383  // Test that when getting register during executing a finalized block or finalize an executed block,
   384  // FinalizedBlockIDAtHeight should not be called
   385  func TestRegisterStoreExecuteFinalizedBlockOrFinalizeExecutedBlockShouldNotCallFinalizedHeight(t *testing.T) {
   386  	t.Parallel()
   387  	withRegisterStore(t, func(
   388  		t *testing.T,
   389  		rs *storehouse.RegisterStore,
   390  		diskStore execution.OnDiskRegisterStore,
   391  		finalized *testutil.MockFinalizedReader,
   392  		rootHeight uint64,
   393  		endHeight uint64,
   394  		headerByHeight map[uint64]*flow.Header,
   395  		n *notifier,
   396  	) {
   397  
   398  		require.Equal(t, 1, finalized.FinalizedCalled()) // called by NewRegisterStore
   399  		// R <- 11 (X: 1)
   400  
   401  		val, err := rs.GetRegister(rootHeight, headerByHeight[rootHeight].ID(), makeReg("X", "").Key)
   402  		require.NoError(t, err)
   403  		require.Nil(t, val)
   404  		require.Equal(t, 1, finalized.FinalizedCalled()) // no FinalizedBlockIDAtHeight called
   405  
   406  		// if execute first then finalize later, should be able to read register at pruned height
   407  		// save block 11
   408  		err = rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")})
   409  		require.NoError(t, err)
   410  		require.Equal(t, 2, finalized.FinalizedCalled()) // called by SaveRegisters with height 11
   411  
   412  		// finalize block 11
   413  		require.NoError(t, finalized.MockFinal(rootHeight+1))
   414  		require.NoError(t, rs.OnBlockFinalized())        // notify 11 is finalized
   415  		require.Equal(t, 4, finalized.FinalizedCalled()) // called by Checking whether height 11 and 12 are finalized
   416  
   417  		// R <- 11 (X: 1) <- 12 (X: 2)
   418  		// if finalize first then execute later, should not be able to read register at pruned height
   419  		// finalize block 12
   420  		require.NoError(t, finalized.MockFinal(rootHeight+2))
   421  		require.NoError(t, rs.OnBlockFinalized())        // notify 12 is finalized
   422  		require.Equal(t, 5, finalized.FinalizedCalled()) // called by Checking whether height 12 and 13 are finalized
   423  
   424  		val, err = rs.GetRegister(rootHeight+1, headerByHeight[rootHeight+1].ID(), makeReg("X", "").Key)
   425  		require.NoError(t, err)
   426  		require.Equal(t, makeReg("X", "1").Value, val)
   427  		require.Equal(t, 5, finalized.FinalizedCalled()) // no FinalizedBlockIDAtHeight call
   428  
   429  		// save block 12
   430  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")})
   431  		require.NoError(t, err)
   432  		require.Equal(t, 7, finalized.FinalizedCalled()) // called by SaveRegisters with height 12 and 13
   433  
   434  	})
   435  }
   436  
   437  // Execute first then finalize later
   438  // SaveRegisters(1), SaveRegisters(2), SaveRegisters(3), then
   439  // OnBlockFinalized(1), OnBlockFinalized(2), OnBlockFinalized(3) should
   440  // 1. update LastFinalizedAndExecutedHeight
   441  // 2. InMemoryRegisterStore should have correct pruned height
   442  // 3. NewRegisterStore with the same OnDiskRegisterStore again should return correct LastFinalizedAndExecutedHeight
   443  func TestRegisterStoreExecuteFirstFinalizeLater(t *testing.T) {
   444  	t.Parallel()
   445  	withRegisterStore(t, func(
   446  		t *testing.T,
   447  		rs *storehouse.RegisterStore,
   448  		diskStore execution.OnDiskRegisterStore,
   449  		finalized *testutil.MockFinalizedReader,
   450  		rootHeight uint64,
   451  		endHeight uint64,
   452  		headerByHeight map[uint64]*flow.Header,
   453  		n *notifier,
   454  	) {
   455  		// save block 11
   456  		err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")})
   457  		require.NoError(t, err)
   458  		require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight())
   459  
   460  		// save block 12
   461  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")})
   462  		require.NoError(t, err)
   463  		require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight())
   464  
   465  		// save block 13
   466  		err = rs.SaveRegisters(headerByHeight[rootHeight+3], flow.RegisterEntries{makeReg("X", "3")})
   467  		require.NoError(t, err)
   468  		require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight())
   469  
   470  		require.Equal(t, rootHeight, n.height)
   471  
   472  		require.NoError(t, finalized.MockFinal(rootHeight+1))
   473  		require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized
   474  		require.Equal(t, rootHeight+1, rs.LastFinalizedAndExecutedHeight())
   475  		require.Equal(t, rootHeight+1, n.height)
   476  
   477  		require.NoError(t, finalized.MockFinal(rootHeight+2))
   478  		require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized
   479  		require.Equal(t, rootHeight+2, rs.LastFinalizedAndExecutedHeight())
   480  		require.Equal(t, rootHeight+2, n.height)
   481  
   482  		require.NoError(t, finalized.MockFinal(rootHeight+3))
   483  		require.NoError(t, rs.OnBlockFinalized()) // notify 13 is finalized
   484  		require.Equal(t, rootHeight+3, rs.LastFinalizedAndExecutedHeight())
   485  		require.Equal(t, rootHeight+3, n.height)
   486  	})
   487  }
   488  
   489  // Finalize first then execute later
   490  // OnBlockFinalized(1), OnBlockFinalized(2), OnBlockFinalized(3), then
   491  // SaveRegisters(1), SaveRegisters(2), SaveRegisters(3) should
   492  // 1. update LastFinalizedAndExecutedHeight
   493  // 2. InMemoryRegisterStore should have correct pruned height
   494  // 3. NewRegisterStore with the same OnDiskRegisterStore again should return correct LastFinalizedAndExecutedHeight
   495  func TestRegisterStoreFinalizeFirstExecuteLater(t *testing.T) {
   496  	t.Parallel()
   497  	withRegisterStore(t, func(
   498  		t *testing.T,
   499  		rs *storehouse.RegisterStore,
   500  		diskStore execution.OnDiskRegisterStore,
   501  		finalized *testutil.MockFinalizedReader,
   502  		rootHeight uint64,
   503  		endHeight uint64,
   504  		headerByHeight map[uint64]*flow.Header,
   505  		n *notifier,
   506  	) {
   507  		require.NoError(t, finalized.MockFinal(rootHeight+1))
   508  		require.NoError(t, rs.OnBlockFinalized()) // notify 11 is finalized
   509  		require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight(), fmt.Sprintf("LastFinalizedAndExecutedHeight: %d", rs.LastFinalizedAndExecutedHeight()))
   510  
   511  		require.NoError(t, finalized.MockFinal(rootHeight+2))
   512  		require.NoError(t, rs.OnBlockFinalized()) // notify 12 is finalized
   513  		require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight(), fmt.Sprintf("LastFinalizedAndExecutedHeight: %d", rs.LastFinalizedAndExecutedHeight()))
   514  
   515  		require.NoError(t, finalized.MockFinal(rootHeight+3))
   516  		require.NoError(t, rs.OnBlockFinalized()) // notify 13 is finalized
   517  		require.Equal(t, rootHeight, rs.LastFinalizedAndExecutedHeight())
   518  
   519  		require.Equal(t, rootHeight, n.height)
   520  
   521  		// save block 11
   522  		err := rs.SaveRegisters(headerByHeight[rootHeight+1], flow.RegisterEntries{makeReg("X", "1")})
   523  		require.NoError(t, err)
   524  		require.Equal(t, rootHeight+1, rs.LastFinalizedAndExecutedHeight())
   525  		require.Equal(t, rootHeight+1, n.height)
   526  
   527  		// save block 12
   528  		err = rs.SaveRegisters(headerByHeight[rootHeight+2], flow.RegisterEntries{makeReg("X", "2")})
   529  		require.NoError(t, err)
   530  		require.Equal(t, rootHeight+2, rs.LastFinalizedAndExecutedHeight())
   531  		require.Equal(t, rootHeight+2, n.height)
   532  
   533  		// save block 13
   534  		err = rs.SaveRegisters(headerByHeight[rootHeight+3], flow.RegisterEntries{makeReg("X", "3")})
   535  		require.NoError(t, err)
   536  		require.Equal(t, rootHeight+3, rs.LastFinalizedAndExecutedHeight())
   537  		require.Equal(t, rootHeight+3, n.height)
   538  	})
   539  }
   540  
   541  // Finalize and Execute concurrently
   542  // SaveRegisters(1), SaveRegisters(2), ... SaveRegisters(100), happen concurrently with
   543  // OnBlockFinalized(1), OnBlockFinalized(2), ... OnBlockFinalized(100), should update LastFinalizedAndExecutedHeight
   544  func TestRegisterStoreConcurrentFinalizeAndExecute(t *testing.T) {
   545  	t.Parallel()
   546  	withRegisterStore(t, func(
   547  		t *testing.T,
   548  		rs *storehouse.RegisterStore,
   549  		diskStore execution.OnDiskRegisterStore,
   550  		finalized *testutil.MockFinalizedReader,
   551  		rootHeight uint64,
   552  		endHeight uint64,
   553  		headerByHeight map[uint64]*flow.Header,
   554  		n *notifier,
   555  	) {
   556  
   557  		var wg sync.WaitGroup
   558  		savedHeights := make(chan uint64, len(headerByHeight)) // enough buffer so that producer won't be blocked
   559  
   560  		wg.Add(1)
   561  		go func() {
   562  			defer wg.Done()
   563  
   564  			for savedHeight := range savedHeights {
   565  				err := finalized.MockFinal(savedHeight)
   566  				require.NoError(t, err)
   567  				require.NoError(t, rs.OnBlockFinalized(), fmt.Sprintf("saved height %v", savedHeight))
   568  			}
   569  		}()
   570  
   571  		for height := rootHeight + 1; height <= endHeight; height++ {
   572  			if height >= 50 {
   573  				savedHeights <- height
   574  			}
   575  
   576  			err := rs.SaveRegisters(headerByHeight[height], flow.RegisterEntries{makeReg("X", fmt.Sprintf("%d", height))})
   577  			require.NoError(t, err)
   578  		}
   579  		close(savedHeights)
   580  
   581  		wg.Wait() // wait until all heights are finalized
   582  
   583  		// after all heights are executed and finalized, the LastFinalizedAndExecutedHeight should be the last height
   584  		require.Equal(t, endHeight, rs.LastFinalizedAndExecutedHeight())
   585  	})
   586  }