github.com/onflow/flow-go@v0.33.17/fvm/fvm_blockcontext_test.go (about) 1 package fvm_test 2 3 import ( 4 "crypto/rand" 5 "encoding/base64" 6 "encoding/hex" 7 "fmt" 8 "math" 9 "strconv" 10 "testing" 11 12 "github.com/onflow/cadence" 13 "github.com/onflow/cadence/encoding/ccf" 14 jsoncdc "github.com/onflow/cadence/encoding/json" 15 "github.com/onflow/cadence/runtime" 16 "github.com/stretchr/testify/mock" 17 "github.com/stretchr/testify/require" 18 19 "github.com/onflow/flow-go/crypto" 20 "github.com/onflow/flow-go/crypto/hash" 21 "github.com/onflow/flow-go/engine/execution/testutil" 22 "github.com/onflow/flow-go/fvm" 23 "github.com/onflow/flow-go/fvm/blueprints" 24 envMock "github.com/onflow/flow-go/fvm/environment/mock" 25 errors "github.com/onflow/flow-go/fvm/errors" 26 "github.com/onflow/flow-go/fvm/storage/snapshot" 27 "github.com/onflow/flow-go/fvm/systemcontracts" 28 "github.com/onflow/flow-go/model/flow" 29 "github.com/onflow/flow-go/utils/unittest" 30 ) 31 32 func transferTokensTx(chain flow.Chain) *flow.TransactionBody { 33 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 34 return flow.NewTransactionBody(). 35 SetScript([]byte(fmt.Sprintf( 36 ` 37 // This transaction is a template for a transaction that 38 // could be used by anyone to send tokens to another account 39 // that has been set up to receive tokens. 40 // 41 // The withdraw amount and the account from getAccount 42 // would be the parameters to the transaction 43 44 import FungibleToken from 0x%s 45 import FlowToken from 0x%s 46 47 transaction(amount: UFix64, to: Address) { 48 49 // The Vault resource that holds the tokens that are being transferred 50 let sentVault: @FungibleToken.Vault 51 52 prepare(signer: AuthAccount) { 53 54 // Get a reference to the signer's stored vault 55 let vaultRef = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) 56 ?? panic("Could not borrow reference to the owner's Vault!") 57 58 // Withdraw tokens from the signer's stored vault 59 self.sentVault <- vaultRef.withdraw(amount: amount) 60 } 61 62 execute { 63 64 // Get the recipient's public account object 65 let recipient = getAccount(to) 66 67 // Get a reference to the recipient's Receiver 68 let receiverRef = recipient.getCapability(/public/flowTokenReceiver) 69 .borrow<&{FungibleToken.Receiver}>() 70 ?? panic("Could not borrow receiver reference to the recipient's Vault") 71 72 // Deposit the withdrawn tokens in the recipient's receiver 73 receiverRef.deposit(from: <-self.sentVault) 74 } 75 }`, 76 sc.FungibleToken.Address.Hex(), 77 sc.FlowToken.Address.Hex(), 78 )), 79 ) 80 } 81 82 func filterAccountCreatedEvents(events []flow.Event) []flow.Event { 83 var accountCreatedEvents []flow.Event 84 for _, event := range events { 85 if event.Type != flow.EventAccountCreated { 86 continue 87 } 88 accountCreatedEvents = append(accountCreatedEvents, event) 89 break 90 } 91 return accountCreatedEvents 92 } 93 94 func TestBlockContext_ExecuteTransaction(t *testing.T) { 95 96 t.Parallel() 97 98 chain, vm := createChainAndVm(flow.Testnet) 99 100 ctx := fvm.NewContext( 101 fvm.WithChain(chain), 102 fvm.WithCadenceLogging(true), 103 ) 104 105 t.Run("Success", func(t *testing.T) { 106 txBody := flow.NewTransactionBody(). 107 SetScript([]byte(` 108 transaction { 109 prepare(signer: AuthAccount) {} 110 } 111 `)). 112 AddAuthorizer(unittest.AddressFixture()) 113 114 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 115 require.NoError(t, err) 116 117 _, output, err := vm.Run( 118 ctx, 119 fvm.Transaction(txBody, 0), 120 testutil.RootBootstrappedLedger(vm, ctx)) 121 require.NoError(t, err) 122 require.NoError(t, output.Err) 123 }) 124 125 t.Run("Failure", func(t *testing.T) { 126 txBody := flow.NewTransactionBody(). 127 SetScript([]byte(` 128 transaction { 129 var x: Int 130 131 prepare(signer: AuthAccount) { 132 self.x = 0 133 } 134 135 execute { 136 self.x = 1 137 } 138 139 post { 140 self.x == 2 141 } 142 } 143 `)) 144 145 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 146 require.NoError(t, err) 147 148 _, output, err := vm.Run( 149 ctx, 150 fvm.Transaction(txBody, 0), 151 testutil.RootBootstrappedLedger(vm, ctx)) 152 require.NoError(t, err) 153 require.Error(t, output.Err) 154 }) 155 156 t.Run("Logs", func(t *testing.T) { 157 txBody := flow.NewTransactionBody(). 158 SetScript([]byte(` 159 transaction { 160 execute { 161 log("foo") 162 log("bar") 163 } 164 } 165 `)) 166 167 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 168 require.NoError(t, err) 169 170 _, output, err := vm.Run( 171 ctx, 172 fvm.Transaction(txBody, 0), 173 testutil.RootBootstrappedLedger(vm, ctx)) 174 require.NoError(t, err) 175 require.NoError(t, output.Err) 176 177 require.Equal(t, []string{"\"foo\"", "\"bar\""}, output.Logs) 178 }) 179 180 t.Run("Events", func(t *testing.T) { 181 txBody := flow.NewTransactionBody(). 182 SetScript([]byte(` 183 transaction { 184 prepare(signer: AuthAccount) { 185 AuthAccount(payer: signer) 186 } 187 } 188 `)). 189 AddAuthorizer(chain.ServiceAddress()) 190 191 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 192 require.NoError(t, err) 193 194 _, output, err := vm.Run( 195 ctx, 196 fvm.Transaction(txBody, 0), 197 testutil.RootBootstrappedLedger(vm, ctx)) 198 require.NoError(t, err) 199 require.NoError(t, output.Err) 200 201 require.Len(t, filterAccountCreatedEvents(output.Events), 1) 202 }) 203 } 204 205 func TestBlockContext_DeployContract(t *testing.T) { 206 207 t.Parallel() 208 209 chain, vm := createChainAndVm(flow.Mainnet) 210 211 ctx := fvm.NewContext( 212 fvm.WithChain(chain), 213 fvm.WithCadenceLogging(true), 214 ) 215 216 t.Run("account update with set code succeeds as service account", func(t *testing.T) { 217 218 // Create an account private key. 219 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 220 require.NoError(t, err) 221 222 // Bootstrap a ledger, creating accounts with the provided private keys 223 // and the root account. 224 snapshotTree, accounts, err := testutil.CreateAccounts( 225 vm, 226 testutil.RootBootstrappedLedger(vm, ctx), 227 privateKeys, 228 chain) 229 require.NoError(t, err) 230 231 txBody := testutil.DeployCounterContractTransaction(accounts[0], chain) 232 233 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 234 txBody.SetPayer(chain.ServiceAddress()) 235 236 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 237 require.NoError(t, err) 238 239 err = testutil.SignEnvelope( 240 txBody, 241 chain.ServiceAddress(), 242 unittest.ServiceAccountPrivateKey) 243 require.NoError(t, err) 244 245 _, output, err := vm.Run( 246 ctx, 247 fvm.Transaction(txBody, 0), 248 snapshotTree) 249 require.NoError(t, err) 250 require.NoError(t, output.Err) 251 }) 252 253 t.Run("account with deployed contract has `contracts.names` filled", func(t *testing.T) { 254 255 // Create an account private key. 256 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 257 require.NoError(t, err) 258 259 // Bootstrap a ledger, creating accounts with the provided private keys 260 // and the root account. 261 snapshotTree, accounts, err := testutil.CreateAccounts( 262 vm, 263 testutil.RootBootstrappedLedger(vm, ctx), 264 privateKeys, 265 chain) 266 require.NoError(t, err) 267 268 txBody := testutil.DeployCounterContractTransaction(accounts[0], chain) 269 270 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 271 txBody.SetPayer(chain.ServiceAddress()) 272 273 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 274 require.NoError(t, err) 275 276 err = testutil.SignEnvelope( 277 txBody, 278 chain.ServiceAddress(), 279 unittest.ServiceAccountPrivateKey) 280 require.NoError(t, err) 281 282 executionSnapshot, output, err := vm.Run( 283 ctx, 284 fvm.Transaction(txBody, 0), 285 snapshotTree) 286 require.NoError(t, err) 287 require.NoError(t, output.Err) 288 289 snapshotTree = snapshotTree.Append(executionSnapshot) 290 291 // transaction will panic if `contracts.names` is incorrect 292 txBody = flow.NewTransactionBody(). 293 SetScript([]byte(` 294 transaction { 295 prepare(signer: AuthAccount) { 296 var s : String = "" 297 for name in signer.contracts.names { 298 s = s.concat(name).concat(",") 299 } 300 if s != "Container," { 301 panic(s) 302 } 303 } 304 } 305 `)). 306 AddAuthorizer(accounts[0]) 307 308 txBody.SetProposalKey(chain.ServiceAddress(), 0, 1) 309 txBody.SetPayer(chain.ServiceAddress()) 310 311 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 312 require.NoError(t, err) 313 314 err = testutil.SignEnvelope( 315 txBody, 316 chain.ServiceAddress(), 317 unittest.ServiceAccountPrivateKey) 318 require.NoError(t, err) 319 320 _, output, err = vm.Run( 321 ctx, 322 fvm.Transaction(txBody, 0), 323 snapshotTree) 324 require.NoError(t, err) 325 require.NoError(t, output.Err) 326 }) 327 328 t.Run("account update with checker heavy contract (local replay limit)", func(t *testing.T) { 329 // Create an account private key. 330 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 331 require.NoError(t, err) 332 333 // Bootstrap a ledger, creating accounts with the provided private keys 334 // and the root account. 335 snapshotTree, accounts, err := testutil.CreateAccounts( 336 vm, 337 testutil.RootBootstrappedLedger(vm, ctx), 338 privateKeys, 339 chain) 340 require.NoError(t, err) 341 342 txBody := testutil.DeployLocalReplayLimitedTransaction( 343 accounts[0], 344 chain) 345 346 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 347 txBody.SetPayer(chain.ServiceAddress()) 348 349 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 350 require.NoError(t, err) 351 352 err = testutil.SignEnvelope( 353 txBody, 354 chain.ServiceAddress(), 355 unittest.ServiceAccountPrivateKey) 356 require.NoError(t, err) 357 358 _, output, err := vm.Run( 359 ctx, 360 fvm.Transaction(txBody, 0), 361 snapshotTree) 362 require.NoError(t, err) 363 364 var parsingCheckingError *runtime.ParsingCheckingError 365 require.ErrorAs(t, output.Err, &parsingCheckingError) 366 require.ErrorContains( 367 t, 368 output.Err, 369 "program too ambiguous, local replay limit of 64 tokens exceeded") 370 }) 371 372 t.Run("account update with checker heavy contract (global replay limit)", func(t *testing.T) { 373 // Create an account private key. 374 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 375 require.NoError(t, err) 376 377 // Bootstrap a ledger, creating accounts with the provided private keys 378 // and the root account. 379 snapshotTree, accounts, err := testutil.CreateAccounts( 380 vm, 381 testutil.RootBootstrappedLedger(vm, ctx), 382 privateKeys, 383 chain) 384 require.NoError(t, err) 385 386 txBody := testutil.DeployGlobalReplayLimitedTransaction( 387 accounts[0], 388 chain) 389 390 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 391 txBody.SetPayer(chain.ServiceAddress()) 392 393 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 394 require.NoError(t, err) 395 396 err = testutil.SignEnvelope( 397 txBody, 398 chain.ServiceAddress(), 399 unittest.ServiceAccountPrivateKey) 400 require.NoError(t, err) 401 402 _, output, err := vm.Run( 403 ctx, 404 fvm.Transaction(txBody, 0), 405 snapshotTree) 406 require.NoError(t, err) 407 408 var parsingCheckingError *runtime.ParsingCheckingError 409 require.ErrorAs(t, output.Err, &parsingCheckingError) 410 require.ErrorContains( 411 t, 412 output.Err, 413 "program too ambiguous, global replay limit of 1024 tokens exceeded") 414 }) 415 416 t.Run("account update with set code fails if not signed by service account", func(t *testing.T) { 417 // Create an account private key. 418 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 419 require.NoError(t, err) 420 421 // Bootstrap a ledger, creating accounts with the provided private keys 422 // and the root account. 423 snapshotTree, accounts, err := testutil.CreateAccounts( 424 vm, 425 testutil.RootBootstrappedLedger(vm, ctx), 426 privateKeys, 427 chain) 428 require.NoError(t, err) 429 430 txBody := testutil.DeployUnauthorizedCounterContractTransaction( 431 accounts[0]) 432 433 err = testutil.SignTransaction(txBody, accounts[0], privateKeys[0], 0) 434 require.NoError(t, err) 435 436 _, output, err := vm.Run( 437 ctx, 438 fvm.Transaction(txBody, 0), 439 snapshotTree) 440 require.NoError(t, err) 441 442 require.Error(t, output.Err) 443 444 require.ErrorContains( 445 t, 446 output.Err, 447 "deploying contracts requires authorization from specific accounts") 448 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 449 }) 450 451 t.Run("account update with set code fails if not signed by service account if dis-allowed in the state", func(t *testing.T) { 452 ctx := fvm.NewContext( 453 fvm.WithChain(chain), 454 fvm.WithCadenceLogging(true), 455 fvm.WithContractDeploymentRestricted(false), 456 ) 457 restricted := true 458 459 // Create an account private key. 460 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 461 require.NoError(t, err) 462 463 // Bootstrap a ledger, creating accounts with the provided private keys 464 // and the root account. 465 snapshotTree, accounts, err := testutil.CreateAccounts( 466 vm, 467 testutil.RootBootstrappedLedger( 468 vm, 469 ctx, 470 fvm.WithRestrictedContractDeployment(&restricted)), 471 privateKeys, 472 chain) 473 require.NoError(t, err) 474 475 txBody := testutil.DeployUnauthorizedCounterContractTransaction( 476 accounts[0]) 477 txBody.SetProposalKey(accounts[0], 0, 0) 478 txBody.SetPayer(accounts[0]) 479 480 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 481 require.NoError(t, err) 482 483 _, output, err := vm.Run( 484 ctx, 485 fvm.Transaction(txBody, 0), 486 487 snapshotTree) 488 require.NoError(t, err) 489 require.Error(t, output.Err) 490 491 require.ErrorContains( 492 t, 493 output.Err, 494 "deploying contracts requires authorization from specific accounts") 495 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 496 }) 497 498 t.Run("account update with set succeeds if not signed by service account if allowed in the state", func(t *testing.T) { 499 500 // Create an account private key. 501 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 502 require.NoError(t, err) 503 504 // Bootstrap a ledger, creating accounts with the provided private keys 505 // and the root account. 506 restricted := false 507 snapshotTree, accounts, err := testutil.CreateAccounts( 508 vm, 509 testutil.RootBootstrappedLedger( 510 vm, 511 ctx, 512 fvm.WithRestrictedContractDeployment(&restricted)), 513 privateKeys, 514 chain) 515 require.NoError(t, err) 516 517 txBody := testutil.DeployUnauthorizedCounterContractTransaction( 518 accounts[0]) 519 txBody.SetProposalKey(accounts[0], 0, 0) 520 txBody.SetPayer(accounts[0]) 521 522 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 523 require.NoError(t, err) 524 525 _, output, err := vm.Run( 526 ctx, 527 fvm.Transaction(txBody, 0), 528 snapshotTree) 529 require.NoError(t, err) 530 require.NoError(t, output.Err) 531 }) 532 533 t.Run("account update with update code succeeds if not signed by service account", func(t *testing.T) { 534 // Create an account private key. 535 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 536 require.NoError(t, err) 537 538 // Bootstrap a ledger, creating accounts with the provided private keys 539 // and the root account. 540 snapshotTree, accounts, err := testutil.CreateAccounts( 541 vm, 542 testutil.RootBootstrappedLedger(vm, ctx), 543 privateKeys, 544 chain) 545 require.NoError(t, err) 546 547 txBody := testutil.DeployCounterContractTransaction(accounts[0], chain) 548 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 549 txBody.SetPayer(chain.ServiceAddress()) 550 551 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 552 require.NoError(t, err) 553 554 err = testutil.SignEnvelope( 555 txBody, 556 chain.ServiceAddress(), 557 unittest.ServiceAccountPrivateKey) 558 require.NoError(t, err) 559 560 executionSnapshot, output, err := vm.Run( 561 ctx, 562 fvm.Transaction(txBody, 0), 563 snapshotTree) 564 require.NoError(t, err) 565 require.NoError(t, output.Err) 566 567 snapshotTree = snapshotTree.Append(executionSnapshot) 568 569 txBody = testutil.UpdateUnauthorizedCounterContractTransaction( 570 accounts[0]) 571 txBody.SetProposalKey(accounts[0], 0, 0) 572 txBody.SetPayer(accounts[0]) 573 574 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 575 require.NoError(t, err) 576 577 _, output, err = vm.Run( 578 ctx, 579 fvm.Transaction(txBody, 0), 580 snapshotTree) 581 require.NoError(t, err) 582 require.NoError(t, output.Err) 583 }) 584 585 t.Run("account update with code removal fails if not signed by service account", func(t *testing.T) { 586 587 // Create an account private key. 588 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 589 require.NoError(t, err) 590 591 // Bootstrap a ledger, creating accounts with the provided private keys 592 // and the root account. 593 snapshotTree, accounts, err := testutil.CreateAccounts( 594 vm, 595 testutil.RootBootstrappedLedger(vm, ctx), 596 privateKeys, 597 chain) 598 require.NoError(t, err) 599 600 txBody := testutil.DeployCounterContractTransaction(accounts[0], chain) 601 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 602 txBody.SetPayer(chain.ServiceAddress()) 603 604 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 605 require.NoError(t, err) 606 607 err = testutil.SignEnvelope( 608 txBody, 609 chain.ServiceAddress(), 610 unittest.ServiceAccountPrivateKey) 611 require.NoError(t, err) 612 613 executionSnapshot, output, err := vm.Run( 614 ctx, 615 fvm.Transaction(txBody, 0), 616 snapshotTree) 617 require.NoError(t, err) 618 require.NoError(t, output.Err) 619 620 snapshotTree = snapshotTree.Append(executionSnapshot) 621 622 txBody = testutil.RemoveUnauthorizedCounterContractTransaction( 623 accounts[0]) 624 txBody.SetProposalKey(accounts[0], 0, 0) 625 txBody.SetPayer(accounts[0]) 626 627 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 628 require.NoError(t, err) 629 630 _, output, err = vm.Run( 631 ctx, 632 fvm.Transaction(txBody, 0), 633 snapshotTree) 634 require.NoError(t, err) 635 require.Error(t, output.Err) 636 637 require.ErrorContains( 638 t, 639 output.Err, 640 "removing contracts requires authorization from specific accounts") 641 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 642 }) 643 644 t.Run("account update with code removal succeeds if signed by service account", func(t *testing.T) { 645 // Create an account private key. 646 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 647 require.NoError(t, err) 648 649 // Bootstrap a ledger, creating accounts with the provided private keys 650 // and the root account. 651 snapshotTree, accounts, err := testutil.CreateAccounts( 652 vm, 653 testutil.RootBootstrappedLedger(vm, ctx), 654 privateKeys, 655 chain) 656 require.NoError(t, err) 657 658 txBody := testutil.DeployCounterContractTransaction(accounts[0], chain) 659 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 660 txBody.SetPayer(chain.ServiceAddress()) 661 662 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 663 require.NoError(t, err) 664 665 err = testutil.SignEnvelope( 666 txBody, 667 chain.ServiceAddress(), 668 unittest.ServiceAccountPrivateKey) 669 require.NoError(t, err) 670 671 executionSnapshot, output, err := vm.Run( 672 ctx, 673 fvm.Transaction(txBody, 0), 674 snapshotTree) 675 require.NoError(t, err) 676 require.NoError(t, output.Err) 677 678 snapshotTree = snapshotTree.Append(executionSnapshot) 679 680 txBody = testutil.RemoveCounterContractTransaction(accounts[0], chain) 681 txBody.SetProposalKey(accounts[0], 0, 0) 682 txBody.SetPayer(chain.ServiceAddress()) 683 684 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 685 require.NoError(t, err) 686 687 err = testutil.SignEnvelope( 688 txBody, 689 chain.ServiceAddress(), 690 unittest.ServiceAccountPrivateKey) 691 require.NoError(t, err) 692 693 _, output, err = vm.Run( 694 ctx, 695 fvm.Transaction(txBody, 0), 696 snapshotTree) 697 require.NoError(t, err) 698 require.NoError(t, output.Err) 699 }) 700 701 t.Run("account update with set code succeeds when account is added as authorized account", func(t *testing.T) { 702 // Create an account private key. 703 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 704 require.NoError(t, err) 705 706 // Bootstrap a ledger, creating accounts with the provided private keys 707 // and the root account. 708 snapshotTree, accounts, err := testutil.CreateAccounts( 709 vm, 710 testutil.RootBootstrappedLedger(vm, ctx), 711 privateKeys, 712 chain) 713 require.NoError(t, err) 714 715 // setup a new authorizer account 716 authTxBody, err := blueprints.SetContractDeploymentAuthorizersTransaction( 717 chain.ServiceAddress(), 718 []flow.Address{chain.ServiceAddress(), accounts[0]}) 719 require.NoError(t, err) 720 721 authTxBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 722 authTxBody.SetPayer(chain.ServiceAddress()) 723 err = testutil.SignEnvelope( 724 authTxBody, 725 chain.ServiceAddress(), 726 unittest.ServiceAccountPrivateKey) 727 require.NoError(t, err) 728 729 executionSnapshot, output, err := vm.Run( 730 ctx, 731 fvm.Transaction(authTxBody, 0), 732 snapshotTree) 733 require.NoError(t, err) 734 require.NoError(t, output.Err) 735 736 snapshotTree = snapshotTree.Append(executionSnapshot) 737 738 // test deploying a new contract (not authorized by service account) 739 txBody := testutil.DeployUnauthorizedCounterContractTransaction(accounts[0]) 740 txBody.SetProposalKey(accounts[0], 0, 0) 741 txBody.SetPayer(accounts[0]) 742 743 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 744 require.NoError(t, err) 745 746 _, output, err = vm.Run( 747 ctx, 748 fvm.Transaction(txBody, 0), 749 snapshotTree) 750 require.NoError(t, err) 751 require.NoError(t, output.Err) 752 }) 753 754 } 755 756 func TestBlockContext_ExecuteTransaction_WithArguments(t *testing.T) { 757 758 t.Parallel() 759 760 chain, vm := createChainAndVm(flow.Mainnet) 761 762 ctx := fvm.NewContext( 763 fvm.WithChain(chain), 764 fvm.WithCadenceLogging(true), 765 ) 766 767 arg1, _ := jsoncdc.Encode(cadence.NewInt(42)) 768 fooString, _ := cadence.NewString("foo") 769 arg2, _ := jsoncdc.Encode(fooString) 770 771 var tests = []struct { 772 label string 773 script string 774 args [][]byte 775 authorizers []flow.Address 776 check func(t *testing.T, output fvm.ProcedureOutput) 777 }{ 778 { 779 label: "No parameters", 780 script: `transaction { execute { log("Hello, World!") } }`, 781 args: [][]byte{arg1}, 782 check: func(t *testing.T, output fvm.ProcedureOutput) { 783 require.Error(t, output.Err) 784 }, 785 }, 786 { 787 label: "Single parameter", 788 script: `transaction(x: Int) { execute { log(x) } }`, 789 args: [][]byte{arg1}, 790 check: func(t *testing.T, output fvm.ProcedureOutput) { 791 require.NoError(t, output.Err) 792 require.Len(t, output.Logs, 1) 793 require.Equal(t, "42", output.Logs[0]) 794 }, 795 }, 796 { 797 label: "Multiple parameters", 798 script: `transaction(x: Int, y: String) { execute { log(x); log(y) } }`, 799 args: [][]byte{arg1, arg2}, 800 check: func(t *testing.T, output fvm.ProcedureOutput) { 801 require.NoError(t, output.Err) 802 require.Len(t, output.Logs, 2) 803 require.Equal(t, "42", output.Logs[0]) 804 require.Equal(t, `"foo"`, output.Logs[1]) 805 }, 806 }, 807 { 808 label: "Parameters and authorizer", 809 script: ` 810 transaction(x: Int, y: String) { 811 prepare(acct: AuthAccount) { log(acct.address) } 812 execute { log(x); log(y) } 813 }`, 814 args: [][]byte{arg1, arg2}, 815 authorizers: []flow.Address{chain.ServiceAddress()}, 816 check: func(t *testing.T, output fvm.ProcedureOutput) { 817 require.NoError(t, output.Err) 818 require.ElementsMatch( 819 t, 820 []string{"0x" + chain.ServiceAddress().Hex(), "42", `"foo"`}, 821 output.Logs) 822 }, 823 }, 824 } 825 826 for _, tt := range tests { 827 t.Run(tt.label, func(t *testing.T) { 828 txBody := flow.NewTransactionBody(). 829 SetScript([]byte(tt.script)). 830 SetArguments(tt.args) 831 832 for _, authorizer := range tt.authorizers { 833 txBody.AddAuthorizer(authorizer) 834 } 835 836 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 837 require.NoError(t, err) 838 839 _, output, err := vm.Run( 840 ctx, 841 fvm.Transaction(txBody, 0), 842 testutil.RootBootstrappedLedger(vm, ctx)) 843 require.NoError(t, err) 844 845 tt.check(t, output) 846 }) 847 } 848 } 849 func gasLimitScript(depth int) string { 850 return fmt.Sprintf(` 851 pub fun foo(_ i: Int) { 852 if i <= 0 { 853 return 854 } 855 log("foo") 856 foo(i-1) 857 } 858 859 transaction { execute { foo(%d) } } 860 `, depth) 861 } 862 863 func TestBlockContext_ExecuteTransaction_GasLimit(t *testing.T) { 864 865 t.Parallel() 866 867 chain, vm := createChainAndVm(flow.Mainnet) 868 869 ctx := fvm.NewContext( 870 fvm.WithChain(chain), 871 fvm.WithCadenceLogging(true), 872 ) 873 874 var tests = []struct { 875 label string 876 script string 877 gasLimit uint64 878 check func(t *testing.T, output fvm.ProcedureOutput) 879 }{ 880 { 881 label: "Zero", 882 script: gasLimitScript(100), 883 gasLimit: 0, 884 check: func(t *testing.T, output fvm.ProcedureOutput) { 885 // gas limit of zero is ignored by runtime 886 require.NoError(t, output.Err) 887 }, 888 }, 889 { 890 label: "Insufficient", 891 script: gasLimitScript(100), 892 gasLimit: 5, 893 check: func(t *testing.T, output fvm.ProcedureOutput) { 894 require.Error(t, output.Err) 895 }, 896 }, 897 { 898 label: "Sufficient", 899 script: gasLimitScript(100), 900 gasLimit: 1000, 901 check: func(t *testing.T, output fvm.ProcedureOutput) { 902 require.NoError(t, output.Err) 903 require.Len(t, output.Logs, 100) 904 }, 905 }, 906 } 907 908 for _, tt := range tests { 909 t.Run(tt.label, func(t *testing.T) { 910 txBody := flow.NewTransactionBody(). 911 SetScript([]byte(tt.script)). 912 SetGasLimit(tt.gasLimit) 913 914 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 915 require.NoError(t, err) 916 917 _, output, err := vm.Run( 918 ctx, 919 fvm.Transaction(txBody, 0), 920 testutil.RootBootstrappedLedger(vm, ctx)) 921 require.NoError(t, err) 922 923 tt.check(t, output) 924 }) 925 } 926 } 927 928 func TestBlockContext_ExecuteTransaction_StorageLimit(t *testing.T) { 929 930 t.Parallel() 931 932 b := make([]byte, 1000000) // 1MB 933 _, err := rand.Read(b) 934 require.NoError(t, err) 935 longString := base64.StdEncoding.EncodeToString(b) // 1.3 times 1MB 936 937 script := fmt.Sprintf(` 938 access(all) contract Container { 939 access(all) resource Counter { 940 pub var longString: String 941 init() { 942 self.longString = "%s" 943 } 944 } 945 }`, longString) 946 947 bootstrapOptions := []fvm.BootstrapProcedureOption{ 948 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 949 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 950 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 951 // The evm account has a storage exception, and if we don't bootstrap with evm, 952 // the first created account will have that address. 953 fvm.WithSetupEVMEnabled(true), 954 } 955 956 t.Run("Storing too much data fails", newVMTest().withBootstrapProcedureOptions(bootstrapOptions...). 957 run( 958 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 959 // this test requires storage limits to be enforced 960 ctx.LimitAccountStorage = true 961 962 // Create an account private key. 963 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 964 require.NoError(t, err) 965 966 // Bootstrap a ledger, creating accounts with the provided 967 // private keys and the root account. 968 snapshotTree, accounts, err := testutil.CreateAccounts( 969 vm, 970 snapshotTree, 971 privateKeys, 972 chain) 973 require.NoError(t, err) 974 975 txBody := testutil.CreateContractDeploymentTransaction( 976 "Container", 977 script, 978 accounts[0], 979 chain) 980 981 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 982 txBody.SetPayer(chain.ServiceAddress()) 983 984 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 985 require.NoError(t, err) 986 987 err = testutil.SignEnvelope( 988 txBody, 989 chain.ServiceAddress(), 990 unittest.ServiceAccountPrivateKey) 991 require.NoError(t, err) 992 993 _, output, err := vm.Run( 994 ctx, 995 fvm.Transaction(txBody, 0), 996 snapshotTree) 997 require.NoError(t, err) 998 999 require.True( 1000 t, 1001 errors.IsStorageCapacityExceededError(output.Err)) 1002 })) 1003 t.Run("Increasing storage capacity works", newVMTest().withBootstrapProcedureOptions(bootstrapOptions...). 1004 run( 1005 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1006 ctx.LimitAccountStorage = true // this test requires storage limits to be enforced 1007 1008 // Create an account private key. 1009 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1010 require.NoError(t, err) 1011 1012 // Bootstrap a ledger, creating accounts with the provided 1013 // private keys and the root account. 1014 snapshotTree, accounts, err := testutil.CreateAccounts( 1015 vm, 1016 snapshotTree, 1017 privateKeys, 1018 chain) 1019 require.NoError(t, err) 1020 1021 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1022 // deposit more flow to increase capacity 1023 txBody := flow.NewTransactionBody(). 1024 SetScript([]byte(fmt.Sprintf( 1025 ` 1026 import FungibleToken from %s 1027 import FlowToken from %s 1028 1029 transaction { 1030 prepare(signer: AuthAccount, service: AuthAccount) { 1031 signer.contracts.add(name: "%s", code: "%s".decodeHex()) 1032 1033 let vaultRef = service.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)! 1034 // deposit additional flow 1035 let payment <- vaultRef.withdraw(amount: 10.0) as! @FlowToken.Vault 1036 1037 let receiver = signer.getCapability(/public/flowTokenReceiver)!.borrow<&{FungibleToken.Receiver}>() 1038 ?? panic("Could not borrow receiver reference to the recipient's Vault") 1039 receiver.deposit(from: <-payment) 1040 } 1041 }`, 1042 sc.FungibleToken.Address.HexWithPrefix(), 1043 sc.FlowToken.Address.HexWithPrefix(), 1044 "Container", 1045 hex.EncodeToString([]byte(script)), 1046 ))). 1047 AddAuthorizer(accounts[0]). 1048 AddAuthorizer(chain.ServiceAddress()). 1049 SetProposalKey(chain.ServiceAddress(), 0, 0). 1050 SetPayer(chain.ServiceAddress()) 1051 1052 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 1053 require.NoError(t, err) 1054 1055 err = testutil.SignEnvelope( 1056 txBody, 1057 chain.ServiceAddress(), 1058 unittest.ServiceAccountPrivateKey) 1059 require.NoError(t, err) 1060 1061 _, output, err := vm.Run( 1062 ctx, 1063 fvm.Transaction(txBody, 0), 1064 snapshotTree) 1065 require.NoError(t, err) 1066 require.NoError(t, output.Err) 1067 })) 1068 } 1069 1070 func TestBlockContext_ExecuteTransaction_InteractionLimitReached(t *testing.T) { 1071 t.Parallel() 1072 1073 b := make([]byte, 1000000) // 1MB 1074 _, err := rand.Read(b) 1075 require.NoError(t, err) 1076 longString := base64.StdEncoding.EncodeToString(b) // ~1.3 times 1MB 1077 1078 // save a really large contract to an account should fail because of interaction limit reached 1079 script := fmt.Sprintf(` 1080 access(all) contract Container { 1081 access(all) resource Counter { 1082 pub var longString: String 1083 init() { 1084 self.longString = "%s" 1085 } 1086 } 1087 }`, longString) 1088 1089 bootstrapOptions := []fvm.BootstrapProcedureOption{ 1090 fvm.WithTransactionFee(fvm.DefaultTransactionFees), 1091 } 1092 1093 t.Run("Using to much interaction fails", newVMTest().withBootstrapProcedureOptions(bootstrapOptions...). 1094 withContextOptions(fvm.WithTransactionFeesEnabled(true)). 1095 run( 1096 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1097 ctx.MaxStateInteractionSize = 500_000 1098 1099 // Create an account private key. 1100 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1101 require.NoError(t, err) 1102 1103 // Bootstrap a ledger, creating accounts with the provided 1104 // private keys and the root account. 1105 snapshotTree, accounts, err := testutil.CreateAccounts( 1106 vm, 1107 snapshotTree, 1108 privateKeys, 1109 chain) 1110 require.NoError(t, err) 1111 1112 n := 0 1113 seqNum := func() uint64 { 1114 sn := n 1115 n++ 1116 return uint64(sn) 1117 } 1118 // fund account so the payer can pay for the next transaction. 1119 txBody := transferTokensTx(chain). 1120 SetProposalKey(chain.ServiceAddress(), 0, seqNum()). 1121 AddAuthorizer(chain.ServiceAddress()). 1122 AddArgument( 1123 jsoncdc.MustEncode(cadence.UFix64(100_000_000))). 1124 AddArgument( 1125 jsoncdc.MustEncode(cadence.NewAddress(accounts[0]))). 1126 SetPayer(chain.ServiceAddress()) 1127 1128 err = testutil.SignEnvelope( 1129 txBody, 1130 chain.ServiceAddress(), 1131 unittest.ServiceAccountPrivateKey) 1132 require.NoError(t, err) 1133 1134 executionSnapshot, output, err := vm.Run( 1135 ctx, 1136 fvm.Transaction(txBody, 0), 1137 snapshotTree) 1138 require.NoError(t, err) 1139 require.NoError(t, output.Err) 1140 1141 snapshotTree = snapshotTree.Append(executionSnapshot) 1142 1143 ctx.MaxStateInteractionSize = 500_000 1144 1145 txBody = testutil.CreateContractDeploymentTransaction( 1146 "Container", 1147 script, 1148 accounts[0], 1149 chain) 1150 1151 txBody.SetProposalKey(chain.ServiceAddress(), 0, seqNum()) 1152 txBody.SetPayer(accounts[0]) 1153 1154 err = testutil.SignPayload( 1155 txBody, 1156 chain.ServiceAddress(), 1157 unittest.ServiceAccountPrivateKey) 1158 require.NoError(t, err) 1159 1160 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 1161 require.NoError(t, err) 1162 1163 _, output, err = vm.Run( 1164 ctx, 1165 fvm.Transaction(txBody, 0), 1166 snapshotTree) 1167 require.NoError(t, err) 1168 1169 require.True( 1170 t, 1171 errors.IsLedgerInteractionLimitExceededError(output.Err)) 1172 })) 1173 1174 t.Run("Using to much interaction but not failing because of service account", newVMTest().withBootstrapProcedureOptions(bootstrapOptions...). 1175 withContextOptions(fvm.WithTransactionFeesEnabled(true)). 1176 run( 1177 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1178 ctx.MaxStateInteractionSize = 500_000 1179 1180 // Create an account private key. 1181 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1182 require.NoError(t, err) 1183 1184 // Bootstrap a ledger, creating accounts with the provided 1185 // private keys and the root account. 1186 snapshotTree, accounts, err := testutil.CreateAccounts( 1187 vm, 1188 snapshotTree, 1189 privateKeys, 1190 chain) 1191 require.NoError(t, err) 1192 1193 txBody := testutil.CreateContractDeploymentTransaction( 1194 "Container", 1195 script, 1196 accounts[0], 1197 chain) 1198 1199 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 1200 txBody.SetPayer(chain.ServiceAddress()) 1201 1202 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 1203 require.NoError(t, err) 1204 1205 err = testutil.SignEnvelope( 1206 txBody, 1207 chain.ServiceAddress(), 1208 unittest.ServiceAccountPrivateKey) 1209 require.NoError(t, err) 1210 1211 _, output, err := vm.Run( 1212 ctx, 1213 fvm.Transaction(txBody, 0), 1214 snapshotTree) 1215 require.NoError(t, err) 1216 require.NoError(t, output.Err) 1217 })) 1218 1219 t.Run("Using to much interaction fails but does not panic", newVMTest().withBootstrapProcedureOptions(bootstrapOptions...). 1220 withContextOptions( 1221 fvm.WithAuthorizationChecksEnabled(false), 1222 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 1223 ). 1224 run( 1225 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1226 ctx.MaxStateInteractionSize = 50_000 1227 1228 // Create an account private key. 1229 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1230 require.NoError(t, err) 1231 1232 // Bootstrap a ledger, creating accounts with the provided 1233 // private keys and the root account. 1234 snapshotTree, accounts, err := testutil.CreateAccounts( 1235 vm, 1236 snapshotTree, 1237 privateKeys, 1238 chain) 1239 require.NoError(t, err) 1240 1241 _, txBody := testutil.CreateMultiAccountCreationTransaction( 1242 t, 1243 chain, 1244 40) 1245 1246 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 1247 txBody.SetPayer(accounts[0]) 1248 1249 err = testutil.SignPayload( 1250 txBody, 1251 chain.ServiceAddress(), 1252 unittest.ServiceAccountPrivateKey) 1253 require.NoError(t, err) 1254 1255 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 1256 require.NoError(t, err) 1257 1258 _, output, err := vm.Run( 1259 ctx, 1260 fvm.Transaction(txBody, 0), 1261 snapshotTree) 1262 require.NoError(t, err) 1263 require.Error(t, output.Err) 1264 1265 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 1266 })) 1267 } 1268 1269 var createAccountScript = []byte(` 1270 transaction { 1271 prepare(signer: AuthAccount) { 1272 let acct = AuthAccount(payer: signer) 1273 } 1274 } 1275 `) 1276 1277 func TestBlockContext_ExecuteScript(t *testing.T) { 1278 1279 t.Parallel() 1280 1281 chain, vm := createChainAndVm(flow.Mainnet) 1282 1283 ctx := fvm.NewContext( 1284 fvm.WithChain(chain), 1285 fvm.WithCadenceLogging(true), 1286 ) 1287 1288 t.Run("script success", func(t *testing.T) { 1289 code := []byte(` 1290 pub fun main(): Int { 1291 return 42 1292 } 1293 `) 1294 1295 _, output, err := vm.Run( 1296 ctx, 1297 fvm.Script(code), 1298 testutil.RootBootstrappedLedger(vm, ctx)) 1299 require.NoError(t, err) 1300 1301 require.NoError(t, output.Err) 1302 }) 1303 1304 t.Run("script failure", func(t *testing.T) { 1305 code := []byte(` 1306 pub fun main(): Int { 1307 assert(1 == 2) 1308 return 42 1309 } 1310 `) 1311 1312 _, output, err := vm.Run( 1313 ctx, 1314 fvm.Script(code), 1315 testutil.RootBootstrappedLedger(vm, ctx)) 1316 require.NoError(t, err) 1317 1318 require.Error(t, output.Err) 1319 }) 1320 1321 t.Run("script logs", func(t *testing.T) { 1322 code := []byte(` 1323 pub fun main(): Int { 1324 log("foo") 1325 log("bar") 1326 return 42 1327 } 1328 `) 1329 1330 _, output, err := vm.Run( 1331 ctx, 1332 fvm.Script(code), 1333 testutil.RootBootstrappedLedger(vm, ctx)) 1334 require.NoError(t, err) 1335 1336 require.NoError(t, output.Err) 1337 require.Len(t, output.Logs, 2) 1338 require.Equal(t, "\"foo\"", output.Logs[0]) 1339 require.Equal(t, "\"bar\"", output.Logs[1]) 1340 }) 1341 1342 t.Run("storage ID allocation", func(t *testing.T) { 1343 // Create an account private key. 1344 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1345 require.NoError(t, err) 1346 1347 // Bootstrap a ledger, creating accounts with the provided private 1348 // keys and the root account. 1349 snapshotTree, accounts, err := testutil.CreateAccounts( 1350 vm, 1351 testutil.RootBootstrappedLedger(vm, ctx), 1352 privateKeys, 1353 chain) 1354 require.NoError(t, err) 1355 1356 // Deploy the test contract 1357 1358 const contract = ` 1359 pub contract Test { 1360 1361 pub struct Foo {} 1362 1363 pub let foos: [Foo] 1364 1365 init() { 1366 self.foos = [] 1367 } 1368 1369 pub fun add() { 1370 self.foos.append(Foo()) 1371 } 1372 } 1373 ` 1374 1375 address := accounts[0] 1376 1377 txBody := testutil.CreateContractDeploymentTransaction( 1378 "Test", 1379 contract, 1380 address, 1381 chain) 1382 1383 txBody.SetProposalKey(chain.ServiceAddress(), 0, 0) 1384 txBody.SetPayer(chain.ServiceAddress()) 1385 1386 err = testutil.SignPayload(txBody, address, privateKeys[0]) 1387 require.NoError(t, err) 1388 1389 err = testutil.SignEnvelope( 1390 txBody, 1391 chain.ServiceAddress(), 1392 unittest.ServiceAccountPrivateKey) 1393 require.NoError(t, err) 1394 1395 executionSnapshot, output, err := vm.Run( 1396 ctx, 1397 fvm.Transaction(txBody, 0), 1398 snapshotTree) 1399 require.NoError(t, err) 1400 require.NoError(t, output.Err) 1401 1402 snapshotTree = snapshotTree.Append(executionSnapshot) 1403 1404 // Run test script 1405 1406 code := []byte(fmt.Sprintf( 1407 ` 1408 import Test from 0x%s 1409 1410 pub fun main() { 1411 Test.add() 1412 } 1413 `, 1414 address.String(), 1415 )) 1416 1417 _, output, err = vm.Run(ctx, fvm.Script(code), snapshotTree) 1418 require.NoError(t, err) 1419 1420 require.NoError(t, output.Err) 1421 }) 1422 } 1423 1424 func TestBlockContext_GetBlockInfo(t *testing.T) { 1425 1426 t.Parallel() 1427 1428 chain, vm := createChainAndVm(flow.Mainnet) 1429 1430 ctx := fvm.NewContext( 1431 fvm.WithChain(chain), 1432 fvm.WithCadenceLogging(true), 1433 ) 1434 1435 blocks := new(envMock.Blocks) 1436 1437 block1 := unittest.BlockFixture() 1438 block2 := unittest.BlockWithParentFixture(block1.Header) 1439 block3 := unittest.BlockWithParentFixture(block2.Header) 1440 1441 blocks.On("ByHeightFrom", block1.Header.Height, block1.Header).Return(block1.Header, nil) 1442 blocks.On("ByHeightFrom", block2.Header.Height, block1.Header).Return(block2.Header, nil) 1443 1444 type logPanic struct{} 1445 blocks.On("ByHeightFrom", block3.Header.Height, block1.Header).Run(func(args mock.Arguments) { panic(logPanic{}) }) 1446 1447 blockCtx := fvm.NewContextFromParent(ctx, fvm.WithBlocks(blocks), fvm.WithBlockHeader(block1.Header)) 1448 1449 t.Run("works as transaction", func(t *testing.T) { 1450 txBody := flow.NewTransactionBody(). 1451 SetScript([]byte(` 1452 transaction { 1453 execute { 1454 let block = getCurrentBlock() 1455 log(block) 1456 1457 let nextBlock = getBlock(at: block.height + UInt64(1)) 1458 log(nextBlock) 1459 } 1460 } 1461 `)) 1462 1463 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1464 require.NoError(t, err) 1465 1466 _, output, err := vm.Run( 1467 blockCtx, 1468 fvm.Transaction(txBody, 0), 1469 testutil.RootBootstrappedLedger(vm, ctx)) 1470 require.NoError(t, err) 1471 require.NoError(t, output.Err) 1472 1473 require.Len(t, output.Logs, 2) 1474 require.Equal( 1475 t, 1476 fmt.Sprintf( 1477 "Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", 1478 block1.Header.Height, 1479 block1.Header.View, 1480 block1.ID(), 1481 float64(block1.Header.Timestamp.Unix()), 1482 ), 1483 output.Logs[0], 1484 ) 1485 require.Equal( 1486 t, 1487 fmt.Sprintf( 1488 "Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", 1489 block2.Header.Height, 1490 block2.Header.View, 1491 block2.ID(), 1492 float64(block2.Header.Timestamp.Unix()), 1493 ), 1494 output.Logs[1], 1495 ) 1496 }) 1497 1498 t.Run("works as script", func(t *testing.T) { 1499 code := []byte(` 1500 pub fun main() { 1501 let block = getCurrentBlock() 1502 log(block) 1503 1504 let nextBlock = getBlock(at: block.height + UInt64(1)) 1505 log(nextBlock) 1506 } 1507 `) 1508 1509 _, output, err := vm.Run( 1510 blockCtx, 1511 fvm.Script(code), 1512 testutil.RootBootstrappedLedger(vm, ctx)) 1513 require.NoError(t, err) 1514 require.NoError(t, output.Err) 1515 1516 require.Len(t, output.Logs, 2) 1517 require.Equal(t, 1518 fmt.Sprintf( 1519 "Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", 1520 block1.Header.Height, 1521 block1.Header.View, 1522 block1.ID(), 1523 float64(block1.Header.Timestamp.Unix()), 1524 ), 1525 output.Logs[0], 1526 ) 1527 require.Equal( 1528 t, 1529 fmt.Sprintf( 1530 "Block(height: %v, view: %v, id: 0x%x, timestamp: %.8f)", 1531 block2.Header.Height, 1532 block2.Header.View, 1533 block2.ID(), 1534 float64(block2.Header.Timestamp.Unix()), 1535 ), 1536 output.Logs[1], 1537 ) 1538 }) 1539 1540 t.Run("panics if external function panics in transaction", func(t *testing.T) { 1541 tx := flow.NewTransactionBody(). 1542 SetScript([]byte(` 1543 transaction { 1544 execute { 1545 let block = getCurrentBlock() 1546 let nextBlock = getBlock(at: block.height + UInt64(2)) 1547 } 1548 } 1549 `)) 1550 1551 err := testutil.SignTransactionAsServiceAccount(tx, 0, chain) 1552 require.NoError(t, err) 1553 1554 _, output, err := vm.Run( 1555 blockCtx, 1556 fvm.Transaction(tx, 0), 1557 testutil.RootBootstrappedLedger(vm, ctx)) 1558 require.NoError(t, err) 1559 require.Error(t, output.Err) 1560 }) 1561 1562 t.Run("panics if external function panics in script", func(t *testing.T) { 1563 script := []byte(` 1564 pub fun main() { 1565 let block = getCurrentBlock() 1566 let nextBlock = getBlock(at: block.height + UInt64(2)) 1567 } 1568 `) 1569 1570 _, output, err := vm.Run( 1571 blockCtx, 1572 fvm.Script(script), 1573 testutil.RootBootstrappedLedger(vm, ctx)) 1574 require.NoError(t, err) 1575 require.Error(t, output.Err) 1576 }) 1577 } 1578 1579 func TestBlockContext_GetAccount(t *testing.T) { 1580 1581 t.Parallel() 1582 1583 const count = 100 1584 1585 chain, vm := createChainAndVm(flow.Mainnet) 1586 1587 ctx := fvm.NewContext( 1588 fvm.WithChain(chain), 1589 fvm.WithCadenceLogging(true), 1590 ) 1591 1592 snapshotTree := testutil.RootBootstrappedLedger(vm, ctx) 1593 sequenceNumber := uint64(0) 1594 1595 createAccount := func() (flow.Address, crypto.PublicKey) { 1596 privateKey, txBody := testutil.CreateAccountCreationTransaction( 1597 t, 1598 chain) 1599 1600 txBody.SetProposalKey(chain.ServiceAddress(), 0, sequenceNumber) 1601 txBody.SetPayer(chain.ServiceAddress()) 1602 sequenceNumber++ 1603 1604 rootHasher := hash.NewSHA2_256() 1605 1606 err := txBody.SignEnvelope( 1607 chain.ServiceAddress(), 1608 0, 1609 unittest.ServiceAccountPrivateKey.PrivateKey, 1610 rootHasher, 1611 ) 1612 require.NoError(t, err) 1613 1614 // execute the transaction 1615 executionSnapshot, output, err := vm.Run( 1616 ctx, 1617 fvm.Transaction(txBody, 0), 1618 snapshotTree) 1619 require.NoError(t, err) 1620 require.NoError(t, output.Err) 1621 1622 snapshotTree = snapshotTree.Append(executionSnapshot) 1623 1624 accountCreatedEvents := filterAccountCreatedEvents(output.Events) 1625 1626 require.Len(t, accountCreatedEvents, 1) 1627 1628 // read the address of the account created (e.g. "0x01" and convert it 1629 // to flow.address) 1630 data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload) 1631 require.NoError(t, err) 1632 address := flow.ConvertAddress( 1633 data.(cadence.Event).Fields[0].(cadence.Address)) 1634 1635 return address, privateKey.PublicKey( 1636 fvm.AccountKeyWeightThreshold).PublicKey 1637 } 1638 1639 addressGen := chain.NewAddressGenerator() 1640 // skip the addresses of 4 reserved accounts 1641 for i := 0; i < 4; i++ { 1642 _, err := addressGen.NextAddress() 1643 require.NoError(t, err) 1644 } 1645 1646 // create a bunch of accounts 1647 accounts := make(map[flow.Address]crypto.PublicKey, count) 1648 for i := 0; i < count; i++ { 1649 address, key := createAccount() 1650 expectedAddress, err := addressGen.NextAddress() 1651 require.NoError(t, err) 1652 1653 require.Equal(t, expectedAddress, address) 1654 accounts[address] = key 1655 } 1656 1657 // happy path - get each of the created account and check if it is the right one 1658 t.Run("get accounts", func(t *testing.T) { 1659 for address, expectedKey := range accounts { 1660 account, err := vm.GetAccount(ctx, address, snapshotTree) 1661 require.NoError(t, err) 1662 1663 require.Len(t, account.Keys, 1) 1664 actualKey := account.Keys[0].PublicKey 1665 require.Equal(t, expectedKey, actualKey) 1666 } 1667 }) 1668 1669 // non-happy path - get an account that was never created 1670 t.Run("get a non-existing account", func(t *testing.T) { 1671 address, err := addressGen.NextAddress() 1672 require.NoError(t, err) 1673 1674 account, err := vm.GetAccount(ctx, address, snapshotTree) 1675 require.True(t, errors.IsAccountNotFoundError(err)) 1676 require.Nil(t, account) 1677 }) 1678 } 1679 1680 func TestBlockContext_Random(t *testing.T) { 1681 chain, vm := createChainAndVm(flow.Mainnet) 1682 header := &flow.Header{Height: 42} 1683 source := testutil.EntropyProviderFixture(nil) 1684 ctx := fvm.NewContext( 1685 fvm.WithChain(chain), 1686 fvm.WithBlockHeader(header), 1687 fvm.WithEntropyProvider(source), 1688 fvm.WithCadenceLogging(true), 1689 ) 1690 1691 tx_code := []byte(` 1692 transaction { 1693 execute { 1694 let rand1 = unsafeRandom() 1695 log(rand1) 1696 let rand2 = unsafeRandom() 1697 log(rand2) 1698 } 1699 } 1700 `) 1701 1702 getTxRandoms := func(t *testing.T) [2]uint64 { 1703 txBody := flow.NewTransactionBody().SetScript(tx_code) 1704 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1705 require.NoError(t, err) 1706 1707 _, output, err := vm.Run( 1708 ctx, 1709 fvm.Transaction(txBody, 0), 1710 testutil.RootBootstrappedLedger(vm, ctx)) 1711 require.NoError(t, err) 1712 require.NoError(t, output.Err) 1713 require.Len(t, output.Logs, 2) 1714 1715 r1, err := strconv.ParseUint(output.Logs[0], 10, 64) 1716 require.NoError(t, err) 1717 r2, err := strconv.ParseUint(output.Logs[1], 10, 64) 1718 require.NoError(t, err) 1719 return [2]uint64{r1, r2} 1720 } 1721 1722 // - checks that unsafeRandom works on transactions 1723 // - (sanity) checks that two successive randoms aren't equal 1724 t.Run("single transaction", func(t *testing.T) { 1725 randoms := getTxRandoms(t) 1726 require.NotEqual(t, randoms[1], randoms[0], "extremely unlikely to be equal") 1727 }) 1728 1729 // checks that two transactions with different IDs do not generate the same randoms 1730 t.Run("two transactions", func(t *testing.T) { 1731 // getLoggedRandoms generates different tx IDs because envelope signature is randomized 1732 randoms1 := getTxRandoms(t) 1733 randoms2 := getTxRandoms(t) 1734 require.NotEqual(t, randoms1[0], randoms2[0], "extremely unlikely to be equal") 1735 }) 1736 1737 script_string := ` 1738 pub fun main(a: Int8) { 1739 let rand = unsafeRandom() 1740 log(rand) 1741 let rand%d = unsafeRandom() 1742 log(rand%d) 1743 } 1744 ` 1745 1746 getScriptRandoms := func(t *testing.T, codeSalt int, arg int) [2]uint64 { 1747 script_code := []byte(fmt.Sprintf(script_string, codeSalt, codeSalt)) 1748 script := fvm.Script(script_code).WithArguments( 1749 jsoncdc.MustEncode(cadence.Int8(arg))) 1750 1751 _, output, err := vm.Run(ctx, script, testutil.RootBootstrappedLedger(vm, ctx)) 1752 require.NoError(t, err) 1753 require.NoError(t, output.Err) 1754 1755 r1, err := strconv.ParseUint(output.Logs[0], 10, 64) 1756 require.NoError(t, err) 1757 r2, err := strconv.ParseUint(output.Logs[1], 10, 64) 1758 require.NoError(t, err) 1759 return [2]uint64{r1, r2} 1760 } 1761 1762 // - checks that unsafeRandom works on scripts 1763 // - (sanity) checks that two successive randoms aren't equal 1764 t.Run("single script", func(t *testing.T) { 1765 randoms := getScriptRandoms(t, 1, 0) 1766 require.NotEqual(t, randoms[1], randoms[0], "extremely unlikely to be equal") 1767 }) 1768 1769 // checks that two scripts with different codes do not generate the same randoms 1770 t.Run("two script codes", func(t *testing.T) { 1771 // getScriptRandoms generates different scripts IDs using different codes 1772 randoms1 := getScriptRandoms(t, 1, 0) 1773 randoms2 := getScriptRandoms(t, 2, 0) 1774 require.NotEqual(t, randoms1[0], randoms2[0], "extremely unlikely to be equal") 1775 }) 1776 1777 // checks that two scripts with same codes but different arguments do not generate the same randoms 1778 t.Run("same script codes different arguments", func(t *testing.T) { 1779 // getScriptRandoms generates different scripts IDs using different arguments 1780 randoms1 := getScriptRandoms(t, 1, 0) 1781 randoms2 := getScriptRandoms(t, 1, 1) 1782 require.NotEqual(t, randoms1[0], randoms2[0], "extremely unlikely to be equal") 1783 }) 1784 1785 } 1786 1787 func TestBlockContext_ExecuteTransaction_CreateAccount_WithMonotonicAddresses(t *testing.T) { 1788 1789 t.Parallel() 1790 1791 chain, vm := createChainAndVm(flow.MonotonicEmulator) 1792 1793 ctx := fvm.NewContext( 1794 fvm.WithChain(chain), 1795 ) 1796 1797 txBody := flow.NewTransactionBody(). 1798 SetScript(createAccountScript). 1799 AddAuthorizer(chain.ServiceAddress()) 1800 1801 err := testutil.SignTransactionAsServiceAccount(txBody, 0, chain) 1802 require.NoError(t, err) 1803 1804 _, output, err := vm.Run( 1805 ctx, 1806 fvm.Transaction(txBody, 0), 1807 testutil.RootBootstrappedLedger(vm, ctx)) 1808 require.NoError(t, err) 1809 require.NoError(t, output.Err) 1810 1811 accountCreatedEvents := filterAccountCreatedEvents(output.Events) 1812 1813 require.Len(t, accountCreatedEvents, 1) 1814 1815 data, err := ccf.Decode(nil, accountCreatedEvents[0].Payload) 1816 require.NoError(t, err) 1817 address := flow.ConvertAddress( 1818 data.(cadence.Event).Fields[0].(cadence.Address)) 1819 1820 require.Equal(t, flow.HexToAddress("05"), address) 1821 } 1822 1823 func TestBlockContext_ExecuteTransaction_FailingTransactions(t *testing.T) { 1824 getBalance := func( 1825 vm fvm.VM, 1826 chain flow.Chain, 1827 ctx fvm.Context, 1828 storageSnapshot snapshot.StorageSnapshot, 1829 address flow.Address, 1830 ) uint64 { 1831 1832 sc := systemcontracts.SystemContractsForChain(chain.ChainID()) 1833 code := []byte(fmt.Sprintf( 1834 ` 1835 import FungibleToken from 0x%s 1836 import FlowToken from 0x%s 1837 1838 pub fun main(account: Address): UFix64 { 1839 let acct = getAccount(account) 1840 let vaultRef = acct.getCapability(/public/flowTokenBalance) 1841 .borrow<&FlowToken.Vault{FungibleToken.Balance}>() 1842 ?? panic("Could not borrow Balance reference to the Vault") 1843 1844 return vaultRef.balance 1845 } 1846 `, 1847 sc.FungibleToken.Address.Hex(), 1848 sc.FlowToken.Address.Hex(), 1849 )) 1850 script := fvm.Script(code).WithArguments( 1851 jsoncdc.MustEncode(cadence.NewAddress(address)), 1852 ) 1853 1854 _, output, err := vm.Run(ctx, script, storageSnapshot) 1855 require.NoError(t, err) 1856 require.NoError(t, output.Err) 1857 return output.Value.ToGoValue().(uint64) 1858 } 1859 1860 t.Run("Transaction fails because of storage", newVMTest().withBootstrapProcedureOptions( 1861 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1862 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1863 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1864 fvm.WithExecutionMemoryLimit(math.MaxUint64), 1865 // The evm account has a storage exception, and if we don't bootstrap with evm, 1866 // the first created account will have that address. 1867 fvm.WithSetupEVMEnabled(true), 1868 ).run( 1869 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1870 ctx.LimitAccountStorage = true // this test requires storage limits to be enforced 1871 1872 // Create an account private key. 1873 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1874 require.NoError(t, err) 1875 1876 // Bootstrap a ledger, creating accounts with the provided private 1877 // keys and the root account. 1878 snapshotTree, accounts, err := testutil.CreateAccounts( 1879 vm, 1880 snapshotTree, 1881 privateKeys, 1882 chain) 1883 require.NoError(t, err) 1884 1885 balanceBefore := getBalance( 1886 vm, 1887 chain, 1888 ctx, 1889 snapshotTree, 1890 accounts[0]) 1891 1892 txBody := transferTokensTx(chain). 1893 AddAuthorizer(accounts[0]). 1894 AddArgument(jsoncdc.MustEncode(cadence.UFix64(1))). 1895 AddArgument(jsoncdc.MustEncode( 1896 cadence.NewAddress(chain.ServiceAddress()))) 1897 1898 txBody.SetProposalKey(accounts[0], 0, 0) 1899 txBody.SetPayer(accounts[0]) 1900 1901 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 1902 require.NoError(t, err) 1903 1904 executionSnapshot, output, err := vm.Run( 1905 ctx, 1906 fvm.Transaction(txBody, 0), 1907 snapshotTree) 1908 require.NoError(t, err) 1909 1910 snapshotTree = snapshotTree.Append(executionSnapshot) 1911 1912 require.True(t, errors.IsStorageCapacityExceededError(output.Err)) 1913 1914 balanceAfter := getBalance( 1915 vm, 1916 chain, 1917 ctx, 1918 snapshotTree.Append(executionSnapshot), 1919 accounts[0]) 1920 1921 require.Equal(t, balanceAfter, balanceBefore) 1922 }), 1923 ) 1924 1925 t.Run("Transaction fails because of recipient account not existing", newVMTest().withBootstrapProcedureOptions( 1926 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1927 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1928 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 1929 fvm.WithExecutionMemoryLimit(math.MaxUint64), 1930 ).run( 1931 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1932 ctx.LimitAccountStorage = true // this test requires storage limits to be enforced 1933 1934 // Create an account private key. 1935 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 1936 require.NoError(t, err) 1937 1938 // Bootstrap a ledger, creating accounts with the provided private 1939 // keys and the root account. 1940 snapshotTree, accounts, err := testutil.CreateAccounts( 1941 vm, 1942 snapshotTree, 1943 privateKeys, 1944 chain) 1945 require.NoError(t, err) 1946 1947 // non-existent account 1948 lastAddress, err := chain.AddressAtIndex((1 << 45) - 1) 1949 require.NoError(t, err) 1950 1951 balanceBefore := getBalance( 1952 vm, 1953 chain, 1954 ctx, 1955 snapshotTree, 1956 accounts[0]) 1957 1958 // transfer tokens to non-existent account 1959 txBody := transferTokensTx(chain). 1960 AddAuthorizer(accounts[0]). 1961 AddArgument(jsoncdc.MustEncode(cadence.UFix64(1))). 1962 AddArgument(jsoncdc.MustEncode(cadence.NewAddress(lastAddress))) 1963 1964 txBody.SetProposalKey(accounts[0], 0, 0) 1965 txBody.SetPayer(accounts[0]) 1966 1967 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 1968 require.NoError(t, err) 1969 1970 executionSnapshot, output, err := vm.Run( 1971 ctx, 1972 fvm.Transaction(txBody, 0), 1973 snapshotTree) 1974 require.NoError(t, err) 1975 1976 snapshotTree = snapshotTree.Append(executionSnapshot) 1977 1978 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 1979 1980 balanceAfter := getBalance( 1981 vm, 1982 chain, 1983 ctx, 1984 snapshotTree.Append(executionSnapshot), 1985 accounts[0]) 1986 1987 require.Equal(t, balanceAfter, balanceBefore) 1988 }), 1989 ) 1990 1991 t.Run("Transaction sequence number check fails and sequence number is not incremented", newVMTest().withBootstrapProcedureOptions( 1992 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 1993 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 1994 ). 1995 run( 1996 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 1997 // this test requires storage limits to be enforced 1998 ctx.LimitAccountStorage = true 1999 2000 // Create an account private key. 2001 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 2002 require.NoError(t, err) 2003 2004 // Bootstrap a ledger, creating accounts with the provided 2005 // private keys and the root account. 2006 snapshotTree, accounts, err := testutil.CreateAccounts( 2007 vm, 2008 snapshotTree, 2009 privateKeys, 2010 chain) 2011 require.NoError(t, err) 2012 2013 txBody := transferTokensTx(chain). 2014 AddAuthorizer(accounts[0]). 2015 AddArgument(jsoncdc.MustEncode(cadence.UFix64(1_0000_0000_0000))). 2016 AddArgument(jsoncdc.MustEncode( 2017 cadence.NewAddress(chain.ServiceAddress()))) 2018 2019 // set wrong sequence number 2020 txBody.SetProposalKey(accounts[0], 0, 10) 2021 txBody.SetPayer(accounts[0]) 2022 2023 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 2024 require.NoError(t, err) 2025 2026 _, output, err := vm.Run( 2027 ctx, 2028 fvm.Transaction(txBody, 0), 2029 snapshotTree) 2030 require.NoError(t, err) 2031 2032 require.Equal( 2033 t, 2034 errors.ErrCodeInvalidProposalSeqNumberError, 2035 output.Err.Code()) 2036 2037 // The outer most coded error is a wrapper, not the actual 2038 // InvalidProposalSeqNumberError itself. 2039 _, ok := output.Err.(errors.InvalidProposalSeqNumberError) 2040 require.False(t, ok) 2041 2042 var seqNumErr errors.InvalidProposalSeqNumberError 2043 ok = errors.As(output.Err, &seqNumErr) 2044 require.True(t, ok) 2045 require.Equal(t, uint64(0), seqNumErr.CurrentSeqNumber()) 2046 }), 2047 ) 2048 2049 t.Run("Transaction invocation fails but sequence number is incremented", newVMTest().withBootstrapProcedureOptions( 2050 fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), 2051 fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), 2052 fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), 2053 ). 2054 run( 2055 func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { 2056 // this test requires storage limits to be enforced 2057 ctx.LimitAccountStorage = true 2058 2059 // Create an account private key. 2060 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 2061 require.NoError(t, err) 2062 2063 // Bootstrap a ledger, creating accounts with the provided 2064 // private keys and the root account. 2065 snapshotTree, accounts, err := testutil.CreateAccounts( 2066 vm, 2067 snapshotTree, 2068 privateKeys, 2069 chain) 2070 require.NoError(t, err) 2071 2072 txBody := transferTokensTx(chain). 2073 AddAuthorizer(accounts[0]). 2074 AddArgument(jsoncdc.MustEncode( 2075 cadence.UFix64(1_0000_0000_0000))). 2076 AddArgument(jsoncdc.MustEncode( 2077 cadence.NewAddress(chain.ServiceAddress()))) 2078 2079 txBody.SetProposalKey(accounts[0], 0, 0) 2080 txBody.SetPayer(accounts[0]) 2081 2082 err = testutil.SignEnvelope(txBody, accounts[0], privateKeys[0]) 2083 require.NoError(t, err) 2084 2085 executionSnapshot, output, err := vm.Run( 2086 ctx, 2087 fvm.Transaction(txBody, 0), 2088 snapshotTree) 2089 require.NoError(t, err) 2090 2091 snapshotTree = snapshotTree.Append(executionSnapshot) 2092 2093 require.True(t, errors.IsCadenceRuntimeError(output.Err)) 2094 2095 // send it again 2096 _, output, err = vm.Run( 2097 ctx, 2098 fvm.Transaction(txBody, 0), 2099 snapshotTree) 2100 require.NoError(t, err) 2101 2102 require.Equal( 2103 t, 2104 errors.ErrCodeInvalidProposalSeqNumberError, 2105 output.Err.Code()) 2106 2107 // The outer most coded error is a wrapper, not the actual 2108 // InvalidProposalSeqNumberError itself. 2109 _, ok := output.Err.(errors.InvalidProposalSeqNumberError) 2110 require.False(t, ok) 2111 2112 var seqNumErr errors.InvalidProposalSeqNumberError 2113 ok = errors.As(output.Err, &seqNumErr) 2114 require.True(t, ok) 2115 require.Equal(t, uint64(1), seqNumErr.CurrentSeqNumber()) 2116 }), 2117 ) 2118 }