github.com/onflow/flow-go@v0.33.17/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{})
    35  		run2 := newSpockState(snapshot.MapStorageSnapshot{})
    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  
   111  	state2 := newSpockState(
   112  		snapshot.MapStorageSnapshot{
   113  			badRegisterId: value2,
   114  		})
   115  
   116  	value, err := state1.Get(badRegisterId)
   117  	require.NoError(t, err)
   118  	require.Equal(t, value1, value)
   119  
   120  	value, err = state2.Get(badRegisterId)
   121  	require.NoError(t, err)
   122  	require.Equal(t, value2, value)
   123  
   124  	// state1 and state2 will have identical spock hash even through they read
   125  	// different values from the underlying storage.  Merkle trie proof will
   126  	// ensure the underlying storage is correct / identical.
   127  	require.Equal(
   128  		t,
   129  		state1.Finalize().SpockSecret,
   130  		state2.Finalize().SpockSecret)
   131  }
   132  
   133  func TestSpockStateGetVsSetNil(t *testing.T) {
   134  	registerId := flow.NewRegisterID(fooOwner, "bar")
   135  
   136  	_ = testSpock(
   137  		t,
   138  		[]spockTestOp{
   139  			func(t *testing.T, state *spockState) {
   140  				err := state.Set(registerId, []byte{})
   141  				require.NoError(t, err)
   142  			},
   143  			func(t *testing.T, state *spockState) {
   144  				_, err := state.Get(registerId)
   145  				require.NoError(t, err)
   146  			},
   147  		})
   148  }
   149  
   150  func TestSpockStateSet(t *testing.T) {
   151  	otherOwner := unittest.RandomAddressFixture()
   152  	registerId := flow.NewRegisterID(fooOwner, "bar")
   153  	value := flow.RegisterValue([]byte("value"))
   154  
   155  	states := testSpock(
   156  		t,
   157  		[]spockTestOp{
   158  			// control experiment
   159  			nil,
   160  			// primary experiment
   161  			func(t *testing.T, state *spockState) {
   162  				err := state.Set(registerId, value)
   163  				require.NoError(t, err)
   164  			},
   165  			// duplicate calls return in different spock
   166  			func(t *testing.T, state *spockState) {
   167  				err := state.Set(registerId, value)
   168  				require.NoError(t, err)
   169  				err = state.Set(registerId, value)
   170  				require.NoError(t, err)
   171  			},
   172  			// Setting different register id will result in different spock
   173  			func(t *testing.T, state *spockState) {
   174  				err := state.Set(flow.NewRegisterID(fooOwner, "baR"), value)
   175  				require.NoError(t, err)
   176  			},
   177  			func(t *testing.T, state *spockState) {
   178  				err := state.Set(flow.NewRegisterID(otherOwner, "bar"), value)
   179  				require.NoError(t, err)
   180  			},
   181  			// Setting different register value will result in different spock
   182  			func(t *testing.T, state *spockState) {
   183  				err := state.Set(registerId, []byte("valuE"))
   184  				require.NoError(t, err)
   185  			},
   186  		})
   187  
   188  	// Sanity check underlying storage state is called.
   189  	require.Equal(
   190  		t,
   191  		map[flow.RegisterID]flow.RegisterValue{
   192  			registerId: value,
   193  		},
   194  		states[1].Finalize().WriteSet)
   195  
   196  	// Sanity check finalized state is no longer accessible.
   197  	err := states[1].Set(registerId, []byte(""))
   198  	require.ErrorContains(t, err, "cannot Set on a finalized state")
   199  }
   200  
   201  func TestSpockStateSetValueInjection(t *testing.T) {
   202  	registerId1 := flow.NewRegisterID(fooOwner, "injection")
   203  	registerId2 := flow.NewRegisterID(fooOwner, "inject")
   204  
   205  	_ = testSpock(
   206  		t,
   207  		[]spockTestOp{
   208  			func(t *testing.T, state *spockState) {
   209  				err := state.Set(registerId1, []byte{})
   210  				require.NoError(t, err)
   211  			},
   212  			func(t *testing.T, state *spockState) {
   213  				err := state.Set(registerId2, []byte("ion"))
   214  				require.NoError(t, err)
   215  			},
   216  		})
   217  }
   218  
   219  func TestSpockStateMerge(t *testing.T) {
   220  	readSet := map[flow.RegisterID]struct{}{
   221  		flow.NewRegisterID(fooOwner, "bar"): struct{}{},
   222  	}
   223  
   224  	states := testSpock(
   225  		t,
   226  		[]spockTestOp{
   227  			// control experiment
   228  			nil,
   229  			// primary experiment
   230  			func(t *testing.T, state *spockState) {
   231  				err := state.Merge(
   232  					&snapshot.ExecutionSnapshot{
   233  						ReadSet:     readSet,
   234  						SpockSecret: []byte("secret"),
   235  					})
   236  				require.NoError(t, err)
   237  			},
   238  			// duplicate calls result in different spock
   239  			func(t *testing.T, state *spockState) {
   240  				err := state.Merge(
   241  					&snapshot.ExecutionSnapshot{
   242  						ReadSet:     readSet,
   243  						SpockSecret: []byte("secret"),
   244  					})
   245  				require.NoError(t, err)
   246  				err = state.Merge(
   247  					&snapshot.ExecutionSnapshot{
   248  						ReadSet:     readSet,
   249  						SpockSecret: []byte("secret"),
   250  					})
   251  				require.NoError(t, err)
   252  			},
   253  			// Merging execution snapshot with different spock will result in
   254  			// different spock
   255  			func(t *testing.T, state *spockState) {
   256  				err := state.Merge(
   257  					&snapshot.ExecutionSnapshot{
   258  						ReadSet:     readSet,
   259  						SpockSecret: []byte("secreT"),
   260  					})
   261  				require.NoError(t, err)
   262  			},
   263  		})
   264  
   265  	// Sanity check underlying storage state is called.
   266  	require.Equal(t, readSet, states[1].Finalize().ReadSet)
   267  
   268  	// Sanity check finalized state is no longer accessible.
   269  	err := states[1].Merge(&snapshot.ExecutionSnapshot{})
   270  	require.ErrorContains(t, err, "cannot Merge on a finalized state")
   271  }
   272  func TestSpockStateDropChanges(t *testing.T) {
   273  	registerId := flow.NewRegisterID(fooOwner, "read")
   274  
   275  	setup := func(t *testing.T, state *spockState) {
   276  		_, err := state.Get(registerId)
   277  		require.NoError(t, err)
   278  
   279  		err = state.Set(flow.NewRegisterID(fooOwner, "write"), []byte("blah"))
   280  		require.NoError(t, err)
   281  	}
   282  
   283  	states := testSpock(
   284  		t,
   285  		[]spockTestOp{
   286  			// control experiment
   287  			setup,
   288  			// primary experiment
   289  			func(t *testing.T, state *spockState) {
   290  				setup(t, state)
   291  				err := state.DropChanges()
   292  				require.NoError(t, err)
   293  			},
   294  			// duplicate calls result in different spock
   295  			func(t *testing.T, state *spockState) {
   296  				setup(t, state)
   297  				err := state.DropChanges()
   298  				require.NoError(t, err)
   299  				err = state.DropChanges()
   300  				require.NoError(t, err)
   301  			},
   302  		})
   303  
   304  	// Sanity check underlying storage state is called.
   305  	snapshot := states[1].Finalize()
   306  	require.Equal(
   307  		t,
   308  		map[flow.RegisterID]struct{}{
   309  			registerId: struct{}{},
   310  		},
   311  		snapshot.ReadSet)
   312  	require.Empty(t, snapshot.WriteSet)
   313  
   314  	// Sanity check finalized state is no longer accessible.
   315  	err := states[1].DropChanges()
   316  	require.ErrorContains(t, err, "cannot DropChanges on a finalized state")
   317  }
   318  
   319  func TestSpockStateRandomOps(t *testing.T) {
   320  	chain := []spockTestOp{
   321  		nil, // control experiment
   322  	}
   323  
   324  	for i := 0; i < 500; i++ {
   325  		roll, err := rand.Uintn(4)
   326  		require.NoError(t, err)
   327  
   328  		switch roll {
   329  		case uint(0):
   330  			id, err := rand.Uint()
   331  			require.NoError(t, err)
   332  
   333  			chain = append(
   334  				chain,
   335  				chainSpockTestOps(
   336  					chain[len(chain)-1],
   337  					func(t *testing.T, state *spockState) {
   338  						_, err := state.Get(
   339  							flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id)))
   340  						require.NoError(t, err)
   341  					}))
   342  		case uint(1):
   343  			id, err := rand.Uint()
   344  			require.NoError(t, err)
   345  
   346  			value, err := rand.Uint()
   347  			require.NoError(t, err)
   348  
   349  			chain = append(
   350  				chain,
   351  				chainSpockTestOps(
   352  					chain[len(chain)-1],
   353  					func(t *testing.T, state *spockState) {
   354  						err := state.Set(
   355  							flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id)),
   356  							[]byte(fmt.Sprintf("%d", value)))
   357  						require.NoError(t, err)
   358  					}))
   359  		case uint(2):
   360  			spock, err := rand.Uint()
   361  			require.NoError(t, err)
   362  
   363  			chain = append(
   364  				chain,
   365  				chainSpockTestOps(
   366  					chain[len(chain)-1],
   367  					func(t *testing.T, state *spockState) {
   368  						err := state.Merge(
   369  							&snapshot.ExecutionSnapshot{
   370  								SpockSecret: []byte(fmt.Sprintf("%d", spock)),
   371  							})
   372  						require.NoError(t, err)
   373  					}))
   374  		case uint(3):
   375  			chain = append(
   376  				chain,
   377  				chainSpockTestOps(
   378  					chain[len(chain)-1],
   379  					func(t *testing.T, state *spockState) {
   380  						err := state.DropChanges()
   381  						require.NoError(t, err)
   382  					}))
   383  		default:
   384  			panic("Unexpected")
   385  		}
   386  	}
   387  
   388  	_ = testSpock(t, chain)
   389  }
   390  func TestSpockStateNewChild(t *testing.T) {
   391  	baseRegisterId := flow.NewRegisterID(flow.EmptyAddress, "base")
   392  	baseValue := flow.RegisterValue([]byte("base"))
   393  
   394  	parentOwner := unittest.RandomAddressFixture()
   395  	childOwner := unittest.RandomAddressFixture()
   396  
   397  	parentRegisterId1 := flow.NewRegisterID(parentOwner, "1")
   398  	parentValue := flow.RegisterValue([]byte("parent"))
   399  
   400  	parentRegisterId2 := flow.NewRegisterID(parentOwner, "2")
   401  
   402  	childRegisterId1 := flow.NewRegisterID(childOwner, "1")
   403  	childValue := flow.RegisterValue([]byte("child"))
   404  
   405  	childRegisterId2 := flow.NewRegisterID(childOwner, "2")
   406  
   407  	parent := newSpockState(
   408  		snapshot.MapStorageSnapshot{
   409  			baseRegisterId: baseValue,
   410  		})
   411  
   412  	err := parent.Set(parentRegisterId1, parentValue)
   413  	require.NoError(t, err)
   414  
   415  	value, err := parent.Get(parentRegisterId2)
   416  	require.NoError(t, err)
   417  	require.Nil(t, value)
   418  
   419  	child := parent.NewChild()
   420  
   421  	value, err = child.Get(baseRegisterId)
   422  	require.NoError(t, err)
   423  	require.Equal(t, value, baseValue)
   424  
   425  	value, err = child.Get(parentRegisterId1)
   426  	require.NoError(t, err)
   427  	require.Equal(t, value, parentValue)
   428  
   429  	value, err = child.Get(childRegisterId2)
   430  	require.NoError(t, err)
   431  	require.Nil(t, value)
   432  
   433  	err = child.Set(childRegisterId1, childValue)
   434  	require.NoError(t, err)
   435  
   436  	childSnapshot := child.Finalize()
   437  	require.Equal(
   438  		t,
   439  		childSnapshot.ReadSet,
   440  		map[flow.RegisterID]struct{}{
   441  			baseRegisterId:    struct{}{},
   442  			parentRegisterId1: struct{}{},
   443  			childRegisterId2:  struct{}{},
   444  		})
   445  
   446  	require.Equal(
   447  		t,
   448  		childSnapshot.WriteSet,
   449  		map[flow.RegisterID]flow.RegisterValue{
   450  			childRegisterId1: childValue,
   451  		})
   452  
   453  	// Finalize parent without merging child to see if they are independent.
   454  	parentSnapshot := parent.Finalize()
   455  	require.Equal(
   456  		t,
   457  		parentSnapshot.ReadSet,
   458  		map[flow.RegisterID]struct{}{
   459  			parentRegisterId2: struct{}{},
   460  		})
   461  
   462  	require.Equal(
   463  		t,
   464  		parentSnapshot.WriteSet,
   465  		map[flow.RegisterID]flow.RegisterValue{
   466  			parentRegisterId1: parentValue,
   467  		})
   468  }