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
* (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).
### 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.
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)
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.
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)
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.
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
// be tracer dependent.
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)
k.WithContext(ctx)
params := k.GetParams(ctx)
coinbase, err := k.GetCoinbaseAddress(ctx)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
params := k.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)
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 {
return nil, status.Error(codes.Internal, err.Error())
return nil, err
}
resultData, err := json.Marshal(result)
@ -372,15 +386,23 @@ func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*typ
}, nil
}
func (k *Keeper) traceTx(c context.Context, coinbase common.Address, signer ethtypes.Signer, txIndex uint64,
params types.Params, ctx sdk.Context, ethCfg *ethparams.ChainConfig, msg *types.MsgEthereumTx, traceConfig *types.TraceConfig) (*interface{}, error) {
func (k *Keeper) traceTx(
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
var (
tracer vm.Tracer
err error
)
coreMessage, err := msg.AsMessage(signer)
msg, err := tx.AsMessage(signer)
if err != nil {
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 {
case traceConfig != nil && traceConfig.Tracer != "":
timeout := defaultTraceTimeout
// TODO change timeout to time.duration
// TODO: change timeout to time.duration
// Used string to comply with go ethereum
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())
}
}
txContext := core.NewEVMTxContext(coreMessage)
txContext := core.NewEVMTxContext(msg)
// 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())
}
// Handle timeouts and RPC cancellations
deadlineCtx, cancel := context.WithTimeout(c, timeout)
deadlineCtx, cancel := context.WithTimeout(ctx.Context(), timeout)
defer cancel()
go func() {
<-deadlineCtx.Done()
if deadlineCtx.Err() == context.DeadlineExceeded {
if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
tracer.(*tracers.Tracer).Stop(errors.New("execution timeout"))
}
}()
defer cancel()
case traceConfig != nil:
logConfig := vm.LogConfig{
DisableMemory: traceConfig.DisableMemory,
@ -420,24 +447,25 @@ func (k *Keeper) traceTx(c context.Context, coinbase common.Address, signer etht
}
tracer = vm.NewStructLogger(&logConfig)
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)
res, err := k.ApplyMessage(evm, coreMessage, ethCfg, true)
res, err := k.ApplyMessage(evm, msg, ethCfg, true)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
var result interface{}
// Depending on the tracer type, format and return the trace result data.
switch tracer := tracer.(type) {
case *vm.StructLogger:
// TODO Return proper returnValue
// TODO: Return proper returnValue
result = types.ExecutionResult{
Gas: res.GasUsed,
Failed: res.Failed(),
@ -451,7 +479,7 @@ func (k *Keeper) traceTx(c context.Context, coinbase common.Address, signer etht
}
default:
return nil, status.Error(codes.InvalidArgument, "invalid tracer type")
return nil, status.Errorf(codes.InvalidArgument, "invalid tracer type %T", tracer)
}
return &result, nil