github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/execution/evm/evm.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package evm 7 8 import ( 9 "bytes" 10 "context" 11 "math" 12 "math/big" 13 "time" 14 15 "github.com/ethereum/go-ethereum/common" 16 "github.com/ethereum/go-ethereum/core/types" 17 "github.com/ethereum/go-ethereum/core/vm" 18 "github.com/ethereum/go-ethereum/crypto" 19 "github.com/ethereum/go-ethereum/params" 20 "github.com/pkg/errors" 21 "go.uber.org/zap" 22 "google.golang.org/protobuf/proto" 23 24 "github.com/iotexproject/go-pkgs/hash" 25 "github.com/iotexproject/iotex-address/address" 26 "github.com/iotexproject/iotex-proto/golang/iotextypes" 27 28 "github.com/iotexproject/iotex-core/action" 29 "github.com/iotexproject/iotex-core/action/protocol" 30 "github.com/iotexproject/iotex-core/blockchain/genesis" 31 "github.com/iotexproject/iotex-core/pkg/log" 32 "github.com/iotexproject/iotex-core/pkg/tracer" 33 "github.com/iotexproject/iotex-core/pkg/util/byteutil" 34 ) 35 36 var ( 37 // TODO: whenever ActionGasLimit is removed from genesis, we need to hard code it to 5M to make it compatible with 38 // the mainnet. 39 _preAleutianActionGasLimit = genesis.Default.ActionGasLimit 40 41 _inContractTransfer = hash.BytesToHash256([]byte{byte(iotextypes.TransactionLogType_IN_CONTRACT_TRANSFER)}) 42 43 // _revertSelector is a special function selector for revert reason unpacking. 44 _revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4] 45 46 // ErrInconsistentNonce is the error that the nonce is different from executor's nonce 47 ErrInconsistentNonce = errors.New("Nonce is not identical to executor nonce") 48 ) 49 50 type ( 51 // GetBlockHash gets block hash by height 52 GetBlockHash func(uint64) (hash.Hash256, error) 53 54 // GetBlockTime gets block time by height 55 GetBlockTime func(uint64) (time.Time, error) 56 57 // DepositGasWithSGD deposits gas with Sharing of Gas-fee with DApps 58 DepositGasWithSGD func(context.Context, protocol.StateManager, address.Address, *big.Int, *big.Int) (*action.TransactionLog, error) 59 60 // SGDRegistry is the interface for handling Sharing of Gas-fee with DApps 61 SGDRegistry interface { 62 CheckContract(context.Context, string, uint64) (address.Address, uint64, bool, error) 63 } 64 ) 65 66 // CanTransfer checks whether the from account has enough balance 67 func CanTransfer(db vm.StateDB, fromHash common.Address, balance *big.Int) bool { 68 return db.GetBalance(fromHash).Cmp(balance) >= 0 69 } 70 71 // MakeTransfer transfers account 72 func MakeTransfer(db vm.StateDB, fromHash, toHash common.Address, amount *big.Int) { 73 db.SubBalance(fromHash, amount) 74 db.AddBalance(toHash, amount) 75 76 db.AddLog(&types.Log{ 77 Topics: []common.Hash{ 78 common.BytesToHash(_inContractTransfer[:]), 79 common.BytesToHash(fromHash[:]), 80 common.BytesToHash(toHash[:]), 81 }, 82 Data: amount.Bytes(), 83 }) 84 } 85 86 type ( 87 // Params is the context and parameters 88 Params struct { 89 context vm.BlockContext 90 txCtx vm.TxContext 91 nonce uint64 92 amount *big.Int 93 contract *common.Address 94 gas uint64 95 data []byte 96 accessList types.AccessList 97 evmConfig vm.Config 98 chainConfig *params.ChainConfig 99 genesis genesis.Blockchain 100 blkCtx protocol.BlockCtx 101 featureCtx protocol.FeatureCtx 102 actionCtx protocol.ActionCtx 103 helperCtx HelperContext 104 } 105 ) 106 107 // newParams creates a new context for use in the EVM. 108 func newParams( 109 ctx context.Context, 110 execution *action.Execution, 111 stateDB *StateDBAdapter, 112 ) (*Params, error) { 113 var ( 114 actionCtx = protocol.MustGetActionCtx(ctx) 115 blkCtx = protocol.MustGetBlockCtx(ctx) 116 featureCtx = protocol.MustGetFeatureCtx(ctx) 117 g = genesis.MustExtractGenesisContext(ctx) 118 helperCtx = mustGetHelperCtx(ctx) 119 evmNetworkID = protocol.MustGetBlockchainCtx(ctx).EvmNetworkID 120 executorAddr = common.BytesToAddress(actionCtx.Caller.Bytes()) 121 getBlockHash = helperCtx.GetBlockHash 122 123 vmConfig vm.Config 124 contractAddrPointer *common.Address 125 getHashFn vm.GetHashFunc 126 ) 127 128 if dest := execution.Contract(); dest != action.EmptyAddress { 129 contract, err := address.FromString(execution.Contract()) 130 if err != nil { 131 return nil, errors.Wrapf(err, "failed to decode contract address %s", dest) 132 } 133 contractAddr := common.BytesToAddress(contract.Bytes()) 134 contractAddrPointer = &contractAddr 135 } 136 137 gasLimit := execution.GasLimit() 138 // Reset gas limit to the system wide action gas limit cap if it's greater than it 139 if blkCtx.BlockHeight > 0 && featureCtx.SystemWideActionGasLimit && gasLimit > _preAleutianActionGasLimit { 140 gasLimit = _preAleutianActionGasLimit 141 } 142 143 switch { 144 case featureCtx.CorrectGetHashFn: 145 getHashFn = func(n uint64) common.Hash { 146 hash, err := getBlockHash(n) 147 if err == nil { 148 return common.BytesToHash(hash[:]) 149 } 150 return common.Hash{} 151 } 152 case featureCtx.FixGetHashFnHeight: 153 getHashFn = func(n uint64) common.Hash { 154 hash, err := getBlockHash(stateDB.blockHeight - (n + 1)) 155 if err == nil { 156 return common.BytesToHash(hash[:]) 157 } 158 return common.Hash{} 159 } 160 default: 161 getHashFn = func(n uint64) common.Hash { 162 hash, err := getBlockHash(stateDB.blockHeight - n) 163 if err != nil { 164 // initial implementation did wrong, should return common.Hash{} in case of error 165 return common.BytesToHash(hash[:]) 166 } 167 return common.Hash{} 168 } 169 } 170 171 context := vm.BlockContext{ 172 CanTransfer: CanTransfer, 173 Transfer: MakeTransfer, 174 GetHash: getHashFn, 175 Coinbase: common.BytesToAddress(blkCtx.Producer.Bytes()), 176 GasLimit: gasLimit, 177 BlockNumber: new(big.Int).SetUint64(blkCtx.BlockHeight), 178 Time: new(big.Int).SetInt64(blkCtx.BlockTimeStamp.Unix()).Uint64(), 179 Difficulty: new(big.Int).SetUint64(uint64(50)), 180 BaseFee: new(big.Int), 181 } 182 if g.IsSumatra(blkCtx.BlockHeight) { 183 // Random opcode (EIP-4399) is not supported 184 context.Random = &common.Hash{} 185 } 186 187 if vmCfg, ok := protocol.GetVMConfigCtx(ctx); ok { 188 vmConfig = vmCfg 189 } 190 chainConfig, err := getChainConfig(g.Blockchain, blkCtx.BlockHeight, evmNetworkID, helperCtx.GetBlockTime) 191 if err != nil { 192 return nil, err 193 } 194 195 return &Params{ 196 context, 197 vm.TxContext{ 198 Origin: executorAddr, 199 GasPrice: execution.GasPrice(), 200 }, 201 execution.Nonce(), 202 execution.Amount(), 203 contractAddrPointer, 204 gasLimit, 205 execution.Data(), 206 execution.AccessList(), 207 vmConfig, 208 chainConfig, 209 g.Blockchain, 210 blkCtx, 211 featureCtx, 212 actionCtx, 213 helperCtx, 214 }, nil 215 } 216 217 func securityDeposit(ps *Params, stateDB vm.StateDB, gasLimit uint64) error { 218 executorNonce := stateDB.GetNonce(ps.txCtx.Origin) 219 if executorNonce > ps.nonce { 220 log.S().Errorf("Nonce on %v: %d vs %d", ps.txCtx.Origin, executorNonce, ps.nonce) 221 // TODO ignore inconsistent nonce problem until the actions are executed sequentially 222 // return ErrInconsistentNonce 223 } 224 if gasLimit < ps.gas { 225 return action.ErrGasLimit 226 } 227 gasConsumed := new(big.Int).Mul(new(big.Int).SetUint64(ps.gas), ps.txCtx.GasPrice) 228 if stateDB.GetBalance(ps.txCtx.Origin).Cmp(gasConsumed) < 0 { 229 return action.ErrInsufficientFunds 230 } 231 stateDB.SubBalance(ps.txCtx.Origin, gasConsumed) 232 return nil 233 } 234 235 // ExecuteContract processes a transfer which contains a contract 236 func ExecuteContract( 237 ctx context.Context, 238 sm protocol.StateManager, 239 execution *action.Execution, 240 ) ([]byte, *action.Receipt, error) { 241 ctx, span := tracer.NewSpan(ctx, "evm.ExecuteContract") 242 defer span.End() 243 244 stateDB, err := prepareStateDB(ctx, sm) 245 if err != nil { 246 return nil, nil, err 247 } 248 ps, err := newParams(ctx, execution, stateDB) 249 if err != nil { 250 return nil, nil, err 251 } 252 sgd := ps.helperCtx.Sgd 253 retval, depositGas, remainingGas, contractAddress, statusCode, err := executeInEVM(ps, stateDB) 254 if err != nil { 255 return nil, nil, err 256 } 257 receipt := &action.Receipt{ 258 GasConsumed: ps.gas - remainingGas, 259 BlockHeight: ps.blkCtx.BlockHeight, 260 ActionHash: ps.actionCtx.ActionHash, 261 ContractAddress: contractAddress, 262 } 263 264 receipt.Status = uint64(statusCode) 265 var ( 266 depositLog, burnLog *action.TransactionLog 267 consumedGas = depositGas - remainingGas 268 ) 269 if ps.featureCtx.FixDoubleChargeGas { 270 // Refund all deposit and, actual gas fee will be subtracted when depositing gas fee to the rewarding protocol 271 stateDB.AddBalance(ps.txCtx.Origin, big.NewInt(0).Mul(big.NewInt(0).SetUint64(depositGas), ps.txCtx.GasPrice)) 272 } else { 273 if remainingGas > 0 { 274 remainingValue := new(big.Int).Mul(new(big.Int).SetUint64(remainingGas), ps.txCtx.GasPrice) 275 stateDB.AddBalance(ps.txCtx.Origin, remainingValue) 276 } 277 if consumedGas > 0 { 278 burnLog = &action.TransactionLog{ 279 Type: iotextypes.TransactionLogType_GAS_FEE, 280 Sender: ps.actionCtx.Caller.String(), 281 Recipient: "", // burned 282 Amount: new(big.Int).Mul(new(big.Int).SetUint64(consumedGas), ps.txCtx.GasPrice), 283 } 284 } 285 } 286 if consumedGas > 0 { 287 var ( 288 receiver address.Address 289 sharedGas uint64 290 sharedGasFee, totalGasFee *big.Int 291 ) 292 if ps.featureCtx.SharedGasWithDapp && sgd != nil { 293 // TODO: sgd is whether nil should be checked in processSGD 294 receiver, sharedGas, err = processSGD(ctx, sm, execution, consumedGas, sgd) 295 if err != nil { 296 return nil, nil, errors.Wrap(err, "failed to process Sharing of Gas-fee with DApps") 297 } 298 } 299 if sharedGas > 0 { 300 sharedGasFee = big.NewInt(int64(sharedGas)) 301 sharedGasFee.Mul(sharedGasFee, ps.txCtx.GasPrice) 302 } 303 totalGasFee = new(big.Int).Mul(new(big.Int).SetUint64(consumedGas), ps.txCtx.GasPrice) 304 if ps.helperCtx.DepositGasFunc != nil { 305 depositLog, err = ps.helperCtx.DepositGasFunc(ctx, sm, receiver, totalGasFee, sharedGasFee) 306 if err != nil { 307 return nil, nil, err 308 } 309 } 310 311 } 312 313 if err := stateDB.CommitContracts(); err != nil { 314 return nil, nil, errors.Wrap(err, "failed to commit contracts to underlying db") 315 } 316 receipt.AddLogs(stateDB.Logs()...).AddTransactionLogs(depositLog, burnLog) 317 if receipt.Status == uint64(iotextypes.ReceiptStatus_Success) || 318 ps.featureCtx.AddOutOfGasToTransactionLog && receipt.Status == uint64(iotextypes.ReceiptStatus_ErrCodeStoreOutOfGas) { 319 receipt.AddTransactionLogs(stateDB.TransactionLogs()...) 320 } 321 stateDB.clear() 322 323 if ps.featureCtx.SetRevertMessageToReceipt && receipt.Status == uint64(iotextypes.ReceiptStatus_ErrExecutionReverted) && retval != nil && bytes.Equal(retval[:4], _revertSelector) { 324 // in case of the execution revert error, parse the retVal and add to receipt 325 data := retval[4:] 326 msgLength := byteutil.BytesToUint64BigEndian(data[56:64]) 327 revertMsg := string(data[64 : 64+msgLength]) 328 receipt.SetExecutionRevertMsg(revertMsg) 329 } 330 log.S().Debugf("Receipt: %+v, %v", receipt, err) 331 return retval, receipt, nil 332 } 333 334 func processSGD(ctx context.Context, sm protocol.StateManager, execution *action.Execution, consumedGas uint64, sgd SGDRegistry, 335 ) (address.Address, uint64, error) { 336 if execution.Contract() == action.EmptyAddress { 337 return nil, 0, nil 338 } 339 height, err := sm.Height() 340 if err != nil { 341 return nil, 0, err 342 } 343 receiver, percentage, ok, err := sgd.CheckContract(ctx, execution.Contract(), height-1) 344 if err != nil || !ok { 345 return nil, 0, err 346 } 347 348 sharedGas := consumedGas * percentage / 100 349 if sharedGas > consumedGas { 350 sharedGas = consumedGas 351 } 352 return receiver, sharedGas, nil 353 } 354 355 // ReadContractStorage reads contract's storage 356 func ReadContractStorage( 357 ctx context.Context, 358 sm protocol.StateManager, 359 contract address.Address, 360 key []byte, 361 ) ([]byte, error) { 362 bcCtx := protocol.MustGetBlockchainCtx(ctx) 363 ctx = protocol.WithFeatureCtx(protocol.WithBlockCtx(protocol.WithActionCtx(ctx, 364 protocol.ActionCtx{ 365 ActionHash: hash.ZeroHash256, 366 }), 367 protocol.BlockCtx{ 368 BlockHeight: bcCtx.Tip.Height + 1, 369 }, 370 )) 371 stateDB, err := prepareStateDB(ctx, sm) 372 if err != nil { 373 return nil, err 374 } 375 res := stateDB.GetState(common.BytesToAddress(contract.Bytes()), common.BytesToHash(key)) 376 return res[:], nil 377 } 378 379 func prepareStateDB(ctx context.Context, sm protocol.StateManager) (*StateDBAdapter, error) { 380 actionCtx := protocol.MustGetActionCtx(ctx) 381 blkCtx := protocol.MustGetBlockCtx(ctx) 382 featureCtx := protocol.MustGetFeatureCtx(ctx) 383 opts := []StateDBAdapterOption{} 384 if featureCtx.CreateLegacyNonceAccount { 385 opts = append(opts, LegacyNonceAccountOption()) 386 } 387 if !featureCtx.FixSortCacheContractsAndUsePendingNonce { 388 opts = append(opts, DisableSortCachedContractsOption(), UseConfirmedNonceOption()) 389 } 390 // Before featureCtx.RefactorFreshAccountConversion is activated, 391 // the type of a legacy fresh account is always 1 392 if featureCtx.RefactorFreshAccountConversion { 393 opts = append(opts, ZeroNonceForFreshAccountOption()) 394 } 395 if featureCtx.NotFixTopicCopyBug { 396 opts = append(opts, NotFixTopicCopyBugOption()) 397 } 398 if featureCtx.AsyncContractTrie { 399 opts = append(opts, AsyncContractTrieOption()) 400 } 401 if featureCtx.FixSnapshotOrder { 402 opts = append(opts, FixSnapshotOrderOption()) 403 } 404 if featureCtx.RevertLog { 405 opts = append(opts, RevertLogOption()) 406 } 407 if !featureCtx.CorrectGasRefund { 408 opts = append(opts, ManualCorrectGasRefundOption()) 409 } 410 if featureCtx.SuicideTxLogMismatchPanic { 411 opts = append(opts, SuicideTxLogMismatchPanicOption()) 412 } 413 414 return NewStateDBAdapter( 415 sm, 416 blkCtx.BlockHeight, 417 actionCtx.ActionHash, 418 opts..., 419 ) 420 } 421 422 func getChainConfig(g genesis.Blockchain, height uint64, id uint32, getBlockTime GetBlockTime) (*params.ChainConfig, error) { 423 var chainConfig params.ChainConfig 424 chainConfig.ConstantinopleBlock = new(big.Int).SetUint64(0) // Constantinople switch block (nil = no fork, 0 = already activated) 425 chainConfig.BeringBlock = new(big.Int).SetUint64(g.BeringBlockHeight) 426 // enable earlier Ethereum forks at Greenland 427 chainConfig.GreenlandBlock = new(big.Int).SetUint64(g.GreenlandBlockHeight) 428 // support chainid and enable Istanbul + MuirGlacier at Iceland 429 chainConfig.IstanbulBlock = new(big.Int).SetUint64(g.IcelandBlockHeight) 430 chainConfig.MuirGlacierBlock = new(big.Int).SetUint64(g.IcelandBlockHeight) 431 if g.IsIceland(height) { 432 chainConfig.ChainID = new(big.Int).SetUint64(uint64(id)) 433 } 434 // enable Berlin and London at Okhotsk 435 chainConfig.BerlinBlock = new(big.Int).SetUint64(g.OkhotskBlockHeight) 436 chainConfig.LondonBlock = new(big.Int).SetUint64(g.OkhotskBlockHeight) 437 // enable ArrowGlacier, GrayGlacier at Redsea 438 chainConfig.ArrowGlacierBlock = new(big.Int).SetUint64(g.RedseaBlockHeight) 439 chainConfig.GrayGlacierBlock = new(big.Int).SetUint64(g.RedseaBlockHeight) 440 // enable Merge, Shanghai at Sumatra 441 chainConfig.MergeNetsplitBlock = new(big.Int).SetUint64(g.SumatraBlockHeight) 442 // Starting Shanghai, fork scheduling on Ethereum was switched from blocks to timestamps 443 sumatraTime, err := getBlockTime(g.SumatraBlockHeight) 444 if err != nil { 445 return nil, err 446 } 447 sumatraTimestamp := (uint64)(sumatraTime.Unix()) 448 chainConfig.ShanghaiTime = &sumatraTimestamp 449 return &chainConfig, nil 450 } 451 452 // Error in executeInEVM is a consensus issue 453 func executeInEVM(evmParams *Params, stateDB *StateDBAdapter) ([]byte, uint64, uint64, string, iotextypes.ReceiptStatus, error) { 454 var ( 455 gasLimit = evmParams.blkCtx.GasLimit 456 blockHeight = evmParams.blkCtx.BlockHeight 457 g = evmParams.genesis 458 remainingGas = evmParams.gas 459 chainConfig = evmParams.chainConfig 460 ) 461 if err := securityDeposit(evmParams, stateDB, gasLimit); err != nil { 462 log.L().Warn("unexpected error: not enough security deposit", zap.Error(err)) 463 return nil, 0, 0, action.EmptyAddress, iotextypes.ReceiptStatus_Failure, err 464 } 465 var ( 466 accessList types.AccessList 467 ) 468 evm := vm.NewEVM(evmParams.context, evmParams.txCtx, stateDB, chainConfig, evmParams.evmConfig) 469 if g.IsOkhotsk(blockHeight) { 470 accessList = evmParams.accessList 471 } 472 intriGas, err := intrinsicGas(uint64(len(evmParams.data)), accessList) 473 if err != nil { 474 return nil, evmParams.gas, remainingGas, action.EmptyAddress, iotextypes.ReceiptStatus_Failure, err 475 } 476 if remainingGas < intriGas { 477 return nil, evmParams.gas, remainingGas, action.EmptyAddress, iotextypes.ReceiptStatus_Failure, action.ErrInsufficientFunds 478 } 479 remainingGas -= intriGas 480 481 // Set up the initial access list 482 rules := chainConfig.Rules(evm.Context.BlockNumber, g.IsSumatra(evmParams.blkCtx.BlockHeight), evmParams.context.Time) 483 if rules.IsBerlin { 484 stateDB.Prepare(rules, evmParams.txCtx.Origin, evmParams.context.Coinbase, evmParams.contract, vm.ActivePrecompiles(rules), evmParams.accessList) 485 } 486 var ( 487 contractRawAddress = action.EmptyAddress 488 executor = vm.AccountRef(evmParams.txCtx.Origin) 489 ret []byte 490 evmErr error 491 refund uint64 492 ) 493 if evmParams.contract == nil { 494 // create contract 495 var evmContractAddress common.Address 496 _, evmContractAddress, remainingGas, evmErr = evm.Create(executor, evmParams.data, remainingGas, evmParams.amount) 497 log.L().Debug("evm Create.", log.Hex("addrHash", evmContractAddress[:])) 498 if evmErr == nil { 499 if contractAddress, err := address.FromBytes(evmContractAddress.Bytes()); err == nil { 500 contractRawAddress = contractAddress.String() 501 } 502 } 503 } else { 504 stateDB.SetNonce(evmParams.txCtx.Origin, stateDB.GetNonce(evmParams.txCtx.Origin)+1) 505 // process contract 506 ret, remainingGas, evmErr = evm.Call(executor, *evmParams.contract, evmParams.data, remainingGas, evmParams.amount) 507 } 508 if evmErr != nil { 509 log.L().Debug("evm error", zap.Error(evmErr)) 510 // The only possible consensus-error would be if there wasn't 511 // sufficient balance to make the transfer happen. 512 // Should be a hard fork (Bering) 513 if evmErr == vm.ErrInsufficientBalance && g.IsBering(blockHeight) { 514 return nil, evmParams.gas, remainingGas, action.EmptyAddress, iotextypes.ReceiptStatus_Failure, evmErr 515 } 516 } 517 if stateDB.Error() != nil { 518 log.L().Debug("statedb error", zap.Error(stateDB.Error())) 519 } 520 if !rules.IsLondon { 521 // Before EIP-3529: refunds were capped to gasUsed / 2 522 refund = (evmParams.gas - remainingGas) / params.RefundQuotient 523 } else { 524 // After EIP-3529: refunds are capped to gasUsed / 5 525 refund = (evmParams.gas - remainingGas) / params.RefundQuotientEIP3529 526 } 527 // before London EVM activation (at Okhotsk height), in certain cases dynamicGas 528 // has caused gas refund to change, which needs to be manually adjusted after 529 // the tx is reverted. After Okhotsk height, it is fixed inside RevertToSnapshot() 530 var ( 531 deltaRefundByDynamicGas = evm.DeltaRefundByDynamicGas 532 featureCtx = evmParams.featureCtx 533 ) 534 if !featureCtx.CorrectGasRefund && deltaRefundByDynamicGas != 0 { 535 if deltaRefundByDynamicGas > 0 { 536 stateDB.SubRefund(uint64(deltaRefundByDynamicGas)) 537 } else { 538 stateDB.AddRefund(uint64(-deltaRefundByDynamicGas)) 539 } 540 } 541 if refund > stateDB.GetRefund() { 542 refund = stateDB.GetRefund() 543 } 544 remainingGas += refund 545 546 errCode := iotextypes.ReceiptStatus_Success 547 if evmErr != nil { 548 errCode = evmErrToErrStatusCode(evmErr, g, blockHeight) 549 if errCode == iotextypes.ReceiptStatus_ErrUnknown { 550 var addr string 551 if evmParams.contract != nil { 552 ioAddr, _ := address.FromBytes((*evmParams.contract)[:]) 553 addr = ioAddr.String() 554 } else { 555 addr = "contract creation" 556 } 557 log.L().Warn("evm internal error", zap.Error(evmErr), 558 zap.String("address", addr), 559 log.Hex("calldata", evmParams.data)) 560 } 561 } 562 return ret, evmParams.gas, remainingGas, contractRawAddress, errCode, nil 563 } 564 565 // evmErrToErrStatusCode returns ReceiptStatuscode which describes error type 566 func evmErrToErrStatusCode(evmErr error, g genesis.Blockchain, height uint64) iotextypes.ReceiptStatus { 567 // specific error starting London 568 if g.IsOkhotsk(height) { 569 if evmErr == vm.ErrInvalidCode { 570 return iotextypes.ReceiptStatus_ErrInvalidCode 571 } 572 } 573 574 // specific error starting Jutland 575 if g.IsJutland(height) { 576 switch evmErr { 577 case vm.ErrInsufficientBalance: 578 return iotextypes.ReceiptStatus_ErrInsufficientBalance 579 case vm.ErrInvalidJump: 580 return iotextypes.ReceiptStatus_ErrInvalidJump 581 case vm.ErrReturnDataOutOfBounds: 582 return iotextypes.ReceiptStatus_ErrReturnDataOutOfBounds 583 case vm.ErrGasUintOverflow: 584 return iotextypes.ReceiptStatus_ErrGasUintOverflow 585 } 586 } 587 588 // specific error starting Bering 589 if g.IsBering(height) { 590 switch evmErr { 591 case vm.ErrOutOfGas: 592 return iotextypes.ReceiptStatus_ErrOutOfGas 593 case vm.ErrCodeStoreOutOfGas: 594 return iotextypes.ReceiptStatus_ErrCodeStoreOutOfGas 595 case vm.ErrDepth: 596 return iotextypes.ReceiptStatus_ErrDepth 597 case vm.ErrContractAddressCollision: 598 return iotextypes.ReceiptStatus_ErrContractAddressCollision 599 case vm.ErrExecutionReverted: 600 return iotextypes.ReceiptStatus_ErrExecutionReverted 601 case vm.ErrMaxCodeSizeExceeded: 602 return iotextypes.ReceiptStatus_ErrMaxCodeSizeExceeded 603 case vm.ErrWriteProtection: 604 return iotextypes.ReceiptStatus_ErrWriteProtection 605 default: 606 // internal errors from go-ethereum are not directly accessible 607 switch evmErr.Error() { 608 case "no compatible interpreter": 609 return iotextypes.ReceiptStatus_ErrNoCompatibleInterpreter 610 default: 611 return iotextypes.ReceiptStatus_ErrUnknown 612 } 613 } 614 } 615 // before Bering height, return one common failure 616 return iotextypes.ReceiptStatus_Failure 617 } 618 619 // intrinsicGas returns the intrinsic gas of an execution 620 func intrinsicGas(size uint64, list types.AccessList) (uint64, error) { 621 if action.ExecutionDataGas == 0 { 622 panic("payload gas price cannot be zero") 623 } 624 625 var accessListGas uint64 626 if len(list) > 0 { 627 accessListGas = uint64(len(list)) * action.TxAccessListAddressGas 628 accessListGas += uint64(list.StorageKeys()) * action.TxAccessListStorageKeyGas 629 } 630 if (math.MaxInt64-action.ExecutionBaseIntrinsicGas-accessListGas)/action.ExecutionDataGas < size { 631 return 0, action.ErrInsufficientFunds 632 } 633 return size*action.ExecutionDataGas + action.ExecutionBaseIntrinsicGas + accessListGas, nil 634 } 635 636 // SimulateExecution simulates the execution in evm 637 func SimulateExecution( 638 ctx context.Context, 639 sm protocol.StateManager, 640 caller address.Address, 641 ex *action.Execution, 642 ) ([]byte, *action.Receipt, error) { 643 ctx, span := tracer.NewSpan(ctx, "evm.SimulateExecution") 644 defer span.End() 645 bcCtx := protocol.MustGetBlockchainCtx(ctx) 646 g := genesis.MustExtractGenesisContext(ctx) 647 ctx = protocol.WithActionCtx( 648 ctx, 649 protocol.ActionCtx{ 650 Caller: caller, 651 ActionHash: hash.Hash256b(byteutil.Must(proto.Marshal(ex.Proto()))), 652 }, 653 ) 654 zeroAddr, err := address.FromString(address.ZeroAddress) 655 if err != nil { 656 return nil, nil, err 657 } 658 ctx = protocol.WithBlockCtx( 659 ctx, 660 protocol.BlockCtx{ 661 BlockHeight: bcCtx.Tip.Height + 1, 662 BlockTimeStamp: bcCtx.Tip.Timestamp.Add(g.BlockInterval), 663 GasLimit: g.BlockGasLimitByHeight(bcCtx.Tip.Height + 1), 664 Producer: zeroAddr, 665 }, 666 ) 667 668 ctx = protocol.WithFeatureCtx(ctx) 669 return ExecuteContract( 670 ctx, 671 sm, 672 ex, 673 ) 674 }