evm: store reverted tx as not failed (#228)

* add tx when evm revert

* add comment

* use cache ctx

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
Thomas Nguy 2021-07-08 17:14:11 +09:00 committed by GitHub
parent 20c42dc10d
commit 735f00d4a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 55 additions and 40 deletions

View File

@ -565,6 +565,12 @@ curl localhost:8545 -H "Content-Type:application/json" -X POST --data '{"jsonrpc
Returns the receipt of a transaction by transaction hash.
Note: Tx Code from Tendermint and the Ethereum receipt status are switched:
| | Tendermint | Ethereum |
| ------- | ---------- | -------- |
| Success | 0 | 1 |
| Fail | 1 | 0 |
#### Parameters
- Hash of a transaction

View File

@ -532,7 +532,7 @@ func (e *PublicAPI) Call(args rpctypes.CallArgs, blockNr rpctypes.BlockNumber, _
}
if data.Reverted {
return []byte{}, rpctypes.ErrRevertedWith(data.Ret)
return []byte{}, evmtypes.NewExecErrorWithReason(data.Ret)
}
return (hexutil.Bytes)(data.Ret), nil
@ -683,7 +683,7 @@ func (e *PublicAPI) EstimateGas(args rpctypes.CallArgs) (hexutil.Uint64, error)
}
if data.Reverted {
return 0, rpctypes.ErrRevertedWith(data.Ret)
return 0, evmtypes.NewExecErrorWithReason(data.Ret)
}
return hexutil.Uint64(data.GasUsed), nil
@ -920,18 +920,12 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
cumulativeGasUsed += uint64(blockRes.TxsResults[i].GasUsed)
}
// Get the transaction result from the log
var status hexutil.Uint
// NOTE: Response{Deliver/Check}Tx Code from Tendermint and the Ethereum receipt status are switched:
// | | Tendermint | Ethereum |
// | ------- | ---------- | -------- |
// | Success | 0 | 1 |
// | Fail | 1 | 0 |
if res.TxResult.Code == 0 {
status = hexutil.Uint(ethtypes.ReceiptStatusSuccessful)
} else {
if strings.Contains(res.TxResult.GetLog(), evmtypes.AttributeKeyEthereumTxReverted) {
status = hexutil.Uint(ethtypes.ReceiptStatusFailed)
} else {
status = hexutil.Uint(ethtypes.ReceiptStatusSuccessful)
}
from, err := msg.GetSender(e.chainIDEpoch)

View File

@ -75,6 +75,10 @@ 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"))
}
// emit events
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(

View File

@ -126,12 +126,16 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
return nil, stacktrace.Propagate(err, "failed to return ethereum transaction as core message")
}
// create an ethereum StateTransition instance and run TransitionDb
// we use a ctx context to avoid modifying to state in case EVM msg is reverted
originalCtx := k.ctx
cacheCtx, commit := k.ctx.CacheContext()
k.ctx = cacheCtx
evm := k.NewEVM(msg, ethCfg)
k.SetTxHashTransient(tx.Hash())
k.IncreaseTxIndexTransient()
// create an ethereum StateTransition instance and run TransitionDb
res, err := k.ApplyMessage(evm, msg, ethCfg)
if err != nil {
return nil, stacktrace.Propagate(err, "failed to apply ethereum core message")
@ -140,14 +144,27 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
txHash := tx.Hash()
res.Hash = txHash.Hex()
// Set the bloom filter and commit only if transaction is NOT reverted
if !res.Reverted {
logs := k.GetTxLogs(txHash)
res.Logs = types.NewLogsFromEth(logs)
// update block bloom filter
bloom := k.GetBlockBloomTransient()
bloom.Or(bloom, big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs)))
k.SetBlockBloomTransient(bloom)
commit()
}
// refund gas prior to handling the vm error in order to set the updated gas meter
k.ctx = originalCtx
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
}
@ -215,26 +232,19 @@ 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())
}
// 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())
}
var reverted bool
if vmErr != nil {
if errors.Is(vmErr, vm.ErrExecutionReverted) {
// unpack the return data bytes from the err if the execution has been "reverted" on the VM
return nil, stacktrace.Propagate(types.NewExecErrorWithReson(ret), "transaction reverted")
}
if !errors.Is(vmErr, vm.ErrExecutionReverted) {
// wrap the VM error
return nil, stacktrace.Propagate(sdkerrors.Wrap(types.ErrVMExecution, vmErr.Error()), "vm execution failed")
}
reverted = true
}
gasUsed := msg.Gas() - leftoverGas
return &types.MsgEthereumTxResponse{
Ret: ret,
Reverted: false,
Reverted: reverted,
GasUsed: gasUsed,
}, nil
}

View File

@ -73,9 +73,9 @@ var (
ErrInconsistentGas = sdkerrors.Register(ModuleName, codeErrInconsistentGas, "inconsistent gas")
)
// NewExecErrorWithReson unpacks the revert return bytes and returns a wrapped error
// NewExecErrorWithReason unpacks the revert return bytes and returns a wrapped error
// with the return reason.
func NewExecErrorWithReson(revertReason []byte) error {
func NewExecErrorWithReason(revertReason []byte) error {
hexValue := hexutil.Encode(revertReason)
reason, errUnpack := abi.UnpackRevert(revertReason)
if errUnpack == nil {

View File

@ -8,6 +8,7 @@ const (
AttributeKeyRecipient = "recipient"
AttributeKeyTxHash = "txHash"
AttributeKeyEthereumTxHash = "ethereumTxHash"
AttributeKeyEthereumTxReverted = "ethereumTxReverted"
AttributeValueCategory = ModuleName
MetricKeyTransitionDB = "transition_db"