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

     1  package environment_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/onflow/cadence/runtime/common"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/onflow/flow-go/fvm"
    10  	"github.com/onflow/flow-go/fvm/environment"
    11  	"github.com/onflow/flow-go/fvm/meter"
    12  	"github.com/onflow/flow-go/fvm/storage"
    13  	"github.com/onflow/flow-go/fvm/storage/derived"
    14  	"github.com/onflow/flow-go/fvm/storage/snapshot"
    15  	"github.com/onflow/flow-go/fvm/storage/state"
    16  	"github.com/onflow/flow-go/model/flow"
    17  	"github.com/onflow/flow-go/utils/unittest"
    18  )
    19  
    20  func TestDerivedDataProgramInvalidator(t *testing.T) {
    21  
    22  	// create the following dependency graph
    23  	// ```mermaid
    24  	// graph TD
    25  	// 	C-->D
    26  	// 	C-->B
    27  	// 	B-->A
    28  	// ```
    29  
    30  	addressA := flow.HexToAddress("0xa")
    31  	cAddressA := common.MustBytesToAddress(addressA.Bytes())
    32  	programALoc := common.AddressLocation{Address: cAddressA, Name: "A"}
    33  	programA2Loc := common.AddressLocation{Address: cAddressA, Name: "A2"}
    34  	programA := &derived.Program{
    35  		Program: nil,
    36  		Dependencies: derived.NewProgramDependencies().
    37  			Add(programALoc),
    38  	}
    39  
    40  	addressB := flow.HexToAddress("0xb")
    41  	cAddressB := common.MustBytesToAddress(addressB.Bytes())
    42  	programBLoc := common.AddressLocation{Address: cAddressB, Name: "B"}
    43  	programBDep := derived.NewProgramDependencies()
    44  	programBDep.Add(programALoc)
    45  	programBDep.Add(programBLoc)
    46  	programB := &derived.Program{
    47  		Program: nil,
    48  		Dependencies: derived.NewProgramDependencies().
    49  			Add(programALoc).
    50  			Add(programBLoc),
    51  	}
    52  
    53  	addressD := flow.HexToAddress("0xd")
    54  	cAddressD := common.MustBytesToAddress(addressD.Bytes())
    55  	programDLoc := common.AddressLocation{Address: cAddressD, Name: "D"}
    56  	programD := &derived.Program{
    57  		Program: nil,
    58  		Dependencies: derived.NewProgramDependencies().
    59  			Add(programDLoc),
    60  	}
    61  
    62  	addressC := flow.HexToAddress("0xc")
    63  	cAddressC := common.MustBytesToAddress(addressC.Bytes())
    64  	programCLoc := common.AddressLocation{Address: cAddressC, Name: "C"}
    65  	programC := &derived.Program{
    66  		Program: nil,
    67  		Dependencies: derived.NewProgramDependencies().
    68  			Add(programALoc).
    69  			Add(programBLoc).
    70  			Add(programCLoc).
    71  			Add(programDLoc),
    72  	}
    73  
    74  	t.Run("empty invalidator does not invalidate entries", func(t *testing.T) {
    75  		invalidator := environment.DerivedDataInvalidator{}.ProgramInvalidator()
    76  
    77  		require.False(t, invalidator.ShouldInvalidateEntries())
    78  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
    79  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
    80  		require.False(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
    81  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
    82  	})
    83  	t.Run("meter parameters invalidator invalidates all entries", func(t *testing.T) {
    84  		invalidator := environment.DerivedDataInvalidator{
    85  			MeterParamOverridesUpdated: true,
    86  		}.ProgramInvalidator()
    87  
    88  		require.True(t, invalidator.ShouldInvalidateEntries())
    89  		require.True(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
    90  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
    91  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
    92  		require.True(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
    93  	})
    94  
    95  	t.Run("contract A update invalidation", func(t *testing.T) {
    96  		invalidator := environment.DerivedDataInvalidator{
    97  			ContractUpdates: environment.ContractUpdates{
    98  				Updates: []common.AddressLocation{
    99  					programALoc,
   100  				},
   101  			},
   102  		}.ProgramInvalidator()
   103  
   104  		require.True(t, invalidator.ShouldInvalidateEntries())
   105  		require.True(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   106  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   107  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   108  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   109  	})
   110  
   111  	t.Run("contract D update invalidate", func(t *testing.T) {
   112  		invalidator := environment.DerivedDataInvalidator{
   113  			ContractUpdates: environment.ContractUpdates{
   114  				Updates: []common.AddressLocation{
   115  					programDLoc,
   116  				},
   117  			},
   118  		}.ProgramInvalidator()
   119  
   120  		require.True(t, invalidator.ShouldInvalidateEntries())
   121  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   122  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   123  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   124  		require.True(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   125  	})
   126  
   127  	t.Run("contract B update invalidate", func(t *testing.T) {
   128  		invalidator := environment.DerivedDataInvalidator{
   129  			ContractUpdates: environment.ContractUpdates{
   130  				Updates: []common.AddressLocation{
   131  					programBLoc,
   132  				},
   133  			},
   134  		}.ProgramInvalidator()
   135  
   136  		require.True(t, invalidator.ShouldInvalidateEntries())
   137  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   138  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   139  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   140  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   141  	})
   142  
   143  	t.Run("contract invalidator C invalidates C", func(t *testing.T) {
   144  		invalidator := environment.DerivedDataInvalidator{
   145  			ContractUpdates: environment.ContractUpdates{
   146  				Updates: []common.AddressLocation{
   147  					programCLoc,
   148  				},
   149  			},
   150  		}.ProgramInvalidator()
   151  
   152  		require.True(t, invalidator.ShouldInvalidateEntries())
   153  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   154  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   155  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   156  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   157  	})
   158  
   159  	t.Run("contract invalidator D invalidates C, D", func(t *testing.T) {
   160  		invalidator := environment.DerivedDataInvalidator{
   161  			ContractUpdates: environment.ContractUpdates{
   162  				Updates: []common.AddressLocation{
   163  					programDLoc,
   164  				},
   165  			},
   166  		}.ProgramInvalidator()
   167  
   168  		require.True(t, invalidator.ShouldInvalidateEntries())
   169  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   170  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   171  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   172  		require.True(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   173  	})
   174  
   175  	t.Run("new contract deploy on address A", func(t *testing.T) {
   176  		invalidator := environment.DerivedDataInvalidator{
   177  			ContractUpdates: environment.ContractUpdates{
   178  				Deploys: []common.AddressLocation{
   179  					programA2Loc,
   180  				},
   181  			},
   182  		}.ProgramInvalidator()
   183  
   184  		require.True(t, invalidator.ShouldInvalidateEntries())
   185  		require.True(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   186  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   187  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   188  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   189  	})
   190  
   191  	t.Run("contract delete on address A", func(t *testing.T) {
   192  		invalidator := environment.DerivedDataInvalidator{
   193  			ContractUpdates: environment.ContractUpdates{
   194  				Deletions: []common.AddressLocation{
   195  					programA2Loc,
   196  				},
   197  			},
   198  		}.ProgramInvalidator()
   199  
   200  		require.True(t, invalidator.ShouldInvalidateEntries())
   201  		require.True(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   202  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   203  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   204  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   205  	})
   206  }
   207  
   208  func TestMeterParamOverridesInvalidator(t *testing.T) {
   209  	invalidator := environment.DerivedDataInvalidator{}.
   210  		MeterParamOverridesInvalidator()
   211  
   212  	require.False(t, invalidator.ShouldInvalidateEntries())
   213  	require.False(t, invalidator.ShouldInvalidateEntry(
   214  		struct{}{},
   215  		derived.MeterParamOverrides{},
   216  		nil))
   217  
   218  	invalidator = environment.DerivedDataInvalidator{
   219  		ContractUpdates:            environment.ContractUpdates{},
   220  		MeterParamOverridesUpdated: true,
   221  	}.MeterParamOverridesInvalidator()
   222  
   223  	require.True(t, invalidator.ShouldInvalidateEntries())
   224  	require.True(t, invalidator.ShouldInvalidateEntry(
   225  		struct{}{},
   226  		derived.MeterParamOverrides{},
   227  		nil))
   228  }
   229  
   230  func TestMeterParamOverridesUpdated(t *testing.T) {
   231  	memoryLimit := uint64(666)
   232  
   233  	compKind := common.ComputationKind(12345)
   234  	compWeight := uint64(10)
   235  	computationWeights := meter.ExecutionEffortWeights{
   236  		compKind: compWeight,
   237  	}
   238  
   239  	memKind := common.MemoryKind(23456)
   240  	memWeight := uint64(20000)
   241  	memoryWeights := meter.ExecutionMemoryWeights{
   242  		memKind: memWeight,
   243  	}
   244  
   245  	snapshotTree := snapshot.NewSnapshotTree(nil)
   246  
   247  	ctx := fvm.NewContext(fvm.WithChain(flow.Testnet.Chain()))
   248  
   249  	vm := fvm.NewVirtualMachine()
   250  	executionSnapshot, _, err := vm.Run(
   251  		ctx,
   252  		fvm.Bootstrap(
   253  			unittest.ServiceAccountPublicKey,
   254  			fvm.WithExecutionMemoryLimit(memoryLimit),
   255  			fvm.WithExecutionEffortWeights(computationWeights),
   256  			fvm.WithExecutionMemoryWeights(memoryWeights)),
   257  		snapshotTree)
   258  	require.NoError(t, err)
   259  
   260  	blockDatabase := storage.NewBlockDatabase(
   261  		snapshotTree.Append(executionSnapshot),
   262  		0,
   263  		nil)
   264  
   265  	txnState, err := blockDatabase.NewTransaction(0, state.DefaultParameters())
   266  	require.NoError(t, err)
   267  
   268  	computer := fvm.NewMeterParamOverridesComputer(ctx, txnState)
   269  
   270  	overrides, err := computer.Compute(txnState, struct{}{})
   271  	require.NoError(t, err)
   272  
   273  	// Sanity check.  Note that bootstrap creates additional computation /
   274  	// memory weight entries.  We'll only check the entries we added.
   275  	require.NotNil(t, overrides.MemoryLimit)
   276  	require.Equal(t, memoryLimit, *overrides.MemoryLimit)
   277  	require.Equal(t, compWeight, overrides.ComputationWeights[compKind])
   278  	require.Equal(t, memWeight, overrides.MemoryWeights[memKind])
   279  
   280  	//
   281  	// Actual test
   282  	//
   283  
   284  	ctx.TxBody = &flow.TransactionBody{}
   285  
   286  	checkForUpdates := func(id flow.RegisterID, expected bool) {
   287  		snapshot := &snapshot.ExecutionSnapshot{
   288  			WriteSet: map[flow.RegisterID]flow.RegisterValue{
   289  				id: flow.RegisterValue("blah"),
   290  			},
   291  		}
   292  
   293  		invalidator := environment.NewDerivedDataInvalidator(
   294  			environment.ContractUpdates{},
   295  			ctx.Chain.ServiceAddress(),
   296  			snapshot)
   297  		require.Equal(t, expected, invalidator.MeterParamOverridesUpdated)
   298  	}
   299  
   300  	executionSnapshot, err = txnState.FinalizeMainTransaction()
   301  	require.NoError(t, err)
   302  
   303  	owner := ctx.Chain.ServiceAddress()
   304  	otherOwner := unittest.RandomAddressFixtureForChain(ctx.Chain.ChainID())
   305  
   306  	for _, registerId := range executionSnapshot.AllRegisterIDs() {
   307  		checkForUpdates(registerId, true)
   308  		checkForUpdates(
   309  			flow.NewRegisterID(otherOwner, registerId.Key),
   310  			false)
   311  	}
   312  
   313  	stabIndexKey := flow.NewRegisterID(owner, "$12345678")
   314  	require.True(t, stabIndexKey.IsSlabIndex())
   315  
   316  	checkForUpdates(stabIndexKey, true)
   317  	checkForUpdates(flow.NewRegisterID(owner, "other keys"), false)
   318  	checkForUpdates(flow.NewRegisterID(otherOwner, stabIndexKey.Key), false)
   319  	checkForUpdates(flow.NewRegisterID(otherOwner, "other key"), false)
   320  }