evm: refactor traceTx (#613)

* DNM: debug traceTx

* c++
This commit is contained in:
Federico Kunze Küllmer 2021-10-04 10:44:38 +02:00 committed by GitHub
parent 34c2593e43
commit 561d5db985
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 20 deletions

View File

@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements ### Improvements
* (evm) [tharsis#613](https://github.com/tharsis/ethermint/pull/613) Refactor `traceTx`
* (deps) [tharsis#610](https://github.com/tharsis/ethermint/pull/610) Bump Cosmos SDK to [v0.44.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.1). * (deps) [tharsis#610](https://github.com/tharsis/ethermint/pull/610) Bump Cosmos SDK to [v0.44.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.1).
### Bug Fixes ### Bug Fixes

View File

@ -208,6 +208,10 @@ func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.Q
// EthCall implements eth_call rpc api. // EthCall implements eth_call rpc api.
func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.MsgEthereumTxResponse, error) { func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.MsgEthereumTxResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(c) ctx := sdk.UnwrapSDKContext(c)
k.WithContext(ctx) k.WithContext(ctx)
@ -242,6 +246,10 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms
// EstimateGas implements eth_estimateGas rpc api. // EstimateGas implements eth_estimateGas rpc api.
func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*types.EstimateGasResponse, error) { func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*types.EstimateGasResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(c) ctx := sdk.UnwrapSDKContext(c)
k.WithContext(ctx) k.WithContext(ctx)
@ -275,7 +283,7 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type
} }
} }
// TODO Recap the highest gas limit with account's available balance. // TODO: Recap the highest gas limit with account's available balance.
// Recap the highest gas allowance with specified gascap. // Recap the highest gas allowance with specified gascap.
if req.GasCap != 0 && hi > req.GasCap { if req.GasCap != 0 && hi > req.GasCap {
@ -346,20 +354,26 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type
// executes the given message in the provided environment. The return value will // executes the given message in the provided environment. The return value will
// be tracer dependent. // be tracer dependent.
func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*types.QueryTraceTxResponse, error) { func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*types.QueryTraceTxResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(c) ctx := sdk.UnwrapSDKContext(c)
k.WithContext(ctx) k.WithContext(ctx)
params := k.GetParams(ctx)
coinbase, err := k.GetCoinbaseAddress(ctx) coinbase, err := k.GetCoinbaseAddress(ctx)
if err != nil { if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }
params := k.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID) ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)
signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight())) signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()))
result, err := k.traceTx(c, coinbase, signer, req.TxIndex, params, ctx, ethCfg, req.Msg, req.TraceConfig) tx := req.Msg.AsTransaction()
result, err := k.traceTx(ctx, coinbase, signer, req.TxIndex, params, ethCfg, tx, req.TraceConfig)
if err != nil { if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, err
} }
resultData, err := json.Marshal(result) resultData, err := json.Marshal(result)
@ -372,15 +386,23 @@ func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*typ
}, nil }, nil
} }
func (k *Keeper) traceTx(c context.Context, coinbase common.Address, signer ethtypes.Signer, txIndex uint64, func (k *Keeper) traceTx(
params types.Params, ctx sdk.Context, ethCfg *ethparams.ChainConfig, msg *types.MsgEthereumTx, traceConfig *types.TraceConfig) (*interface{}, error) { ctx sdk.Context,
coinbase common.Address,
signer ethtypes.Signer,
txIndex uint64,
params types.Params,
ethCfg *ethparams.ChainConfig,
tx *ethtypes.Transaction,
traceConfig *types.TraceConfig,
) (*interface{}, error) {
// Assemble the structured logger or the JavaScript tracer // Assemble the structured logger or the JavaScript tracer
var ( var (
tracer vm.Tracer tracer vm.Tracer
err error err error
) )
coreMessage, err := msg.AsMessage(signer) msg, err := tx.AsMessage(signer)
if err != nil { if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }
@ -388,29 +410,34 @@ func (k *Keeper) traceTx(c context.Context, coinbase common.Address, signer etht
switch { switch {
case traceConfig != nil && traceConfig.Tracer != "": case traceConfig != nil && traceConfig.Tracer != "":
timeout := defaultTraceTimeout timeout := defaultTraceTimeout
// TODO change timeout to time.duration // TODO: change timeout to time.duration
// Used string to comply with go ethereum // Used string to comply with go ethereum
if traceConfig.Timeout != "" { if traceConfig.Timeout != "" {
if timeout, err = time.ParseDuration(traceConfig.Timeout); err != nil { timeout, err = time.ParseDuration(traceConfig.Timeout)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "timeout value: %s", err.Error()) return nil, status.Errorf(codes.InvalidArgument, "timeout value: %s", err.Error())
} }
} }
txContext := core.NewEVMTxContext(coreMessage) txContext := core.NewEVMTxContext(msg)
// Construct the JavaScript tracer to execute with // Construct the JavaScript tracer to execute with
if tracer, err = tracers.New(traceConfig.Tracer, txContext); err != nil { tracer, err = tracers.New(traceConfig.Tracer, txContext)
if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }
// Handle timeouts and RPC cancellations // Handle timeouts and RPC cancellations
deadlineCtx, cancel := context.WithTimeout(c, timeout) deadlineCtx, cancel := context.WithTimeout(ctx.Context(), timeout)
defer cancel()
go func() { go func() {
<-deadlineCtx.Done() <-deadlineCtx.Done()
if deadlineCtx.Err() == context.DeadlineExceeded { if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
tracer.(*tracers.Tracer).Stop(errors.New("execution timeout")) tracer.(*tracers.Tracer).Stop(errors.New("execution timeout"))
} }
}() }()
defer cancel()
case traceConfig != nil: case traceConfig != nil:
logConfig := vm.LogConfig{ logConfig := vm.LogConfig{
DisableMemory: traceConfig.DisableMemory, DisableMemory: traceConfig.DisableMemory,
@ -420,24 +447,25 @@ func (k *Keeper) traceTx(c context.Context, coinbase common.Address, signer etht
} }
tracer = vm.NewStructLogger(&logConfig) tracer = vm.NewStructLogger(&logConfig)
default: default:
tracer = types.NewTracer(types.TracerStruct, coreMessage, ethCfg, ctx.BlockHeight(), true) tracer = types.NewTracer(types.TracerStruct, msg, ethCfg, ctx.BlockHeight(), true)
} }
evm := k.NewEVM(coreMessage, ethCfg, params, coinbase, tracer) evm := k.NewEVM(msg, ethCfg, params, coinbase, tracer)
k.SetTxHashTransient(common.HexToHash(msg.Hash)) k.SetTxHashTransient(tx.Hash())
k.SetTxIndexTransient(txIndex) k.SetTxIndexTransient(txIndex)
res, err := k.ApplyMessage(evm, coreMessage, ethCfg, true) res, err := k.ApplyMessage(evm, msg, ethCfg, true)
if err != nil { if err != nil {
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }
var result interface{} var result interface{}
// Depending on the tracer type, format and return the trace result data. // Depending on the tracer type, format and return the trace result data.
switch tracer := tracer.(type) { switch tracer := tracer.(type) {
case *vm.StructLogger: case *vm.StructLogger:
// TODO Return proper returnValue // TODO: Return proper returnValue
result = types.ExecutionResult{ result = types.ExecutionResult{
Gas: res.GasUsed, Gas: res.GasUsed,
Failed: res.Failed(), Failed: res.Failed(),
@ -451,7 +479,7 @@ func (k *Keeper) traceTx(c context.Context, coinbase common.Address, signer etht
} }
default: default:
return nil, status.Error(codes.InvalidArgument, "invalid tracer type") return nil, status.Errorf(codes.InvalidArgument, "invalid tracer type %T", tracer)
} }
return &result, nil return &result, nil