diff --git a/rpc/eth_api.go b/rpc/eth_api.go index f475c7ece..8058b5e5e 100644 --- a/rpc/eth_api.go +++ b/rpc/eth_api.go @@ -374,7 +374,10 @@ type account struct { // DoCall performs a simulated call operation through the evm func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasCap *big.Int) (sdk.Result, error) { // Set height for historical queries - ctx := e.cliCtx.WithHeight(blockNr.Int64()) + ctx := e.cliCtx + if blockNr.Int64() != 0 { + ctx = e.cliCtx.WithHeight(blockNr.Int64()) + } // Set sender address or use a default if none specified var addr common.Address @@ -452,13 +455,14 @@ func (e *PublicEthAPI) doCall(args CallArgs, blockNr rpc.BlockNumber, globalGasC } // EstimateGas estimates gas usage for the given smart contract call. -func (e *PublicEthAPI) EstimateGas(args CallArgs, blockNr rpc.BlockNumber) (hexutil.Uint64, error) { - result, err := e.doCall(args, blockNr, big.NewInt(emint.DefaultRPCGasLimit)) +func (e *PublicEthAPI) EstimateGas(args CallArgs) (hexutil.Uint64, error) { + result, err := e.doCall(args, 0, big.NewInt(emint.DefaultRPCGasLimit)) if err != nil { return 0, err } - return hexutil.Uint64(result.GasUsed), nil + // TODO: change 1000 buffer for more accurate buffer (must be at least 1 to not run OOG) + return hexutil.Uint64(result.GasUsed + 1000), nil } // GetBlockByHash returns the block identified by hash. @@ -916,7 +920,7 @@ func (e *PublicEthAPI) GenerateFromArgs(args params.SendTxArgs) (msg *types.Ethe Value: args.Value, Data: args.Data, } - g, _ := e.EstimateGas(callArgs, rpc.BlockNumber(e.cliCtx.Height)) + g, _ := e.EstimateGas(callArgs) gasLimit = uint64(g) } else { gasLimit = (uint64)(*args.Gas) diff --git a/x/evm/types/state_transition.go b/x/evm/types/state_transition.go index 0a4b22ea9..4944e74a9 100644 --- a/x/evm/types/state_transition.go +++ b/x/evm/types/state_transition.go @@ -44,13 +44,19 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result) // This gas limit the the transaction gas limit with intrinsic gas subtracted gasLimit := st.GasLimit - ctx.GasMeter().GasConsumed() - var snapshot int + csdb := st.Csdb if st.Simulate { // gasLimit is set here because stdTxs incur gaskv charges in the ante handler, but for eth_call // the cost needs to be the same as an Ethereum transaction sent through the web3 API + consumedGas := ctx.GasMeter().GasConsumed() gasLimit = st.GasLimit - cost + if consumedGas < cost { + // If Cosmos standard tx ante handler cost is less than EVM intrinsic cost + // gas must be consumed to match to accurately simulate an Ethereum transaction + ctx.GasMeter().ConsumeGas(cost-consumedGas, "Intrinsic gas match") + } - snapshot = st.Csdb.Snapshot() + csdb = st.Csdb.Copy() } // Create context for evm @@ -70,7 +76,7 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result) evmGasMeter := sdk.NewInfiniteGasMeter() vmenv := vm.NewEVM( - context, st.Csdb.WithContext(ctx.WithGasMeter(evmGasMeter)), + context, csdb.WithContext(ctx.WithGasMeter(evmGasMeter)), GenerateChainConfig(st.ChainID), vm.Config{}, ) @@ -86,15 +92,15 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result) ret, addr, leftOverGas, vmerr = vmenv.Create(senderRef, st.Payload, gasLimit, st.Amount) } else { // Increment the nonce for the next transaction - st.Csdb.SetNonce(st.Sender, st.Csdb.GetNonce(st.Sender)+1) + csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1) ret, leftOverGas, vmerr = vmenv.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount) } // Generate bloom filter to be saved in tx receipt data bloomInt := big.NewInt(0) var bloomFilter ethtypes.Bloom - if st.THash != nil { - logs := st.Csdb.GetLogs(*st.THash) + if st.THash != nil && !st.Simulate { + logs := csdb.GetLogs(*st.THash) bloomInt = ethtypes.LogsBloom(logs) bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes()) } @@ -114,13 +120,11 @@ func (st StateTransition) TransitionCSDB(ctx sdk.Context) (*big.Int, sdk.Result) return nil, res } - if st.Simulate { - st.Csdb.RevertToSnapshot(snapshot) - } - // TODO: Refund unused gas here, if intended in future - - st.Csdb.Finalise(true) // Change to depend on config + if !st.Simulate { + // Finalise state if not a simulated transaction + st.Csdb.Finalise(true) // Change to depend on config + } // Consume gas from evm execution // Out of gas check does not need to be done here since it is done within the EVM execution diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index c944bf115..32a3a9613 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -571,7 +571,7 @@ func (csdb *CommitStateDB) CreateAccount(addr ethcmn.Address) { // Copy creates a deep, independent copy of the state. // // NOTE: Snapshots of the copied state cannot be applied to the copy. -func (csdb *CommitStateDB) Copy() ethvm.StateDB { +func (csdb *CommitStateDB) Copy() *CommitStateDB { csdb.lock.Lock() defer csdb.lock.Unlock()