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

RPC: debug_traceBlockByNumber #555

Merged
merged 30 commits into from
Sep 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
64eaacd
Refactor traceTx
crypto-facs Sep 12, 2021
5bc3527
add getTendermintBlock on backend
crypto-facs Sep 12, 2021
bbcbf18
keeper concurrency
crypto-facs Sep 13, 2021
0c4b322
first version
crypto-facs Sep 13, 2021
055870a
json rpc concurrency
crypto-facs Sep 13, 2021
df5d302
rever makefile change
crypto-facs Sep 13, 2021
99bab87
remove grpc traceblock
crypto-facs Sep 14, 2021
086cefc
create internal traceBlock function
crypto-facs Sep 14, 2021
c488e33
added types to evm module
crypto-facs Sep 14, 2021
0782e37
tendermintBlockByNumber rename
crypto-facs Sep 14, 2021
832078e
added safe message check
crypto-facs Sep 14, 2021
bfa4e8f
remove unnecesary line
crypto-facs Sep 14, 2021
1163597
check error
crypto-facs Sep 14, 2021
9541bd0
fix lint
crypto-facs Sep 14, 2021
44f4693
fix linter
crypto-facs Sep 14, 2021
756319d
Update ethereum/rpc/namespaces/debug/api.go
crypto-facs Sep 14, 2021
012fcc6
Update ethereum/rpc/namespaces/debug/api.go
crypto-facs Sep 14, 2021
05c4217
Update ethereum/rpc/backend/backend.go
crypto-facs Sep 14, 2021
1ea48b9
improve traceBlock performance
crypto-facs Sep 14, 2021
a022c8e
fix linter
crypto-facs Sep 14, 2021
50a1b61
Merge branch 'trace-block' of github.com:crypto-facs/ethermint into t…
crypto-facs Sep 14, 2021
a795c61
add extra line on function parameters
crypto-facs Sep 14, 2021
e6c8f6d
changed index to uint 64
crypto-facs Sep 14, 2021
aa4816f
fix lint
crypto-facs Sep 14, 2021
2746285
proto gen
crypto-facs Sep 14, 2021
607f0b0
update endpoints documentation
crypto-facs Sep 15, 2021
1090ed2
update changelog
crypto-facs Sep 15, 2021
2872935
Apply suggestions from code review
fedekunze Sep 15, 2021
7803ad5
Update ethereum/rpc/namespaces/eth/filters/filters.go
fedekunze Sep 15, 2021
181d552
Merge branch 'main' into trace-block
fedekunze Sep 15, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
crypto-facs marked this conversation as resolved.
Show resolved Hide resolved

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