evm: treat all vm errors the same as reverted (#276)

Closes: #274

evm: fix `ExtraEIP` activation (#288)

Closes: #287

Update x/evm/types/utils.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

Add `Failed` utility function and changelog
This commit is contained in:
yihuang
2021-07-15 02:01:05 -04:00
committed by GitHub
co-authored by Federico Kunze Küllmer
parent 9d1ce30ecd
commit 297a35dbdd
12 changed files with 416 additions and 201 deletions
+2 -5
View File
@@ -385,14 +385,11 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms
}
evm := k.NewEVM(msg, ethCfg, params, coinbase)
res, err := k.ApplyMessage(evm, msg, ethCfg)
// pass true means execute in query mode, which don't do actual gas refund.
res, err := k.ApplyMessage(evm, msg, ethCfg, true)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
// ApplyMessage don't handle gas refund, let's do it here
refund := k.GasToRefund(res.GasUsed)
res.GasUsed -= refund
return res, nil
}
+2 -2
View File
@@ -75,8 +75,8 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyRecipient, tx.To().Hex()))
}
if response.Reverted {
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyEthereumTxReverted, "true"))
if response.Failed() {
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyEthereumTxFailed, response.VmError))
}
// emit events
+32 -22
View File
@@ -1,7 +1,6 @@
package keeper
import (
"errors"
"math/big"
"os"
"time"
@@ -140,7 +139,8 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
k.SetTxHashTransient(tx.Hash())
k.IncreaseTxIndexTransient()
res, err := k.ApplyMessage(evm, msg, ethCfg)
// pass false to execute in real mode, which do actual gas refunding
res, err := k.ApplyMessage(evm, msg, ethCfg, false)
if err != nil {
return nil, stacktrace.Propagate(err, "failed to apply ethereum core message")
}
@@ -150,14 +150,14 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
logs := k.GetTxLogs(txHash)
// Commit and switch to original context
if !res.Reverted {
if !res.Failed() {
commit()
}
k.ctx = originalCtx
// Logs needs to be ignored when tx is reverted
// Set the log and bloom filter only when the tx is NOT REVERTED
if !res.Reverted {
if !res.Failed() {
res.Logs = types.NewLogsFromEth(logs)
// Update block bloom filter in the original context because blockbloom is set in EndBlock
bloom := k.GetBlockBloomTransient()
@@ -165,14 +165,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
k.SetBlockBloomTransient(bloom)
}
// refund gas prior to handling the vm error in order to set the updated gas meter
leftoverGas := msg.Gas() - res.GasUsed
leftoverGas, err = k.RefundGas(msg, leftoverGas)
if err != nil {
return nil, stacktrace.Propagate(err, "failed to refund gas leftover gas to sender %s", msg.From())
}
// update the gas used after refund
res.GasUsed = msg.Gas() - leftoverGas
k.resetGasMeterAndConsumeGas(res.GasUsed)
return res, nil
}
@@ -217,7 +210,12 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
// The preprocessing steps performed by the AnteHandler are:
//
// 1. set up the initial access list (iff fork > Berlin)
func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainConfig) (*types.MsgEthereumTxResponse, error) {
//
// Query mode
//
// The grpc query endpoint EthCall calls this in query mode, and since the query handler don't call AnteHandler,
// so we don't do real gas refund in that case.
func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainConfig, query bool) (*types.MsgEthereumTxResponse, error) {
var (
ret []byte // return bytes from evm execution
vmErr error // vm errors do not effect consensus and are therefore not assigned to err
@@ -231,7 +229,11 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo
// should have already been checked on Ante Handler
return nil, stacktrace.Propagate(err, "intrinsic gas failed")
}
// should be > 0 as it is checked on Ante Handler
// Should check again even if it is checked on Ante Handler, because eth_call don't go through Ante Handler.
if msg.Gas() < intrinsicGas {
// eth_estimateGas will check for this exact error
return nil, stacktrace.Propagate(core.ErrIntrinsicGas, "intrinsic gas too low")
}
leftoverGas := msg.Gas() - intrinsicGas
if contractCreation {
@@ -240,20 +242,28 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainCo
ret, leftoverGas, vmErr = evm.Call(sender, *msg.To(), msg.Data(), leftoverGas, msg.Value())
}
var reverted bool
if vmErr != nil {
if !errors.Is(vmErr, vm.ErrExecutionReverted) {
// wrap the VM error
return nil, stacktrace.Propagate(sdkerrors.Wrap(types.ErrVMExecution, vmErr.Error()), "vm execution failed")
if query {
// query handlers don't call ante handler to deduct gas fee, so don't do actual refund here, because the
// module account balance might not be enough
leftoverGas += k.GasToRefund(msg.Gas() - leftoverGas)
} else {
// refund gas prior to handling the vm error in order to set the updated gas meter
leftoverGas, err = k.RefundGas(msg, leftoverGas)
if err != nil {
return nil, stacktrace.Propagate(err, "failed to refund gas leftover gas to sender %s", msg.From())
}
reverted = true
}
var vmError string
if vmErr != nil {
vmError = vmErr.Error()
}
gasUsed := msg.Gas() - leftoverGas
return &types.MsgEthereumTxResponse{
Ret: ret,
Reverted: reverted,
GasUsed: gasUsed,
GasUsed: gasUsed,
VmError: vmError,
Ret: ret,
}, nil
}