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

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    10  	"github.com/onflow/flow-go/model/flow"
    11  	"github.com/onflow/flow-go/utils/rand"
    12  	"github.com/onflow/flow-go/utils/unittest"
    13  )
    14  
    15  type spockTestOp func(*testing.T, *spockState)
    16  
    17  var fooOwner = unittest.RandomAddressFixture()
    18  
    19  func chainSpockTestOps(prevOps spockTestOp, op spockTestOp) spockTestOp {
    20  	return func(t *testing.T, state *spockState) {
    21  		if prevOps != nil {
    22  			prevOps(t, state)
    23  		}
    24  		op(t, state)
    25  	}
    26  }
    27  
    28  func testSpock(
    29  	t *testing.T,
    30  	counterfactualExperiments []spockTestOp,
    31  ) []*spockState {
    32  	resultStates := []*spockState{}
    33  	for _, experiment := range counterfactualExperiments {
    34  		run1 := newSpockState(snapshot.MapStorageSnapshot{}, DefaultSpockSecretHasher)
    35  		run2 := newSpockState(snapshot.MapStorageSnapshot{}, DefaultSpockSecretHasher)
    36  
    37  		if experiment != nil {
    38  			experiment(t, run1)
    39  			experiment(t, run2)
    40  		}
    41  
    42  		spock := run1.Finalize().SpockSecret
    43  		require.Equal(t, spock, run2.Finalize().SpockSecret)
    44  
    45  		for _, previous := range resultStates {
    46  			require.NotEqual(t, spock, previous.Finalize().SpockSecret)
    47  		}
    48  
    49  		resultStates = append(resultStates, run1)
    50  	}
    51  
    52  	return resultStates
    53  }
    54  
    55  func TestSpockStateGet(t *testing.T) {
    56  	otherOwner := unittest.RandomAddressFixture()
    57  	registerId := flow.NewRegisterID(fooOwner, "bar")
    58  
    59  	states := testSpock(
    60  		t,
    61  		[]spockTestOp{
    62  			// control experiment
    63  			nil,
    64  			// primary experiment
    65  			func(t *testing.T, state *spockState) {
    66  				_, err := state.Get(registerId)
    67  				require.NoError(t, err)
    68  			},
    69  			// duplicate calls return in different spock
    70  			func(t *testing.T, state *spockState) {
    71  				_, err := state.Get(registerId)
    72  				require.NoError(t, err)
    73  				_, err = state.Get(registerId)
    74  				require.NoError(t, err)
    75  			},
    76  			// Reading different register ids will result in different spock
    77  			func(t *testing.T, state *spockState) {
    78  				_, err := state.Get(flow.NewRegisterID(otherOwner, "bar"))
    79  				require.NoError(t, err)
    80  			},
    81  			func(t *testing.T, state *spockState) {
    82  				_, err := state.Get(flow.NewRegisterID(fooOwner, "baR"))
    83  				require.NoError(t, err)
    84  			},
    85  		})
    86  
    87  	// Sanity check underlying storage state is called.
    88  	require.Equal(
    89  		t,
    90  		map[flow.RegisterID]struct{}{
    91  			registerId: struct{}{},
    92  		},
    93  		states[1].Finalize().ReadSet)
    94  
    95  	// Sanity check finalized state is no longer accessible.
    96  	_, err := states[1].Get(registerId)
    97  	require.ErrorContains(t, err, "cannot Get on a finalized state")
    98  }
    99  
   100  func TestSpockStateGetDifferentUnderlyingStorage(t *testing.T) {
   101  	badRegisterId := flow.NewRegisterID(fooOwner, "bad")
   102  
   103  	value1 := flow.RegisterValue([]byte("abc"))
   104  	value2 := flow.RegisterValue([]byte("blah"))
   105  
   106  	state1 := newSpockState(
   107  		snapshot.MapStorageSnapshot{
   108  			badRegisterId: value1,
   109  		},
   110  		DefaultSpockSecretHasher,
   111  	)
   112  
   113  	state2 := newSpockState(
   114  		snapshot.MapStorageSnapshot{
   115  			badRegisterId: value2,
   116  		},
   117  		DefaultSpockSecretHasher,
   118  	)
   119  
   120  	value, err := state1.Get(badRegisterId)
   121  	require.NoError(t, err)
   122  	require.Equal(t, value1, value)
   123  
   124  	value, err = state2.Get(badRegisterId)
   125  	require.NoError(t, err)
   126  	require.Equal(t, value2, value)
   127  
   128  	// state1 and state2 will have identical spock hash even through they read
   129  	// different values from the underlying storage.  Merkle trie proof will
   130  	// ensure the underlying storage is correct / identical.
   131  	require.Equal(
   132  		t,
   133  		state1.Finalize().SpockSecret,
   134  		state2.Finalize().SpockSecret)
   135  }
   136  
   137  func TestSpockStateGetVsSetNil(t *testing.T) {
   138  	registerId := flow.NewRegisterID(fooOwner, "bar")
   139  
   140  	_ = testSpock(
   141  		t,
   142  		[]spockTestOp{
   143  			func(t *testing.T, state *spockState) {
   144  				err := state.Set(registerId, []byte{})
   145  				require.NoError(t, err)
   146  			},
   147  			func(t *testing.T, state *spockState) {
   148  				_, err := state.Get(registerId)
   149  				require.NoError(t, err)
   150  			},
   151  		})
   152  }
   153  
   154  func TestSpockStateSet(t *testing.T) {
   155  	otherOwner := unittest.RandomAddressFixture()
   156  	registerId := flow.NewRegisterID(fooOwner, "bar")
   157  	value := flow.RegisterValue([]byte("value"))
   158  
   159  	states := testSpock(
   160  		t,
   161  		[]spockTestOp{
   162  			// control experiment
   163  			nil,
   164  			// primary experiment
   165  			func(t *testing.T, state *spockState) {
   166  				err := state.Set(registerId, value)
   167  				require.NoError(t, err)
   168  			},
   169  			// duplicate calls return in different spock
   170  			func(t *testing.T, state *spockState) {
   171  				err := state.Set(registerId, value)
   172  				require.NoError(t, err)
   173  				err = state.Set(registerId, value)
   174  				require.NoError(t, err)
   175  			},
   176  			// Setting different register id will result in different spock
   177  			func(t *testing.T, state *spockState) {
   178  				err := state.Set(flow.NewRegisterID(fooOwner, "baR"), value)
   179  				require.NoError(t, err)
   180  			},
   181  			func(t *testing.T, state *spockState) {
   182  				err := state.Set(flow.NewRegisterID(otherOwner, "bar"), value)
   183  				require.NoError(t, err)
   184  			},
   185  			// Setting different register value will result in different spock
   186  			func(t *testing.T, state *spockState) {
   187  				err := state.Set(registerId, []byte("valuE"))
   188  				require.NoError(t, err)
   189  			},
   190  		})
   191  
   192  	// Sanity check underlying storage state is called.
   193  	require.Equal(
   194  		t,
   195  		map[flow.RegisterID]flow.RegisterValue{
   196  			registerId: value,
   197  		},
   198  		states[1].Finalize().WriteSet)
   199  
   200  	// Sanity check finalized state is no longer accessible.
   201  	err := states[1].Set(registerId, []byte(""))
   202  	require.ErrorContains(t, err, "cannot Set on a finalized state")
   203  }
   204  
   205  func TestSpockStateSetValueInjection(t *testing.T) {
   206  	registerId1 := flow.NewRegisterID(fooOwner, "injection")
   207  	registerId2 := flow.NewRegisterID(fooOwner, "inject")
   208  
   209  	_ = testSpock(
   210  		t,
   211  		[]spockTestOp{
   212  			func(t *testing.T, state *spockState) {
   213  				err := state.Set(registerId1, []byte{})
   214  				require.NoError(t, err)
   215  			},
   216  			func(t *testing.T, state *spockState) {
   217  				err := state.Set(registerId2, []byte("ion"))
   218  				require.NoError(t, err)
   219  			},
   220  		})
   221  }
   222  
   223  func TestSpockStateMerge(t *testing.T) {
   224  	readSet := map[flow.RegisterID]struct{}{
   225  		flow.NewRegisterID(fooOwner, "bar"): struct{}{},
   226  	}
   227  
   228  	states := testSpock(
   229  		t,
   230  		[]spockTestOp{
   231  			// control experiment
   232  			nil,
   233  			// primary experiment
   234  			func(t *testing.T, state *spockState) {
   235  				err := state.Merge(
   236  					&snapshot.ExecutionSnapshot{
   237  						ReadSet:     readSet,
   238  						SpockSecret: []byte("secret"),
   239  					})
   240  				require.NoError(t, err)
   241  			},
   242  			// duplicate calls result in different spock
   243  			func(t *testing.T, state *spockState) {
   244  				err := state.Merge(
   245  					&snapshot.ExecutionSnapshot{
   246  						ReadSet:     readSet,
   247  						SpockSecret: []byte("secret"),
   248  					})
   249  				require.NoError(t, err)
   250  				err = state.Merge(
   251  					&snapshot.ExecutionSnapshot{
   252  						ReadSet:     readSet,
   253  						SpockSecret: []byte("secret"),
   254  					})
   255  				require.NoError(t, err)
   256  			},
   257  			// Merging execution snapshot with different spock will result in
   258  			// different spock
   259  			func(t *testing.T, state *spockState) {
   260  				err := state.Merge(
   261  					&snapshot.ExecutionSnapshot{
   262  						ReadSet:     readSet,
   263  						SpockSecret: []byte("secreT"),
   264  					})
   265  				require.NoError(t, err)
   266  			},
   267  		})
   268  
   269  	// Sanity check underlying storage state is called.
   270  	require.Equal(t, readSet, states[1].Finalize().ReadSet)
   271  
   272  	// Sanity check finalized state is no longer accessible.
   273  	err := states[1].Merge(&snapshot.ExecutionSnapshot{})
   274  	require.ErrorContains(t, err, "cannot Merge on a finalized state")
   275  }
   276  func TestSpockStateDropChanges(t *testing.T) {
   277  	registerId := flow.NewRegisterID(fooOwner, "read")
   278  
   279  	setup := func(t *testing.T, state *spockState) {
   280  		_, err := state.Get(registerId)
   281  		require.NoError(t, err)
   282  
   283  		err = state.Set(flow.NewRegisterID(fooOwner, "write"), []byte("blah"))
   284  		require.NoError(t, err)
   285  	}
   286  
   287  	states := testSpock(
   288  		t,
   289  		[]spockTestOp{
   290  			// control experiment
   291  			setup,
   292  			// primary experiment
   293  			func(t *testing.T, state *spockState) {
   294  				setup(t, state)
   295  				err := state.DropChanges()
   296  				require.NoError(t, err)
   297  			},
   298  			// duplicate calls result in different spock
   299  			func(t *testing.T, state *spockState) {
   300  				setup(t, state)
   301  				err := state.DropChanges()
   302  				require.NoError(t, err)
   303  				err = state.DropChanges()
   304  				require.NoError(t, err)
   305  			},
   306  		})
   307  
   308  	// Sanity check underlying storage state is called.
   309  	snapshot := states[1].Finalize()
   310  	require.Equal(
   311  		t,
   312  		map[flow.RegisterID]struct{}{
   313  			registerId: struct{}{},
   314  		},
   315  		snapshot.ReadSet)
   316  	require.Empty(t, snapshot.WriteSet)
   317  
   318  	// Sanity check finalized state is no longer accessible.
   319  	err := states[1].DropChanges()
   320  	require.ErrorContains(t, err, "cannot DropChanges on a finalized state")
   321  }
   322  
   323  func TestSpockStateRandomOps(t *testing.T) {
   324  	chain := []spockTestOp{
   325  		nil, // control experiment
   326  	}
   327  
   328  	for i := 0; i < 500; i++ {
   329  		roll, err := rand.Uintn(4)
   330  		require.NoError(t, err)
   331  
   332  		switch roll {
   333  		case uint(0):
   334  			id, err := rand.Uint()
   335  			require.NoError(t, err)
   336  
   337  			chain = append(
   338  				chain,
   339  				chainSpockTestOps(
   340  					chain[len(chain)-1],
   341  					func(t *testing.T, state *spockState) {
   342  						_, err := state.Get(
   343  							flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id)))
   344  						require.NoError(t, err)
   345  					}))
   346  		case uint(1):
   347  			id, err := rand.Uint()
   348  			require.NoError(t, err)
   349  
   350  			value, err := rand.Uint()
   351  			require.NoError(t, err)
   352  
   353  			chain = append(
   354  				chain,
   355  				chainSpockTestOps(
   356  					chain[len(chain)-1],
   357  					func(t *testing.T, state *spockState) {
   358  						err := state.Set(
   359  							flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id)),
   360  							[]byte(fmt.Sprintf("%d", value)))
   361  						require.NoError(t, err)
   362  					}))
   363  		case uint(2):
   364  			spock, err := rand.Uint()
   365  			require.NoError(t, err)
   366  
   367  			chain = append(
   368  				chain,
   369  				chainSpockTestOps(
   370  					chain[len(chain)-1],
   371  					func(t *testing.T, state *spockState) {
   372  						err := state.Merge(
   373  							&snapshot.ExecutionSnapshot{
   374  								SpockSecret: []byte(fmt.Sprintf("%d", spock)),
   375  							})
   376  						require.NoError(t, err)
   377  					}))
   378  		case uint(3):
   379  			chain = append(
   380  				chain,
   381  				chainSpockTestOps(
   382  					chain[len(chain)-1],
   383  					func(t *testing.T, state *spockState) {
   384  						err := state.DropChanges()
   385  						require.NoError(t, err)
   386  					}))
   387  		default:
   388  			panic("Unexpected")
   389  		}
   390  	}
   391  
   392  	_ = testSpock(t, chain)
   393  }
   394  func TestSpockStateNewChild(t *testing.T) {
   395  	baseRegisterId := flow.NewRegisterID(flow.EmptyAddress, "base")
   396  	baseValue := flow.RegisterValue([]byte("base"))
   397  
   398  	parentOwner := unittest.RandomAddressFixture()
   399  	childOwner := unittest.RandomAddressFixture()
   400  
   401  	parentRegisterId1 := flow.NewRegisterID(parentOwner, "1")
   402  	parentValue := flow.RegisterValue([]byte("parent"))
   403  
   404  	parentRegisterId2 := flow.NewRegisterID(parentOwner, "2")
   405  
   406  	childRegisterId1 := flow.NewRegisterID(childOwner, "1")
   407  	childValue := flow.RegisterValue([]byte("child"))
   408  
   409  	childRegisterId2 := flow.NewRegisterID(childOwner, "2")
   410  
   411  	parent := newSpockState(
   412  		snapshot.MapStorageSnapshot{
   413  			baseRegisterId: baseValue,
   414  		},
   415  		DefaultSpockSecretHasher,
   416  	)
   417  
   418  	err := parent.Set(parentRegisterId1, parentValue)
   419  	require.NoError(t, err)
   420  
   421  	value, err := parent.Get(parentRegisterId2)
   422  	require.NoError(t, err)
   423  	require.Nil(t, value)
   424  
   425  	child := parent.NewChild()
   426  
   427  	value, err = child.Get(baseRegisterId)
   428  	require.NoError(t, err)
   429  	require.Equal(t, value, baseValue)
   430  
   431  	value, err = child.Get(parentRegisterId1)
   432  	require.NoError(t, err)
   433  	require.Equal(t, value, parentValue)
   434  
   435  	value, err = child.Get(childRegisterId2)
   436  	require.NoError(t, err)
   437  	require.Nil(t, value)
   438  
   439  	err = child.Set(childRegisterId1, childValue)
   440  	require.NoError(t, err)
   441  
   442  	childSnapshot := child.Finalize()
   443  	require.Equal(
   444  		t,
   445  		childSnapshot.ReadSet,
   446  		map[flow.RegisterID]struct{}{
   447  			baseRegisterId:    struct{}{},
   448  			parentRegisterId1: struct{}{},
   449  			childRegisterId2:  struct{}{},
   450  		})
   451  
   452  	require.Equal(
   453  		t,
   454  		childSnapshot.WriteSet,
   455  		map[flow.RegisterID]flow.RegisterValue{
   456  			childRegisterId1: childValue,
   457  		})
   458  
   459  	// Finalize parent without merging child to see if they are independent.
   460  	parentSnapshot := parent.Finalize()
   461  	require.Equal(
   462  		t,
   463  		parentSnapshot.ReadSet,
   464  		map[flow.RegisterID]struct{}{
   465  			parentRegisterId2: struct{}{},
   466  		})
   467  
   468  	require.Equal(
   469  		t,
   470  		parentSnapshot.WriteSet,
   471  		map[flow.RegisterID]flow.RegisterValue{
   472  			parentRegisterId1: parentValue,
   473  		})
   474  }