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

Commit

Permalink
rpc: debug_traceBlockByNumber endpoint (#555)
Browse files Browse the repository at this point in the history
* Refactor traceTx

* add getTendermintBlock on backend

* keeper concurrency

* first version

* json rpc concurrency

* rever makefile change

* remove grpc traceblock

* create internal traceBlock function

* added types to evm module

* tendermintBlockByNumber rename

* added safe message check

* remove unnecesary line

* check error

* fix lint

* fix linter

* Update ethereum/rpc/namespaces/debug/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/namespaces/debug/api.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Update ethereum/rpc/backend/backend.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* improve traceBlock performance

* fix linter

* add extra line on function parameters

* changed index to uint 64

* fix lint

* proto gen

* update endpoints documentation

* update changelog

* Apply suggestions from code review

* Update ethereum/rpc/namespaces/eth/filters/filters.go

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
  • Loading branch information
crypto-facs and fedekunze authored Sep 15, 2021
1 parent ec7d8a3 commit 3f1eeb3
Show file tree
Hide file tree
Showing 11 changed files with 346 additions and 151 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,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
* (rpc) [tharsis#555](https://github.com/tharsis/ethermint/pull/555) Support for `debug_traceBlockByNumber` RPC endpoint

### Bug Fixes

Expand Down
18 changes: 17 additions & 1 deletion docs/api/json-rpc/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ Check the JSON-RPC methods supported on Ethermint. {synopsis}
| `debug_startGoTrace` | Debug || | |
| `debug_stopCPUProfile` | Debug || | |
| `debug_stopGoTrace` | Debug || | |
| `debug_traceBlock` | Debug | | | |
| `debug_traceBlock`(#debug-traceblock) | Debug | | | |
| `debug_traceBlockByNumber` | Debug | | | |
| `debug_traceBlockByHash` | Debug | | | |
| `debug_traceBlockFromFile` | Debug | | | |
Expand Down Expand Up @@ -1007,6 +1007,22 @@ curl -X POST --data '{"jsonrpc":"2.0","method":"debug_traceTransaction","params"
["68410", "51470"]
```

### `debug_traceBlockByNumber`

The `traceBlockByNumber` endpoint accepts a block number and will replay the block that is already present in the database.

#### Parameters

- Trace Config

```json
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"debug_traceBlockByNumber","params":["0xe", {"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
{"jsonrpc":"2.0","id":1,"result":[{"result":["68410", "51470"]}]}
```


## Miner Methods

Expand Down
2 changes: 1 addition & 1 deletion docs/api/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,7 @@ 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 |
| `tx_index` | [uint64](#uint64) | | transaction index |
| `trace_config` | [TraceConfig](#ethermint.evm.v1.TraceConfig) | | TraceConfig holds extra parameters to trace functions. |


Expand Down
57 changes: 34 additions & 23 deletions ethereum/rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type Backend interface {
BlockNumber() (hexutil.Uint64, error)
GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map[string]interface{}, error)
GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error)
GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpctypes.ResultBlock, error)
HeaderByNumber(blockNum types.BlockNumber) (*ethtypes.Header, error)
HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error)
PendingTransactions() ([]*sdk.Tx, error)
Expand Down Expand Up @@ -116,6 +117,38 @@ func (e *EVMBackend) BlockNumber() (hexutil.Uint64, error) {

// GetBlockByNumber returns the block identified by number.
func (e *EVMBackend) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (map[string]interface{}, error) {
resBlock, err := e.GetTendermintBlockByNumber(blockNum)
if err != nil {
return nil, err
}

res, err := e.EthBlockFromTendermint(resBlock.Block, fullTx)
if err != nil {
e.logger.Debug("EthBlockFromTendermint failed", "height", blockNum, "error", err.Error())
return nil, err
}

return res, nil
}

// GetBlockByHash returns the block identified by hash.
func (e *EVMBackend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) {
resBlock, err := e.clientCtx.Client.BlockByHash(e.ctx, hash.Bytes())
if err != nil {
e.logger.Debug("BlockByHash block not found", "hash", hash.Hex(), "error", err.Error())
return nil, err
}

if resBlock.Block == nil {
e.logger.Debug("BlockByHash block not found", "hash", hash.Hex())
return nil, nil
}

return e.EthBlockFromTendermint(resBlock.Block, fullTx)
}

// GetTendermintBlockByNumber returns a Tendermint format block by block number
func (e *EVMBackend) GetTendermintBlockByNumber(blockNum types.BlockNumber) (*tmrpctypes.ResultBlock, error) {
height := blockNum.Int64()
currentBlockNumber, _ := e.BlockNumber()

Expand Down Expand Up @@ -152,29 +185,7 @@ func (e *EVMBackend) GetBlockByNumber(blockNum types.BlockNumber, fullTx bool) (
e.logger.Debug("GetBlockByNumber block not found", "height", height)
return nil, nil
}

res, err := e.EthBlockFromTendermint(resBlock.Block, fullTx)
if err != nil {
e.logger.Debug("EthBlockFromTendermint failed", "height", height, "error", err.Error())
}

return res, err
}

// GetBlockByHash returns the block identified by hash.
func (e *EVMBackend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) {
resBlock, err := e.clientCtx.Client.BlockByHash(e.ctx, hash.Bytes())
if err != nil {
e.logger.Debug("BlockByHash block not found", "hash", hash.Hex(), "error", err.Error())
return nil, err
}

if resBlock.Block == nil {
e.logger.Debug("BlockByHash block not found", "hash", hash.Hex())
return nil, nil
}

return e.EthBlockFromTendermint(resBlock.Block, fullTx)
return resBlock, nil
}

// EthBlockFromTendermint returns a JSON-RPC compatible Ethereum block from a given Tendermint block and its block result.
Expand Down
100 changes: 99 additions & 1 deletion ethereum/rpc/namespaces/debug/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"sync"
"time"

"github.com/tendermint/tendermint/types"

evmtypes "github.com/tharsis/ethermint/x/evm/types"

"github.com/cosmos/cosmos-sdk/client"
Expand Down Expand Up @@ -89,7 +91,7 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (

traceTxRequest := evmtypes.QueryTraceTxRequest{
Msg: ethMessage,
TxIndex: transaction.Index,
TxIndex: uint64(transaction.Index),
}

if config != nil {
Expand All @@ -112,6 +114,102 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (
return decodedResult, nil
}

// TraceBlockByNumber returns the structured logs created during the execution of
// EVM and returns them as a JSON object.
func (a *API) TraceBlockByNumber(height rpctypes.BlockNumber, config *evmtypes.TraceConfig) ([]*evmtypes.TxTraceResult, error) {
a.logger.Debug("debug_traceBlockByNumber", "height", height)
if height == 0 {
return nil, errors.New("genesis is not traceable")
}
// Get Tendermint Block
resBlock, err := a.backend.GetTendermintBlockByNumber(height)
if err != nil {
return nil, err
}

return a.traceBlock(height, config, resBlock.Block.Txs)
}

// traceBlock configures a new tracer according to the provided configuration, and
// executes all the transactions contained within. The return value will be one item
// per transaction, dependent on the requested tracer.
func (a API) traceBlock(height rpctypes.BlockNumber, config *evmtypes.TraceConfig, txs types.Txs) ([]*evmtypes.TxTraceResult, error) {
txsLength := len(txs)

if txsLength == 0 {
// If there are no transactions return empty array
return []*evmtypes.TxTraceResult{}, nil
}

var (
results = make([]*evmtypes.TxTraceResult, txsLength)
wg = new(sync.WaitGroup)
jobs = make(chan *evmtypes.TxTraceTask, txsLength)
)

threads := runtime.NumCPU()
if threads > txsLength {
threads = txsLength
}

ctxWithHeight := rpctypes.ContextWithHeight(int64(height))

wg.Add(threads)
for th := 0; th < threads; th++ {
go func() {
defer wg.Done()
txDecoder := a.clientCtx.TxConfig.TxDecoder()
// Fetch and execute the next transaction trace tasks
for task := range jobs {
tx, err := txDecoder(txs[task.Index])
if err != nil {
a.logger.Error("failed to decode transaction", "hash", txs[task.Index].Hash(), "error", err.Error())
continue
}

messages := tx.GetMsgs()
if len(messages) == 0 {
continue
}
ethMessage, ok := messages[0].(*evmtypes.MsgEthereumTx)
if !ok {
// Just considers Ethereum transactions
continue
}

traceTxRequest := &evmtypes.QueryTraceTxRequest{
Msg: ethMessage,
TxIndex: uint64(task.Index),
TraceConfig: config,
}

res, err := a.queryClient.TraceTx(ctxWithHeight, traceTxRequest)
if err != nil {
results[task.Index] = &evmtypes.TxTraceResult{Error: err.Error()}
continue
}
// Response format is unknown due to custom tracer config param
// More information can be found here https://geth.ethereum.org/docs/dapp/tracing-filtered
var decodedResult interface{}
if err := json.Unmarshal(res.Data, &decodedResult); err != nil {
results[task.Index] = &evmtypes.TxTraceResult{Error: err.Error()}
continue
}
results[task.Index] = &evmtypes.TxTraceResult{Result: decodedResult}
}
}()
}

for i := range txs {
jobs <- &evmtypes.TxTraceTask{Index: i}
}

close(jobs)
wg.Wait()

return results, nil
}

// BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to
// file. It uses a profile rate of 1 for most accurate information. If a different rate is
// desired, set the rate and write the profile manually.
Expand Down
12 changes: 11 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ require (
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect
golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af
google.golang.org/grpc v1.40.0
gopkg.in/yaml.v2 v2.4.0
)
Expand All @@ -44,9 +44,13 @@ require (
github.com/99designs/keyring v1.1.6 // indirect
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
github.com/DataDog/zstd v1.4.8 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
github.com/VictoriaMetrics/fastcache v1.5.7 // indirect
github.com/Workiva/go-datastructures v1.0.52 // indirect
github.com/aokoli/goutils v1.1.1 // indirect
github.com/armon/go-metrics v0.3.9 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
Expand All @@ -68,6 +72,7 @@ require (
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect
github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 // indirect
github.com/envoyproxy/protoc-gen-validate v0.6.1 // indirect
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
Expand All @@ -92,7 +97,9 @@ require (
github.com/hdevalence/ed25519consensus v0.0.0-20210204194344-59a8610d2b87 // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.1.1 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
Expand All @@ -105,8 +112,10 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
github.com/minio/highwayhash v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/mwitkow/go-proto-validators v0.3.2 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
Expand All @@ -116,6 +125,7 @@ require (
github.com/prometheus/common v0.29.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/prometheus/tsdb v0.7.1 // indirect
github.com/pseudomuto/protoc-gen-doc v1.5.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rjeczalik/notify v0.9.1 // indirect
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect
Expand Down
Loading

0 comments on commit 3f1eeb3

Please sign in to comment.