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