github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/storage/state/execution_state_test.go (about)

     1  package state_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	"github.com/onflow/flow-go/fvm/meter"
     9  	"github.com/onflow/flow-go/fvm/storage/state"
    10  	"github.com/onflow/flow-go/model/flow"
    11  	"github.com/onflow/flow-go/utils/unittest"
    12  )
    13  
    14  func createByteArray(size int) []byte {
    15  	bytes := make([]byte, size)
    16  	for i := range bytes {
    17  		bytes[i] = 255
    18  	}
    19  	return bytes
    20  }
    21  
    22  func TestExecutionState_Finalize(t *testing.T) {
    23  	parent := state.NewExecutionState(nil, state.DefaultParameters())
    24  
    25  	child := parent.NewChild()
    26  
    27  	readId := flow.NewRegisterID(unittest.RandomAddressFixture(), "x")
    28  
    29  	_, err := child.Get(readId)
    30  	require.NoError(t, err)
    31  
    32  	writeId := flow.NewRegisterID(unittest.RandomAddressFixture(), "y")
    33  	writeValue := flow.RegisterValue("a")
    34  
    35  	err = child.Set(writeId, writeValue)
    36  	require.NoError(t, err)
    37  
    38  	childSnapshot := child.Finalize()
    39  
    40  	require.Equal(
    41  		t,
    42  		map[flow.RegisterID]struct{}{
    43  			readId: {},
    44  		},
    45  		childSnapshot.ReadSet)
    46  
    47  	require.Equal(
    48  		t,
    49  		map[flow.RegisterID]flow.RegisterValue{
    50  			writeId: writeValue,
    51  		},
    52  		childSnapshot.WriteSet)
    53  
    54  	require.NotNil(t, childSnapshot.SpockSecret)
    55  	require.NotNil(t, childSnapshot.Meter)
    56  
    57  	parentSnapshot := parent.Finalize()
    58  	// empty read / write set since child was not merged.
    59  	require.Empty(t, parentSnapshot.ReadSet)
    60  	require.Empty(t, parentSnapshot.WriteSet)
    61  	require.NotNil(t, parentSnapshot.SpockSecret)
    62  	require.NotNil(t, parentSnapshot.Meter)
    63  
    64  }
    65  
    66  func TestExecutionState_ChildMergeFunctionality(t *testing.T) {
    67  	st := state.NewExecutionState(nil, state.DefaultParameters())
    68  
    69  	owner := unittest.RandomAddressFixture()
    70  
    71  	t.Run("test read from parent state (backoff)", func(t *testing.T) {
    72  		key := flow.NewRegisterID(owner, "key1")
    73  		value := createByteArray(1)
    74  		// set key1 on parent
    75  		err := st.Set(key, value)
    76  		require.NoError(t, err)
    77  
    78  		// read key1 on child
    79  		stChild := st.NewChild()
    80  		v, err := stChild.Get(key)
    81  		require.NoError(t, err)
    82  		require.Equal(t, v, value)
    83  	})
    84  
    85  	t.Run("test write to child (no merge)", func(t *testing.T) {
    86  		key := flow.NewRegisterID(owner, "key2")
    87  		value := createByteArray(2)
    88  		stChild := st.NewChild()
    89  
    90  		// set key2 on child
    91  		err := stChild.Set(key, value)
    92  		require.NoError(t, err)
    93  
    94  		// read key2 on parent
    95  		v, err := st.Get(key)
    96  		require.NoError(t, err)
    97  		require.Equal(t, len(v), 0)
    98  	})
    99  
   100  	t.Run("test write to child and merge", func(t *testing.T) {
   101  		key := flow.NewRegisterID(owner, "key3")
   102  		value := createByteArray(3)
   103  		stChild := st.NewChild()
   104  
   105  		// set key3 on child
   106  		err := stChild.Set(key, value)
   107  		require.NoError(t, err)
   108  
   109  		// read before merge
   110  		v, err := st.Get(key)
   111  		require.NoError(t, err)
   112  		require.Equal(t, len(v), 0)
   113  
   114  		// merge to parent
   115  		err = st.Merge(stChild.Finalize())
   116  		require.NoError(t, err)
   117  
   118  		// read key3 on parent
   119  		v, err = st.Get(key)
   120  		require.NoError(t, err)
   121  		require.Equal(t, v, value)
   122  	})
   123  
   124  	t.Run("test write to ledger", func(t *testing.T) {
   125  		key := flow.NewRegisterID(owner, "key4")
   126  		value := createByteArray(4)
   127  		// set key4 on parent
   128  		err := st.Set(key, value)
   129  		require.NoError(t, err)
   130  
   131  		// now should be part of the ledger
   132  		v, err := st.Get(key)
   133  		require.NoError(t, err)
   134  		require.Equal(t, v, value)
   135  	})
   136  
   137  }
   138  
   139  func TestExecutionState_MaxValueSize(t *testing.T) {
   140  	st := state.NewExecutionState(
   141  		nil,
   142  		state.DefaultParameters().WithMaxValueSizeAllowed(6))
   143  
   144  	key := flow.NewRegisterID(unittest.RandomAddressFixture(), "key")
   145  
   146  	// update should pass
   147  	value := createByteArray(5)
   148  	err := st.Set(key, value)
   149  	require.NoError(t, err)
   150  
   151  	// update shouldn't pass
   152  	value = createByteArray(7)
   153  	err = st.Set(key, value)
   154  	require.Error(t, err)
   155  }
   156  
   157  func TestExecutionState_MaxKeySize(t *testing.T) {
   158  	st := state.NewExecutionState(
   159  		nil,
   160  		// Note: owners are always 8 bytes
   161  		state.DefaultParameters().WithMaxKeySizeAllowed(8+2))
   162  
   163  	key1 := flow.NewRegisterID(unittest.RandomAddressFixture(), "23")
   164  	key2 := flow.NewRegisterID(unittest.RandomAddressFixture(), "234")
   165  
   166  	// read
   167  	_, err := st.Get(key1)
   168  	require.NoError(t, err)
   169  
   170  	// read
   171  	_, err = st.Get(key2)
   172  	require.Error(t, err)
   173  
   174  	// update
   175  	err = st.Set(key1, []byte{})
   176  	require.NoError(t, err)
   177  
   178  	// read
   179  	err = st.Set(key2, []byte{})
   180  	require.Error(t, err)
   181  
   182  }
   183  
   184  func TestExecutionState_MaxInteraction(t *testing.T) {
   185  	key1 := flow.NewRegisterID(unittest.RandomAddressFixture(), "2")
   186  	key1Size := uint64(8 + 1)
   187  
   188  	value1 := []byte("A")
   189  	value1Size := uint64(1)
   190  
   191  	key2 := flow.NewRegisterID(unittest.RandomAddressFixture(), "23")
   192  	key2Size := uint64(8 + 2)
   193  
   194  	key3 := flow.NewRegisterID(unittest.RandomAddressFixture(), "345")
   195  	key3Size := uint64(8 + 3)
   196  
   197  	key4 := flow.NewRegisterID(unittest.RandomAddressFixture(), "4567")
   198  	key4Size := uint64(8 + 4)
   199  
   200  	st := state.NewExecutionState(
   201  		nil,
   202  		state.DefaultParameters().
   203  			WithMeterParameters(
   204  				meter.DefaultParameters().WithStorageInteractionLimit(
   205  					key1Size+key2Size+key3Size-1)))
   206  
   207  	// read - interaction 2
   208  	_, err := st.Get(key1)
   209  	require.Equal(t, st.InteractionUsed(), key1Size)
   210  	require.NoError(t, err)
   211  
   212  	// read - interaction 8
   213  	_, err = st.Get(key2)
   214  	require.NoError(t, err)
   215  	require.Equal(t, st.InteractionUsed(), key1Size+key2Size)
   216  
   217  	// read - interaction 14
   218  	_, err = st.Get(key3)
   219  	require.Error(t, err)
   220  	require.Equal(t, st.InteractionUsed(), key1Size+key2Size+key3Size)
   221  
   222  	st = state.NewExecutionState(
   223  		nil,
   224  		state.DefaultParameters().
   225  			WithMeterParameters(
   226  				meter.DefaultParameters().WithStorageInteractionLimit(
   227  					key1Size+value1Size+key2Size)))
   228  	stChild := st.NewChild()
   229  
   230  	// update - 0
   231  	err = stChild.Set(key1, value1)
   232  	require.NoError(t, err)
   233  	require.Equal(t, st.InteractionUsed(), uint64(0))
   234  
   235  	// commit
   236  	err = st.Merge(stChild.Finalize())
   237  	require.NoError(t, err)
   238  	require.Equal(t, st.InteractionUsed(), key1Size+value1Size)
   239  
   240  	// read - interaction 3 (already in read cache)
   241  	_, err = st.Get(key1)
   242  	require.NoError(t, err)
   243  	require.Equal(t, st.InteractionUsed(), key1Size+value1Size)
   244  
   245  	// read - interaction 5
   246  	_, err = st.Get(key2)
   247  	require.NoError(t, err)
   248  	require.Equal(t, st.InteractionUsed(), key1Size+value1Size+key2Size)
   249  
   250  	// read - interaction 7
   251  	_, err = st.Get(key4)
   252  	require.Error(t, err)
   253  	require.Equal(
   254  		t,
   255  		st.InteractionUsed(),
   256  		key1Size+value1Size+key2Size+key4Size)
   257  }