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

     1  package state_test
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	"github.com/onflow/cadence/runtime/common"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/koko1123/flow-go-1/fvm/meter"
    11  	"github.com/koko1123/flow-go-1/fvm/state"
    12  	"github.com/koko1123/flow-go-1/fvm/utils"
    13  )
    14  
    15  func newTestTransactionState() *state.TransactionState {
    16  	return state.NewTransactionState(
    17  		utils.NewSimpleView(),
    18  		state.DefaultParameters(),
    19  	)
    20  }
    21  
    22  func TestUnrestrictedNestedTransactionBasic(t *testing.T) {
    23  	txn := newTestTransactionState()
    24  
    25  	mainState := txn.MainTransactionId().StateForTestingOnly()
    26  
    27  	require.Equal(t, 0, txn.NumNestedTransactions())
    28  	require.False(t, txn.IsParseRestricted())
    29  
    30  	id1, err := txn.BeginNestedTransaction()
    31  	require.NoError(t, err)
    32  
    33  	require.Equal(t, 1, txn.NumNestedTransactions())
    34  	require.False(t, txn.IsParseRestricted())
    35  
    36  	require.True(t, txn.IsCurrent(id1))
    37  
    38  	nestedState1 := id1.StateForTestingOnly()
    39  
    40  	id2, err := txn.BeginNestedTransaction()
    41  	require.NoError(t, err)
    42  
    43  	require.Equal(t, 2, txn.NumNestedTransactions())
    44  	require.False(t, txn.IsParseRestricted())
    45  
    46  	require.False(t, txn.IsCurrent(id1))
    47  	require.True(t, txn.IsCurrent(id2))
    48  
    49  	nestedState2 := id2.StateForTestingOnly()
    50  
    51  	// Ensure the values are written to the correctly nested state
    52  
    53  	addr := "address"
    54  	key := "key"
    55  	val := createByteArray(2)
    56  
    57  	err = txn.Set(addr, key, val, true)
    58  	require.NoError(t, err)
    59  
    60  	v, err := nestedState2.Get(addr, key, true)
    61  	require.NoError(t, err)
    62  	require.Equal(t, val, v)
    63  
    64  	v, err = nestedState1.Get(addr, key, true)
    65  	require.NoError(t, err)
    66  	require.Nil(t, v)
    67  
    68  	v, err = mainState.Get(addr, key, true)
    69  	require.NoError(t, err)
    70  	require.Nil(t, v)
    71  
    72  	// Ensure nested transactions are merged correctly
    73  
    74  	_, err = txn.Commit(id2)
    75  	require.NoError(t, err)
    76  
    77  	require.Equal(t, 1, txn.NumNestedTransactions())
    78  	require.True(t, txn.IsCurrent(id1))
    79  
    80  	v, err = nestedState1.Get(addr, key, true)
    81  	require.NoError(t, err)
    82  	require.Equal(t, val, v)
    83  
    84  	v, err = mainState.Get(addr, key, true)
    85  	require.NoError(t, err)
    86  	require.Nil(t, v)
    87  
    88  	_, err = txn.Commit(id1)
    89  	require.NoError(t, err)
    90  
    91  	require.Equal(t, 0, txn.NumNestedTransactions())
    92  	require.True(t, txn.IsCurrent(txn.MainTransactionId()))
    93  
    94  	v, err = mainState.Get(addr, key, true)
    95  	require.NoError(t, err)
    96  	require.Equal(t, val, v)
    97  }
    98  
    99  func TestUnrestrictedNestedTransactionDifferentMeterParams(t *testing.T) {
   100  	txn := newTestTransactionState()
   101  
   102  	mainState := txn.MainTransactionId().StateForTestingOnly()
   103  
   104  	require.Equal(t, uint(math.MaxUint), mainState.TotalMemoryLimit())
   105  
   106  	id1, err := txn.BeginNestedTransactionWithMeterParams(
   107  		meter.DefaultParameters().WithMemoryLimit(1))
   108  	require.NoError(t, err)
   109  
   110  	nestedState1 := id1.StateForTestingOnly()
   111  
   112  	require.Equal(t, uint(1), nestedState1.TotalMemoryLimit())
   113  
   114  	id2, err := txn.BeginNestedTransactionWithMeterParams(
   115  		meter.DefaultParameters().WithMemoryLimit(2))
   116  	require.NoError(t, err)
   117  
   118  	nestedState2 := id2.StateForTestingOnly()
   119  
   120  	require.Equal(t, uint(2), nestedState2.TotalMemoryLimit())
   121  
   122  	// inherits memory limit from parent
   123  
   124  	id3, err := txn.BeginNestedTransaction()
   125  	require.NoError(t, err)
   126  
   127  	nestedState3 := id3.StateForTestingOnly()
   128  
   129  	require.Equal(t, uint(2), nestedState3.TotalMemoryLimit())
   130  }
   131  
   132  func TestParseRestrictedNestedTransactionBasic(t *testing.T) {
   133  	txn := newTestTransactionState()
   134  
   135  	mainId := txn.MainTransactionId()
   136  	mainState := mainId.StateForTestingOnly()
   137  
   138  	require.Equal(t, 0, txn.NumNestedTransactions())
   139  	require.False(t, txn.IsParseRestricted())
   140  
   141  	id1, err := txn.BeginNestedTransaction()
   142  	require.NoError(t, err)
   143  
   144  	require.Equal(t, 1, txn.NumNestedTransactions())
   145  	require.False(t, txn.IsParseRestricted())
   146  
   147  	nestedState := id1.StateForTestingOnly()
   148  
   149  	loc1 := common.AddressLocation{
   150  		Address: common.MustBytesToAddress([]byte{1, 1, 1}),
   151  		Name:    "loc1",
   152  	}
   153  
   154  	restrictedId1, err := txn.BeginParseRestrictedNestedTransaction(loc1)
   155  	require.NoError(t, err)
   156  
   157  	require.Equal(t, 2, txn.NumNestedTransactions())
   158  	require.True(t, txn.IsParseRestricted())
   159  
   160  	restrictedNestedState1 := restrictedId1.StateForTestingOnly()
   161  
   162  	loc2 := common.AddressLocation{
   163  		Address: common.MustBytesToAddress([]byte{2, 2, 2}),
   164  		Name:    "loc2",
   165  	}
   166  
   167  	restrictedId2, err := txn.BeginParseRestrictedNestedTransaction(loc2)
   168  	require.NoError(t, err)
   169  
   170  	require.Equal(t, 3, txn.NumNestedTransactions())
   171  	require.True(t, txn.IsParseRestricted())
   172  
   173  	restrictedNestedState2 := restrictedId2.StateForTestingOnly()
   174  
   175  	// Sanity check
   176  
   177  	addr := "address"
   178  	key := "key"
   179  
   180  	v, err := restrictedNestedState2.Get(addr, key, true)
   181  	require.NoError(t, err)
   182  	require.Nil(t, v)
   183  
   184  	v, err = restrictedNestedState1.Get(addr, key, true)
   185  	require.NoError(t, err)
   186  	require.Nil(t, v)
   187  
   188  	v, err = nestedState.Get(addr, key, true)
   189  	require.NoError(t, err)
   190  	require.Nil(t, v)
   191  
   192  	v, err = mainState.Get(addr, key, true)
   193  	require.NoError(t, err)
   194  	require.Nil(t, v)
   195  
   196  	// Ensures attaching and committing cached nested transaction works
   197  
   198  	val := createByteArray(2)
   199  
   200  	cachedState := state.NewState(
   201  		utils.NewSimpleView(),
   202  		state.DefaultParameters(),
   203  	)
   204  
   205  	err = cachedState.Set(addr, key, val, true)
   206  	require.NoError(t, err)
   207  
   208  	err = txn.AttachAndCommit(cachedState)
   209  	require.NoError(t, err)
   210  
   211  	require.Equal(t, 3, txn.NumNestedTransactions())
   212  	require.True(t, txn.IsCurrent(restrictedId2))
   213  
   214  	v, err = restrictedNestedState2.Get(addr, key, true)
   215  	require.NoError(t, err)
   216  	require.Equal(t, val, v)
   217  
   218  	v, err = restrictedNestedState1.Get(addr, key, true)
   219  	require.NoError(t, err)
   220  	require.Nil(t, v)
   221  
   222  	v, err = nestedState.Get(addr, key, true)
   223  	require.NoError(t, err)
   224  	require.Nil(t, v)
   225  
   226  	v, err = mainState.Get(addr, key, true)
   227  	require.NoError(t, err)
   228  	require.Nil(t, v)
   229  
   230  	// Ensure nested transactions are merged correctly
   231  
   232  	state, err := txn.CommitParseRestricted(loc2)
   233  	require.NoError(t, err)
   234  	require.Equal(t, restrictedNestedState2, state)
   235  
   236  	require.Equal(t, 2, txn.NumNestedTransactions())
   237  	require.True(t, txn.IsCurrent(restrictedId1))
   238  
   239  	v, err = restrictedNestedState1.Get(addr, key, true)
   240  	require.NoError(t, err)
   241  	require.Equal(t, val, v)
   242  
   243  	v, err = nestedState.Get(addr, key, true)
   244  	require.NoError(t, err)
   245  	require.Nil(t, v)
   246  
   247  	v, err = mainState.Get(addr, key, true)
   248  	require.NoError(t, err)
   249  	require.Nil(t, v)
   250  
   251  	state, err = txn.CommitParseRestricted(loc1)
   252  	require.NoError(t, err)
   253  	require.Equal(t, restrictedNestedState1, state)
   254  
   255  	require.Equal(t, 1, txn.NumNestedTransactions())
   256  	require.True(t, txn.IsCurrent(id1))
   257  
   258  	v, err = nestedState.Get(addr, key, true)
   259  	require.NoError(t, err)
   260  	require.Equal(t, val, v)
   261  
   262  	v, err = mainState.Get(addr, key, true)
   263  	require.NoError(t, err)
   264  	require.Nil(t, v)
   265  
   266  	_, err = txn.Commit(id1)
   267  	require.NoError(t, err)
   268  
   269  	require.Equal(t, 0, txn.NumNestedTransactions())
   270  	require.True(t, txn.IsCurrent(mainId))
   271  
   272  	v, err = mainState.Get(addr, key, true)
   273  	require.NoError(t, err)
   274  	require.Equal(t, val, v)
   275  }
   276  
   277  func TestRestartNestedTransaction(t *testing.T) {
   278  	txn := newTestTransactionState()
   279  
   280  	require.Equal(t, 0, txn.NumNestedTransactions())
   281  
   282  	id, err := txn.BeginNestedTransaction()
   283  	require.NoError(t, err)
   284  
   285  	addr := "address"
   286  	key := "key"
   287  	val := createByteArray(2)
   288  
   289  	for i := 0; i < 10; i++ {
   290  		_, err := txn.BeginNestedTransaction()
   291  		require.NoError(t, err)
   292  
   293  		err = txn.Set(addr, key, val, true)
   294  		require.NoError(t, err)
   295  	}
   296  
   297  	loc := common.AddressLocation{
   298  		Address: common.MustBytesToAddress([]byte{1, 1, 1}),
   299  		Name:    "loc",
   300  	}
   301  
   302  	for i := 0; i < 5; i++ {
   303  		_, err := txn.BeginParseRestrictedNestedTransaction(loc)
   304  		require.NoError(t, err)
   305  
   306  		err = txn.Set(addr, key, val, true)
   307  		require.NoError(t, err)
   308  	}
   309  
   310  	require.Equal(t, 16, txn.NumNestedTransactions())
   311  
   312  	state := id.StateForTestingOnly()
   313  	require.Equal(t, uint64(0), state.InteractionUsed())
   314  
   315  	// Restart will merge the meter stat, but not the view delta
   316  
   317  	err = txn.RestartNestedTransaction(id)
   318  	require.NoError(t, err)
   319  
   320  	require.Equal(t, 1, txn.NumNestedTransactions())
   321  	require.True(t, txn.IsCurrent(id))
   322  
   323  	require.Greater(t, state.InteractionUsed(), uint64(0))
   324  
   325  	v, err := state.Get(addr, key, true)
   326  	require.NoError(t, err)
   327  	require.Nil(t, v)
   328  }
   329  
   330  func TestRestartNestedTransactionWithInvalidId(t *testing.T) {
   331  	txn := newTestTransactionState()
   332  
   333  	require.Equal(t, 0, txn.NumNestedTransactions())
   334  
   335  	id, err := txn.BeginNestedTransaction()
   336  	require.NoError(t, err)
   337  
   338  	addr := "address"
   339  	key := "key"
   340  	val := createByteArray(2)
   341  
   342  	err = txn.Set(addr, key, val, true)
   343  	require.NoError(t, err)
   344  
   345  	var otherId state.NestedTransactionId
   346  	for i := 0; i < 10; i++ {
   347  		otherId, err = txn.BeginNestedTransaction()
   348  		require.NoError(t, err)
   349  
   350  		_, err = txn.Commit(otherId)
   351  		require.NoError(t, err)
   352  	}
   353  
   354  	require.True(t, txn.IsCurrent(id))
   355  
   356  	err = txn.RestartNestedTransaction(otherId)
   357  	require.Error(t, err)
   358  
   359  	require.True(t, txn.IsCurrent(id))
   360  
   361  	v, err := txn.Get(addr, key, true)
   362  	require.NoError(t, err)
   363  	require.Equal(t, val, v)
   364  }
   365  
   366  func TestUnrestrictedCannotCommitParseRestricted(t *testing.T) {
   367  	txn := newTestTransactionState()
   368  
   369  	loc := common.AddressLocation{
   370  		Address: common.MustBytesToAddress([]byte{1, 1, 1}),
   371  		Name:    "loc",
   372  	}
   373  
   374  	id, err := txn.BeginNestedTransaction()
   375  	require.NoError(t, err)
   376  
   377  	require.Equal(t, 1, txn.NumNestedTransactions())
   378  	require.False(t, txn.IsParseRestricted())
   379  
   380  	_, err = txn.CommitParseRestricted(loc)
   381  	require.Error(t, err)
   382  
   383  	require.Equal(t, 1, txn.NumNestedTransactions())
   384  	require.True(t, txn.IsCurrent(id))
   385  }
   386  
   387  func TestUnrestrictedCannotCommitMainTransaction(t *testing.T) {
   388  	txn := newTestTransactionState()
   389  
   390  	id1, err := txn.BeginNestedTransaction()
   391  	require.NoError(t, err)
   392  
   393  	id2, err := txn.BeginNestedTransaction()
   394  	require.NoError(t, err)
   395  
   396  	require.Equal(t, 2, txn.NumNestedTransactions())
   397  
   398  	_, err = txn.Commit(id1)
   399  	require.Error(t, err)
   400  
   401  	require.Equal(t, 2, txn.NumNestedTransactions())
   402  	require.True(t, txn.IsCurrent(id2))
   403  }
   404  
   405  func TestUnrestrictedCannotCommitUnexpectedNested(t *testing.T) {
   406  	txn := newTestTransactionState()
   407  
   408  	mainId := txn.MainTransactionId()
   409  
   410  	require.Equal(t, 0, txn.NumNestedTransactions())
   411  
   412  	_, err := txn.Commit(mainId)
   413  	require.Error(t, err)
   414  
   415  	require.Equal(t, 0, txn.NumNestedTransactions())
   416  	require.True(t, txn.IsCurrent(mainId))
   417  }
   418  
   419  func TestParseRestrictedCannotBeginUnrestrictedNestedTransaction(t *testing.T) {
   420  	txn := newTestTransactionState()
   421  
   422  	loc := common.AddressLocation{
   423  		Address: common.MustBytesToAddress([]byte{1, 1, 1}),
   424  		Name:    "loc",
   425  	}
   426  
   427  	id1, err := txn.BeginParseRestrictedNestedTransaction(loc)
   428  	require.NoError(t, err)
   429  
   430  	require.Equal(t, 1, txn.NumNestedTransactions())
   431  
   432  	id2, err := txn.BeginNestedTransaction()
   433  	require.Error(t, err)
   434  
   435  	require.Equal(t, 1, txn.NumNestedTransactions())
   436  	require.True(t, txn.IsCurrent(id1))
   437  	require.False(t, txn.IsCurrent(id2))
   438  }
   439  
   440  func TestParseRestrictedCannotCommitUnrestricted(t *testing.T) {
   441  	txn := newTestTransactionState()
   442  
   443  	loc := common.AddressLocation{
   444  		Address: common.MustBytesToAddress([]byte{1, 1, 1}),
   445  		Name:    "loc",
   446  	}
   447  
   448  	id, err := txn.BeginParseRestrictedNestedTransaction(loc)
   449  	require.NoError(t, err)
   450  
   451  	require.Equal(t, 1, txn.NumNestedTransactions())
   452  
   453  	_, err = txn.Commit(id)
   454  	require.Error(t, err)
   455  
   456  	require.Equal(t, 1, txn.NumNestedTransactions())
   457  	require.True(t, txn.IsCurrent(id))
   458  }
   459  
   460  func TestParseRestrictedCannotCommitLocationMismatch(t *testing.T) {
   461  	txn := newTestTransactionState()
   462  
   463  	loc := common.AddressLocation{
   464  		Address: common.MustBytesToAddress([]byte{1, 1, 1}),
   465  		Name:    "loc",
   466  	}
   467  
   468  	id, err := txn.BeginParseRestrictedNestedTransaction(loc)
   469  	require.NoError(t, err)
   470  
   471  	require.Equal(t, 1, txn.NumNestedTransactions())
   472  
   473  	other := common.AddressLocation{
   474  		Address: common.MustBytesToAddress([]byte{1, 1, 1}),
   475  		Name:    "other",
   476  	}
   477  
   478  	cacheableState, err := txn.CommitParseRestricted(other)
   479  	require.Error(t, err)
   480  	require.Nil(t, cacheableState)
   481  
   482  	require.Equal(t, 1, txn.NumNestedTransactions())
   483  	require.True(t, txn.IsCurrent(id))
   484  }
   485  
   486  func TestPauseAndResume(t *testing.T) {
   487  	txn := newTestTransactionState()
   488  
   489  	val, err := txn.Get("addr", "key", true)
   490  	require.NoError(t, err)
   491  	require.Nil(t, val)
   492  
   493  	id1, err := txn.BeginNestedTransaction()
   494  	require.NoError(t, err)
   495  
   496  	err = txn.Set("addr", "key", createByteArray(2), true)
   497  	require.NoError(t, err)
   498  
   499  	val, err = txn.Get("addr", "key", true)
   500  	require.NoError(t, err)
   501  	require.NotNil(t, val)
   502  
   503  	pausedState, err := txn.Pause(id1)
   504  	require.NoError(t, err)
   505  
   506  	val, err = txn.Get("addr", "key", true)
   507  	require.NoError(t, err)
   508  	require.Nil(t, val)
   509  
   510  	txn.Resume(pausedState)
   511  
   512  	val, err = txn.Get("addr", "key", true)
   513  	require.NoError(t, err)
   514  	require.NotNil(t, val)
   515  
   516  	err = txn.Set("addr2", "key2", createByteArray(2), true)
   517  	require.NoError(t, err)
   518  
   519  	_, err = txn.Commit(id1)
   520  	require.NoError(t, err)
   521  
   522  	val, err = txn.Get("addr2", "key2", true)
   523  	require.NoError(t, err)
   524  	require.NotNil(t, val)
   525  }
   526  
   527  func TestInvalidCommittedStateModification(t *testing.T) {
   528  	txn := newTestTransactionState()
   529  
   530  	id1, err := txn.BeginNestedTransaction()
   531  	require.NoError(t, err)
   532  
   533  	err = txn.Set("addr", "key", createByteArray(2), true)
   534  	require.NoError(t, err)
   535  
   536  	_, err = txn.Get("addr", "key", true)
   537  	require.NoError(t, err)
   538  
   539  	committedState, err := txn.Commit(id1)
   540  	require.NoError(t, err)
   541  
   542  	err = committedState.MergeState(
   543  		state.NewState(utils.NewSimpleView(), state.DefaultParameters()))
   544  	require.ErrorContains(t, err, "cannot MergeState on a committed state")
   545  
   546  	txn.Resume(committedState)
   547  
   548  	err = txn.Set("addr", "key", createByteArray(2), true)
   549  	require.ErrorContains(t, err, "cannot Set on a committed state")
   550  
   551  	_, err = txn.Get("addr", "key", true)
   552  	require.ErrorContains(t, err, "cannot Get on a committed state")
   553  
   554  	_, err = txn.Commit(id1)
   555  	require.NoError(t, err)
   556  }