github.com/koko1123/flow-go-1@v0.29.6/fvm/derived/table_test.go (about)

     1  package derived
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/koko1123/flow-go-1/fvm/state"
    10  	"github.com/koko1123/flow-go-1/fvm/utils"
    11  	"github.com/koko1123/flow-go-1/model/flow"
    12  )
    13  
    14  func newEmptyTestBlock() *DerivedDataTable[string, *string] {
    15  	return NewEmptyTable[string, *string]()
    16  }
    17  
    18  func TestDerivedDataTableWithTransactionOffset(t *testing.T) {
    19  	block := NewEmptyTableWithOffset[string, *string](18)
    20  
    21  	require.Equal(
    22  		t,
    23  		LogicalTime(17),
    24  		block.LatestCommitExecutionTimeForTestingOnly())
    25  }
    26  
    27  func TestTxnDerivedDataNormalTransactionInvalidExecutionTimeBound(t *testing.T) {
    28  	block := newEmptyTestBlock()
    29  
    30  	_, err := block.NewTableTransaction(-1, -1)
    31  	require.ErrorContains(t, err, "execution time out of bound")
    32  
    33  	_, err = block.NewTableTransaction(0, 0)
    34  	require.NoError(t, err)
    35  
    36  	_, err = block.NewTableTransaction(0, EndOfBlockExecutionTime)
    37  	require.ErrorContains(t, err, "execution time out of bound")
    38  
    39  	_, err = block.NewTableTransaction(0, EndOfBlockExecutionTime-1)
    40  	require.NoError(t, err)
    41  }
    42  
    43  func TestTxnDerivedDataNormalTransactionInvalidSnapshotTime(t *testing.T) {
    44  	block := newEmptyTestBlock()
    45  
    46  	_, err := block.NewTableTransaction(10, 0)
    47  	require.ErrorContains(t, err, "snapshot > execution")
    48  
    49  	_, err = block.NewTableTransaction(10, 10)
    50  	require.NoError(t, err)
    51  
    52  	_, err = block.NewTableTransaction(999, 998)
    53  	require.ErrorContains(t, err, "snapshot > execution")
    54  
    55  	_, err = block.NewTableTransaction(999, 999)
    56  	require.NoError(t, err)
    57  }
    58  
    59  func TestTxnDerivedDataSnapshotReadTransactionInvalidExecutionTimeBound(t *testing.T) {
    60  	block := newEmptyTestBlock()
    61  
    62  	_, err := block.NewSnapshotReadTableTransaction(
    63  		ParentBlockTime,
    64  		ParentBlockTime)
    65  	require.ErrorContains(t, err, "execution time out of bound")
    66  
    67  	_, err = block.NewSnapshotReadTableTransaction(ParentBlockTime, 0)
    68  	require.NoError(t, err)
    69  
    70  	_, err = block.NewSnapshotReadTableTransaction(0, ChildBlockTime)
    71  	require.ErrorContains(t, err, "execution time out of bound")
    72  
    73  	_, err = block.NewSnapshotReadTableTransaction(
    74  		0,
    75  		EndOfBlockExecutionTime)
    76  	require.NoError(t, err)
    77  }
    78  
    79  func TestTxnDerivedDataValidateRejectOutOfOrderCommit(t *testing.T) {
    80  	block := newEmptyTestBlock()
    81  
    82  	testTxn, err := block.NewTableTransaction(0, 0)
    83  	require.NoError(t, err)
    84  
    85  	testSetupTxn, err := block.NewTableTransaction(0, 1)
    86  	require.NoError(t, err)
    87  
    88  	validateErr := testTxn.Validate()
    89  	require.NoError(t, validateErr)
    90  
    91  	err = testSetupTxn.Commit()
    92  	require.NoError(t, err)
    93  
    94  	validateErr = testTxn.Validate()
    95  	require.ErrorContains(t, validateErr, "non-increasing time")
    96  	require.False(t, validateErr.IsRetryable())
    97  }
    98  
    99  func TestTxnDerivedDataValidateRejectNonIncreasingExecutionTime(t *testing.T) {
   100  	block := newEmptyTestBlock()
   101  
   102  	testSetupTxn, err := block.NewTableTransaction(0, 0)
   103  	require.NoError(t, err)
   104  
   105  	err = testSetupTxn.Commit()
   106  	require.NoError(t, err)
   107  
   108  	testTxn, err := block.NewTableTransaction(0, 0)
   109  	require.NoError(t, err)
   110  
   111  	validateErr := testTxn.Validate()
   112  	require.ErrorContains(t, validateErr, "non-increasing time")
   113  	require.False(t, validateErr.IsRetryable())
   114  }
   115  
   116  func TestTxnDerivedDataValidateRejectCommitGapForNormalTxn(t *testing.T) {
   117  	block := newEmptyTestBlock()
   118  
   119  	commitTime := LogicalTime(5)
   120  	testSetupTxn, err := block.NewTableTransaction(0, commitTime)
   121  	require.NoError(t, err)
   122  
   123  	err = testSetupTxn.Commit()
   124  	require.NoError(t, err)
   125  
   126  	require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly())
   127  
   128  	testTxn, err := block.NewTableTransaction(10, 10)
   129  	require.NoError(t, err)
   130  
   131  	validateErr := testTxn.Validate()
   132  	require.ErrorContains(t, validateErr, "missing commit range [6, 10)")
   133  	require.False(t, validateErr.IsRetryable())
   134  }
   135  
   136  func TestTxnDerivedDataValidateRejectCommitGapForSnapshotRead(t *testing.T) {
   137  	block := newEmptyTestBlock()
   138  
   139  	commitTime := LogicalTime(5)
   140  	testSetupTxn, err := block.NewTableTransaction(0, commitTime)
   141  	require.NoError(t, err)
   142  
   143  	err = testSetupTxn.Commit()
   144  	require.NoError(t, err)
   145  
   146  	require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly())
   147  
   148  	testTxn, err := block.NewSnapshotReadTableTransaction(10, 10)
   149  	require.NoError(t, err)
   150  
   151  	validateErr := testTxn.Validate()
   152  	require.ErrorContains(t, validateErr, "missing commit range [6, 10)")
   153  	require.False(t, validateErr.IsRetryable())
   154  }
   155  
   156  func TestTxnDerivedDataValidateRejectOutdatedReadSet(t *testing.T) {
   157  	block := newEmptyTestBlock()
   158  
   159  	testSetupTxn1, err := block.NewTableTransaction(0, 0)
   160  	require.NoError(t, err)
   161  
   162  	testSetupTxn2, err := block.NewTableTransaction(0, 1)
   163  	require.NoError(t, err)
   164  
   165  	testTxn, err := block.NewTableTransaction(0, 2)
   166  	require.NoError(t, err)
   167  
   168  	key := "abc"
   169  	valueString := "value"
   170  	expectedValue := &valueString
   171  	expectedState := &state.State{}
   172  
   173  	testSetupTxn1.Set(key, expectedValue, expectedState)
   174  
   175  	testSetupTxn1.AddInvalidator(testInvalidator{})
   176  
   177  	err = testSetupTxn1.Commit()
   178  	require.NoError(t, err)
   179  
   180  	validateErr := testTxn.Validate()
   181  	require.NoError(t, validateErr)
   182  
   183  	actualProg, actualState, ok := testTxn.Get(key)
   184  	require.True(t, ok)
   185  	require.Same(t, expectedValue, actualProg)
   186  	require.Same(t, expectedState, actualState)
   187  
   188  	validateErr = testTxn.Validate()
   189  	require.NoError(t, validateErr)
   190  
   191  	testSetupTxn2.AddInvalidator(testInvalidator{invalidateAll: true})
   192  
   193  	err = testSetupTxn2.Commit()
   194  	require.NoError(t, err)
   195  
   196  	validateErr = testTxn.Validate()
   197  	require.ErrorContains(t, validateErr, "outdated read set")
   198  	require.True(t, validateErr.IsRetryable())
   199  }
   200  
   201  func TestTxnDerivedDataValidateRejectOutdatedWriteSet(t *testing.T) {
   202  	block := newEmptyTestBlock()
   203  
   204  	testSetupTxn, err := block.NewTableTransaction(0, 0)
   205  	require.NoError(t, err)
   206  
   207  	testSetupTxn.AddInvalidator(testInvalidator{invalidateAll: true})
   208  
   209  	err = testSetupTxn.Commit()
   210  	require.NoError(t, err)
   211  
   212  	require.Equal(t, 1, len(block.InvalidatorsForTestingOnly()))
   213  
   214  	testTxn, err := block.NewTableTransaction(0, 1)
   215  	require.NoError(t, err)
   216  
   217  	value := "value"
   218  	testTxn.Set("key", &value, &state.State{})
   219  
   220  	validateErr := testTxn.Validate()
   221  	require.ErrorContains(t, validateErr, "outdated write set")
   222  	require.True(t, validateErr.IsRetryable())
   223  }
   224  
   225  func TestTxnDerivedDataValidateIgnoreInvalidatorsOlderThanSnapshot(t *testing.T) {
   226  	block := newEmptyTestBlock()
   227  
   228  	testSetupTxn, err := block.NewTableTransaction(0, 0)
   229  	require.NoError(t, err)
   230  
   231  	testSetupTxn.AddInvalidator(testInvalidator{invalidateAll: true})
   232  	err = testSetupTxn.Commit()
   233  	require.NoError(t, err)
   234  
   235  	require.Equal(t, 1, len(block.InvalidatorsForTestingOnly()))
   236  
   237  	testTxn, err := block.NewTableTransaction(1, 1)
   238  	require.NoError(t, err)
   239  
   240  	value := "value"
   241  	testTxn.Set("key", &value, &state.State{})
   242  
   243  	err = testTxn.Validate()
   244  	require.NoError(t, err)
   245  }
   246  
   247  func TestTxnDerivedDataCommitEndOfBlockSnapshotRead(t *testing.T) {
   248  	block := newEmptyTestBlock()
   249  
   250  	commitTime := LogicalTime(5)
   251  	testSetupTxn, err := block.NewTableTransaction(0, commitTime)
   252  	require.NoError(t, err)
   253  
   254  	err = testSetupTxn.Commit()
   255  	require.NoError(t, err)
   256  
   257  	require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly())
   258  
   259  	testTxn, err := block.NewSnapshotReadTableTransaction(
   260  		EndOfBlockExecutionTime,
   261  		EndOfBlockExecutionTime)
   262  	require.NoError(t, err)
   263  
   264  	err = testTxn.Commit()
   265  	require.NoError(t, err)
   266  
   267  	require.Equal(t, commitTime, block.LatestCommitExecutionTimeForTestingOnly())
   268  }
   269  
   270  func TestTxnDerivedDataCommitSnapshotReadDontAdvanceTime(t *testing.T) {
   271  	block := newEmptyTestBlock()
   272  
   273  	commitTime := LogicalTime(71)
   274  	testSetupTxn, err := block.NewTableTransaction(0, commitTime)
   275  	require.NoError(t, err)
   276  
   277  	err = testSetupTxn.Commit()
   278  	require.NoError(t, err)
   279  
   280  	repeatedTime := commitTime + 1
   281  	for i := 0; i < 10; i++ {
   282  		txn, err := block.NewSnapshotReadTableTransaction(0, repeatedTime)
   283  		require.NoError(t, err)
   284  
   285  		err = txn.Commit()
   286  		require.NoError(t, err)
   287  	}
   288  
   289  	require.Equal(
   290  		t,
   291  		commitTime,
   292  		block.LatestCommitExecutionTimeForTestingOnly())
   293  }
   294  
   295  func TestTxnDerivedDataCommitWriteOnlyTransactionNoInvalidation(t *testing.T) {
   296  	block := newEmptyTestBlock()
   297  
   298  	testTxn, err := block.NewTableTransaction(0, 0)
   299  	require.NoError(t, err)
   300  
   301  	key := "234"
   302  
   303  	actualValue, actualState, ok := testTxn.Get(key)
   304  	require.False(t, ok)
   305  	require.Nil(t, actualValue)
   306  	require.Nil(t, actualState)
   307  
   308  	valueString := "stuff"
   309  	expectedValue := &valueString
   310  	expectedState := &state.State{}
   311  
   312  	testTxn.Set(key, expectedValue, expectedState)
   313  
   314  	actualValue, actualState, ok = testTxn.Get(key)
   315  	require.True(t, ok)
   316  	require.Same(t, expectedValue, actualValue)
   317  	require.Same(t, expectedState, actualState)
   318  
   319  	testTxn.AddInvalidator(testInvalidator{})
   320  
   321  	err = testTxn.Commit()
   322  	require.NoError(t, err)
   323  
   324  	// Sanity check
   325  
   326  	require.Equal(
   327  		t,
   328  		LogicalTime(0),
   329  		block.LatestCommitExecutionTimeForTestingOnly())
   330  
   331  	require.Equal(t, 0, len(block.InvalidatorsForTestingOnly()))
   332  
   333  	entries := block.EntriesForTestingOnly()
   334  	require.Equal(t, 1, len(entries))
   335  
   336  	entry, ok := entries[key]
   337  	require.True(t, ok)
   338  	require.False(t, entry.isInvalid)
   339  	require.Same(t, expectedValue, entry.Value)
   340  	require.Same(t, expectedState, entry.State)
   341  }
   342  
   343  func TestTxnDerivedDataCommitWriteOnlyTransactionWithInvalidation(t *testing.T) {
   344  	block := newEmptyTestBlock()
   345  
   346  	testTxnTime := LogicalTime(47)
   347  	testTxn, err := block.NewTableTransaction(0, testTxnTime)
   348  	require.NoError(t, err)
   349  
   350  	key := "999"
   351  
   352  	actualValue, actualState, ok := testTxn.Get(key)
   353  	require.False(t, ok)
   354  	require.Nil(t, actualValue)
   355  	require.Nil(t, actualState)
   356  
   357  	valueString := "blah"
   358  	expectedValue := &valueString
   359  	expectedState := &state.State{}
   360  
   361  	testTxn.Set(key, expectedValue, expectedState)
   362  
   363  	actualValue, actualState, ok = testTxn.Get(key)
   364  	require.True(t, ok)
   365  	require.Same(t, expectedValue, actualValue)
   366  	require.Same(t, expectedState, actualState)
   367  
   368  	invalidator := testInvalidator{invalidateAll: true}
   369  
   370  	testTxn.AddInvalidator(invalidator)
   371  
   372  	err = testTxn.Commit()
   373  	require.NoError(t, err)
   374  
   375  	// Sanity check
   376  
   377  	require.Equal(
   378  		t,
   379  		testTxnTime,
   380  		block.LatestCommitExecutionTimeForTestingOnly())
   381  
   382  	require.Equal(
   383  		t,
   384  		chainedTableInvalidators[string, *string]{
   385  			{
   386  				TableInvalidator: invalidator,
   387  				executionTime:    testTxnTime,
   388  			},
   389  		},
   390  		block.InvalidatorsForTestingOnly())
   391  
   392  	require.Equal(t, 0, len(block.EntriesForTestingOnly()))
   393  }
   394  
   395  func TestTxnDerivedDataCommitUseOriginalEntryOnDuplicateWriteEntries(t *testing.T) {
   396  	block := newEmptyTestBlock()
   397  
   398  	testSetupTxn, err := block.NewTableTransaction(0, 11)
   399  	require.NoError(t, err)
   400  
   401  	testTxn, err := block.NewTableTransaction(10, 12)
   402  	require.NoError(t, err)
   403  
   404  	key := "17"
   405  	valueString := "foo"
   406  	expectedValue := &valueString
   407  	expectedState := &state.State{}
   408  
   409  	testSetupTxn.Set(key, expectedValue, expectedState)
   410  
   411  	err = testSetupTxn.Commit()
   412  	require.NoError(t, err)
   413  
   414  	entries := block.EntriesForTestingOnly()
   415  	require.Equal(t, 1, len(entries))
   416  
   417  	expectedEntry, ok := entries[key]
   418  	require.True(t, ok)
   419  
   420  	otherString := "other"
   421  	otherValue := &otherString
   422  	otherState := &state.State{}
   423  
   424  	testTxn.Set(key, otherValue, otherState)
   425  
   426  	err = testTxn.Commit()
   427  	require.NoError(t, err)
   428  
   429  	entries = block.EntriesForTestingOnly()
   430  	require.Equal(t, 1, len(entries))
   431  
   432  	actualEntry, ok := entries[key]
   433  	require.True(t, ok)
   434  
   435  	require.Same(t, expectedEntry, actualEntry)
   436  	require.False(t, actualEntry.isInvalid)
   437  	require.Same(t, expectedValue, actualEntry.Value)
   438  	require.Same(t, expectedState, actualEntry.State)
   439  	require.NotSame(t, otherValue, actualEntry.Value)
   440  	require.NotSame(t, otherState, actualEntry.State)
   441  }
   442  
   443  func TestTxnDerivedDataCommitReadOnlyTransactionNoInvalidation(t *testing.T) {
   444  	block := newEmptyTestBlock()
   445  
   446  	testSetupTxn, err := block.NewTableTransaction(0, 0)
   447  	require.NoError(t, err)
   448  
   449  	testTxn, err := block.NewTableTransaction(0, 1)
   450  	require.NoError(t, err)
   451  
   452  	key1 := "key1"
   453  	valStr1 := "value1"
   454  	expectedValue1 := &valStr1
   455  	expectedState1 := &state.State{}
   456  
   457  	testSetupTxn.Set(key1, expectedValue1, expectedState1)
   458  
   459  	key2 := "key2"
   460  	valStr2 := "value2"
   461  	expectedValue2 := &valStr2
   462  	expectedState2 := &state.State{}
   463  
   464  	testSetupTxn.Set(key2, expectedValue2, expectedState2)
   465  
   466  	err = testSetupTxn.Commit()
   467  	require.NoError(t, err)
   468  
   469  	actualValue, actualState, ok := testTxn.Get(key1)
   470  	require.True(t, ok)
   471  	require.Same(t, expectedValue1, actualValue)
   472  	require.Same(t, expectedState1, actualState)
   473  
   474  	actualValue, actualState, ok = testTxn.Get(key2)
   475  	require.True(t, ok)
   476  	require.Same(t, expectedValue2, actualValue)
   477  	require.Same(t, expectedState2, actualState)
   478  
   479  	actualValue, actualState, ok = testTxn.Get("key3")
   480  	require.False(t, ok)
   481  	require.Nil(t, actualValue)
   482  	require.Nil(t, actualState)
   483  
   484  	testTxn.AddInvalidator(testInvalidator{})
   485  
   486  	err = testTxn.Commit()
   487  	require.NoError(t, err)
   488  
   489  	// Sanity check
   490  
   491  	require.Equal(
   492  		t,
   493  		LogicalTime(1),
   494  		block.LatestCommitExecutionTimeForTestingOnly())
   495  
   496  	require.Equal(t, 0, len(block.InvalidatorsForTestingOnly()))
   497  
   498  	entries := block.EntriesForTestingOnly()
   499  	require.Equal(t, 2, len(entries))
   500  
   501  	entry, ok := entries[key1]
   502  	require.True(t, ok)
   503  	require.False(t, entry.isInvalid)
   504  	require.Same(t, expectedValue1, entry.Value)
   505  	require.Same(t, expectedState1, entry.State)
   506  
   507  	entry, ok = entries[key2]
   508  	require.True(t, ok)
   509  	require.False(t, entry.isInvalid)
   510  	require.Same(t, expectedValue2, entry.Value)
   511  	require.Same(t, expectedState2, entry.State)
   512  }
   513  
   514  func TestTxnDerivedDataCommitReadOnlyTransactionWithInvalidation(t *testing.T) {
   515  	block := newEmptyTestBlock()
   516  
   517  	testSetupTxn1Time := LogicalTime(2)
   518  	testSetupTxn1, err := block.NewTableTransaction(0, testSetupTxn1Time)
   519  	require.NoError(t, err)
   520  
   521  	testSetupTxn2, err := block.NewTableTransaction(0, 4)
   522  	require.NoError(t, err)
   523  
   524  	testTxnTime := LogicalTime(6)
   525  	testTxn, err := block.NewTableTransaction(0, testTxnTime)
   526  	require.NoError(t, err)
   527  
   528  	testSetupTxn1Invalidator := testInvalidator{
   529  		invalidateName: "blah",
   530  	}
   531  	testSetupTxn1.AddInvalidator(testSetupTxn1Invalidator)
   532  
   533  	err = testSetupTxn1.Commit()
   534  	require.NoError(t, err)
   535  
   536  	key1 := "key1"
   537  	valStr1 := "v1"
   538  	expectedValue1 := &valStr1
   539  	expectedState1 := &state.State{}
   540  
   541  	testSetupTxn2.Set(key1, expectedValue1, expectedState1)
   542  
   543  	key2 := "key2"
   544  	valStr2 := "v2"
   545  	expectedValue2 := &valStr2
   546  	expectedState2 := &state.State{}
   547  
   548  	testSetupTxn2.Set(key2, expectedValue2, expectedState2)
   549  
   550  	err = testSetupTxn2.Commit()
   551  	require.NoError(t, err)
   552  
   553  	actualValue, actualState, ok := testTxn.Get(key1)
   554  	require.True(t, ok)
   555  	require.Same(t, expectedValue1, actualValue)
   556  	require.Same(t, expectedState1, actualState)
   557  
   558  	actualValue, actualState, ok = testTxn.Get(key2)
   559  	require.True(t, ok)
   560  	require.Same(t, expectedValue2, actualValue)
   561  	require.Same(t, expectedState2, actualState)
   562  
   563  	actualValue, actualState, ok = testTxn.Get("key3")
   564  	require.False(t, ok)
   565  	require.Nil(t, actualValue)
   566  	require.Nil(t, actualState)
   567  
   568  	testTxnInvalidator := testInvalidator{invalidateAll: true}
   569  	testTxn.AddInvalidator(testTxnInvalidator)
   570  
   571  	err = testTxn.Commit()
   572  	require.NoError(t, err)
   573  
   574  	// Sanity check
   575  
   576  	require.Equal(
   577  		t,
   578  		testTxnTime,
   579  		block.LatestCommitExecutionTimeForTestingOnly())
   580  
   581  	require.Equal(
   582  		t,
   583  		chainedTableInvalidators[string, *string]{
   584  			{
   585  				TableInvalidator: testSetupTxn1Invalidator,
   586  				executionTime:    testSetupTxn1Time,
   587  			},
   588  			{
   589  				TableInvalidator: testTxnInvalidator,
   590  				executionTime:    testTxnTime,
   591  			},
   592  		},
   593  		block.InvalidatorsForTestingOnly())
   594  
   595  	require.Equal(t, 0, len(block.EntriesForTestingOnly()))
   596  }
   597  
   598  func TestTxnDerivedDataCommitValidateError(t *testing.T) {
   599  	block := newEmptyTestBlock()
   600  
   601  	testSetupTxn, err := block.NewTableTransaction(0, 10)
   602  	require.NoError(t, err)
   603  
   604  	err = testSetupTxn.Commit()
   605  	require.NoError(t, err)
   606  
   607  	testTxn, err := block.NewTableTransaction(10, 10)
   608  	require.NoError(t, err)
   609  
   610  	commitErr := testTxn.Commit()
   611  	require.ErrorContains(t, commitErr, "non-increasing time")
   612  	require.False(t, commitErr.IsRetryable())
   613  }
   614  
   615  func TestTxnDerivedDataCommitSnapshotReadDoesNotAdvanceCommitTime(t *testing.T) {
   616  	block := newEmptyTestBlock()
   617  
   618  	expectedTime := LogicalTime(10)
   619  	testSetupTxn, err := block.NewTableTransaction(0, expectedTime)
   620  	require.NoError(t, err)
   621  
   622  	err = testSetupTxn.Commit()
   623  	require.NoError(t, err)
   624  
   625  	testTxn, err := block.NewSnapshotReadTableTransaction(0, 11)
   626  	require.NoError(t, err)
   627  
   628  	err = testTxn.Commit()
   629  	require.NoError(t, err)
   630  
   631  	require.Equal(
   632  		t,
   633  		expectedTime,
   634  		block.LatestCommitExecutionTimeForTestingOnly())
   635  }
   636  
   637  func TestTxnDerivedDataCommitBadSnapshotReadInvalidator(t *testing.T) {
   638  	block := newEmptyTestBlock()
   639  
   640  	testTxn, err := block.NewSnapshotReadTableTransaction(0, 42)
   641  	require.NoError(t, err)
   642  
   643  	testTxn.AddInvalidator(testInvalidator{invalidateAll: true})
   644  
   645  	commitErr := testTxn.Commit()
   646  	require.ErrorContains(t, commitErr, "snapshot read can't invalidate")
   647  	require.False(t, commitErr.IsRetryable())
   648  }
   649  
   650  func TestTxnDerivedDataCommitFineGrainInvalidation(t *testing.T) {
   651  	block := newEmptyTestBlock()
   652  
   653  	// Setup the database with two read entries
   654  
   655  	testSetupTxn, err := block.NewTableTransaction(0, 0)
   656  	require.NoError(t, err)
   657  
   658  	readKey1 := "read-key-1"
   659  	readValStr1 := "read-value-1"
   660  	readValue1 := &readValStr1
   661  	readState1 := &state.State{}
   662  
   663  	readKey2 := "read-key-2"
   664  	readValStr2 := "read-value-2"
   665  	readValue2 := &readValStr2
   666  	readState2 := &state.State{}
   667  
   668  	testSetupTxn.Set(readKey1, readValue1, readState1)
   669  	testSetupTxn.Set(readKey2, readValue2, readState2)
   670  
   671  	err = testSetupTxn.Commit()
   672  	require.NoError(t, err)
   673  
   674  	// Setup the test transaction by read both existing entries and writing
   675  	// two new ones,
   676  
   677  	testTxnTime := LogicalTime(15)
   678  	testTxn, err := block.NewTableTransaction(1, testTxnTime)
   679  	require.NoError(t, err)
   680  
   681  	actualValue, actualState, ok := testTxn.Get(readKey1)
   682  	require.True(t, ok)
   683  	require.Same(t, readValue1, actualValue)
   684  	require.Same(t, readState1, actualState)
   685  
   686  	actualValue, actualState, ok = testTxn.Get(readKey2)
   687  	require.True(t, ok)
   688  	require.Same(t, readValue2, actualValue)
   689  	require.Same(t, readState2, actualState)
   690  
   691  	writeKey1 := "write key 1"
   692  	writeValStr1 := "write value 1"
   693  	writeValue1 := &writeValStr1
   694  	writeState1 := &state.State{}
   695  
   696  	writeKey2 := "write key 2"
   697  	writeValStr2 := "write value 2"
   698  	writeValue2 := &writeValStr2
   699  	writeState2 := &state.State{}
   700  
   701  	testTxn.Set(writeKey1, writeValue1, writeState1)
   702  	testTxn.Set(writeKey2, writeValue2, writeState2)
   703  
   704  	// Actual test.  Invalidate one pre-existing entry and one new entry.
   705  
   706  	invalidator1 := testInvalidator{
   707  		invalidateName: readKey1,
   708  	}
   709  	invalidator2 := testInvalidator{
   710  		invalidateName: writeKey1,
   711  	}
   712  	testTxn.AddInvalidator(nil)
   713  	testTxn.AddInvalidator(invalidator1)
   714  	testTxn.AddInvalidator(testInvalidator{})
   715  	testTxn.AddInvalidator(invalidator2)
   716  	testTxn.AddInvalidator(testInvalidator{})
   717  
   718  	err = testTxn.Commit()
   719  	require.NoError(t, err)
   720  
   721  	require.Equal(
   722  		t,
   723  		testTxnTime,
   724  		block.LatestCommitExecutionTimeForTestingOnly())
   725  
   726  	require.Equal(
   727  		t,
   728  		chainedTableInvalidators[string, *string]{
   729  			{
   730  				TableInvalidator: invalidator1,
   731  				executionTime:    testTxnTime,
   732  			},
   733  			{
   734  				TableInvalidator: invalidator2,
   735  				executionTime:    testTxnTime,
   736  			},
   737  		},
   738  		block.InvalidatorsForTestingOnly())
   739  
   740  	entries := block.EntriesForTestingOnly()
   741  	require.Equal(t, 2, len(entries))
   742  
   743  	entry, ok := entries[readKey2]
   744  	require.True(t, ok)
   745  	require.False(t, entry.isInvalid)
   746  	require.Same(t, readValue2, entry.Value)
   747  	require.Same(t, readState2, entry.State)
   748  
   749  	entry, ok = entries[writeKey2]
   750  	require.True(t, ok)
   751  	require.False(t, entry.isInvalid)
   752  	require.Same(t, writeValue2, entry.Value)
   753  	require.Same(t, writeState2, entry.State)
   754  }
   755  
   756  func TestDerivedDataTableNewChildDerivedBlockData(t *testing.T) {
   757  	parentBlock := newEmptyTestBlock()
   758  
   759  	require.Equal(
   760  		t,
   761  		ParentBlockTime,
   762  		parentBlock.LatestCommitExecutionTimeForTestingOnly())
   763  	require.Equal(t, 0, len(parentBlock.InvalidatorsForTestingOnly()))
   764  	require.Equal(t, 0, len(parentBlock.EntriesForTestingOnly()))
   765  
   766  	txn, err := parentBlock.NewTableTransaction(0, 0)
   767  	require.NoError(t, err)
   768  
   769  	txn.AddInvalidator(testInvalidator{invalidateAll: true})
   770  
   771  	err = txn.Commit()
   772  	require.NoError(t, err)
   773  
   774  	txn, err = parentBlock.NewTableTransaction(1, 1)
   775  	require.NoError(t, err)
   776  
   777  	key := "foo bar"
   778  	valStr := "zzz"
   779  	value := &valStr
   780  	state := &state.State{}
   781  
   782  	txn.Set(key, value, state)
   783  
   784  	err = txn.Commit()
   785  	require.NoError(t, err)
   786  
   787  	// Sanity check parent block
   788  
   789  	require.Equal(
   790  		t,
   791  		LogicalTime(1),
   792  		parentBlock.LatestCommitExecutionTimeForTestingOnly())
   793  
   794  	require.Equal(t, 1, len(parentBlock.InvalidatorsForTestingOnly()))
   795  
   796  	parentEntries := parentBlock.EntriesForTestingOnly()
   797  	require.Equal(t, 1, len(parentEntries))
   798  
   799  	parentEntry, ok := parentEntries[key]
   800  	require.True(t, ok)
   801  	require.False(t, parentEntry.isInvalid)
   802  	require.Same(t, value, parentEntry.Value)
   803  	require.Same(t, state, parentEntry.State)
   804  
   805  	// Verify child is correctly initialized
   806  
   807  	childBlock := parentBlock.NewChildTable()
   808  
   809  	require.Equal(
   810  		t,
   811  		ParentBlockTime,
   812  		childBlock.LatestCommitExecutionTimeForTestingOnly())
   813  
   814  	require.Equal(t, 0, len(childBlock.InvalidatorsForTestingOnly()))
   815  
   816  	childEntries := childBlock.EntriesForTestingOnly()
   817  	require.Equal(t, 1, len(childEntries))
   818  
   819  	childEntry, ok := childEntries[key]
   820  	require.True(t, ok)
   821  	require.False(t, childEntry.isInvalid)
   822  	require.Same(t, value, childEntry.Value)
   823  	require.Same(t, state, childEntry.State)
   824  
   825  	require.NotSame(t, parentEntry, childEntry)
   826  }
   827  
   828  type testValueComputer struct {
   829  	value  int
   830  	called bool
   831  }
   832  
   833  func (computer *testValueComputer) Compute(
   834  	txnState *state.TransactionState,
   835  	key string,
   836  ) (
   837  	int,
   838  	error,
   839  ) {
   840  	computer.called = true
   841  	_, err := txnState.Get("addr", key, true)
   842  	if err != nil {
   843  		return 0, err
   844  	}
   845  
   846  	return computer.value, nil
   847  }
   848  
   849  func TestTxnDerivedDataGetOrCompute(t *testing.T) {
   850  	blockDerivedData := NewEmptyTable[string, int]()
   851  
   852  	key := "key"
   853  	value := 12345
   854  
   855  	t.Run("compute value", func(t *testing.T) {
   856  		view := utils.NewSimpleView()
   857  		txnState := state.NewTransactionState(view, state.DefaultParameters())
   858  
   859  		txnDerivedData, err := blockDerivedData.NewTableTransaction(0, 0)
   860  		assert.Nil(t, err)
   861  
   862  		computer := &testValueComputer{value: value}
   863  		val, err := txnDerivedData.GetOrCompute(txnState, key, computer)
   864  		assert.Nil(t, err)
   865  		assert.Equal(t, value, val)
   866  		assert.True(t, computer.called)
   867  
   868  		_, ok := view.Ledger.RegisterTouches[flow.RegisterID{Owner: "addr", Key: key}]
   869  		assert.True(t, ok)
   870  
   871  		// Commit to setup the next test.
   872  		err = txnDerivedData.Commit()
   873  		assert.Nil(t, err)
   874  	})
   875  
   876  	t.Run("get value", func(t *testing.T) {
   877  		view := utils.NewSimpleView()
   878  		txnState := state.NewTransactionState(view, state.DefaultParameters())
   879  
   880  		txnDerivedData, err := blockDerivedData.NewTableTransaction(1, 1)
   881  		assert.Nil(t, err)
   882  
   883  		computer := &testValueComputer{value: value}
   884  		val, err := txnDerivedData.GetOrCompute(txnState, key, computer)
   885  		assert.Nil(t, err)
   886  		assert.Equal(t, value, val)
   887  		assert.False(t, computer.called)
   888  
   889  		_, ok := view.Ledger.RegisterTouches[flow.RegisterID{Owner: "addr", Key: key}]
   890  		assert.True(t, ok)
   891  	})
   892  }