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