github.com/koko1123/flow-go-1@v0.29.6/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/koko1123/flow-go-1/fvm"
    10  	"github.com/koko1123/flow-go-1/fvm/derived"
    11  	"github.com/koko1123/flow-go-1/fvm/environment"
    12  	"github.com/koko1123/flow-go-1/fvm/meter"
    13  	"github.com/koko1123/flow-go-1/fvm/state"
    14  	"github.com/koko1123/flow-go-1/fvm/tracing"
    15  	"github.com/koko1123/flow-go-1/fvm/utils"
    16  	"github.com/koko1123/flow-go-1/model/flow"
    17  	"github.com/koko1123/flow-go-1/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  	programA := &derived.Program{
    34  		Program: nil,
    35  		Dependencies: map[common.Address]struct{}{
    36  			cAddressA: {},
    37  		},
    38  	}
    39  
    40  	addressB := flow.HexToAddress("0xb")
    41  	cAddressB := common.MustBytesToAddress(addressB.Bytes())
    42  	programBLoc := common.AddressLocation{Address: cAddressB, Name: "B"}
    43  	programB := &derived.Program{
    44  		Program: nil,
    45  		Dependencies: map[common.Address]struct{}{
    46  			cAddressA: {},
    47  			cAddressB: {},
    48  		},
    49  	}
    50  
    51  	addressD := flow.HexToAddress("0xd")
    52  	cAddressD := common.MustBytesToAddress(addressD.Bytes())
    53  	programDLoc := common.AddressLocation{Address: cAddressD, Name: "D"}
    54  	programD := &derived.Program{
    55  		Program: nil,
    56  		Dependencies: map[common.Address]struct{}{
    57  			cAddressD: {},
    58  		},
    59  	}
    60  
    61  	addressC := flow.HexToAddress("0xc")
    62  	cAddressC := common.MustBytesToAddress(addressC.Bytes())
    63  	programCLoc := common.AddressLocation{Address: cAddressC, Name: "C"}
    64  	programC := &derived.Program{
    65  		Program: nil,
    66  		Dependencies: map[common.Address]struct{}{
    67  			// C indirectly depends on A trough B
    68  			cAddressA: {},
    69  			cAddressB: {},
    70  			cAddressC: {},
    71  			cAddressD: {},
    72  		},
    73  	}
    74  
    75  	t.Run("empty invalidator does not invalidate entries", func(t *testing.T) {
    76  		invalidator := environment.DerivedDataInvalidator{}.ProgramInvalidator()
    77  
    78  		require.False(t, invalidator.ShouldInvalidateEntries())
    79  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
    80  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
    81  		require.False(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
    82  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
    83  	})
    84  	t.Run("meter parameters invalidator invalidates all entries", func(t *testing.T) {
    85  		invalidator := environment.DerivedDataInvalidator{
    86  			MeterParamOverridesUpdated: true,
    87  		}.ProgramInvalidator()
    88  
    89  		require.True(t, invalidator.ShouldInvalidateEntries())
    90  		require.True(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
    91  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
    92  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
    93  		require.True(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
    94  	})
    95  
    96  	t.Run("address invalidator A invalidates all but D", func(t *testing.T) {
    97  		invalidator := environment.DerivedDataInvalidator{
    98  			FrozenAccounts: []common.Address{
    99  				cAddressA,
   100  			},
   101  		}.ProgramInvalidator()
   102  
   103  		require.True(t, invalidator.ShouldInvalidateEntries())
   104  		require.True(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   105  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   106  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   107  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   108  	})
   109  
   110  	t.Run("address invalidator D invalidates D, C", func(t *testing.T) {
   111  		invalidator := environment.DerivedDataInvalidator{
   112  			FrozenAccounts: []common.Address{
   113  				cAddressD,
   114  			},
   115  		}.ProgramInvalidator()
   116  
   117  		require.True(t, invalidator.ShouldInvalidateEntries())
   118  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   119  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   120  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   121  		require.True(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   122  	})
   123  
   124  	t.Run("address invalidator B invalidates B, C", func(t *testing.T) {
   125  		invalidator := environment.DerivedDataInvalidator{
   126  			FrozenAccounts: []common.Address{
   127  				cAddressB,
   128  			},
   129  		}.ProgramInvalidator()
   130  
   131  		require.True(t, invalidator.ShouldInvalidateEntries())
   132  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   133  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   134  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   135  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   136  	})
   137  
   138  	t.Run("contract invalidator A invalidates all but D", func(t *testing.T) {
   139  		invalidator := environment.DerivedDataInvalidator{
   140  			ContractUpdateKeys: []environment.ContractUpdateKey{
   141  				{
   142  					Address: cAddressA,
   143  					Name:    "A",
   144  				},
   145  			},
   146  		}.ProgramInvalidator()
   147  
   148  		require.True(t, invalidator.ShouldInvalidateEntries())
   149  		require.True(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   150  		require.True(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   151  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   152  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   153  	})
   154  
   155  	t.Run("contract invalidator C invalidates C", func(t *testing.T) {
   156  		invalidator := environment.DerivedDataInvalidator{
   157  			ContractUpdateKeys: []environment.ContractUpdateKey{
   158  				{
   159  					Address: cAddressC,
   160  					Name:    "C",
   161  				},
   162  			},
   163  		}.ProgramInvalidator()
   164  
   165  		require.True(t, invalidator.ShouldInvalidateEntries())
   166  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   167  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   168  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   169  		require.False(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   170  	})
   171  
   172  	t.Run("contract invalidator D invalidates C, D", func(t *testing.T) {
   173  		invalidator := environment.DerivedDataInvalidator{
   174  			ContractUpdateKeys: []environment.ContractUpdateKey{
   175  				{
   176  					Address: cAddressD,
   177  					Name:    "D",
   178  				},
   179  			},
   180  		}.ProgramInvalidator()
   181  
   182  		require.True(t, invalidator.ShouldInvalidateEntries())
   183  		require.False(t, invalidator.ShouldInvalidateEntry(programALoc, programA, nil))
   184  		require.False(t, invalidator.ShouldInvalidateEntry(programBLoc, programB, nil))
   185  		require.True(t, invalidator.ShouldInvalidateEntry(programCLoc, programC, nil))
   186  		require.True(t, invalidator.ShouldInvalidateEntry(programDLoc, programD, nil))
   187  	})
   188  }
   189  
   190  func TestMeterParamOverridesInvalidator(t *testing.T) {
   191  	invalidator := environment.DerivedDataInvalidator{}.
   192  		MeterParamOverridesInvalidator()
   193  
   194  	require.False(t, invalidator.ShouldInvalidateEntries())
   195  	require.False(t, invalidator.ShouldInvalidateEntry(
   196  		struct{}{},
   197  		derived.MeterParamOverrides{},
   198  		nil))
   199  
   200  	invalidator = environment.DerivedDataInvalidator{
   201  		ContractUpdateKeys:         nil,
   202  		FrozenAccounts:             nil,
   203  		MeterParamOverridesUpdated: true,
   204  	}.MeterParamOverridesInvalidator()
   205  
   206  	require.True(t, invalidator.ShouldInvalidateEntries())
   207  	require.True(t, invalidator.ShouldInvalidateEntry(
   208  		struct{}{},
   209  		derived.MeterParamOverrides{},
   210  		nil))
   211  }
   212  
   213  func TestMeterParamOverridesUpdated(t *testing.T) {
   214  	memoryLimit := uint64(666)
   215  
   216  	compKind := common.ComputationKind(12345)
   217  	compWeight := uint64(10)
   218  	computationWeights := meter.ExecutionEffortWeights{
   219  		compKind: compWeight,
   220  	}
   221  
   222  	memKind := common.MemoryKind(23456)
   223  	memWeight := uint64(20000)
   224  	memoryWeights := meter.ExecutionMemoryWeights{
   225  		memKind: memWeight,
   226  	}
   227  
   228  	baseView := utils.NewSimpleView()
   229  	ctx := fvm.NewContext(fvm.WithChain(flow.Testnet.Chain()))
   230  
   231  	vm := fvm.NewVirtualMachine()
   232  	err := vm.Run(
   233  		ctx,
   234  		fvm.Bootstrap(
   235  			unittest.ServiceAccountPublicKey,
   236  			fvm.WithExecutionMemoryLimit(memoryLimit),
   237  			fvm.WithExecutionEffortWeights(computationWeights),
   238  			fvm.WithExecutionMemoryWeights(memoryWeights)),
   239  		baseView)
   240  	require.NoError(t, err)
   241  
   242  	view := baseView.NewChild().(*utils.SimpleView)
   243  	txnState := state.NewTransactionState(view, state.DefaultParameters())
   244  
   245  	derivedBlockData := derived.NewEmptyDerivedBlockData()
   246  	derivedTxnData, err := derivedBlockData.NewDerivedTransactionData(0, 0)
   247  	require.NoError(t, err)
   248  
   249  	computer := fvm.NewMeterParamOverridesComputer(ctx, derivedTxnData)
   250  
   251  	overrides, err := computer.Compute(txnState, struct{}{})
   252  	require.NoError(t, err)
   253  
   254  	// Sanity check.  Note that bootstrap creates additional computation /
   255  	// memory weight entries.  We'll only check the entries we added.
   256  	require.NotNil(t, overrides.MemoryLimit)
   257  	require.Equal(t, memoryLimit, *overrides.MemoryLimit)
   258  	require.Equal(t, compWeight, overrides.ComputationWeights[compKind])
   259  	require.Equal(t, memWeight, overrides.MemoryWeights[memKind])
   260  
   261  	//
   262  	// Actual test
   263  	//
   264  
   265  	ctx.TxBody = &flow.TransactionBody{}
   266  
   267  	checkForUpdates := func(owner string, key string, expected bool) {
   268  		view := utils.NewSimpleView()
   269  		txnState := state.NewTransactionState(view, state.DefaultParameters())
   270  
   271  		err := txnState.Set(owner, key, flow.RegisterValue("blah"), false)
   272  		require.NoError(t, err)
   273  
   274  		env := environment.NewTransactionEnvironment(
   275  			tracing.NewTracerSpan(),
   276  			ctx.EnvironmentParams,
   277  			txnState,
   278  			nil)
   279  
   280  		invalidator := environment.NewDerivedDataInvalidator(nil, env)
   281  		require.Equal(t, expected, invalidator.MeterParamOverridesUpdated)
   282  	}
   283  
   284  	for registerId := range view.Ledger.RegisterTouches {
   285  		checkForUpdates(registerId.Owner, registerId.Key, true)
   286  		checkForUpdates("other owner", registerId.Key, false)
   287  	}
   288  
   289  	stabIndex := "$12345678"
   290  	require.True(t, state.IsSlabIndex(stabIndex))
   291  
   292  	checkForUpdates(
   293  		string(ctx.Chain.ServiceAddress().Bytes()),
   294  		stabIndex,
   295  		true)
   296  
   297  	checkForUpdates(
   298  		string(ctx.Chain.ServiceAddress().Bytes()),
   299  		"other keys",
   300  		false)
   301  
   302  	checkForUpdates("other owner", stabIndex, false)
   303  
   304  	checkForUpdates("other owner", "other key", false)
   305  }