github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/fvm_test.go (about) 1 package fvm_test 2 3 import ( 4 "crypto/rand" 5 "encoding/hex" 6 "fmt" 7 "math" 8 "strings" 9 "testing" 10 11 stdlib2 "github.com/onflow/cadence/runtime/stdlib" 12 13 envMock "github.com/onflow/flow-go/fvm/environment/mock" 14 15 "github.com/onflow/cadence" 16 "github.com/onflow/cadence/encoding/ccf" 17 jsoncdc "github.com/onflow/cadence/encoding/json" 18 "github.com/onflow/cadence/runtime" 19 "github.com/onflow/cadence/runtime/common" 20 cadenceErrors "github.com/onflow/cadence/runtime/errors" 21 "github.com/onflow/cadence/runtime/tests/utils" 22 "github.com/onflow/crypto" 23 "github.com/stretchr/testify/assert" 24 mockery "github.com/stretchr/testify/mock" 25 "github.com/stretchr/testify/require" 26 27 "github.com/onflow/flow-go/engine/execution/testutil" 28 exeUtils "github.com/onflow/flow-go/engine/execution/utils" 29 "github.com/onflow/flow-go/fvm" 30 fvmCrypto "github.com/onflow/flow-go/fvm/crypto" 31 "github.com/onflow/flow-go/fvm/environment" 32 "github.com/onflow/flow-go/fvm/errors" 33 "github.com/onflow/flow-go/fvm/evm/stdlib" 34 "github.com/onflow/flow-go/fvm/evm/types" 35 "github.com/onflow/flow-go/fvm/meter" 36 reusableRuntime "github.com/onflow/flow-go/fvm/runtime" 37 "github.com/onflow/flow-go/fvm/storage/snapshot" 38 "github.com/onflow/flow-go/fvm/storage/snapshot/mock" 39 "github.com/onflow/flow-go/fvm/storage/testutils" 40 "github.com/onflow/flow-go/fvm/systemcontracts" 41 "github.com/onflow/flow-go/model/flow" 42 "github.com/onflow/flow-go/utils/unittest" 43 ) 44 45 type vmTest struct { 46 bootstrapOptions []fvm.BootstrapProcedureOption 47 contextOptions []fvm.Option 48 } 49 50 func newVMTest() vmTest { 51 return vmTest{} 52 } 53 54 func (vmt vmTest) withBootstrapProcedureOptions(opts ...fvm.BootstrapProcedureOption) vmTest { 55 vmt.bootstrapOptions = append(vmt.bootstrapOptions, opts...) 56 return vmt 57 } 58 59 func (vmt vmTest) withContextOptions(opts ...fvm.Option) vmTest { 60 vmt.contextOptions = append(vmt.contextOptions, opts...) 61 return vmt 62 } 63 64 func createChainAndVm(chainID flow.ChainID) (flow.Chain, fvm.VM) { 65 return chainID.Chain(), fvm.NewVirtualMachine() 66 } 67 68 func (vmt vmTest) run( 69 f func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree), 70 ) func(t *testing.T) { 71 return func(t *testing.T) { 72 baseOpts := []fvm.Option{ 73 // default chain is Testnet 74 fvm.WithChain(flow.Testnet.Chain()), 75 fvm.WithEntropyProvider(testutil.EntropyProviderFixture(nil)), 76 } 77 78 opts := append(baseOpts, vmt.contextOptions...) 79 ctx := fvm.NewContext(opts...) 80 81 chain := ctx.Chain 82 vm := fvm.NewVirtualMachine() 83 84 snapshotTree := snapshot.NewSnapshotTree(nil) 85 86 baseBootstrapOpts := []fvm.BootstrapProcedureOption{ 87 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 88 } 89 90 bootstrapOpts := append(baseBootstrapOpts, vmt.bootstrapOptions...) 91 92 executionSnapshot, _, err := vm.Run( 93 ctx, 94 fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...), 95 snapshotTree) 96 require.NoError(t, err) 97 98 snapshotTree = snapshotTree.Append(executionSnapshot) 99 100 f(t, vm, chain, ctx, snapshotTree) 101 } 102 } 103 104 // bootstrapWith executes the bootstrap procedure and the custom bootstrap function 105 // and returns a prepared bootstrappedVmTest with all the state needed 106 func (vmt vmTest) bootstrapWith( 107 bootstrap func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) (snapshot.SnapshotTree, error), 108 ) (bootstrappedVmTest, error) { 109 110 baseOpts := []fvm.Option{ 111 // default chain is Testnet 112 fvm.WithChain(flow.Testnet.Chain()), 113 } 114 115 opts := append(baseOpts, vmt.contextOptions...) 116 ctx := fvm.NewContext(opts...) 117 118 chain := ctx.Chain 119 vm := fvm.NewVirtualMachine() 120 121 snapshotTree := snapshot.NewSnapshotTree(nil) 122 123 baseBootstrapOpts := []fvm.BootstrapProcedureOption{ 124 fvm.WithInitialTokenSupply(unittest.GenesisTokenSupply), 125 } 126 127 bootstrapOpts := append(baseBootstrapOpts, vmt.bootstrapOptions...) 128 129 executionSnapshot, _, err := vm.Run( 130 ctx, 131 fvm.Bootstrap(unittest.ServiceAccountPublicKey, bootstrapOpts...), 132 snapshotTree) 133 if err != nil { 134 return bootstrappedVmTest{}, err 135 } 136 137 snapshotTree = snapshotTree.Append(executionSnapshot) 138 139 snapshotTree, err = bootstrap(vm, chain, ctx, snapshotTree) 140 if err != nil { 141 return bootstrappedVmTest{}, err 142 } 143 144 return bootstrappedVmTest{chain, ctx, snapshotTree}, nil 145 } 146 147 type bootstrappedVmTest struct { 148 chain flow.Chain 149 ctx fvm.Context 150 snapshotTree snapshot.SnapshotTree 151 } 152 153 // run Runs a test from the bootstrapped state, without changing the bootstrapped state 154 func (vmt bootstrappedVmTest) run( 155 f func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree), 156 ) func(t *testing.T) { 157 return func(t *testing.T) { 158 f(t, fvm.NewVirtualMachine(), vmt.chain, vmt.ctx, vmt.snapshotTree) 159 } 160 } 161 162 func TestHashing(t *testing.T) { 163 164 t.Parallel() 165 166 chain, vm := createChainAndVm(flow.Mainnet) 167 168 ctx := fvm.NewContext( 169 fvm.WithChain(chain), 170 fvm.WithCadenceLogging(true), 171 ) 172 173 snapshotTree := testutil.RootBootstrappedLedger(vm, ctx) 174 175 hashScript := func(hashName string) []byte { 176 return []byte(fmt.Sprintf( 177 ` 178 import Crypto 179 180 access(all) 181 fun main(data: [UInt8]): [UInt8] { 182 return Crypto.hash(data, algorithm: HashAlgorithm.%s) 183 } 184 `, hashName)) 185 } 186 hashWithTagScript := func(hashName string) []byte { 187 return []byte(fmt.Sprintf( 188 ` 189 import Crypto 190 191 access(all) fun main(data: [UInt8], tag: String): [UInt8] { 192 return Crypto.hashWithTag(data, tag: tag, algorithm: HashAlgorithm.%s) 193 } 194 `, hashName)) 195 } 196 197 data := []byte("some random message") 198 encodedBytes := make([]cadence.Value, len(data)) 199 for i := range encodedBytes { 200 encodedBytes[i] = cadence.NewUInt8(data[i]) 201 } 202 cadenceData := jsoncdc.MustEncode(cadence.NewArray(encodedBytes)) 203 204 // ===== Test Cases ===== 205 cases := []struct { 206 Algo runtime.HashAlgorithm 207 WithTag bool 208 Tag string 209 Check func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) 210 }{ 211 { 212 Algo: runtime.HashAlgorithmSHA2_256, 213 WithTag: false, 214 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 215 require.NoError(t, scriptErr) 216 require.NoError(t, executionErr) 217 require.Equal(t, "68fb87dfba69b956f4ba98b748a75a604f99b38a4f2740290037957f7e830da8", result) 218 }, 219 }, 220 { 221 Algo: runtime.HashAlgorithmSHA2_384, 222 WithTag: false, 223 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 224 require.NoError(t, scriptErr) 225 require.NoError(t, executionErr) 226 require.Equal(t, "a9b3e62ab9b2a33020e015f245b82e063afd1398211326408bc8fc31c2c15859594b0aee263fbb02f6d8b5065ad49df2", result) 227 }, 228 }, 229 { 230 Algo: runtime.HashAlgorithmSHA3_256, 231 WithTag: false, 232 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 233 require.NoError(t, scriptErr) 234 require.NoError(t, executionErr) 235 require.Equal(t, "38effea5ab9082a2cb0dc9adfafaf88523e8f3ce74bfbeac85ffc719cc2c4677", result) 236 }, 237 }, 238 { 239 Algo: runtime.HashAlgorithmSHA3_384, 240 WithTag: false, 241 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 242 require.NoError(t, scriptErr) 243 require.NoError(t, executionErr) 244 require.Equal(t, "f41e8de9af0c1f46fc56d5a776f1bd500530879a85f3b904821810295927e13a54f3e936dddb84669021052eb12966c3", result) 245 }, 246 }, 247 { 248 Algo: runtime.HashAlgorithmKECCAK_256, 249 WithTag: false, 250 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 251 require.NoError(t, scriptErr) 252 require.NoError(t, executionErr) 253 require.Equal(t, "1d5ced4738dd4e0bb4628dad7a7b59b8e339a75ece97a4ad004773a49ed7b5bc", result) 254 }, 255 }, 256 { 257 Algo: runtime.HashAlgorithmKECCAK_256, 258 WithTag: true, 259 Tag: "some_tag", 260 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 261 require.NoError(t, scriptErr) 262 require.NoError(t, executionErr) 263 require.Equal(t, "8454ec77f76b229a473770c91e3ea6e7e852416d747805215d15d53bdc56ce5f", result) 264 }, 265 }, 266 { 267 Algo: runtime.HashAlgorithmSHA2_256, 268 WithTag: true, 269 Tag: "some_tag", 270 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 271 require.NoError(t, scriptErr) 272 require.NoError(t, executionErr) 273 require.Equal(t, "4e07609b9a856a5e10703d1dba73be34d9ca0f4e780859d66983f41d746ec8b2", result) 274 }, 275 }, 276 { 277 Algo: runtime.HashAlgorithmSHA2_384, 278 WithTag: true, 279 Tag: "some_tag", 280 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 281 require.NoError(t, scriptErr) 282 require.NoError(t, executionErr) 283 require.Equal(t, "f9bd89e15f341a225656944dc8b3c405e66a0f97838ad44c9803164c911e677aea7ad4e24486fba3f803d83ed1ccfce5", result) 284 }, 285 }, 286 { 287 Algo: runtime.HashAlgorithmSHA3_256, 288 WithTag: true, 289 Tag: "some_tag", 290 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 291 require.NoError(t, scriptErr) 292 require.NoError(t, executionErr) 293 require.Equal(t, "f59e2ccc9d7f008a96948a31573670d9976a4a161601ab1cd1d2da019779a0f6", result) 294 }, 295 }, 296 { 297 Algo: runtime.HashAlgorithmSHA3_384, 298 WithTag: true, 299 Tag: "some_tag", 300 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 301 require.NoError(t, scriptErr) 302 require.NoError(t, executionErr) 303 require.Equal(t, "e7875eafdb53327faeace8478d1650c6547d04fb4fb42f34509ad64bde0267bea7e1b3af8fda3ef9d9c9327dd4e97a96", result) 304 }, 305 }, 306 { 307 Algo: runtime.HashAlgorithmKMAC128_BLS_BLS12_381, 308 WithTag: false, 309 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 310 require.NoError(t, scriptErr) 311 require.NoError(t, executionErr) 312 require.Equal(t, "44dc46111abacfe2bb4a04cea4805aad03f84e4849f138cc3ed431478472b185548628e96d0c963b21ebaf17132d73fc13031eb82d5f4cbe3b6047ff54d20e8d663904373d73348b97ce18305ebc56114cb7e7394e486684007f78aa59abc5d0a8f6bae6bd186db32528af80857cd12112ce6960be29c96074df9c4aaed5b0e6", result) 313 }, 314 }, 315 { 316 Algo: runtime.HashAlgorithmKMAC128_BLS_BLS12_381, 317 WithTag: true, 318 Tag: "some_tag", 319 Check: func(t *testing.T, result string, scriptErr errors.CodedError, executionErr error) { 320 require.NoError(t, scriptErr) 321 require.NoError(t, executionErr) 322 require.Equal(t, "de7d9aa24274fa12c98cce5c09eea0634108ead2e91828b9a9a450e878088393e3e63eb4b19834f579ce215b00a9915919b67a71dab1112560319e6e1e5e9ad0fb670e8a09d586508c84547cee7ddbe8c9362c996846154865eb271bdc4523dbcdbdae5a77391fb54374f37534c8bb2281589cb2e3d62742596cdad7e4f9f35c", result) 323 }, 324 }, 325 } 326 // ====================== 327 328 for i, c := range cases { 329 t.Run(fmt.Sprintf("case %d: %s with tag: %v", i, c.Algo, c.WithTag), func(t *testing.T) { 330 code := hashScript(c.Algo.Name()) 331 if c.WithTag { 332 code = hashWithTagScript(c.Algo.Name()) 333 } 334 335 script := fvm.Script(code) 336 337 if c.WithTag { 338 script = script.WithArguments( 339 cadenceData, 340 jsoncdc.MustEncode(cadence.String(c.Tag)), 341 ) 342 } else { 343 script = script.WithArguments( 344 cadenceData, 345 ) 346 } 347 348 _, output, err := vm.Run(ctx, script, snapshotTree) 349 require.NoError(t, err) 350 351 byteResult := make([]byte, 0) 352 if err == nil && output.Err == nil { 353 cadenceArray := output.Value.(cadence.Array) 354 for _, value := range cadenceArray.Values { 355 byteResult = append(byteResult, uint8(value.(cadence.UInt8))) 356 } 357 } 358 359 c.Check(t, hex.EncodeToString(byteResult), output.Err, err) 360 }) 361 } 362 363 hashAlgos := []runtime.HashAlgorithm{ 364 runtime.HashAlgorithmSHA2_256, 365 runtime.HashAlgorithmSHA3_256, 366 runtime.HashAlgorithmSHA2_384, 367 runtime.HashAlgorithmSHA3_384, 368 runtime.HashAlgorithmKMAC128_BLS_BLS12_381, 369 runtime.HashAlgorithmKECCAK_256, 370 } 371 372 for i, algo := range hashAlgos { 373 t.Run(fmt.Sprintf("compare hash results without tag %v: %v", i, algo), func(t *testing.T) { 374 code := hashWithTagScript(algo.Name()) 375 script := fvm.Script(code) 376 script = script.WithArguments( 377 cadenceData, 378 jsoncdc.MustEncode(cadence.String("")), 379 ) 380 _, output, err := vm.Run(ctx, script, snapshotTree) 381 require.NoError(t, err) 382 require.NoError(t, output.Err) 383 384 result1 := make([]byte, 0) 385 cadenceArray := output.Value.(cadence.Array) 386 for _, value := range cadenceArray.Values { 387 result1 = append(result1, uint8(value.(cadence.UInt8))) 388 } 389 390 code = hashScript(algo.Name()) 391 script = fvm.Script(code) 392 script = script.WithArguments( 393 cadenceData, 394 ) 395 _, output, err = vm.Run(ctx, script, snapshotTree) 396 require.NoError(t, err) 397 require.NoError(t, output.Err) 398 399 result2 := make([]byte, 0) 400 cadenceArray = output.Value.(cadence.Array) 401 for _, value := range cadenceArray.Values { 402 result2 = append(result2, uint8(value.(cadence.UInt8))) 403 } 404 405 result3, err := fvmCrypto.HashWithTag(fvmCrypto.RuntimeToCryptoHashingAlgorithm(algo), "", data) 406 require.NoError(t, err) 407 408 require.Equal(t, result1, result2) 409 require.Equal(t, result1, result3) 410 }) 411 } 412 } 413 414 func TestWithServiceAccount(t *testing.T) { 415 416 t.Parallel() 417 418 chain, vm := createChainAndVm(flow.Mainnet) 419 420 ctxA := fvm.NewContext( 421 fvm.WithChain(chain), 422 fvm.WithAuthorizationChecksEnabled(false), 423 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 424 ) 425 426 snapshotTree := snapshot.NewSnapshotTree(nil) 427 428 txBody := flow.NewTransactionBody(). 429 SetScript([]byte(`transaction { prepare(signer: auth(BorrowValue) &Account) { Account(payer: signer) } }`)). 430 AddAuthorizer(chain.ServiceAddress()) 431 432 t.Run("With service account enabled", func(t *testing.T) { 433 executionSnapshot, output, err := vm.Run( 434 ctxA, 435 fvm.Transaction(txBody, 0), 436 snapshotTree) 437 require.NoError(t, err) 438 439 // transaction should fail on non-bootstrapped ledger 440 require.Error(t, output.Err) 441 442 snapshotTree = snapshotTree.Append(executionSnapshot) 443 }) 444 445 t.Run("With service account disabled", func(t *testing.T) { 446 ctxB := fvm.NewContextFromParent( 447 ctxA, 448 fvm.WithServiceAccount(false)) 449 450 _, output, err := vm.Run( 451 ctxB, 452 fvm.Transaction(txBody, 0), 453 snapshotTree) 454 require.NoError(t, err) 455 456 // transaction should succeed on non-bootstrapped ledger 457 require.NoError(t, output.Err) 458 }) 459 } 460 461 func TestEventLimits(t *testing.T) { 462 chain, vm := createChainAndVm(flow.Mainnet) 463 464 ctx := fvm.NewContext( 465 fvm.WithChain(chain), 466 fvm.WithAuthorizationChecksEnabled(false), 467 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 468 ) 469 470 snapshotTree := testutil.RootBootstrappedLedger(vm, ctx) 471 472 testContract := ` 473 access(all) contract TestContract { 474 access(all) event LargeEvent(value: Int256, str: String, list: [UInt256], dic: {String: String}) 475 access(all) fun EmitEvent() { 476 var s: Int256 = 1024102410241024 477 var i = 0 478 479 while i < 20 { 480 emit LargeEvent(value: s, str: s.toString(), list:[], dic:{s.toString():s.toString()}) 481 i = i + 1 482 } 483 } 484 } 485 ` 486 487 deployingContractScriptTemplate := ` 488 transaction { 489 prepare(signer: auth(AddContract) &Account) { 490 let code = "%s".decodeHex() 491 signer.contracts.add( 492 name: "TestContract", 493 code: code 494 ) 495 } 496 } 497 ` 498 499 ctx = fvm.NewContextFromParent( 500 ctx, 501 fvm.WithEventCollectionSizeLimit(2)) 502 503 txBody := flow.NewTransactionBody(). 504 SetScript([]byte(fmt.Sprintf(deployingContractScriptTemplate, hex.EncodeToString([]byte(testContract))))). 505 SetPayer(chain.ServiceAddress()). 506 AddAuthorizer(chain.ServiceAddress()) 507 508 executionSnapshot, output, err := vm.Run( 509 ctx, 510 fvm.Transaction(txBody, 0), 511 snapshotTree) 512 require.NoError(t, err) 513 require.NoError(t, output.Err) 514 515 snapshotTree = snapshotTree.Append(executionSnapshot) 516 517 txBody = flow.NewTransactionBody(). 518 SetScript([]byte(fmt.Sprintf(` 519 import TestContract from 0x%s 520 transaction { 521 prepare(acct: &Account) {} 522 execute { 523 TestContract.EmitEvent() 524 } 525 }`, chain.ServiceAddress()))). 526 AddAuthorizer(chain.ServiceAddress()) 527 528 t.Run("With limits", func(t *testing.T) { 529 txBody.Payer = unittest.RandomAddressFixture() 530 531 executionSnapshot, output, err := vm.Run( 532 ctx, 533 fvm.Transaction(txBody, 0), 534 snapshotTree) 535 require.NoError(t, err) 536 537 // transaction should fail due to event size limit 538 require.Error(t, output.Err) 539 540 snapshotTree = snapshotTree.Append(executionSnapshot) 541 }) 542 543 t.Run("With service account as payer", func(t *testing.T) { 544 txBody.Payer = chain.ServiceAddress() 545 546 _, output, err := vm.Run( 547 ctx, 548 fvm.Transaction(txBody, 0), 549 snapshotTree) 550 require.NoError(t, err) 551 552 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 553 554 // transaction should not fail due to event size limit 555 require.NoError(t, output.Err) 556 }) 557 } 558 559 // TestHappyPathSigning checks that a signing a transaction with `Sign` doesn't produce an error. 560 // Transaction verification tests are in `TestVerifySignatureFromTransaction`. 561 func TestHappyPathTransactionSigning(t *testing.T) { 562 563 newVMTest().run( 564 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 565 // Create an account private key. 566 privateKey, err := testutil.GenerateAccountPrivateKey() 567 require.NoError(t, err) 568 569 // Bootstrap a ledger, creating accounts with the provided private 570 // keys and the root account. 571 snapshotTree, accounts, err := testutil.CreateAccounts( 572 vm, 573 snapshotTree, 574 []flow.AccountPrivateKey{privateKey}, 575 chain) 576 require.NoError(t, err) 577 578 txBody := flow.NewTransactionBody(). 579 SetScript([]byte(`transaction(){}`)) 580 581 txBody.SetProposalKey(accounts[0], 0, 0) 582 txBody.SetPayer(accounts[0]) 583 584 hasher, err := exeUtils.NewHasher(privateKey.HashAlgo) 585 require.NoError(t, err) 586 587 sig, err := txBody.Sign(txBody.EnvelopeMessage(), privateKey.PrivateKey, hasher) 588 require.NoError(t, err) 589 txBody.AddEnvelopeSignature(accounts[0], 0, sig) 590 591 _, output, err := vm.Run( 592 ctx, 593 fvm.Transaction(txBody, 0), 594 snapshotTree) 595 require.NoError(t, err) 596 require.NoError(t, output.Err) 597 }, 598 ) 599 } 600 601 func TestTransactionFeeDeduction(t *testing.T) { 602 getBalance := func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree, address flow.Address) uint64 { 603 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 604 code := []byte(fmt.Sprintf( 605 ` 606 import FungibleToken from 0x%s 607 import FlowToken from 0x%s 608 609 access(all) fun main(account: Address): UFix64 { 610 let acct = getAccount(account) 611 let vaultRef = acct.capabilities.borrow<&FlowToken.Vault>(/public/flowTokenBalance) 612 ?? panic("Could not borrow Balance reference to the Vault") 613 614 return vaultRef.balance 615 } 616 `, 617 sc.FungibleToken.Address.Hex(), 618 sc.FlowToken.Address.Hex(), 619 )) 620 script := fvm.Script(code).WithArguments( 621 jsoncdc.MustEncode(cadence.NewAddress(address)), 622 ) 623 624 _, output, err := vm.Run(ctx, script, snapshotTree) 625 require.NoError(t, err) 626 require.NoError(t, output.Err) 627 return uint64(output.Value.(cadence.UFix64)) 628 } 629 630 type testCase struct { 631 name string 632 fundWith uint64 633 tryToTransfer uint64 634 gasLimit uint64 635 checkResult func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) 636 } 637 638 txFees := uint64(1_000) // 0.00001 639 fundingAmount := uint64(100_000_000) // 1.0 640 transferAmount := uint64(123_456) 641 minimumStorageReservation := uint64(fvm.DefaultMinimumStorageReservation) 642 643 chain := flow.Testnet.Chain() 644 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 645 depositedEvent := fmt.Sprintf("A.%s.FlowToken.TokensDeposited", sc.FlowToken.Address) 646 withdrawnEvent := fmt.Sprintf("A.%s.FlowToken.TokensWithdrawn", sc.FlowToken.Address) 647 feesDeductedEvent := fmt.Sprintf("A.%s.FlowFees.FeesDeducted", sc.FlowFees.Address) 648 649 testCases := []testCase{ 650 { 651 name: "Transaction fees are deducted", 652 fundWith: fundingAmount, 653 tryToTransfer: 0, 654 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 655 require.NoError(t, output.Err) 656 require.Equal(t, txFees, balanceBefore-balanceAfter) 657 }, 658 }, 659 { 660 name: "Transaction fee deduction emits events", 661 fundWith: fundingAmount, 662 tryToTransfer: 0, 663 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 664 require.NoError(t, output.Err) 665 666 var deposits []flow.Event 667 var withdraws []flow.Event 668 669 chain := flow.Testnet.Chain() 670 for _, e := range output.Events { 671 if string(e.Type) == depositedEvent { 672 deposits = append(deposits, e) 673 } 674 if string(e.Type) == withdrawnEvent { 675 withdraws = append(withdraws, e) 676 } 677 } 678 679 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 680 require.Len(t, deposits, 2) 681 require.Len(t, withdraws, 2) 682 }, 683 }, 684 { 685 name: "Transaction fees are deducted and tx is applied", 686 fundWith: fundingAmount, 687 tryToTransfer: transferAmount, 688 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 689 require.NoError(t, output.Err) 690 require.Equal(t, txFees+transferAmount, balanceBefore-balanceAfter) 691 }, 692 }, 693 { 694 name: "Transaction fees are deducted and fee deduction is emitted", 695 fundWith: fundingAmount, 696 tryToTransfer: transferAmount, 697 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 698 require.NoError(t, output.Err) 699 chain := flow.Testnet.Chain() 700 701 var feeDeduction flow.Event // fee deduction event 702 for _, e := range output.Events { 703 if string(e.Type) == feesDeductedEvent { 704 feeDeduction = e 705 break 706 } 707 } 708 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 709 require.NotEmpty(t, feeDeduction.Payload) 710 711 payload, err := ccf.Decode(nil, feeDeduction.Payload) 712 require.NoError(t, err) 713 714 event := payload.(cadence.Event) 715 716 fields := cadence.FieldsMappedByName(event) 717 718 actualTXFees := fields["amount"] 719 actualExecutionEffort := fields["executionEffort"] 720 actualInclusionEffort := fields["inclusionEffort"] 721 722 require.Equal(t, 723 txFees, 724 uint64(actualTXFees.(cadence.UFix64)), 725 ) 726 // Inclusion effort should be equivalent to 1.0 UFix64 727 require.Equal(t, 728 uint64(100_000_000), 729 uint64(actualInclusionEffort.(cadence.UFix64)), 730 ) 731 // Execution effort should be non-0 732 require.Greater(t, actualExecutionEffort, uint64(0)) 733 734 }, 735 }, 736 { 737 name: "If just enough balance, fees are deducted", 738 fundWith: txFees + transferAmount, 739 tryToTransfer: transferAmount, 740 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 741 require.NoError(t, output.Err) 742 require.Equal(t, uint64(0), balanceAfter) 743 }, 744 }, 745 { 746 // this is an edge case that is not applicable to any network. 747 // If storage limits were on this would fail due to storage limits 748 name: "If not enough balance, transaction succeeds and fees are deducted to 0", 749 fundWith: txFees, 750 tryToTransfer: 1, 751 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 752 require.NoError(t, output.Err) 753 require.Equal(t, uint64(0), balanceAfter) 754 }, 755 }, 756 { 757 name: "If tx fails, fees are deducted", 758 fundWith: fundingAmount, 759 tryToTransfer: 2 * fundingAmount, 760 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 761 require.Error(t, output.Err) 762 require.Equal(t, fundingAmount-txFees, balanceAfter) 763 }, 764 }, 765 { 766 name: "If tx fails, fee deduction events are emitted", 767 fundWith: fundingAmount, 768 tryToTransfer: 2 * fundingAmount, 769 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 770 require.Error(t, output.Err) 771 772 var deposits []flow.Event 773 var withdraws []flow.Event 774 775 chain := flow.Testnet.Chain() 776 777 for _, e := range output.Events { 778 if string(e.Type) == depositedEvent { 779 deposits = append(deposits, e) 780 } 781 if string(e.Type) == withdrawnEvent { 782 withdraws = append(withdraws, e) 783 } 784 } 785 786 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 787 require.Len(t, deposits, 1) 788 require.Len(t, withdraws, 1) 789 }, 790 }, 791 { 792 name: "If tx fails because of gas limit reached, fee deduction events are emitted", 793 fundWith: txFees + transferAmount, 794 tryToTransfer: transferAmount, 795 gasLimit: uint64(2), 796 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 797 require.ErrorContains(t, output.Err, "computation exceeds limit (2)") 798 799 var deposits []flow.Event 800 var withdraws []flow.Event 801 802 chain := flow.Testnet.Chain() 803 804 for _, e := range output.Events { 805 if string(e.Type) == depositedEvent { 806 deposits = append(deposits, e) 807 } 808 if string(e.Type) == withdrawnEvent { 809 withdraws = append(withdraws, e) 810 } 811 } 812 813 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 814 require.Len(t, deposits, 1) 815 require.Len(t, withdraws, 1) 816 }, 817 }, 818 } 819 820 testCasesWithStorageEnabled := []testCase{ 821 { 822 name: "Transaction fees are deducted", 823 fundWith: fundingAmount, 824 tryToTransfer: 0, 825 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 826 require.NoError(t, output.Err) 827 require.Equal(t, txFees, balanceBefore-balanceAfter) 828 }, 829 }, 830 { 831 name: "Transaction fee deduction emits events", 832 fundWith: fundingAmount, 833 tryToTransfer: 0, 834 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 835 require.NoError(t, output.Err) 836 837 var deposits []flow.Event 838 var withdraws []flow.Event 839 840 chain := flow.Testnet.Chain() 841 842 for _, e := range output.Events { 843 if string(e.Type) == depositedEvent { 844 deposits = append(deposits, e) 845 } 846 if string(e.Type) == withdrawnEvent { 847 withdraws = append(withdraws, e) 848 } 849 } 850 851 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 852 require.Len(t, deposits, 2) 853 require.Len(t, withdraws, 2) 854 }, 855 }, 856 { 857 name: "Transaction fees are deducted and tx is applied", 858 fundWith: fundingAmount, 859 tryToTransfer: transferAmount, 860 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 861 require.NoError(t, output.Err) 862 require.Equal(t, txFees+transferAmount, balanceBefore-balanceAfter) 863 }, 864 }, 865 { 866 name: "If just enough balance, fees are deducted", 867 fundWith: txFees + transferAmount, 868 tryToTransfer: transferAmount, 869 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 870 require.NoError(t, output.Err) 871 require.Equal(t, minimumStorageReservation, balanceAfter) 872 }, 873 }, 874 { 875 name: "If tx fails, fees are deducted", 876 fundWith: fundingAmount, 877 tryToTransfer: 2 * fundingAmount, 878 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 879 require.Error(t, output.Err) 880 require.Equal(t, fundingAmount-txFees+minimumStorageReservation, balanceAfter) 881 }, 882 }, 883 { 884 name: "If tx fails, fee deduction events are emitted", 885 fundWith: fundingAmount, 886 tryToTransfer: 2 * fundingAmount, 887 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 888 require.Error(t, output.Err) 889 890 var deposits []flow.Event 891 var withdraws []flow.Event 892 893 chain := flow.Testnet.Chain() 894 895 for _, e := range output.Events { 896 if string(e.Type) == depositedEvent { 897 deposits = append(deposits, e) 898 } 899 if string(e.Type) == withdrawnEvent { 900 withdraws = append(withdraws, e) 901 } 902 } 903 904 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 905 require.Len(t, deposits, 1) 906 require.Len(t, withdraws, 1) 907 }, 908 }, 909 { 910 name: "If balance at minimum, transaction fails, fees are deducted and fee deduction events are emitted", 911 fundWith: 0, 912 tryToTransfer: 0, 913 checkResult: func(t *testing.T, balanceBefore uint64, balanceAfter uint64, output fvm.ProcedureOutput) { 914 require.Error(t, output.Err) 915 require.Equal(t, minimumStorageReservation-txFees, balanceAfter) 916 917 var deposits []flow.Event 918 var withdraws []flow.Event 919 920 chain := flow.Testnet.Chain() 921 922 for _, e := range output.Events { 923 if string(e.Type) == depositedEvent { 924 deposits = append(deposits, e) 925 } 926 if string(e.Type) == withdrawnEvent { 927 withdraws = append(withdraws, e) 928 } 929 } 930 931 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 932 require.Len(t, deposits, 1) 933 require.Len(t, withdraws, 1) 934 }, 935 }, 936 } 937 938 runTx := func(tc testCase) func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 939 return func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 940 // ==== Create an account ==== 941 privateKey, txBody := testutil.CreateAccountCreationTransaction(t, chain) 942 943 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 944 require.NoError(t, err) 945 946 executionSnapshot, output, err := vm.Run( 947 ctx, 948 fvm.Transaction(txBody, 0), 949 snapshotTree) 950 require.NoError(t, err) 951 require.NoError(t, output.Err) 952 953 snapshotTree = snapshotTree.Append(executionSnapshot) 954 955 require.Len(t, output.Events, 16) 956 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 957 958 accountCreatedEvents := filterAccountCreatedEvents(output.Events) 959 960 require.Len(t, accountCreatedEvents, 1) 961 962 // read the address of the account created (e.g. "0x01" and convert it to flow.address) 963 data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload) 964 require.NoError(t, err) 965 966 address := flow.ConvertAddress( 967 cadence.SearchFieldByName( 968 data.(cadence.Event), 969 stdlib2.AccountEventAddressParameter.Identifier, 970 ).(cadence.Address), 971 ) 972 973 // ==== Transfer tokens to new account ==== 974 txBody = transferTokensTx(chain). 975 AddAuthorizer(chain.ServiceAddress()). 976 AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.fundWith))). 977 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address))) 978 979 txBody.SetProposalKey(chain.ServiceAddress(), 0, 1) 980 txBody.SetPayer(chain.ServiceAddress()) 981 982 err = testutil.SignEnvelope( 983 txBody, 984 chain.ServiceAddress(), 985 unittest.ServiceAccountPrivateKey, 986 ) 987 require.NoError(t, err) 988 989 executionSnapshot, output, err = vm.Run( 990 ctx, 991 fvm.Transaction(txBody, 0), 992 snapshotTree) 993 require.NoError(t, err) 994 require.NoError(t, output.Err) 995 996 snapshotTree = snapshotTree.Append(executionSnapshot) 997 998 balanceBefore := getBalance(vm, chain, ctx, snapshotTree, address) 999 1000 // ==== Transfer tokens from new account ==== 1001 1002 txBody = transferTokensTx(chain). 1003 AddAuthorizer(address). 1004 AddArgument(jsoncdc.MustEncode(cadence.UFix64(tc.tryToTransfer))). 1005 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress()))) 1006 1007 txBody.SetProposalKey(address, 0, 0) 1008 txBody.SetPayer(address) 1009 1010 if tc.gasLimit == 0 { 1011 txBody.SetComputeLimit(fvm.DefaultComputationLimit) 1012 } else { 1013 txBody.SetComputeLimit(tc.gasLimit) 1014 } 1015 1016 err = testutil.SignEnvelope( 1017 txBody, 1018 address, 1019 privateKey, 1020 ) 1021 require.NoError(t, err) 1022 1023 executionSnapshot, output, err = vm.Run( 1024 ctx, 1025 fvm.Transaction(txBody, 0), 1026 snapshotTree) 1027 require.NoError(t, err) 1028 1029 snapshotTree = snapshotTree.Append(executionSnapshot) 1030 1031 balanceAfter := getBalance(vm, chain, ctx, snapshotTree, address) 1032 1033 tc.checkResult( 1034 t, 1035 balanceBefore, 1036 balanceAfter, 1037 output, 1038 ) 1039 } 1040 } 1041 1042 for i, tc := range testCases { 1043 t.Run(fmt.Sprintf("Transaction Fees %d: %s", i, tc.name), newVMTest().withBootstrapProcedureOptions( 1044 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 1045 fvm.WithExecutionMemoryLimit(math.MaxUint64), 1046 fvm.WithExecutionEffortWeights(environment.MainnetExecutionEffortWeights), 1047 fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights), 1048 ).withContextOptions( 1049 fvm.WithTransactionFeesEnabled(true), 1050 fvm.WithChain(chain), 1051 ).run( 1052 runTx(tc)), 1053 ) 1054 } 1055 1056 for i, tc := range testCasesWithStorageEnabled { 1057 t.Run(fmt.Sprintf("Transaction Fees with storage %d: %s", i, tc.name), newVMTest().withBootstrapProcedureOptions( 1058 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 1059 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1060 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1061 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1062 fvm.WithExecutionMemoryLimit(math.MaxUint64), 1063 fvm.WithExecutionEffortWeights(environment.MainnetExecutionEffortWeights), 1064 fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights), 1065 ).withContextOptions( 1066 fvm.WithTransactionFeesEnabled(true), 1067 fvm.WithAccountStorageLimit(true), 1068 fvm.WithChain(chain), 1069 ).run( 1070 runTx(tc)), 1071 ) 1072 } 1073 } 1074 1075 func TestSettingExecutionWeights(t *testing.T) { 1076 1077 t.Run("transaction should fail with high weights", newVMTest().withBootstrapProcedureOptions( 1078 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1079 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1080 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1081 fvm.WithExecutionEffortWeights( 1082 meter.ExecutionEffortWeights{ 1083 common.ComputationKindLoop: 100_000 << meter.MeterExecutionInternalPrecisionBytes, 1084 }, 1085 ), 1086 ).run( 1087 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1088 1089 txBody := flow.NewTransactionBody(). 1090 SetScript([]byte(` 1091 transaction { 1092 prepare(signer: &Account) { 1093 var a = 0 1094 while a < 100 { 1095 a = a + 1 1096 } 1097 } 1098 } 1099 `)). 1100 SetProposalKey(chain.ServiceAddress(), 0, 0). 1101 AddAuthorizer(chain.ServiceAddress()). 1102 SetPayer(chain.ServiceAddress()) 1103 1104 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1105 require.NoError(t, err) 1106 1107 _, output, err := vm.Run( 1108 ctx, 1109 fvm.Transaction(txBody, 0), 1110 snapshotTree) 1111 require.NoError(t, err) 1112 1113 require.True(t, errors.IsComputationLimitExceededError(output.Err)) 1114 }, 1115 )) 1116 1117 memoryWeights := make(map[common.MemoryKind]uint64) 1118 for k, v := range meter.DefaultMemoryWeights { 1119 memoryWeights[k] = v 1120 } 1121 1122 const highWeight = 20_000_000_000 1123 memoryWeights[common.MemoryKindIntegerExpression] = highWeight 1124 1125 t.Run("normal transactions should fail with high memory weights", newVMTest().withBootstrapProcedureOptions( 1126 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1127 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1128 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1129 fvm.WithExecutionMemoryWeights( 1130 memoryWeights, 1131 ), 1132 ).withContextOptions( 1133 fvm.WithMemoryLimit(10_000_000_000), 1134 ).run( 1135 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1136 // Create an account private key. 1137 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1138 require.NoError(t, err) 1139 1140 // Bootstrap a ledger, creating accounts with the provided private 1141 // keys and the root account. 1142 snapshotTree, accounts, err := testutil.CreateAccounts( 1143 vm, 1144 snapshotTree, 1145 privateKeys, 1146 chain) 1147 require.NoError(t, err) 1148 1149 txBody := flow.NewTransactionBody(). 1150 SetScript([]byte(` 1151 transaction { 1152 prepare(signer: &Account) { 1153 var a = 1 1154 } 1155 } 1156 `)). 1157 SetProposalKey(accounts[0], 0, 0). 1158 AddAuthorizer(accounts[0]). 1159 SetPayer(accounts[0]) 1160 1161 err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0) 1162 require.NoError(t, err) 1163 1164 _, output, err := vm.Run( 1165 ctx, 1166 fvm.Transaction(txBody, 0), 1167 snapshotTree) 1168 require.NoError(t, err) 1169 require.Greater(t, output.MemoryEstimate, uint64(highWeight)) 1170 1171 require.True(t, errors.IsMemoryLimitExceededError(output.Err)) 1172 }, 1173 )) 1174 1175 t.Run("service account transactions should not fail with high memory weights", newVMTest().withBootstrapProcedureOptions( 1176 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1177 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1178 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1179 fvm.WithExecutionMemoryWeights( 1180 memoryWeights, 1181 ), 1182 ).withContextOptions( 1183 fvm.WithMemoryLimit(10_000_000_000), 1184 ).run( 1185 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1186 1187 txBody := flow.NewTransactionBody(). 1188 SetScript([]byte(` 1189 transaction { 1190 prepare(signer: &Account) { 1191 var a = 1 1192 } 1193 } 1194 `)). 1195 SetProposalKey(chain.ServiceAddress(), 0, 0). 1196 AddAuthorizer(chain.ServiceAddress()). 1197 SetPayer(chain.ServiceAddress()) 1198 1199 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1200 require.NoError(t, err) 1201 1202 _, output, err := vm.Run( 1203 ctx, 1204 fvm.Transaction(txBody, 0), 1205 snapshotTree) 1206 require.NoError(t, err) 1207 require.Greater(t, output.MemoryEstimate, uint64(highWeight)) 1208 1209 require.NoError(t, output.Err) 1210 }, 1211 )) 1212 1213 memoryWeights = make(map[common.MemoryKind]uint64) 1214 for k, v := range meter.DefaultMemoryWeights { 1215 memoryWeights[k] = v 1216 } 1217 memoryWeights[common.MemoryKindBreakStatement] = 1_000_000 1218 t.Run("transaction should fail with low memory limit (set in the state)", newVMTest().withBootstrapProcedureOptions( 1219 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1220 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1221 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1222 fvm.WithExecutionMemoryLimit( 1223 100_000_000, 1224 ), 1225 fvm.WithExecutionMemoryWeights( 1226 memoryWeights, 1227 ), 1228 ).run( 1229 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1230 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1231 require.NoError(t, err) 1232 1233 snapshotTree, accounts, err := testutil.CreateAccounts( 1234 vm, 1235 snapshotTree, 1236 privateKeys, 1237 chain) 1238 require.NoError(t, err) 1239 1240 // This transaction is specially designed to use a lot of breaks 1241 // as the weight for breaks is much higher than usual. 1242 // putting a `while true {break}` in a loop does not use the same amount of memory. 1243 txBody := flow.NewTransactionBody(). 1244 SetScript([]byte(` 1245 transaction { 1246 prepare(signer: &Account) { 1247 while true {break};while true {break};while true {break};while true {break};while true {break}; 1248 while true {break};while true {break};while true {break};while true {break};while true {break}; 1249 while true {break};while true {break};while true {break};while true {break};while true {break}; 1250 while true {break};while true {break};while true {break};while true {break};while true {break}; 1251 while true {break};while true {break};while true {break};while true {break};while true {break}; 1252 while true {break};while true {break};while true {break};while true {break};while true {break}; 1253 while true {break};while true {break};while true {break};while true {break};while true {break}; 1254 while true {break};while true {break};while true {break};while true {break};while true {break}; 1255 while true {break};while true {break};while true {break};while true {break};while true {break}; 1256 while true {break};while true {break};while true {break};while true {break};while true {break}; 1257 while true {break};while true {break};while true {break};while true {break};while true {break}; 1258 while true {break};while true {break};while true {break};while true {break};while true {break}; 1259 while true {break};while true {break};while true {break};while true {break};while true {break}; 1260 while true {break};while true {break};while true {break};while true {break};while true {break}; 1261 while true {break};while true {break};while true {break};while true {break};while true {break}; 1262 while true {break};while true {break};while true {break};while true {break};while true {break}; 1263 while true {break};while true {break};while true {break};while true {break};while true {break}; 1264 while true {break};while true {break};while true {break};while true {break};while true {break}; 1265 while true {break};while true {break};while true {break};while true {break};while true {break}; 1266 while true {break};while true {break};while true {break};while true {break};while true {break}; 1267 } 1268 } 1269 `)) 1270 1271 err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0) 1272 require.NoError(t, err) 1273 1274 _, output, err := vm.Run( 1275 ctx, 1276 fvm.Transaction(txBody, 0), 1277 snapshotTree) 1278 require.NoError(t, err) 1279 // There are 100 breaks and each break uses 1_000_000 memory 1280 require.Greater(t, output.MemoryEstimate, uint64(100_000_000)) 1281 1282 require.True(t, errors.IsMemoryLimitExceededError(output.Err)) 1283 }, 1284 )) 1285 1286 t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions( 1287 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1288 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1289 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1290 fvm.WithExecutionEffortWeights( 1291 meter.ExecutionEffortWeights{ 1292 environment.ComputationKindCreateAccount: (fvm.DefaultComputationLimit + 1) << meter.MeterExecutionInternalPrecisionBytes, 1293 }, 1294 ), 1295 ).run( 1296 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1297 txBody := flow.NewTransactionBody(). 1298 SetScript([]byte(` 1299 transaction { 1300 prepare(signer: auth(BorrowValue) &Account) { 1301 Account(payer: signer) 1302 } 1303 } 1304 `)). 1305 SetProposalKey(chain.ServiceAddress(), 0, 0). 1306 AddAuthorizer(chain.ServiceAddress()). 1307 SetPayer(chain.ServiceAddress()) 1308 1309 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1310 require.NoError(t, err) 1311 1312 _, output, err := vm.Run( 1313 ctx, 1314 fvm.Transaction(txBody, 0), 1315 snapshotTree) 1316 require.NoError(t, err) 1317 1318 require.True(t, errors.IsComputationLimitExceededError(output.Err)) 1319 }, 1320 )) 1321 1322 t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions( 1323 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1324 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1325 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1326 fvm.WithExecutionEffortWeights( 1327 meter.ExecutionEffortWeights{ 1328 environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes, 1329 }, 1330 ), 1331 ).run( 1332 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1333 1334 txBody := flow.NewTransactionBody(). 1335 SetScript([]byte(` 1336 transaction { 1337 prepare(signer: auth(BorrowValue) &Account) { 1338 Account(payer: signer) 1339 } 1340 } 1341 `)). 1342 SetProposalKey(chain.ServiceAddress(), 0, 0). 1343 AddAuthorizer(chain.ServiceAddress()). 1344 SetPayer(chain.ServiceAddress()) 1345 1346 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1347 require.NoError(t, err) 1348 1349 _, output, err := vm.Run( 1350 ctx, 1351 fvm.Transaction(txBody, 0), 1352 snapshotTree) 1353 require.NoError(t, err) 1354 1355 require.True(t, errors.IsComputationLimitExceededError(output.Err)) 1356 }, 1357 )) 1358 1359 t.Run("transaction should fail if create account weight is high", newVMTest().withBootstrapProcedureOptions( 1360 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1361 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1362 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1363 fvm.WithExecutionEffortWeights( 1364 meter.ExecutionEffortWeights{ 1365 environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes, 1366 }, 1367 ), 1368 ).run( 1369 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1370 txBody := flow.NewTransactionBody(). 1371 SetScript([]byte(` 1372 transaction { 1373 prepare(signer: auth(BorrowValue) &Account) { 1374 Account(payer: signer) 1375 } 1376 } 1377 `)). 1378 SetProposalKey(chain.ServiceAddress(), 0, 0). 1379 AddAuthorizer(chain.ServiceAddress()). 1380 SetPayer(chain.ServiceAddress()) 1381 1382 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1383 require.NoError(t, err) 1384 1385 _, output, err := vm.Run( 1386 ctx, 1387 fvm.Transaction(txBody, 0), 1388 snapshotTree) 1389 require.NoError(t, err) 1390 1391 require.True(t, errors.IsComputationLimitExceededError(output.Err)) 1392 }, 1393 )) 1394 1395 t.Run("transaction should not use up more computation that the transaction body itself", newVMTest().withBootstrapProcedureOptions( 1396 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1397 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1398 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1399 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 1400 fvm.WithExecutionEffortWeights( 1401 meter.ExecutionEffortWeights{ 1402 common.ComputationKindStatement: 0, 1403 common.ComputationKindLoop: 1 << meter.MeterExecutionInternalPrecisionBytes, 1404 common.ComputationKindFunctionInvocation: 0, 1405 }, 1406 ), 1407 ).withContextOptions( 1408 fvm.WithAccountStorageLimit(true), 1409 fvm.WithTransactionFeesEnabled(true), 1410 fvm.WithMemoryLimit(math.MaxUint64), 1411 ).run( 1412 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1413 // Use the maximum amount of computation so that the transaction still passes. 1414 loops := uint64(996) 1415 executionEffortNeededToCheckStorage := uint64(1) 1416 maxExecutionEffort := uint64(997) 1417 txBody := flow.NewTransactionBody(). 1418 SetScript([]byte(fmt.Sprintf(` 1419 transaction() {prepare(signer: &Account){var i=0; while i < %d {i = i +1 } } execute{}} 1420 `, loops))). 1421 SetProposalKey(chain.ServiceAddress(), 0, 0). 1422 AddAuthorizer(chain.ServiceAddress()). 1423 SetPayer(chain.ServiceAddress()). 1424 SetComputeLimit(maxExecutionEffort) 1425 1426 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1427 require.NoError(t, err) 1428 1429 executionSnapshot, output, err := vm.Run( 1430 ctx, 1431 fvm.Transaction(txBody, 0), 1432 snapshotTree) 1433 require.NoError(t, err) 1434 require.NoError(t, output.Err) 1435 1436 snapshotTree = snapshotTree.Append(executionSnapshot) 1437 1438 // expected computation used is number of loops + 1 (from the storage limit check). 1439 require.Equal(t, loops+executionEffortNeededToCheckStorage, output.ComputationUsed) 1440 1441 // increasing the number of loops should fail the transaction. 1442 loops = loops + 1 1443 txBody = flow.NewTransactionBody(). 1444 SetScript([]byte(fmt.Sprintf(` 1445 transaction() {prepare(signer: &Account){var i=0; while i < %d {i = i +1 } } execute{}} 1446 `, loops))). 1447 SetProposalKey(chain.ServiceAddress(), 0, 1). 1448 AddAuthorizer(chain.ServiceAddress()). 1449 SetPayer(chain.ServiceAddress()). 1450 SetComputeLimit(maxExecutionEffort) 1451 1452 err = testutil.SignTransactionAsServiceAccount(txBody, 1, chain) 1453 require.NoError(t, err) 1454 1455 _, output, err = vm.Run( 1456 ctx, 1457 fvm.Transaction(txBody, 0), 1458 snapshotTree) 1459 require.NoError(t, err) 1460 1461 require.ErrorContains(t, output.Err, "computation exceeds limit (997)") 1462 // expected computation used is still number of loops + 1 (from the storage limit check). 1463 require.Equal(t, loops+executionEffortNeededToCheckStorage, output.ComputationUsed) 1464 1465 for _, event := range output.Events { 1466 // the fee deduction event should only contain the max gas worth of execution effort. 1467 if strings.Contains(string(event.Type), "FlowFees.FeesDeducted") { 1468 v, err := ccf.Decode(nil, event.Payload) 1469 require.NoError(t, err) 1470 1471 ev := v.(cadence.Event) 1472 1473 actualExecutionEffort := cadence.SearchFieldByName(ev, "executionEffort") 1474 1475 require.Equal( 1476 t, 1477 maxExecutionEffort, 1478 uint64(actualExecutionEffort.(cadence.UFix64)), 1479 ) 1480 } 1481 } 1482 unittest.EnsureEventsIndexSeq(t, output.Events, chain.ChainID()) 1483 }, 1484 )) 1485 1486 t.Run("transaction with more accounts touched uses more computation", newVMTest().withBootstrapProcedureOptions( 1487 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1488 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1489 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1490 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 1491 fvm.WithExecutionEffortWeights( 1492 meter.ExecutionEffortWeights{ 1493 common.ComputationKindStatement: 0, 1494 // only count loops 1495 // the storage check has a loop 1496 common.ComputationKindLoop: 1 << meter.MeterExecutionInternalPrecisionBytes, 1497 common.ComputationKindFunctionInvocation: 0, 1498 }, 1499 ), 1500 ).withContextOptions( 1501 fvm.WithAccountStorageLimit(true), 1502 fvm.WithTransactionFeesEnabled(true), 1503 fvm.WithMemoryLimit(math.MaxUint64), 1504 ).run( 1505 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1506 // Create an account private key. 1507 privateKeys, err := testutil.GenerateAccountPrivateKeys(5) 1508 require.NoError(t, err) 1509 1510 // Bootstrap a ledger, creating accounts with the provided 1511 // private keys and the root account. 1512 snapshotTree, accounts, err := testutil.CreateAccounts( 1513 vm, 1514 snapshotTree, 1515 privateKeys, 1516 chain) 1517 require.NoError(t, err) 1518 1519 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1520 1521 // create a transaction without loops so only the looping in the storage check is counted. 1522 txBody := flow.NewTransactionBody(). 1523 SetScript([]byte(fmt.Sprintf(` 1524 import FungibleToken from 0x%s 1525 import FlowToken from 0x%s 1526 1527 transaction() { 1528 let sentVault: @{FungibleToken.Vault} 1529 1530 prepare(signer: auth(BorrowValue) &Account) { 1531 let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) 1532 ?? panic("Could not borrow reference to the owner's Vault!") 1533 1534 self.sentVault <- vaultRef.withdraw(amount: 5.0) 1535 } 1536 1537 execute { 1538 let recipient1 = getAccount(%s) 1539 let recipient2 = getAccount(%s) 1540 let recipient3 = getAccount(%s) 1541 let recipient4 = getAccount(%s) 1542 let recipient5 = getAccount(%s) 1543 1544 let receiverRef1 = recipient1.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 1545 ?? panic("Could not borrow receiver reference to the recipient's Vault") 1546 let receiverRef2 = recipient2.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 1547 ?? panic("Could not borrow receiver reference to the recipient's Vault") 1548 let receiverRef3 = recipient3.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 1549 ?? panic("Could not borrow receiver reference to the recipient's Vault") 1550 let receiverRef4 = recipient4.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 1551 ?? panic("Could not borrow receiver reference to the recipient's Vault") 1552 let receiverRef5 = recipient5.capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 1553 ?? panic("Could not borrow receiver reference to the recipient's Vault") 1554 1555 receiverRef1.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) 1556 receiverRef2.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) 1557 receiverRef3.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) 1558 receiverRef4.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) 1559 receiverRef5.deposit(from: <-self.sentVault.withdraw(amount: 1.0)) 1560 1561 destroy self.sentVault 1562 } 1563 }`, 1564 sc.FungibleToken.Address, 1565 sc.FlowToken.Address, 1566 accounts[0].HexWithPrefix(), 1567 accounts[1].HexWithPrefix(), 1568 accounts[2].HexWithPrefix(), 1569 accounts[3].HexWithPrefix(), 1570 accounts[4].HexWithPrefix(), 1571 ))). 1572 SetProposalKey(chain.ServiceAddress(), 0, 0). 1573 AddAuthorizer(chain.ServiceAddress()). 1574 SetPayer(chain.ServiceAddress()) 1575 1576 err = testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1577 require.NoError(t, err) 1578 1579 _, output, err := vm.Run( 1580 ctx, 1581 fvm.Transaction(txBody, 0), 1582 snapshotTree) 1583 require.NoError(t, err) 1584 require.NoError(t, output.Err) 1585 1586 // The storage check should loop once for each of the five accounts created + 1587 // once for the service account 1588 require.Equal(t, uint64(5+1), output.ComputationUsed) 1589 }, 1590 )) 1591 } 1592 1593 func TestStorageUsed(t *testing.T) { 1594 t.Parallel() 1595 1596 chain, vm := createChainAndVm(flow.Testnet) 1597 1598 ctx := fvm.NewContext( 1599 fvm.WithChain(chain), 1600 fvm.WithCadenceLogging(true), 1601 ) 1602 1603 code := []byte(` 1604 access(all) fun main(): UInt64 { 1605 1606 var addresses: [Address]= [ 1607 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1608 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1609 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1610 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1611 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1612 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1613 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1614 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1615 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1616 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 1617 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731, 0x2a3c4c2581cef731 1618 ] 1619 1620 var storageUsed: UInt64 = 0 1621 for address in addresses { 1622 let account = getAccount(address) 1623 storageUsed = account.storage.used 1624 } 1625 1626 return storageUsed 1627 } 1628 `) 1629 1630 address, err := hex.DecodeString("2a3c4c2581cef731") 1631 require.NoError(t, err) 1632 1633 accountStatusId := flow.AccountStatusRegisterID( 1634 flow.BytesToAddress(address)) 1635 1636 status := environment.NewAccountStatus() 1637 status.SetStorageUsed(5) 1638 1639 _, output, err := vm.Run( 1640 ctx, 1641 fvm.Script(code), 1642 snapshot.MapStorageSnapshot{ 1643 accountStatusId: status.ToBytes(), 1644 }) 1645 require.NoError(t, err) 1646 1647 require.Equal(t, cadence.NewUInt64(5), output.Value) 1648 } 1649 1650 func TestEnforcingComputationLimit(t *testing.T) { 1651 t.Parallel() 1652 1653 chain, vm := createChainAndVm(flow.Testnet) 1654 1655 const computationLimit = 5 1656 1657 type test struct { 1658 name string 1659 code string 1660 payerIsServAcc bool 1661 ok bool 1662 expCompUsed uint64 1663 } 1664 1665 tests := []test{ 1666 { 1667 name: "infinite while loop", 1668 code: ` 1669 while true {} 1670 `, 1671 payerIsServAcc: false, 1672 ok: false, 1673 expCompUsed: computationLimit + 1, 1674 }, 1675 { 1676 name: "limited while loop", 1677 code: ` 1678 var i = 0 1679 while i < 5 { 1680 i = i + 1 1681 } 1682 `, 1683 payerIsServAcc: false, 1684 ok: false, 1685 expCompUsed: computationLimit + 1, 1686 }, 1687 { 1688 name: "too many for-in loop iterations", 1689 code: ` 1690 for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {} 1691 `, 1692 payerIsServAcc: false, 1693 ok: false, 1694 expCompUsed: computationLimit + 1, 1695 }, 1696 { 1697 name: "too many for-in loop iterations", 1698 code: ` 1699 for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] {} 1700 `, 1701 payerIsServAcc: true, 1702 ok: true, 1703 expCompUsed: 11, 1704 }, 1705 { 1706 name: "some for-in loop iterations", 1707 code: ` 1708 for i in [1, 2, 3, 4] {} 1709 `, 1710 payerIsServAcc: false, 1711 ok: true, 1712 expCompUsed: 5, 1713 }, 1714 } 1715 1716 for _, test := range tests { 1717 1718 t.Run(test.name, func(t *testing.T) { 1719 ctx := fvm.NewContext( 1720 fvm.WithChain(chain), 1721 fvm.WithAuthorizationChecksEnabled(false), 1722 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1723 ) 1724 1725 script := []byte( 1726 fmt.Sprintf( 1727 ` 1728 transaction { 1729 prepare() { 1730 %s 1731 } 1732 } 1733 `, 1734 test.code, 1735 ), 1736 ) 1737 1738 txBody := flow.NewTransactionBody(). 1739 SetScript(script). 1740 SetComputeLimit(computationLimit) 1741 1742 if test.payerIsServAcc { 1743 txBody.SetPayer(chain.ServiceAddress()). 1744 SetComputeLimit(0) 1745 } 1746 tx := fvm.Transaction(txBody, 0) 1747 1748 _, output, err := vm.Run(ctx, tx, nil) 1749 require.NoError(t, err) 1750 require.Equal(t, test.expCompUsed, output.ComputationUsed) 1751 if test.ok { 1752 require.NoError(t, output.Err) 1753 } else { 1754 require.Error(t, output.Err) 1755 } 1756 1757 }) 1758 } 1759 } 1760 1761 func TestStorageCapacity(t *testing.T) { 1762 t.Run("Storage capacity updates on FLOW transfer", newVMTest(). 1763 withContextOptions( 1764 fvm.WithAuthorizationChecksEnabled(false), 1765 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1766 fvm.WithCadenceLogging(true), 1767 ). 1768 withBootstrapProcedureOptions( 1769 fvm.WithStorageMBPerFLOW(10_0000_0000), 1770 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1771 ). 1772 run(func( 1773 t *testing.T, 1774 vm fvm.VM, 1775 chain flow.Chain, 1776 ctx fvm.Context, 1777 snapshotTree snapshot.SnapshotTree, 1778 ) { 1779 service := chain.ServiceAddress() 1780 snapshotTree, signer := createAccount( 1781 t, 1782 vm, 1783 chain, 1784 ctx, 1785 snapshotTree) 1786 snapshotTree, target := createAccount( 1787 t, 1788 vm, 1789 chain, 1790 ctx, 1791 snapshotTree) 1792 1793 // Transfer FLOW from service account to test accounts 1794 1795 transferTxBody := transferTokensTx(chain). 1796 AddAuthorizer(service). 1797 AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))). 1798 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(signer))). 1799 SetProposalKey(service, 0, 0). 1800 SetPayer(service) 1801 1802 executionSnapshot, output, err := vm.Run( 1803 ctx, 1804 fvm.Transaction(transferTxBody, 0), 1805 snapshotTree) 1806 require.NoError(t, err) 1807 require.NoError(t, output.Err) 1808 1809 snapshotTree = snapshotTree.Append(executionSnapshot) 1810 1811 transferTxBody = transferTokensTx(chain). 1812 AddAuthorizer(service). 1813 AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))). 1814 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(target))). 1815 SetProposalKey(service, 0, 0). 1816 SetPayer(service) 1817 1818 executionSnapshot, output, err = vm.Run( 1819 ctx, 1820 fvm.Transaction(transferTxBody, 0), 1821 snapshotTree) 1822 require.NoError(t, err) 1823 require.NoError(t, output.Err) 1824 1825 snapshotTree = snapshotTree.Append(executionSnapshot) 1826 1827 // Perform test 1828 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1829 1830 txBody := flow.NewTransactionBody(). 1831 SetScript([]byte(fmt.Sprintf( 1832 ` 1833 import FungibleToken from 0x%s 1834 import FlowToken from 0x%s 1835 1836 transaction(target: Address) { 1837 prepare(signer: auth(BorrowValue) &Account) { 1838 let receiverRef = getAccount(target) 1839 .capabilities.borrow<&{FungibleToken.Receiver}>(/public/flowTokenReceiver) 1840 ?? panic("Could not borrow receiver reference to the recipient''s Vault") 1841 1842 let vaultRef = signer.storage 1843 .borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) 1844 ?? panic("Could not borrow reference to the owner''s Vault!") 1845 1846 var cap0: UInt64 = signer.storage.capacity 1847 1848 receiverRef.deposit(from: <- vaultRef.withdraw(amount: 0.0000001)) 1849 1850 var cap1: UInt64 = signer.storage.capacity 1851 1852 log(cap0 - cap1) 1853 } 1854 }`, 1855 sc.FungibleToken.Address.Hex(), 1856 sc.FlowToken.Address.Hex(), 1857 ))). 1858 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(target))). 1859 AddAuthorizer(signer) 1860 1861 _, output, err = vm.Run( 1862 ctx, 1863 fvm.Transaction(txBody, 0), 1864 snapshotTree) 1865 require.NoError(t, err) 1866 require.NoError(t, output.Err) 1867 1868 require.Len(t, output.Logs, 1) 1869 require.Equal(t, output.Logs[0], "1") 1870 }), 1871 ) 1872 } 1873 1874 func TestScriptContractMutationsFailure(t *testing.T) { 1875 t.Parallel() 1876 1877 t.Run("contract additions are not committed", 1878 newVMTest().run( 1879 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1880 // Create an account private key. 1881 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1882 require.NoError(t, err) 1883 1884 // Bootstrap a ledger, creating accounts with the provided 1885 // private keys and the root account. 1886 snapshotTree, accounts, err := testutil.CreateAccounts( 1887 vm, 1888 snapshotTree, 1889 privateKeys, 1890 chain) 1891 require.NoError(t, err) 1892 account := accounts[0] 1893 address := cadence.NewAddress(account) 1894 1895 scriptCtx := fvm.NewContextFromParent(ctx) 1896 1897 contract := "access(all) contract Foo {}" 1898 1899 script := fvm.Script([]byte(fmt.Sprintf(` 1900 access(all) fun main(account: Address) { 1901 let acc = getAuthAccount<auth(AddContract) &Account>(account) 1902 acc.contracts.add(name: "Foo", code: "%s".decodeHex()) 1903 }`, hex.EncodeToString([]byte(contract))), 1904 )).WithArguments( 1905 jsoncdc.MustEncode(address), 1906 ) 1907 1908 _, output, err := vm.Run(scriptCtx, script, snapshotTree) 1909 require.NoError(t, err) 1910 require.Error(t, output.Err) 1911 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 1912 // modifications to contracts are not supported in scripts 1913 require.True(t, errors.IsOperationNotSupportedError(output.Err)) 1914 }, 1915 ), 1916 ) 1917 1918 t.Run("contract removals are not committed", 1919 newVMTest().run( 1920 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1921 // Create an account private key. 1922 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1923 privateKey := privateKeys[0] 1924 require.NoError(t, err) 1925 1926 // Bootstrap a ledger, creating accounts with the provided 1927 // private keys and the root account. 1928 snapshotTree, accounts, err := testutil.CreateAccounts( 1929 vm, 1930 snapshotTree, 1931 privateKeys, 1932 chain) 1933 require.NoError(t, err) 1934 account := accounts[0] 1935 address := cadence.NewAddress(account) 1936 1937 subCtx := fvm.NewContextFromParent(ctx) 1938 1939 contract := "access(all) contract Foo {}" 1940 1941 txBody := flow.NewTransactionBody().SetScript([]byte(fmt.Sprintf(` 1942 transaction { 1943 prepare(signer: auth(AddContract) &Account, service: &Account) { 1944 signer.contracts.add(name: "Foo", code: "%s".decodeHex()) 1945 } 1946 } 1947 `, hex.EncodeToString([]byte(contract))))). 1948 AddAuthorizer(account). 1949 AddAuthorizer(chain.ServiceAddress()). 1950 SetPayer(chain.ServiceAddress()). 1951 SetProposalKey(chain.ServiceAddress(), 0, 0) 1952 1953 _ = testutil.SignPayload(txBody, account, privateKey) 1954 _ = testutil.SignEnvelope( 1955 txBody, 1956 chain.ServiceAddress(), 1957 unittest.ServiceAccountPrivateKey) 1958 1959 executionSnapshot, output, err := vm.Run( 1960 subCtx, 1961 fvm.Transaction(txBody, 0), 1962 snapshotTree) 1963 require.NoError(t, err) 1964 require.NoError(t, output.Err) 1965 1966 snapshotTree = snapshotTree.Append(executionSnapshot) 1967 1968 script := fvm.Script([]byte(` 1969 access(all) fun main(account: Address) { 1970 let acc = getAuthAccount<auth(RemoveContract) &Account>(account) 1971 let n = acc.contracts.names[0] 1972 acc.contracts.remove(name: n) 1973 }`, 1974 )).WithArguments( 1975 jsoncdc.MustEncode(address), 1976 ) 1977 1978 _, output, err = vm.Run(subCtx, script, snapshotTree) 1979 require.NoError(t, err) 1980 require.Error(t, output.Err) 1981 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 1982 // modifications to contracts are not supported in scripts 1983 require.True(t, errors.IsOperationNotSupportedError(output.Err)) 1984 }, 1985 ), 1986 ) 1987 1988 t.Run("contract updates are not committed", 1989 newVMTest().run( 1990 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1991 // Create an account private key. 1992 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1993 privateKey := privateKeys[0] 1994 require.NoError(t, err) 1995 1996 // Bootstrap a ledger, creating accounts with the provided 1997 // private keys and the root account. 1998 snapshotTree, accounts, err := testutil.CreateAccounts( 1999 vm, 2000 snapshotTree, 2001 privateKeys, 2002 chain) 2003 require.NoError(t, err) 2004 account := accounts[0] 2005 address := cadence.NewAddress(account) 2006 2007 subCtx := fvm.NewContextFromParent(ctx) 2008 2009 contract := "access(all) contract Foo {}" 2010 2011 txBody := flow.NewTransactionBody().SetScript([]byte(fmt.Sprintf(` 2012 transaction { 2013 prepare(signer: auth(AddContract) &Account, service: &Account) { 2014 signer.contracts.add(name: "Foo", code: "%s".decodeHex()) 2015 } 2016 } 2017 `, hex.EncodeToString([]byte(contract))))). 2018 AddAuthorizer(account). 2019 AddAuthorizer(chain.ServiceAddress()). 2020 SetPayer(chain.ServiceAddress()). 2021 SetProposalKey(chain.ServiceAddress(), 0, 0) 2022 2023 _ = testutil.SignPayload(txBody, account, privateKey) 2024 _ = testutil.SignEnvelope( 2025 txBody, 2026 chain.ServiceAddress(), 2027 unittest.ServiceAccountPrivateKey) 2028 2029 executionSnapshot, output, err := vm.Run( 2030 subCtx, 2031 fvm.Transaction(txBody, 0), 2032 snapshotTree) 2033 require.NoError(t, err) 2034 require.NoError(t, output.Err) 2035 2036 snapshotTree = snapshotTree.Append(executionSnapshot) 2037 2038 script := fvm.Script([]byte(fmt.Sprintf(` 2039 access(all) fun main(account: Address) { 2040 let acc = getAuthAccount<auth(UpdateContract) &Account>(account) 2041 let n = acc.contracts.names[0] 2042 acc.contracts.update(name: n, code: "%s".decodeHex()) 2043 }`, hex.EncodeToString([]byte(contract))))).WithArguments( 2044 jsoncdc.MustEncode(address), 2045 ) 2046 2047 _, output, err = vm.Run(subCtx, script, snapshotTree) 2048 require.NoError(t, err) 2049 require.Error(t, output.Err) 2050 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 2051 // modifications to contracts are not supported in scripts 2052 require.True(t, errors.IsOperationNotSupportedError(output.Err)) 2053 }, 2054 ), 2055 ) 2056 } 2057 2058 func TestScriptAccountKeyMutationsFailure(t *testing.T) { 2059 t.Parallel() 2060 2061 t.Run("Account key additions are not committed", 2062 newVMTest().run( 2063 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 2064 // Create an account private key. 2065 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 2066 require.NoError(t, err) 2067 2068 // Bootstrap a ledger, creating accounts with the provided 2069 // private keys and the root account. 2070 snapshotTree, accounts, err := testutil.CreateAccounts( 2071 vm, 2072 snapshotTree, 2073 privateKeys, 2074 chain) 2075 require.NoError(t, err) 2076 account := accounts[0] 2077 address := cadence.NewAddress(account) 2078 2079 scriptCtx := fvm.NewContextFromParent(ctx) 2080 2081 seed := make([]byte, crypto.KeyGenSeedMinLen) 2082 _, _ = rand.Read(seed) 2083 2084 privateKey, _ := crypto.GeneratePrivateKey(crypto.ECDSAP256, seed) 2085 2086 script := fvm.Script([]byte(` 2087 access(all) fun main(account: Address, k: [UInt8]) { 2088 let acc = getAuthAccount<auth(AddKey) &Account>(account) 2089 acc.keys.add( 2090 publicKey: PublicKey( 2091 publicKey: k, 2092 signatureAlgorithm: SignatureAlgorithm.ECDSA_P256 2093 ), 2094 hashAlgorithm: HashAlgorithm.SHA3_256, 2095 weight: 100.0 2096 ) 2097 }`, 2098 )).WithArguments( 2099 jsoncdc.MustEncode(address), 2100 jsoncdc.MustEncode(testutil.BytesToCadenceArray( 2101 privateKey.PublicKey().Encode(), 2102 )), 2103 ) 2104 2105 _, output, err := vm.Run(scriptCtx, script, snapshotTree) 2106 require.NoError(t, err) 2107 require.Error(t, output.Err) 2108 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 2109 // modifications to public keys are not supported in scripts 2110 require.True(t, errors.IsOperationNotSupportedError(output.Err)) 2111 }, 2112 ), 2113 ) 2114 2115 t.Run("Account key removals are not committed", 2116 newVMTest().run( 2117 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 2118 // Create an account private key. 2119 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 2120 require.NoError(t, err) 2121 2122 // Bootstrap a ledger, creating accounts with the provided 2123 // private keys and the root account. 2124 snapshotTree, accounts, err := testutil.CreateAccounts( 2125 vm, 2126 snapshotTree, 2127 privateKeys, 2128 chain) 2129 require.NoError(t, err) 2130 account := accounts[0] 2131 address := cadence.NewAddress(account) 2132 2133 scriptCtx := fvm.NewContextFromParent(ctx) 2134 2135 script := fvm.Script([]byte(` 2136 access(all) fun main(account: Address) { 2137 let acc = getAuthAccount<auth(RevokeKey) &Account>(account) 2138 acc.keys.revoke(keyIndex: 0) 2139 }`, 2140 )).WithArguments( 2141 jsoncdc.MustEncode(address), 2142 ) 2143 2144 _, output, err := vm.Run(scriptCtx, script, snapshotTree) 2145 require.NoError(t, err) 2146 require.Error(t, output.Err) 2147 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 2148 // modifications to public keys are not supported in scripts 2149 require.True(t, errors.IsOperationNotSupportedError(output.Err)) 2150 }, 2151 ), 2152 ) 2153 } 2154 2155 func TestScriptExecutionLimit(t *testing.T) { 2156 2157 t.Parallel() 2158 2159 script := fvm.Script([]byte(` 2160 access(all) fun main() { 2161 var s: Int256 = 1024102410241024 2162 var i: Int256 = 0 2163 var a: Int256 = 7 2164 var b: Int256 = 5 2165 var c: Int256 = 2 2166 2167 while i < 150000 { 2168 s = s * a 2169 s = s / b 2170 s = s / c 2171 i = i + 1 2172 } 2173 } 2174 `)) 2175 2176 bootstrapProcedureOptions := []fvm.BootstrapProcedureOption{ 2177 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 2178 fvm.WithExecutionMemoryLimit(math.MaxUint32), 2179 fvm.WithExecutionEffortWeights(map[common.ComputationKind]uint64{ 2180 common.ComputationKindStatement: 1569, 2181 common.ComputationKindLoop: 1569, 2182 common.ComputationKindFunctionInvocation: 1569, 2183 environment.ComputationKindGetValue: 808, 2184 environment.ComputationKindCreateAccount: 2837670, 2185 environment.ComputationKindSetValue: 765, 2186 }), 2187 fvm.WithExecutionMemoryWeights(meter.DefaultMemoryWeights), 2188 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 2189 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 2190 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 2191 } 2192 2193 t.Run("Exceeding computation limit", 2194 newVMTest().withBootstrapProcedureOptions( 2195 bootstrapProcedureOptions..., 2196 ).withContextOptions( 2197 fvm.WithTransactionFeesEnabled(true), 2198 fvm.WithAccountStorageLimit(true), 2199 fvm.WithComputationLimit(10000), 2200 ).run( 2201 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 2202 scriptCtx := fvm.NewContextFromParent(ctx) 2203 2204 _, output, err := vm.Run(scriptCtx, script, snapshotTree) 2205 require.NoError(t, err) 2206 require.Error(t, output.Err) 2207 require.True(t, errors.IsComputationLimitExceededError(output.Err)) 2208 require.ErrorContains(t, output.Err, "computation exceeds limit (10000)") 2209 require.GreaterOrEqual(t, output.ComputationUsed, uint64(10000)) 2210 require.GreaterOrEqual(t, output.MemoryEstimate, uint64(548020260)) 2211 }, 2212 ), 2213 ) 2214 2215 t.Run("Sufficient computation limit", 2216 newVMTest().withBootstrapProcedureOptions( 2217 bootstrapProcedureOptions..., 2218 ).withContextOptions( 2219 fvm.WithTransactionFeesEnabled(true), 2220 fvm.WithAccountStorageLimit(true), 2221 fvm.WithComputationLimit(20000), 2222 ).run( 2223 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 2224 scriptCtx := fvm.NewContextFromParent(ctx) 2225 2226 _, output, err := vm.Run(scriptCtx, script, snapshotTree) 2227 require.NoError(t, err) 2228 require.NoError(t, output.Err) 2229 require.GreaterOrEqual(t, output.ComputationUsed, uint64(17955)) 2230 require.GreaterOrEqual(t, output.MemoryEstimate, uint64(984017413)) 2231 }, 2232 ), 2233 ) 2234 } 2235 2236 func TestInteractionLimit(t *testing.T) { 2237 type testCase struct { 2238 name string 2239 interactionLimit uint64 2240 require func(t *testing.T, output fvm.ProcedureOutput) 2241 } 2242 2243 testCases := []testCase{ 2244 { 2245 name: "high limit succeeds", 2246 interactionLimit: math.MaxUint64, 2247 require: func(t *testing.T, output fvm.ProcedureOutput) { 2248 require.NoError(t, output.Err) 2249 require.Len(t, output.Events, 9) 2250 }, 2251 }, 2252 { 2253 name: "default limit succeeds", 2254 interactionLimit: fvm.DefaultMaxInteractionSize, 2255 require: func(t *testing.T, output fvm.ProcedureOutput) { 2256 require.NoError(t, output.Err) 2257 require.Len(t, output.Events, 9) 2258 unittest.EnsureEventsIndexSeq(t, output.Events, flow.Testnet.Chain().ChainID()) 2259 }, 2260 }, 2261 { 2262 name: "low limit succeeds", 2263 interactionLimit: 170000, 2264 require: func(t *testing.T, output fvm.ProcedureOutput) { 2265 require.NoError(t, output.Err) 2266 require.Len(t, output.Events, 9) 2267 unittest.EnsureEventsIndexSeq(t, output.Events, flow.Testnet.Chain().ChainID()) 2268 }, 2269 }, 2270 { 2271 name: "even lower low limit fails, and has only 5 events", 2272 interactionLimit: 5000, 2273 require: func(t *testing.T, output fvm.ProcedureOutput) { 2274 require.Error(t, output.Err) 2275 require.Len(t, output.Events, 5) 2276 unittest.EnsureEventsIndexSeq(t, output.Events, flow.Testnet.Chain().ChainID()) 2277 }, 2278 }, 2279 } 2280 2281 // === setup === 2282 // setup an address with some funds 2283 var privateKey flow.AccountPrivateKey 2284 var address flow.Address 2285 vmt, err := newVMTest().withBootstrapProcedureOptions( 2286 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 2287 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 2288 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 2289 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 2290 fvm.WithExecutionMemoryLimit(math.MaxUint64), 2291 ).withContextOptions( 2292 fvm.WithTransactionFeesEnabled(true), 2293 fvm.WithAccountStorageLimit(true), 2294 ).bootstrapWith( 2295 func(vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) (snapshot.SnapshotTree, error) { 2296 // ==== Create an account ==== 2297 var txBody *flow.TransactionBody 2298 privateKey, txBody = testutil.CreateAccountCreationTransaction(t, chain) 2299 2300 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 2301 if err != nil { 2302 return snapshotTree, err 2303 } 2304 2305 executionSnapshot, output, err := vm.Run( 2306 ctx, 2307 fvm.Transaction(txBody, 0), 2308 snapshotTree) 2309 if err != nil { 2310 return snapshotTree, err 2311 } 2312 2313 snapshotTree = snapshotTree.Append(executionSnapshot) 2314 2315 if output.Err != nil { 2316 return snapshotTree, output.Err 2317 } 2318 2319 accountCreatedEvents := filterAccountCreatedEvents(output.Events) 2320 2321 // read the address of the account created (e.g. "0x01" and convert it to flow.address) 2322 data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload) 2323 if err != nil { 2324 return snapshotTree, err 2325 } 2326 2327 address = flow.ConvertAddress( 2328 cadence.SearchFieldByName( 2329 data.(cadence.Event), 2330 stdlib2.AccountEventAddressParameter.Identifier, 2331 ).(cadence.Address), 2332 ) 2333 2334 // ==== Transfer tokens to new account ==== 2335 txBody = transferTokensTx(chain). 2336 AddAuthorizer(chain.ServiceAddress()). 2337 AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_000_000))). 2338 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(address))) 2339 2340 txBody.SetProposalKey(chain.ServiceAddress(), 0, 1) 2341 txBody.SetPayer(chain.ServiceAddress()) 2342 2343 err = testutil.SignEnvelope( 2344 txBody, 2345 chain.ServiceAddress(), 2346 unittest.ServiceAccountPrivateKey, 2347 ) 2348 if err != nil { 2349 return snapshotTree, err 2350 } 2351 2352 executionSnapshot, output, err = vm.Run( 2353 ctx, 2354 fvm.Transaction(txBody, 0), 2355 snapshotTree) 2356 if err != nil { 2357 return snapshotTree, err 2358 } 2359 2360 return snapshotTree.Append(executionSnapshot), output.Err 2361 }, 2362 ) 2363 require.NoError(t, err) 2364 2365 for _, tc := range testCases { 2366 t.Run(tc.name, vmt.run( 2367 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 2368 // ==== Transfer funds with lowe interaction limit ==== 2369 txBody := transferTokensTx(chain). 2370 AddAuthorizer(address). 2371 AddArgument(jsoncdc.MustEncode(cadence.UFix64(1))). 2372 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(chain.ServiceAddress()))) 2373 2374 txBody.SetProposalKey(address, 0, 0) 2375 txBody.SetPayer(address) 2376 2377 hasher, err := exeUtils.NewHasher(privateKey.HashAlgo) 2378 require.NoError(t, err) 2379 2380 sig, err := txBody.Sign(txBody.EnvelopeMessage(), privateKey.PrivateKey, hasher) 2381 require.NoError(t, err) 2382 txBody.AddEnvelopeSignature(address, 0, sig) 2383 2384 // ==== IMPORTANT LINE ==== 2385 ctx.MaxStateInteractionSize = tc.interactionLimit 2386 2387 _, output, err := vm.Run( 2388 ctx, 2389 fvm.Transaction(txBody, 0), 2390 snapshotTree) 2391 require.NoError(t, err) 2392 tc.require(t, output) 2393 }), 2394 ) 2395 } 2396 } 2397 2398 func TestAttachments(t *testing.T) { 2399 2400 newVMTest(). 2401 withBootstrapProcedureOptions(). 2402 withContextOptions( 2403 fvm.WithReusableCadenceRuntimePool( 2404 reusableRuntime.NewReusableCadenceRuntimePool( 2405 1, 2406 runtime.Config{ 2407 AttachmentsEnabled: true, 2408 }, 2409 ), 2410 ), 2411 ). 2412 run( 2413 func( 2414 t *testing.T, 2415 vm fvm.VM, 2416 chain flow.Chain, 2417 ctx fvm.Context, 2418 snapshotTree snapshot.SnapshotTree, 2419 ) { 2420 script := fvm.Script([]byte(` 2421 2422 access(all) resource R {} 2423 2424 access(all) attachment A for R {} 2425 2426 access(all) fun main() { 2427 let r <- create R() 2428 r[A] 2429 destroy r 2430 } 2431 `)) 2432 2433 _, output, err := vm.Run(ctx, script, snapshotTree) 2434 require.NoError(t, err) 2435 require.NoError(t, output.Err) 2436 2437 }, 2438 )(t) 2439 2440 } 2441 2442 func TestCapabilityControllers(t *testing.T) { 2443 test := func(t *testing.T) { 2444 newVMTest(). 2445 withBootstrapProcedureOptions(). 2446 withContextOptions( 2447 fvm.WithReusableCadenceRuntimePool( 2448 reusableRuntime.NewReusableCadenceRuntimePool( 2449 1, 2450 runtime.Config{}, 2451 ), 2452 ), 2453 ). 2454 run(func( 2455 t *testing.T, 2456 vm fvm.VM, 2457 chain flow.Chain, 2458 ctx fvm.Context, 2459 snapshotTree snapshot.SnapshotTree, 2460 ) { 2461 txBody := flow.NewTransactionBody(). 2462 SetScript([]byte(` 2463 transaction { 2464 prepare(signer: auth(Capabilities) &Account) { 2465 let cap = signer.capabilities.storage.issue<&Int>(/storage/foo) 2466 assert(cap.id == 6) 2467 2468 let cap2 = signer.capabilities.storage.issue<&String>(/storage/bar) 2469 assert(cap2.id == 7) 2470 } 2471 } 2472 `)). 2473 SetProposalKey(chain.ServiceAddress(), 0, 0). 2474 AddAuthorizer(chain.ServiceAddress()). 2475 SetPayer(chain.ServiceAddress()) 2476 2477 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 2478 require.NoError(t, err) 2479 2480 _, output, err := vm.Run( 2481 ctx, 2482 fvm.Transaction(txBody, 0), 2483 snapshotTree) 2484 require.NoError(t, err) 2485 require.NoError(t, output.Err) 2486 }, 2487 )(t) 2488 } 2489 2490 test(t) 2491 2492 } 2493 2494 func TestStorageIterationWithBrokenValues(t *testing.T) { 2495 2496 t.Parallel() 2497 2498 newVMTest(). 2499 withBootstrapProcedureOptions(). 2500 withContextOptions( 2501 fvm.WithReusableCadenceRuntimePool( 2502 reusableRuntime.NewReusableCadenceRuntimePool( 2503 1, 2504 runtime.Config{}, 2505 ), 2506 ), 2507 fvm.WithContractDeploymentRestricted(false), 2508 ). 2509 run( 2510 func( 2511 t *testing.T, 2512 vm fvm.VM, 2513 chain flow.Chain, 2514 ctx fvm.Context, 2515 snapshotTree snapshot.SnapshotTree, 2516 ) { 2517 // Create a private key 2518 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 2519 require.NoError(t, err) 2520 2521 // Bootstrap a ledger, creating an account with the provided private key and the root account. 2522 snapshotTree, accounts, err := testutil.CreateAccounts( 2523 vm, 2524 snapshotTree, 2525 privateKeys, 2526 chain, 2527 ) 2528 require.NoError(t, err) 2529 2530 contractA := ` 2531 access(all) contract A { 2532 access(all) struct interface Foo{} 2533 } 2534 ` 2535 2536 updatedContractA := ` 2537 access(all) contract A { 2538 access(all) struct interface Foo{ 2539 access(all) fun hello() 2540 } 2541 } 2542 ` 2543 2544 contractB := fmt.Sprintf(` 2545 import A from %s 2546 2547 access(all) contract B { 2548 access(all) struct Bar : A.Foo {} 2549 2550 access(all) struct interface Foo2{} 2551 }`, 2552 accounts[0].HexWithPrefix(), 2553 ) 2554 2555 contractC := fmt.Sprintf(` 2556 import B from %s 2557 import A from %s 2558 2559 access(all) contract C { 2560 access(all) struct Bar : A.Foo, B.Foo2 {} 2561 2562 access(all) struct interface Foo3{} 2563 }`, 2564 accounts[0].HexWithPrefix(), 2565 accounts[0].HexWithPrefix(), 2566 ) 2567 2568 contractD := fmt.Sprintf(` 2569 import C from %s 2570 import B from %s 2571 import A from %s 2572 2573 access(all) contract D { 2574 access(all) struct Bar : A.Foo, B.Foo2, C.Foo3 {} 2575 }`, 2576 accounts[0].HexWithPrefix(), 2577 accounts[0].HexWithPrefix(), 2578 accounts[0].HexWithPrefix(), 2579 ) 2580 2581 var sequenceNumber uint64 = 0 2582 2583 runTransaction := func(code []byte) { 2584 txBody := flow.NewTransactionBody(). 2585 SetScript(code). 2586 SetPayer(chain.ServiceAddress()). 2587 SetProposalKey(chain.ServiceAddress(), 0, sequenceNumber). 2588 AddAuthorizer(accounts[0]) 2589 2590 _ = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 2591 _ = testutil.SignEnvelope(txBody, chain.ServiceAddress(), unittest.ServiceAccountPrivateKey) 2592 2593 executionSnapshot, output, err := vm.Run( 2594 ctx, 2595 fvm.Transaction(txBody, 0), 2596 snapshotTree, 2597 ) 2598 require.NoError(t, err) 2599 require.NoError(t, output.Err) 2600 2601 snapshotTree = snapshotTree.Append(executionSnapshot) 2602 2603 // increment sequence number 2604 sequenceNumber++ 2605 } 2606 2607 // Deploy `A` 2608 runTransaction(utils.DeploymentTransaction( 2609 "A", 2610 []byte(contractA), 2611 )) 2612 2613 // Deploy `B` 2614 runTransaction(utils.DeploymentTransaction( 2615 "B", 2616 []byte(contractB), 2617 )) 2618 2619 // Deploy `C` 2620 runTransaction(utils.DeploymentTransaction( 2621 "C", 2622 []byte(contractC), 2623 )) 2624 2625 // Deploy `D` 2626 runTransaction(utils.DeploymentTransaction( 2627 "D", 2628 []byte(contractD), 2629 )) 2630 2631 // Store values 2632 runTransaction([]byte(fmt.Sprintf( 2633 ` 2634 import D from %s 2635 import C from %s 2636 import B from %s 2637 2638 transaction { 2639 prepare(signer: auth(Capabilities, Storage) &Account) { 2640 signer.storage.save("Hello, World!", to: /storage/a) 2641 signer.storage.save(["one", "two", "three"], to: /storage/b) 2642 signer.storage.save(D.Bar(), to: /storage/c) 2643 signer.storage.save(C.Bar(), to: /storage/d) 2644 signer.storage.save(B.Bar(), to: /storage/e) 2645 2646 let aCap = signer.capabilities.storage.issue<&String>(/storage/a) 2647 signer.capabilities.publish(aCap, at: /public/a) 2648 2649 let bCap = signer.capabilities.storage.issue<&[String]>(/storage/b) 2650 signer.capabilities.publish(bCap, at: /public/b) 2651 2652 let cCap = signer.capabilities.storage.issue<&D.Bar>(/storage/c) 2653 signer.capabilities.publish(cCap, at: /public/c) 2654 2655 let dCap = signer.capabilities.storage.issue<&C.Bar>(/storage/d) 2656 signer.capabilities.publish(dCap, at: /public/d) 2657 2658 let eCap = signer.capabilities.storage.issue<&B.Bar>(/storage/e) 2659 signer.capabilities.publish(eCap, at: /public/e) 2660 } 2661 }`, 2662 accounts[0].HexWithPrefix(), 2663 accounts[0].HexWithPrefix(), 2664 accounts[0].HexWithPrefix(), 2665 ))) 2666 2667 // Update `A`, such that `B`, `C` and `D` are now broken. 2668 runTransaction(utils.UpdateTransaction( 2669 "A", 2670 []byte(updatedContractA), 2671 )) 2672 2673 // Iterate stored values 2674 runTransaction([]byte( 2675 ` 2676 transaction { 2677 prepare(account: auth(Storage) &Account) { 2678 var total = 0 2679 account.storage.forEachPublic(fun (path: PublicPath, type: Type): Bool { 2680 let cap = account.capabilities.get<&AnyStruct>(path) 2681 if cap.check() { 2682 total = total + 1 2683 } 2684 return true 2685 }) 2686 assert(total == 2, message:"found ".concat(total.toString())) 2687 2688 total = 0 2689 account.storage.forEachStored(fun (path: StoragePath, type: Type): Bool { 2690 if account.storage.check<AnyStruct>(from: path) { 2691 account.storage.copy<AnyStruct>(from: path) 2692 total = total + 1 2693 } 2694 return true 2695 }) 2696 2697 assert(total == 2, message:"found ".concat(total.toString())) 2698 } 2699 }`, 2700 )) 2701 }, 2702 )(t) 2703 } 2704 2705 func TestEntropyCallOnlyOkIfAllowed(t *testing.T) { 2706 source := testutil.EntropyProviderFixture(nil) 2707 2708 test := func(t *testing.T, allowed bool) { 2709 newVMTest(). 2710 withBootstrapProcedureOptions(). 2711 withContextOptions( 2712 fvm.WithRandomSourceHistoryCallAllowed(allowed), 2713 fvm.WithEntropyProvider(source), 2714 ). 2715 run(func( 2716 t *testing.T, 2717 vm fvm.VM, 2718 chain flow.Chain, 2719 ctx fvm.Context, 2720 snapshotTree snapshot.SnapshotTree, 2721 ) { 2722 txBody := flow.NewTransactionBody(). 2723 SetScript([]byte(` 2724 transaction { 2725 prepare() { 2726 randomSourceHistory() 2727 } 2728 } 2729 `)). 2730 SetProposalKey(chain.ServiceAddress(), 0, 0). 2731 SetPayer(chain.ServiceAddress()) 2732 2733 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 2734 require.NoError(t, err) 2735 2736 _, output, err := vm.Run( 2737 ctx, 2738 fvm.Transaction(txBody, 0), 2739 snapshotTree) 2740 require.NoError(t, err) 2741 2742 if allowed { 2743 require.NoError(t, output.Err) 2744 } else { 2745 require.Error(t, output.Err) 2746 require.True(t, errors.HasErrorCode(output.Err, errors.ErrCodeOperationNotSupportedError)) 2747 } 2748 }, 2749 )(t) 2750 } 2751 2752 t.Run("enabled", func(t *testing.T) { 2753 test(t, true) 2754 }) 2755 2756 t.Run("disabled", func(t *testing.T) { 2757 test(t, false) 2758 }) 2759 } 2760 2761 func TestEntropyCallExpectsNoParameters(t *testing.T) { 2762 source := testutil.EntropyProviderFixture(nil) 2763 newVMTest(). 2764 withBootstrapProcedureOptions(). 2765 withContextOptions( 2766 fvm.WithRandomSourceHistoryCallAllowed(true), 2767 fvm.WithEntropyProvider(source), 2768 ). 2769 run(func( 2770 t *testing.T, 2771 vm fvm.VM, 2772 chain flow.Chain, 2773 ctx fvm.Context, 2774 snapshotTree snapshot.SnapshotTree, 2775 ) { 2776 txBody := flow.NewTransactionBody(). 2777 SetScript([]byte(` 2778 transaction { 2779 prepare() { 2780 randomSourceHistory("foo") 2781 } 2782 } 2783 `)). 2784 SetProposalKey(chain.ServiceAddress(), 0, 0). 2785 SetPayer(chain.ServiceAddress()) 2786 2787 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 2788 require.NoError(t, err) 2789 2790 _, output, err := vm.Run( 2791 ctx, 2792 fvm.Transaction(txBody, 0), 2793 snapshotTree) 2794 require.NoError(t, err) 2795 2796 require.ErrorContains(t, output.Err, "too many arguments") 2797 }, 2798 )(t) 2799 } 2800 2801 func TestTransientNetworkCoreContractAddresses(t *testing.T) { 2802 2803 // This test ensures that the transient networks have the correct core contract addresses. 2804 newVMTest(). 2805 run( 2806 func( 2807 t *testing.T, 2808 vm fvm.VM, 2809 chain flow.Chain, 2810 ctx fvm.Context, 2811 snapshotTree snapshot.SnapshotTree, 2812 ) { 2813 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 2814 2815 for _, contract := range sc.All() { 2816 txnState := testutils.NewSimpleTransaction(snapshotTree) 2817 accounts := environment.NewAccounts(txnState) 2818 2819 yes, err := accounts.ContractExists(contract.Name, contract.Address) 2820 require.NoError(t, err) 2821 require.True(t, yes, "contract %s does not exist", contract.Name) 2822 } 2823 }) 2824 } 2825 2826 func TestEVM(t *testing.T) { 2827 blocks := new(envMock.Blocks) 2828 block1 := unittest.BlockFixture() 2829 blocks.On("ByHeightFrom", 2830 block1.Header.Height, 2831 block1.Header, 2832 ).Return(block1.Header, nil) 2833 2834 ctxOpts := []fvm.Option{ 2835 // default is testnet, but testnet has a special EVM storage contract location 2836 // so we have to use emulator here so that the EVM storage contract is deployed 2837 // to the 5th address 2838 fvm.WithChain(flow.Emulator.Chain()), 2839 fvm.WithEVMEnabled(true), 2840 fvm.WithBlocks(blocks), 2841 fvm.WithBlockHeader(block1.Header), 2842 fvm.WithCadenceLogging(true), 2843 } 2844 2845 t.Run("successful transaction", newVMTest(). 2846 withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)). 2847 withContextOptions(ctxOpts...). 2848 run(func( 2849 t *testing.T, 2850 vm fvm.VM, 2851 chain flow.Chain, 2852 ctx fvm.Context, 2853 snapshotTree snapshot.SnapshotTree, 2854 ) { 2855 // generate test address 2856 genArr := make([]cadence.Value, 20) 2857 for i := range genArr { 2858 genArr[i] = cadence.UInt8(i) 2859 } 2860 addrBytes := cadence.NewArray(genArr).WithType(stdlib.EVMAddressBytesCadenceType) 2861 encodedArg, err := jsoncdc.Encode(addrBytes) 2862 require.NoError(t, err) 2863 2864 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 2865 2866 txBody := flow.NewTransactionBody(). 2867 SetScript([]byte(fmt.Sprintf(` 2868 import EVM from %s 2869 2870 transaction(bytes: [UInt8; 20]) { 2871 execute { 2872 let addr = EVM.EVMAddress(bytes: bytes) 2873 log(addr) 2874 } 2875 } 2876 `, sc.EVMContract.Address.HexWithPrefix()))). 2877 SetProposalKey(chain.ServiceAddress(), 0, 0). 2878 SetPayer(chain.ServiceAddress()). 2879 AddArgument(encodedArg) 2880 2881 err = testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 2882 require.NoError(t, err) 2883 2884 _, output, err := vm.Run( 2885 ctx, 2886 fvm.Transaction(txBody, 0), 2887 snapshotTree) 2888 2889 require.NoError(t, err) 2890 require.NoError(t, output.Err) 2891 require.Len(t, output.Logs, 1) 2892 require.Equal(t, output.Logs[0], fmt.Sprintf( 2893 "A.%s.EVM.EVMAddress(bytes: %s)", 2894 sc.EVMContract.Address, 2895 addrBytes.String(), 2896 )) 2897 }), 2898 ) 2899 2900 // this test makes sure the execution error is correctly handled and returned as a correct type 2901 t.Run("execution reverted", newVMTest(). 2902 withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)). 2903 withContextOptions(ctxOpts...). 2904 run(func( 2905 t *testing.T, 2906 vm fvm.VM, 2907 chain flow.Chain, 2908 ctx fvm.Context, 2909 snapshotTree snapshot.SnapshotTree, 2910 ) { 2911 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 2912 script := fvm.Script([]byte(fmt.Sprintf(` 2913 import EVM from %s 2914 2915 access(all) fun main() { 2916 let bal = EVM.Balance(attoflow: 1000000000000000000) 2917 let acc <- EVM.createCadenceOwnedAccount() 2918 2919 // withdraw insufficient balance 2920 destroy acc.withdraw(balance: bal) 2921 destroy acc 2922 } 2923 `, sc.EVMContract.Address.HexWithPrefix()))) 2924 2925 _, output, err := vm.Run(ctx, script, snapshotTree) 2926 2927 require.NoError(t, err) 2928 require.Error(t, output.Err) 2929 require.True(t, errors.IsEVMError(output.Err)) 2930 2931 // make sure error is not treated as internal error by Cadence 2932 var internal cadenceErrors.InternalError 2933 require.False(t, errors.As(output.Err, &internal)) 2934 }), 2935 ) 2936 2937 // this test makes sure the EVM error is correctly returned as an error and has a correct type 2938 // we have implemented a snapshot wrapper to return an error from the EVM 2939 t.Run("internal evm error handling", newVMTest(). 2940 withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)). 2941 withContextOptions(ctxOpts...). 2942 run(func( 2943 t *testing.T, 2944 vm fvm.VM, 2945 chain flow.Chain, 2946 ctx fvm.Context, 2947 snapshotTree snapshot.SnapshotTree, 2948 ) { 2949 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 2950 2951 tests := []struct { 2952 err error 2953 errChecker func(error) bool 2954 }{{ 2955 types.ErrNotImplemented, 2956 types.IsAFatalError, 2957 }, { 2958 types.NewStateError(fmt.Errorf("test state error")), 2959 types.IsAStateError, 2960 }} 2961 2962 for _, e := range tests { 2963 // this mock will return an error we provide with the test once it starts to access address allocator registers 2964 // that is done to make sure the error is coming out of EVM execution 2965 errStorage := &mock.StorageSnapshot{} 2966 errStorage. 2967 On("Get", mockery.AnythingOfType("flow.RegisterID")). 2968 Return(func(id flow.RegisterID) (flow.RegisterValue, error) { 2969 if id.Key == "LatestBlock" { 2970 return nil, e.err 2971 } 2972 return snapshotTree.Get(id) 2973 }) 2974 2975 script := fvm.Script([]byte(fmt.Sprintf(` 2976 import EVM from %s 2977 2978 access(all) 2979 fun main() { 2980 destroy <- EVM.createCadenceOwnedAccount() 2981 } 2982 `, sc.EVMContract.Address.HexWithPrefix()))) 2983 2984 _, output, err := vm.Run(ctx, script, errStorage) 2985 2986 require.NoError(t, output.Err) 2987 require.Error(t, err) 2988 // make sure error it's the right type of error 2989 require.True(t, e.errChecker(err), "error is not of the right type") 2990 } 2991 }), 2992 ) 2993 2994 t.Run("deploy contract code", newVMTest(). 2995 withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)). 2996 withContextOptions(ctxOpts...). 2997 run(func( 2998 t *testing.T, 2999 vm fvm.VM, 3000 chain flow.Chain, 3001 ctx fvm.Context, 3002 snapshotTree snapshot.SnapshotTree, 3003 ) { 3004 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 3005 3006 txBody := flow.NewTransactionBody(). 3007 SetScript([]byte(fmt.Sprintf(` 3008 import FungibleToken from %s 3009 import FlowToken from %s 3010 import EVM from %s 3011 3012 transaction() { 3013 prepare(acc: auth(Storage) &Account) { 3014 let vaultRef = acc.storage 3015 .borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(from: /storage/flowTokenVault) 3016 ?? panic("Could not borrow reference to the owner's Vault!") 3017 3018 let acc <- EVM.createCadenceOwnedAccount() 3019 let amount <- vaultRef.withdraw(amount: 0.0000001) as! @FlowToken.Vault 3020 acc.deposit(from: <- amount) 3021 destroy acc 3022 } 3023 }`, 3024 sc.FungibleToken.Address.HexWithPrefix(), 3025 sc.FlowToken.Address.HexWithPrefix(), 3026 sc.FlowServiceAccount.Address.HexWithPrefix(), // TODO this should be sc.EVM.Address not found there??? 3027 ))). 3028 SetProposalKey(chain.ServiceAddress(), 0, 0). 3029 AddAuthorizer(chain.ServiceAddress()). 3030 SetPayer(chain.ServiceAddress()) 3031 3032 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 3033 require.NoError(t, err) 3034 3035 ctx = fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true)) 3036 _, output, err := vm.Run( 3037 ctx, 3038 fvm.Transaction(txBody, 0), 3039 snapshotTree) 3040 3041 require.NoError(t, err) 3042 require.NoError(t, output.Err) 3043 require.Len(t, output.Events, 7) 3044 3045 txExe, blockExe := output.Events[4], output.Events[5] 3046 txExecutedID := common.NewAddressLocation( 3047 nil, 3048 common.Address(sc.EVMContract.Address), 3049 string(types.EventTypeTransactionExecuted), 3050 ).ID() 3051 blockExecutedID := common.NewAddressLocation( 3052 nil, 3053 common.Address(sc.EVMContract.Address), 3054 string(types.EventTypeBlockExecuted), 3055 ).ID() 3056 assert.Equal(t, txExecutedID, string(txExe.Type)) 3057 assert.Equal(t, blockExecutedID, string(blockExe.Type)) 3058 3059 // convert events to type ids 3060 eventTypeIDs := make([]common.TypeID, 0, len(output.Events)) 3061 3062 for _, event := range output.Events { 3063 eventTypeIDs = append(eventTypeIDs, common.TypeID(event.Type)) 3064 } 3065 3066 assert.ElementsMatch( 3067 t, 3068 []common.TypeID{ 3069 common.TypeID(txExecutedID), 3070 common.TypeID(blockExecutedID), 3071 "A.f8d6e0586b0a20c7.EVM.CadenceOwnedAccountCreated", 3072 "A.ee82856bf20e2aa6.FungibleToken.Withdrawn", 3073 common.TypeID(txExecutedID), 3074 common.TypeID(blockExecutedID), 3075 "A.f8d6e0586b0a20c7.EVM.FLOWTokensDeposited", 3076 }, 3077 eventTypeIDs, 3078 ) 3079 }), 3080 ) 3081 }