Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Commit

Permalink
rpc, evm: debug_traceTransaction endpoint (#506)
Browse files Browse the repository at this point in the history
* fix typo

* Added tracers package to debug API

* Add GetTransactionByHash function to backend package

* first version

* traceTransaction first version

* clean PR

* revert debug changes

* Update proto/ethermint/evm/v1/query.proto

Co-authored-by: Federico Kunze Küllmer <[email protected]>

* remove unnecesary panic

* remove internal debug api

* trace transaction javascript tracer

* add support for custom logConfig

* added comment

* traceTransactions tests

* fix linter

* remove unused

* add comments to traceConfig

* update dependencies

* updated endpoints md

* Apply suggestions from code review

* Update x/evm/keeper/grpc_query.go

Co-authored-by: Federico Kunze Küllmer <[email protected]>

* Update x/evm/keeper/grpc_query.go

Co-authored-by: Federico Kunze Küllmer <[email protected]>

Co-authored-by: Federico Kunze Küllmer <[email protected]>
  • Loading branch information
crypto-facs and fedekunze authored Sep 4, 2021
1 parent 6794daf commit c7554e9
Show file tree
Hide file tree
Showing 22 changed files with 2,198 additions and 285 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

* (evm) [tharsis#469](https://github.com/tharsis/ethermint/pull/469) Support [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)
* (evm) [tharsis#417](https://github.com/tharsis/ethermint/pull/417) Add `EvmHooks` for tx post-processing
* (rpc) [tharsis#506](https://github.com/tharsis/ethermint/pull/506) Support for `debug_traceTransaction` RPC endpoint

### Bug Fixes

Expand Down
21 changes: 20 additions & 1 deletion docs/api/json-rpc/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ Check the JSON-RPC methods supported on Ethermint. {synopsis}
| `debug_traceBlockFromFile` | Debug | | | |
| `debug_standardTraceBlockToFile` | Debug | | | |
| `debug_standardTraceBadBlockToFile` | Debug | | | |
| `debug_traceTransaction` | Debug | | | |
| [`debug_traceTransaction`](#debug-tracetransaction) | Debug | | | |
| `debug_verbosity` | Debug | | | |
| `debug_vmodule` | Debug | | | |
| `debug_writeBlockProfile` | Debug || | |
Expand Down Expand Up @@ -989,6 +989,25 @@ curl -X POST --data '{"jsonrpc":"2.0","method":"personal_ecRecover","params":["0
{"jsonrpc":"2.0","id":1,"result":"0x3b7252d007059ffc82d16d022da3cbf9992d2f70"}
```

## Debug Methods

### `debug_traceTransaction`

The `traceTransaction` debugging method will attempt to run the transaction in the exact same manner as it was executed on the network. It will replay any transaction that may have been executed prior to this one before it will finally attempt to execute the transaction that corresponds to the given hash.

#### Parameters

- Trace Config

```json
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"debug_traceTransaction","params":["0xddecdb13226339681372b44e01df0fbc0f446fca6f834b2de5ecb1e569022ec8", {"tracer": "{data: [], fault: function(log) {}, step: function(log) { if(log.op.toString() == \"CALL\") this.data.push(log.stack.peek(0)); }, result: function() { return this.data; }}"}],"id":1}' -H "Content-Type: application/json" http://localhost:8545

//Result
["68410", "51470"]
```


## Miner Methods

### `miner_getHashrate`
Expand Down
76 changes: 76 additions & 0 deletions docs/api/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
- [AccessTuple](#ethermint.evm.v1.AccessTuple)
- [ChainConfig](#ethermint.evm.v1.ChainConfig)
- [Log](#ethermint.evm.v1.Log)
- [LogConfig](#ethermint.evm.v1.LogConfig)
- [Params](#ethermint.evm.v1.Params)
- [State](#ethermint.evm.v1.State)
- [TraceConfig](#ethermint.evm.v1.TraceConfig)
- [TransactionLogs](#ethermint.evm.v1.TransactionLogs)
- [TxResult](#ethermint.evm.v1.TxResult)

Expand Down Expand Up @@ -53,6 +55,8 @@
- [QueryStaticCallResponse](#ethermint.evm.v1.QueryStaticCallResponse)
- [QueryStorageRequest](#ethermint.evm.v1.QueryStorageRequest)
- [QueryStorageResponse](#ethermint.evm.v1.QueryStorageResponse)
- [QueryTraceTxRequest](#ethermint.evm.v1.QueryTraceTxRequest)
- [QueryTraceTxResponse](#ethermint.evm.v1.QueryTraceTxResponse)
- [QueryTxLogsRequest](#ethermint.evm.v1.QueryTxLogsRequest)
- [QueryTxLogsResponse](#ethermint.evm.v1.QueryTxLogsResponse)
- [QueryValidatorAccountRequest](#ethermint.evm.v1.QueryValidatorAccountRequest)
Expand Down Expand Up @@ -213,6 +217,27 @@ the node.



<a name="ethermint.evm.v1.LogConfig"></a>

### LogConfig
LogConfig are the configuration options for structured logger the EVM


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `disable_memory` | [bool](#bool) | | disable memory capture |
| `disable_stack` | [bool](#bool) | | disable stack capture |
| `disable_storage` | [bool](#bool) | | disable storage capture |
| `disable_return_data` | [bool](#bool) | | disable return data capture |
| `debug` | [bool](#bool) | | print output during capture end |
| `limit` | [int32](#int32) | | maximum length of output, but zero means unlimited |
| `overrides` | [ChainConfig](#ethermint.evm.v1.ChainConfig) | | Chain overrides, can be used to execute a trace using future fork rules |






<a name="ethermint.evm.v1.Params"></a>

### Params
Expand Down Expand Up @@ -248,6 +273,24 @@ State represents a single Storage key value pair item.



<a name="ethermint.evm.v1.TraceConfig"></a>

### TraceConfig
TraceConfig holds extra parameters to trace functions.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `tracer` | [string](#string) | | custom javascript tracer |
| `timeout` | [string](#string) | | overrides the default timeout of 5 seconds for JavaScript-based tracing calls |
| `reexec` | [uint64](#uint64) | | number of blocks the tracer is willing to go back |
| `log_config` | [LogConfig](#ethermint.evm.v1.LogConfig) | | configuration options for structured logger the EVM |






<a name="ethermint.evm.v1.TransactionLogs"></a>

### TransactionLogs
Expand Down Expand Up @@ -823,6 +866,38 @@ method.



<a name="ethermint.evm.v1.QueryTraceTxRequest"></a>

### QueryTraceTxRequest
QueryTraceTxRequest defines TraceTx request


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `msg` | [MsgEthereumTx](#ethermint.evm.v1.MsgEthereumTx) | | msgEthereumTx for the requested transaction |
| `tx_index` | [uint32](#uint32) | | transaction index |
| `trace_config` | [TraceConfig](#ethermint.evm.v1.TraceConfig) | | TraceConfig holds extra parameters to trace functions. |






<a name="ethermint.evm.v1.QueryTraceTxResponse"></a>

### QueryTraceTxResponse
QueryTraceTxResponse defines TraceTx response


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `data` | [bytes](#bytes) | | response serialized in bytes |






<a name="ethermint.evm.v1.QueryTxLogsRequest"></a>

### QueryTxLogsRequest
Expand Down Expand Up @@ -912,6 +987,7 @@ Query defines the gRPC querier service.
| `Params` | [QueryParamsRequest](#ethermint.evm.v1.QueryParamsRequest) | [QueryParamsResponse](#ethermint.evm.v1.QueryParamsResponse) | Params queries the parameters of x/evm module. | GET|/ethermint/evm/v1/params|
| `EthCall` | [EthCallRequest](#ethermint.evm.v1.EthCallRequest) | [MsgEthereumTxResponse](#ethermint.evm.v1.MsgEthereumTxResponse) | EthCall implements the `eth_call` rpc api | GET|/ethermint/evm/v1/eth_call|
| `EstimateGas` | [EthCallRequest](#ethermint.evm.v1.EthCallRequest) | [EstimateGasResponse](#ethermint.evm.v1.EstimateGasResponse) | EstimateGas implements the `eth_estimateGas` rpc api | GET|/ethermint/evm/v1/estimate_gas|
| `TraceTx` | [QueryTraceTxRequest](#ethermint.evm.v1.QueryTraceTxRequest) | [QueryTraceTxResponse](#ethermint.evm.v1.QueryTraceTxResponse) | TraceTx implements the `debug_traceTransaction` rpc api | GET|/ethermint/evm/v1/trace_tx|

<!-- end services -->

Expand Down
2 changes: 1 addition & 1 deletion ethereum/rpc/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func GetRPCAPIs(ctx *server.Context, clientCtx client.Context, tmWSClient *rpccl
rpc.API{
Namespace: DebugNamespace,
Version: apiVersion,
Service: debug.NewInternalAPI(ctx),
Service: debug.NewAPI(ctx, evmBackend, clientCtx),
Public: true,
},
)
Expand Down
82 changes: 82 additions & 0 deletions ethereum/rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/server"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/ethereum/go-ethereum/accounts/keystore"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"

"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
Expand Down Expand Up @@ -49,6 +50,8 @@ type Backend interface {
GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error)
BloomStatus() (uint64, uint64)
GetCoinbase() (sdk.AccAddress, error)
GetTransactionByHash(txHash common.Hash) (*types.RPCTransaction, error)
GetTxByEthHash(txHash common.Hash) (*tmrpctypes.ResultTx, error)
EstimateGas(args evmtypes.CallArgs, blockNrOptional *types.BlockNumber) (hexutil.Uint64, error)
RPCGasCap() uint64
}
Expand Down Expand Up @@ -461,6 +464,85 @@ func (e *EVMBackend) GetCoinbase() (sdk.AccAddress, error) {
return address, nil
}

// GetTransactionByHash returns the Ethereum format transaction identified by Ethereum transaction hash
func (e *EVMBackend) GetTransactionByHash(txHash common.Hash) (*types.RPCTransaction, error) {
res, err := e.GetTxByEthHash(txHash)
if err != nil {
// try to find tx in mempool
txs, err := e.PendingTransactions()
if err != nil {
e.logger.Debug("tx not found", "hash", txHash.Hex(), "error", err.Error())
return nil, nil
}

for _, tx := range txs {
msg, err := evmtypes.UnwrapEthereumMsg(tx)
if err != nil {
// not ethereum tx
continue
}

if msg.Hash == txHash.Hex() {
rpctx, err := types.NewTransactionFromMsg(
msg,
common.Hash{},
uint64(0),
uint64(0),
e.chainID,
)
if err != nil {
return nil, err
}
return rpctx, nil
}
}

e.logger.Debug("tx not found", "hash", txHash.Hex())
return nil, nil
}

resBlock, err := e.clientCtx.Client.Block(e.ctx, &res.Height)
if err != nil {
e.logger.Debug("block not found", "height", res.Height, "error", err.Error())
return nil, nil
}

tx, err := e.clientCtx.TxConfig.TxDecoder()(res.Tx)
if err != nil {
e.logger.Debug("decoding failed", "error", err.Error())
return nil, fmt.Errorf("failed to decode tx: %w", err)
}

msg, err := evmtypes.UnwrapEthereumMsg(&tx)
if err != nil {
e.logger.Debug("invalid tx", "error", err.Error())
return nil, err
}

return types.NewTransactionFromMsg(
msg,
common.BytesToHash(resBlock.Block.Hash()),
uint64(res.Height),
uint64(res.Index),
e.chainID,
)
}

// GetTxByEthHash uses `/tx_query` to find transaction by ethereum tx hash
// TODO: Don't need to convert once hashing is fixed on Tendermint
// https://github.com/tendermint/tendermint/issues/6539
func (e *EVMBackend) GetTxByEthHash(hash common.Hash) (*tmrpctypes.ResultTx, error) {
query := fmt.Sprintf("%s.%s='%s'", evmtypes.TypeMsgEthereumTx, evmtypes.AttributeKeyEthereumTxHash, hash.Hex())
resTxs, err := e.clientCtx.Client.TxSearch(e.ctx, query, false, nil, nil, "")
if err != nil {
return nil, err
}
if len(resTxs.Txs) == 0 {
return nil, errors.Errorf("ethereum tx not found for hash %s", hash.Hex())
}
return resTxs.Txs[0], nil
}

func (e *EVMBackend) SendTransaction(args types.SendTxArgs) (common.Hash, error) {
// Look up the wallet containing the requested signer
_, err := e.clientCtx.Keyring.KeyByAddress(sdk.AccAddress(args.From.Bytes()))
Expand Down
Loading

0 comments on commit c7554e9

Please sign in to comment.