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 }