github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/evm/handler/handler.go (about) 1 package handler 2 3 import ( 4 "math/big" 5 6 "github.com/onflow/cadence/runtime/common" 7 gethCommon "github.com/onflow/go-ethereum/common" 8 gethTypes "github.com/onflow/go-ethereum/core/types" 9 10 "github.com/onflow/flow-go/fvm/environment" 11 fvmErrors "github.com/onflow/flow-go/fvm/errors" 12 "github.com/onflow/flow-go/fvm/evm/handler/coa" 13 "github.com/onflow/flow-go/fvm/evm/types" 14 "github.com/onflow/flow-go/model/flow" 15 ) 16 17 // ContractHandler is responsible for triggering calls to emulator, metering, 18 // event emission and updating the block 19 type ContractHandler struct { 20 flowChainID flow.ChainID 21 evmContractAddress flow.Address 22 flowTokenAddress common.Address 23 blockStore types.BlockStore 24 addressAllocator types.AddressAllocator 25 backend types.Backend 26 emulator types.Emulator 27 precompiles []types.Precompile 28 } 29 30 func (h *ContractHandler) FlowTokenAddress() common.Address { 31 return h.flowTokenAddress 32 } 33 34 func (h *ContractHandler) EVMContractAddress() common.Address { 35 return common.Address(h.evmContractAddress) 36 } 37 38 var _ types.ContractHandler = &ContractHandler{} 39 40 func NewContractHandler( 41 flowChainID flow.ChainID, 42 evmContractAddress flow.Address, 43 flowTokenAddress common.Address, 44 randomBeaconAddress flow.Address, 45 blockStore types.BlockStore, 46 addressAllocator types.AddressAllocator, 47 backend types.Backend, 48 emulator types.Emulator, 49 ) *ContractHandler { 50 return &ContractHandler{ 51 flowChainID: flowChainID, 52 evmContractAddress: evmContractAddress, 53 flowTokenAddress: flowTokenAddress, 54 blockStore: blockStore, 55 addressAllocator: addressAllocator, 56 backend: backend, 57 emulator: emulator, 58 precompiles: preparePrecompiles(evmContractAddress, randomBeaconAddress, addressAllocator, backend), 59 } 60 } 61 62 // DeployCOA deploys a cadence-owned-account and returns the address 63 func (h *ContractHandler) DeployCOA(uuid uint64) types.Address { 64 res, err := h.deployCOA(uuid) 65 panicOnErrorOrInvalidOrFailedState(res, err) 66 return *res.DeployedContractAddress 67 } 68 69 func (h *ContractHandler) deployCOA(uuid uint64) (*types.Result, error) { 70 target := h.addressAllocator.AllocateCOAAddress(uuid) 71 gaslimit := types.GasLimit(coa.ContractDeploymentRequiredGas) 72 err := h.checkGasLimit(gaslimit) 73 if err != nil { 74 return nil, err 75 } 76 77 factory := h.addressAllocator.COAFactoryAddress() 78 factoryAccount := h.AccountByAddress(factory, false) 79 call := types.NewDeployCallWithTargetAddress( 80 factory, 81 target, 82 coa.ContractBytes, 83 uint64(gaslimit), 84 new(big.Int), 85 factoryAccount.Nonce(), 86 ) 87 88 ctx, err := h.getBlockContext() 89 if err != nil { 90 return nil, err 91 } 92 return h.executeAndHandleCall(ctx, call, nil, false) 93 } 94 95 // AccountByAddress returns the account for the given address, 96 // if isAuthorized is set, account is controlled by the FVM (COAs) 97 func (h *ContractHandler) AccountByAddress(addr types.Address, isAuthorized bool) types.Account { 98 return newAccount(h, addr, isAuthorized) 99 } 100 101 // LastExecutedBlock returns the last executed block 102 func (h *ContractHandler) LastExecutedBlock() *types.Block { 103 block, err := h.blockStore.LatestBlock() 104 panicOnError(err) 105 return block 106 } 107 108 // RunOrPanic runs an rlpencoded evm transaction and 109 // collects the gas fees and pay it to the coinbase address provided. 110 func (h *ContractHandler) RunOrPanic(rlpEncodedTx []byte, coinbase types.Address) { 111 res, err := h.run(rlpEncodedTx, coinbase) 112 panicOnErrorOrInvalidOrFailedState(res, err) 113 } 114 115 // Run tries to run an rlpencoded evm transaction and 116 // collects the gas fees and pay it to the coinbase address provided. 117 func (h *ContractHandler) Run(rlpEncodedTx []byte, coinbase types.Address) *types.ResultSummary { 118 res, err := h.run(rlpEncodedTx, coinbase) 119 panicOnError(err) 120 return res.ResultSummary() 121 } 122 123 // BatchRun tries to run batch of rlp-encoded transactions and 124 // collects the gas fees and pay it to the coinbase address provided. 125 // All transactions provided in the batch are included in a single block, 126 // except for invalid transactions 127 func (h *ContractHandler) BatchRun(rlpEncodedTxs [][]byte, coinbase types.Address) []*types.ResultSummary { 128 res, err := h.batchRun(rlpEncodedTxs, coinbase) 129 panicOnError(err) 130 131 resSummary := make([]*types.ResultSummary, len(res)) 132 for i, r := range res { 133 resSummary[i] = r.ResultSummary() 134 } 135 return resSummary 136 } 137 138 func (h *ContractHandler) batchRun(rlpEncodedTxs [][]byte, coinbase types.Address) ([]*types.Result, error) { 139 // prepare block view used to run the batch 140 ctx, err := h.getBlockContext() 141 if err != nil { 142 return nil, err 143 } 144 145 ctx.GasFeeCollector = coinbase 146 blk, err := h.emulator.NewBlockView(ctx) 147 if err != nil { 148 return nil, err 149 } 150 151 bp, err := h.blockStore.BlockProposal() 152 if err != nil { 153 return nil, err 154 } 155 156 // decode all transactions and calculate total gas limit 157 var totalGasLimit types.GasLimit 158 batchLen := len(rlpEncodedTxs) 159 txs := make([]*gethTypes.Transaction, batchLen) 160 161 for i, rlpEncodedTx := range rlpEncodedTxs { 162 tx, err := h.decodeTransaction(rlpEncodedTx) 163 if err != nil { 164 return nil, err 165 } 166 167 txs[i] = tx 168 totalGasLimit += types.GasLimit(tx.Gas()) 169 } 170 171 // check if all transactions in the batch are below gas limit 172 err = h.checkGasLimit(totalGasLimit) 173 if err != nil { 174 return nil, err 175 } 176 177 res, err := blk.BatchRunTransactions(txs) 178 if err != nil { 179 return nil, err 180 } 181 182 // saftey check for result 183 if len(res) == 0 { 184 return nil, types.ErrUnexpectedEmptyResult 185 } 186 187 bp.PopulateReceiptRoot(res) 188 189 // meter all the transaction gas usage and append hashes to the block 190 for _, r := range res { 191 // meter gas anyway (even for invalid or failed states) 192 err = h.meterGasUsage(r) 193 if err != nil { 194 return nil, err 195 } 196 197 // include it in a block only if valid (not invalid) 198 if !r.Invalid() { 199 bp.AppendTxHash(r.TxHash) 200 } 201 } 202 203 blockHash, err := bp.Hash() 204 if err != nil { 205 return nil, err 206 } 207 208 // if there were no valid transactions skip emitting events 209 // and commiting a new block 210 if len(bp.TransactionHashes) == 0 { 211 return res, nil 212 } 213 214 for i, r := range res { 215 if r.Invalid() { // don't emit events for invalid tx 216 continue 217 } 218 err = h.emitEvent(types.NewTransactionEvent( 219 r, 220 rlpEncodedTxs[i], 221 bp.Height, 222 blockHash, 223 )) 224 if err != nil { 225 return nil, err 226 } 227 } 228 229 err = h.emitEvent(types.NewBlockEvent(bp)) 230 if err != nil { 231 return nil, err 232 } 233 234 err = h.blockStore.CommitBlockProposal() 235 if err != nil { 236 return nil, err 237 } 238 239 return res, nil 240 } 241 242 func (h *ContractHandler) run( 243 rlpEncodedTx []byte, 244 coinbase types.Address, 245 ) (*types.Result, error) { 246 // step 1 - transaction decoding 247 tx, err := h.decodeTransaction(rlpEncodedTx) 248 if err != nil { 249 return nil, err 250 } 251 252 // step 2 - run transaction 253 err = h.checkGasLimit(types.GasLimit(tx.Gas())) 254 if err != nil { 255 return nil, err 256 } 257 258 ctx, err := h.getBlockContext() 259 if err != nil { 260 return nil, err 261 } 262 ctx.GasFeeCollector = coinbase 263 blk, err := h.emulator.NewBlockView(ctx) 264 if err != nil { 265 return nil, err 266 } 267 268 res, err := blk.RunTransaction(tx) 269 if err != nil { 270 return nil, err 271 } 272 273 // saftey check for result 274 if res == nil { 275 return nil, types.ErrUnexpectedEmptyResult 276 } 277 278 // meter gas anyway (even for invalid or failed states) 279 err = h.meterGasUsage(res) 280 if err != nil { 281 return nil, err 282 } 283 284 // if is invalid tx skip the next steps (forming block, ...) 285 if res.Invalid() { 286 return res, nil 287 } 288 289 // step 3 - update block proposal 290 bp, err := h.blockStore.BlockProposal() 291 if err != nil { 292 return nil, err 293 } 294 295 bp.AppendTxHash(res.TxHash) 296 297 // populate receipt root 298 bp.PopulateReceiptRoot([]*types.Result{res}) 299 bp.CalculateGasUsage([]types.Result{*res}) 300 301 blockHash, err := bp.Hash() 302 if err != nil { 303 return nil, err 304 } 305 306 // step 4 - emit events 307 err = h.emitEvent(types.NewTransactionEvent(res, rlpEncodedTx, bp.Height, blockHash)) 308 if err != nil { 309 return nil, err 310 } 311 312 err = h.emitEvent(types.NewBlockEvent(bp)) 313 if err != nil { 314 return nil, err 315 } 316 317 // step 5 - commit block proposal 318 err = h.blockStore.CommitBlockProposal() 319 if err != nil { 320 return nil, err 321 } 322 return res, nil 323 } 324 325 func (h *ContractHandler) DryRun( 326 rlpEncodedTx []byte, 327 from types.Address, 328 ) *types.ResultSummary { 329 res, err := h.dryRun(rlpEncodedTx, from) 330 panicOnError(err) 331 return res.ResultSummary() 332 } 333 334 func (h *ContractHandler) dryRun( 335 rlpEncodedTx []byte, 336 from types.Address, 337 ) (*types.Result, error) { 338 // step 1 - transaction decoding 339 encodedLen := uint(len(rlpEncodedTx)) 340 err := h.backend.MeterComputation(environment.ComputationKindRLPDecoding, encodedLen) 341 if err != nil { 342 return nil, err 343 } 344 345 tx := gethTypes.Transaction{} 346 err = tx.UnmarshalBinary(rlpEncodedTx) 347 if err != nil { 348 return nil, err 349 } 350 351 ctx, err := h.getBlockContext() 352 if err != nil { 353 return nil, err 354 } 355 356 blk, err := h.emulator.NewBlockView(ctx) 357 if err != nil { 358 return nil, err 359 } 360 361 res, err := blk.DryRunTransaction(&tx, from.ToCommon()) 362 if err != nil { 363 return nil, err 364 } 365 366 // saftey check for result 367 if res == nil { 368 return nil, types.ErrUnexpectedEmptyResult 369 } 370 371 return res, nil 372 } 373 374 func (h *ContractHandler) checkGasLimit(limit types.GasLimit) error { 375 // check gas limit against what has been left on the transaction side 376 if !h.backend.ComputationAvailable(environment.ComputationKindEVMGasUsage, uint(limit)) { 377 return types.ErrInsufficientComputation 378 } 379 return nil 380 } 381 382 // decodeTransaction decodes RLP encoded transaction payload and meters the resources used. 383 func (h *ContractHandler) decodeTransaction(encodedTx []byte) (*gethTypes.Transaction, error) { 384 encodedLen := uint(len(encodedTx)) 385 err := h.backend.MeterComputation(environment.ComputationKindRLPDecoding, encodedLen) 386 if err != nil { 387 return nil, err 388 } 389 390 tx := gethTypes.Transaction{} 391 if err := tx.UnmarshalBinary(encodedTx); err != nil { 392 return nil, err 393 } 394 395 return &tx, nil 396 } 397 398 func (h *ContractHandler) meterGasUsage(res *types.Result) error { 399 return h.backend.MeterComputation(environment.ComputationKindEVMGasUsage, uint(res.GasConsumed)) 400 } 401 402 func (h *ContractHandler) emitEvent(event *types.Event) error { 403 location := common.NewAddressLocation(nil, common.Address(h.evmContractAddress), "EVM") 404 ev, err := event.Payload.ToCadence(location) 405 if err != nil { 406 return err 407 } 408 return h.backend.EmitEvent(ev) 409 } 410 411 func (h *ContractHandler) getBlockContext() (types.BlockContext, error) { 412 bp, err := h.blockStore.BlockProposal() 413 if err != nil { 414 return types.BlockContext{}, err 415 } 416 rand := gethCommon.Hash{} 417 err = h.backend.ReadRandom(rand[:]) 418 if err != nil { 419 return types.BlockContext{}, err 420 } 421 422 return types.BlockContext{ 423 ChainID: types.EVMChainIDFromFlowChainID(h.flowChainID), 424 BlockNumber: bp.Height, 425 BlockTimestamp: bp.Timestamp, 426 DirectCallBaseGasUsage: types.DefaultDirectCallBaseGasUsage, 427 GetHashFunc: func(n uint64) gethCommon.Hash { 428 hash, err := h.blockStore.BlockHash(n) 429 panicOnError(err) // we have to handle it here given we can't continue with it even in try case 430 return hash 431 }, 432 ExtraPrecompiles: h.precompiles, 433 Random: rand, 434 }, nil 435 } 436 437 func (h *ContractHandler) executeAndHandleCall( 438 ctx types.BlockContext, 439 call *types.DirectCall, 440 totalSupplyDiff *big.Int, 441 deductSupplyDiff bool, 442 ) (*types.Result, error) { 443 // execute the call 444 blk, err := h.emulator.NewBlockView(ctx) 445 if err != nil { 446 return nil, err 447 } 448 449 res, err := blk.DirectCall(call) 450 // check backend errors first 451 if err != nil { 452 return nil, err 453 } 454 455 // saftey check for result 456 if res == nil { 457 return nil, types.ErrUnexpectedEmptyResult 458 } 459 460 // gas meter even invalid or failed status 461 err = h.meterGasUsage(res) 462 if err != nil { 463 return nil, err 464 } 465 466 // if is invalid skip the rest of states 467 if res.Invalid() { 468 return res, nil 469 } 470 471 // update block proposal 472 bp, err := h.blockStore.BlockProposal() 473 if err != nil { 474 return nil, err 475 } 476 477 bp.AppendTxHash(res.TxHash) 478 479 // Populate receipt root 480 bp.PopulateReceiptRoot([]*types.Result{res}) 481 482 if totalSupplyDiff != nil { 483 if deductSupplyDiff { 484 bp.TotalSupply = new(big.Int).Sub(bp.TotalSupply, totalSupplyDiff) 485 if bp.TotalSupply.Sign() < 0 { 486 return nil, types.ErrInsufficientTotalSupply 487 } 488 } else { 489 bp.TotalSupply = new(big.Int).Add(bp.TotalSupply, totalSupplyDiff) 490 } 491 } 492 493 blockHash, err := bp.Hash() 494 if err != nil { 495 return nil, err 496 } 497 498 // emit events 499 encoded, err := call.Encode() 500 if err != nil { 501 return nil, err 502 } 503 504 err = h.emitEvent( 505 types.NewTransactionEvent(res, encoded, bp.Height, blockHash), 506 ) 507 if err != nil { 508 return nil, err 509 } 510 511 err = h.emitEvent(types.NewBlockEvent(bp)) 512 if err != nil { 513 return nil, err 514 } 515 516 // commit block proposal 517 err = h.blockStore.CommitBlockProposal() 518 if err != nil { 519 return nil, err 520 } 521 522 return res, nil 523 } 524 525 func (h *ContractHandler) GenerateResourceUUID() uint64 { 526 uuid, err := h.backend.GenerateUUID() 527 panicOnError(err) 528 return uuid 529 } 530 531 type Account struct { 532 isAuthorized bool 533 address types.Address 534 fch *ContractHandler 535 } 536 537 // newAccount creates a new evm account 538 func newAccount(fch *ContractHandler, addr types.Address, isAuthorized bool) *Account { 539 return &Account{ 540 isAuthorized: isAuthorized, 541 fch: fch, 542 address: addr, 543 } 544 } 545 546 // Address returns the address associated with the account 547 func (a *Account) Address() types.Address { 548 return a.address 549 } 550 551 // Nonce returns the nonce of this account 552 // 553 // Note: we don't meter any extra computation given reading data 554 // from the storage already transalates into computation 555 func (a *Account) Nonce() uint64 { 556 nonce, err := a.nonce() 557 panicOnError(err) 558 return nonce 559 } 560 561 func (a *Account) nonce() (uint64, error) { 562 ctx, err := a.fch.getBlockContext() 563 if err != nil { 564 return 0, err 565 } 566 567 blk, err := a.fch.emulator.NewReadOnlyBlockView(ctx) 568 if err != nil { 569 return 0, err 570 } 571 572 return blk.NonceOf(a.address) 573 } 574 575 // Balance returns the balance of this account 576 // 577 // Note: we don't meter any extra computation given reading data 578 // from the storage already transalates into computation 579 func (a *Account) Balance() types.Balance { 580 bal, err := a.balance() 581 panicOnError(err) 582 return bal 583 } 584 585 func (a *Account) balance() (types.Balance, error) { 586 ctx, err := a.fch.getBlockContext() 587 if err != nil { 588 return nil, err 589 } 590 591 blk, err := a.fch.emulator.NewReadOnlyBlockView(ctx) 592 if err != nil { 593 return nil, err 594 } 595 596 bl, err := blk.BalanceOf(a.address) 597 return types.NewBalance(bl), err 598 } 599 600 // Code returns the code of this account 601 // 602 // Note: we don't meter any extra computation given reading data 603 // from the storage already transalates into computation 604 func (a *Account) Code() types.Code { 605 code, err := a.code() 606 panicOnError(err) 607 return code 608 } 609 610 func (a *Account) code() (types.Code, error) { 611 ctx, err := a.fch.getBlockContext() 612 if err != nil { 613 return nil, err 614 } 615 616 blk, err := a.fch.emulator.NewReadOnlyBlockView(ctx) 617 if err != nil { 618 return nil, err 619 } 620 return blk.CodeOf(a.address) 621 } 622 623 // CodeHash returns the code hash of this account 624 // 625 // Note: we don't meter any extra computation given reading data 626 // from the storage already transalates into computation 627 func (a *Account) CodeHash() []byte { 628 codeHash, err := a.codeHash() 629 panicOnError(err) 630 return codeHash 631 } 632 633 func (a *Account) codeHash() ([]byte, error) { 634 ctx, err := a.fch.getBlockContext() 635 if err != nil { 636 return nil, err 637 } 638 639 blk, err := a.fch.emulator.NewReadOnlyBlockView(ctx) 640 if err != nil { 641 return nil, err 642 } 643 return blk.CodeHashOf(a.address) 644 } 645 646 // Deposit deposits the token from the given vault into the flow evm main vault 647 // and update the account balance with the new amount 648 func (a *Account) Deposit(v *types.FLOWTokenVault) { 649 res, err := a.deposit(v) 650 panicOnErrorOrInvalidOrFailedState(res, err) 651 } 652 653 func (a *Account) deposit(v *types.FLOWTokenVault) (*types.Result, error) { 654 bridge := a.fch.addressAllocator.NativeTokenBridgeAddress() 655 bridgeAccount := a.fch.AccountByAddress(bridge, false) 656 657 call := types.NewDepositCall( 658 bridge, 659 a.address, 660 v.Balance(), 661 bridgeAccount.Nonce(), 662 ) 663 ctx, err := a.precheck(false, types.GasLimit(call.GasLimit)) 664 if err != nil { 665 return nil, err 666 } 667 668 return a.fch.executeAndHandleCall(ctx, call, v.Balance(), false) 669 } 670 671 // Withdraw deducts the balance from the account and 672 // withdraw and return flow token from the Flex main vault. 673 func (a *Account) Withdraw(b types.Balance) *types.FLOWTokenVault { 674 res, err := a.withdraw(b) 675 panicOnErrorOrInvalidOrFailedState(res, err) 676 677 return types.NewFlowTokenVault(b) 678 } 679 680 func (a *Account) withdraw(b types.Balance) (*types.Result, error) { 681 call := types.NewWithdrawCall( 682 a.fch.addressAllocator.NativeTokenBridgeAddress(), 683 a.address, 684 b, 685 a.Nonce(), 686 ) 687 688 ctx, err := a.precheck(true, types.GasLimit(call.GasLimit)) 689 if err != nil { 690 return nil, err 691 } 692 693 // Don't allow withdraw for balances that has rounding error 694 if types.BalanceConvertionToUFix64ProneToRoundingError(b) { 695 return nil, types.ErrWithdrawBalanceRounding 696 } 697 698 return a.fch.executeAndHandleCall(ctx, call, b, true) 699 } 700 701 // Transfer transfers tokens between accounts 702 func (a *Account) Transfer(to types.Address, balance types.Balance) { 703 res, err := a.transfer(to, balance) 704 panicOnErrorOrInvalidOrFailedState(res, err) 705 } 706 707 func (a *Account) transfer(to types.Address, balance types.Balance) (*types.Result, error) { 708 call := types.NewTransferCall( 709 a.address, 710 to, 711 balance, 712 a.Nonce(), 713 ) 714 ctx, err := a.precheck(true, types.GasLimit(call.GasLimit)) 715 if err != nil { 716 return nil, err 717 } 718 719 return a.fch.executeAndHandleCall(ctx, call, nil, false) 720 } 721 722 // Deploy deploys a contract to the EVM environment 723 // the new deployed contract would be at the returned address 724 // contained in the result summary as data and 725 // the contract data is not controlled by the caller accounts 726 func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) *types.ResultSummary { 727 res, err := a.deploy(code, gaslimit, balance) 728 panicOnError(err) 729 return res.ResultSummary() 730 } 731 732 func (a *Account) deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) (*types.Result, error) { 733 ctx, err := a.precheck(true, gaslimit) 734 if err != nil { 735 return nil, err 736 } 737 738 call := types.NewDeployCall( 739 a.address, 740 code, 741 uint64(gaslimit), 742 balance, 743 a.Nonce(), 744 ) 745 return a.fch.executeAndHandleCall(ctx, call, nil, false) 746 } 747 748 // Call calls a smart contract function with the given data 749 // it would limit the gas used according to the limit provided 750 // given it doesn't goes beyond what Flow transaction allows. 751 // the balance would be deducted from the OFA account and would be transferred to the target address 752 func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimit, balance types.Balance) *types.ResultSummary { 753 res, err := a.call(to, data, gaslimit, balance) 754 panicOnError(err) 755 return res.ResultSummary() 756 } 757 758 func (a *Account) call(to types.Address, data types.Data, gaslimit types.GasLimit, balance types.Balance) (*types.Result, error) { 759 ctx, err := a.precheck(true, gaslimit) 760 if err != nil { 761 return nil, err 762 } 763 call := types.NewContractCall( 764 a.address, 765 to, 766 data, 767 uint64(gaslimit), 768 balance, 769 a.Nonce(), 770 ) 771 772 return a.fch.executeAndHandleCall(ctx, call, nil, false) 773 } 774 775 func (a *Account) precheck(authroized bool, gaslimit types.GasLimit) (types.BlockContext, error) { 776 // check if account is authorized (i.e. is a COA) 777 if authroized && !a.isAuthorized { 778 return types.BlockContext{}, types.ErrUnAuthroizedMethodCall 779 } 780 err := a.fch.checkGasLimit(gaslimit) 781 if err != nil { 782 return types.BlockContext{}, err 783 } 784 785 return a.fch.getBlockContext() 786 } 787 788 func panicOnErrorOrInvalidOrFailedState(res *types.Result, err error) { 789 790 if res != nil && res.Invalid() { 791 panic(fvmErrors.NewEVMError(res.ValidationError)) 792 } 793 794 if res != nil && res.Failed() { 795 panic(fvmErrors.NewEVMError(res.VMError)) 796 } 797 798 // this should never happen 799 if err == nil && res == nil { 800 panic(fvmErrors.NewEVMError(types.ErrUnexpectedEmptyResult)) 801 } 802 803 panicOnError(err) 804 } 805 806 // panicOnError errors panic on returned errors 807 func panicOnError(err error) { 808 if err == nil { 809 return 810 } 811 812 if types.IsAFatalError(err) { 813 panic(fvmErrors.NewEVMFailure(err)) 814 } 815 816 if types.IsABackendError(err) { 817 // backend errors doesn't need wrapping 818 panic(err) 819 } 820 821 // any other returned errors are non-fatal errors 822 panic(fvmErrors.NewEVMError(err)) 823 }