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:
parent
20c42dc10d
commit
735f00d4a3
@ -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.
|
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
|
#### Parameters
|
||||||
|
|
||||||
- Hash of a transaction
|
- Hash of a transaction
|
||||||
|
@ -532,7 +532,7 @@ func (e *PublicAPI) Call(args rpctypes.CallArgs, blockNr rpctypes.BlockNumber, _
|
|||||||
}
|
}
|
||||||
|
|
||||||
if data.Reverted {
|
if data.Reverted {
|
||||||
return []byte{}, rpctypes.ErrRevertedWith(data.Ret)
|
return []byte{}, evmtypes.NewExecErrorWithReason(data.Ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (hexutil.Bytes)(data.Ret), nil
|
return (hexutil.Bytes)(data.Ret), nil
|
||||||
@ -683,7 +683,7 @@ func (e *PublicAPI) EstimateGas(args rpctypes.CallArgs) (hexutil.Uint64, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if data.Reverted {
|
if data.Reverted {
|
||||||
return 0, rpctypes.ErrRevertedWith(data.Ret)
|
return 0, evmtypes.NewExecErrorWithReason(data.Ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
return hexutil.Uint64(data.GasUsed), nil
|
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)
|
cumulativeGasUsed += uint64(blockRes.TxsResults[i].GasUsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the transaction result from the log
|
||||||
var status hexutil.Uint
|
var status hexutil.Uint
|
||||||
|
if strings.Contains(res.TxResult.GetLog(), evmtypes.AttributeKeyEthereumTxReverted) {
|
||||||
// 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 {
|
|
||||||
status = hexutil.Uint(ethtypes.ReceiptStatusFailed)
|
status = hexutil.Uint(ethtypes.ReceiptStatusFailed)
|
||||||
|
} else {
|
||||||
|
status = hexutil.Uint(ethtypes.ReceiptStatusSuccessful)
|
||||||
}
|
}
|
||||||
|
|
||||||
from, err := msg.GetSender(e.chainIDEpoch)
|
from, err := msg.GetSender(e.chainIDEpoch)
|
||||||
|
@ -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()))
|
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyRecipient, tx.To().Hex()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if response.Reverted {
|
||||||
|
attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyEthereumTxReverted, "true"))
|
||||||
|
}
|
||||||
|
|
||||||
// emit events
|
// emit events
|
||||||
ctx.EventManager().EmitEvents(sdk.Events{
|
ctx.EventManager().EmitEvents(sdk.Events{
|
||||||
sdk.NewEvent(
|
sdk.NewEvent(
|
||||||
|
@ -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")
|
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)
|
evm := k.NewEVM(msg, ethCfg)
|
||||||
|
|
||||||
k.SetTxHashTransient(tx.Hash())
|
k.SetTxHashTransient(tx.Hash())
|
||||||
k.IncreaseTxIndexTransient()
|
k.IncreaseTxIndexTransient()
|
||||||
|
|
||||||
// create an ethereum StateTransition instance and run TransitionDb
|
|
||||||
res, err := k.ApplyMessage(evm, msg, ethCfg)
|
res, err := k.ApplyMessage(evm, msg, ethCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, stacktrace.Propagate(err, "failed to apply ethereum core message")
|
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()
|
txHash := tx.Hash()
|
||||||
res.Hash = txHash.Hex()
|
res.Hash = txHash.Hex()
|
||||||
|
|
||||||
|
// Set the bloom filter and commit only if transaction is NOT reverted
|
||||||
|
if !res.Reverted {
|
||||||
logs := k.GetTxLogs(txHash)
|
logs := k.GetTxLogs(txHash)
|
||||||
res.Logs = types.NewLogsFromEth(logs)
|
res.Logs = types.NewLogsFromEth(logs)
|
||||||
|
|
||||||
// update block bloom filter
|
// update block bloom filter
|
||||||
bloom := k.GetBlockBloomTransient()
|
bloom := k.GetBlockBloomTransient()
|
||||||
bloom.Or(bloom, big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs)))
|
bloom.Or(bloom, big.NewInt(0).SetBytes(ethtypes.LogsBloom(logs)))
|
||||||
k.SetBlockBloomTransient(bloom)
|
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)
|
k.resetGasMeterAndConsumeGas(res.GasUsed)
|
||||||
return res, nil
|
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())
|
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
|
var reverted bool
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
if vmErr != nil {
|
if vmErr != nil {
|
||||||
if errors.Is(vmErr, vm.ErrExecutionReverted) {
|
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap the VM error
|
// wrap the VM error
|
||||||
return nil, stacktrace.Propagate(sdkerrors.Wrap(types.ErrVMExecution, vmErr.Error()), "vm execution failed")
|
return nil, stacktrace.Propagate(sdkerrors.Wrap(types.ErrVMExecution, vmErr.Error()), "vm execution failed")
|
||||||
}
|
}
|
||||||
|
reverted = true
|
||||||
|
}
|
||||||
|
|
||||||
gasUsed := msg.Gas() - leftoverGas
|
gasUsed := msg.Gas() - leftoverGas
|
||||||
return &types.MsgEthereumTxResponse{
|
return &types.MsgEthereumTxResponse{
|
||||||
Ret: ret,
|
Ret: ret,
|
||||||
Reverted: false,
|
Reverted: reverted,
|
||||||
GasUsed: gasUsed,
|
GasUsed: gasUsed,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -73,9 +73,9 @@ var (
|
|||||||
ErrInconsistentGas = sdkerrors.Register(ModuleName, codeErrInconsistentGas, "inconsistent gas")
|
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.
|
// with the return reason.
|
||||||
func NewExecErrorWithReson(revertReason []byte) error {
|
func NewExecErrorWithReason(revertReason []byte) error {
|
||||||
hexValue := hexutil.Encode(revertReason)
|
hexValue := hexutil.Encode(revertReason)
|
||||||
reason, errUnpack := abi.UnpackRevert(revertReason)
|
reason, errUnpack := abi.UnpackRevert(revertReason)
|
||||||
if errUnpack == nil {
|
if errUnpack == nil {
|
||||||
|
@ -8,6 +8,7 @@ const (
|
|||||||
AttributeKeyRecipient = "recipient"
|
AttributeKeyRecipient = "recipient"
|
||||||
AttributeKeyTxHash = "txHash"
|
AttributeKeyTxHash = "txHash"
|
||||||
AttributeKeyEthereumTxHash = "ethereumTxHash"
|
AttributeKeyEthereumTxHash = "ethereumTxHash"
|
||||||
|
AttributeKeyEthereumTxReverted = "ethereumTxReverted"
|
||||||
AttributeValueCategory = ModuleName
|
AttributeValueCategory = ModuleName
|
||||||
|
|
||||||
MetricKeyTransitionDB = "transition_db"
|
MetricKeyTransitionDB = "transition_db"
|
||||||
|
Loading…
Reference in New Issue
Block a user