Skip to content

Commit

Permalink
cherry-pick (tharsis#720)
Browse files Browse the repository at this point in the history
Co-authored-by: Freddy Caceres <[email protected]>
Co-authored-by: crypto-facs <[email protected]>
Co-authored-by: Federico Kunze Küllmer <[email protected]>
  • Loading branch information
4 people authored and thomas-nguy committed Nov 17, 2021
1 parent 3dc1a6f commit 9f1478e
Show file tree
Hide file tree
Showing 12 changed files with 3,463 additions and 498 deletions.
2,149 changes: 1,856 additions & 293 deletions client/docs/swagger-ui/swagger.yaml

Large diffs are not rendered by default.

43 changes: 42 additions & 1 deletion docs/api/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
- [QueryStaticCallResponse](#ethermint.evm.v1.QueryStaticCallResponse)
- [QueryStorageRequest](#ethermint.evm.v1.QueryStorageRequest)
- [QueryStorageResponse](#ethermint.evm.v1.QueryStorageResponse)
- [QueryTraceBlockRequest](#ethermint.evm.v1.QueryTraceBlockRequest)
- [QueryTraceBlockResponse](#ethermint.evm.v1.QueryTraceBlockResponse)
- [QueryTraceTxRequest](#ethermint.evm.v1.QueryTraceTxRequest)
- [QueryTraceTxResponse](#ethermint.evm.v1.QueryTraceTxResponse)
- [QueryTxLogsRequest](#ethermint.evm.v1.QueryTxLogsRequest)
Expand Down Expand Up @@ -510,7 +512,7 @@ Msg defines the evm Msg service.

| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `EthereumTx` | [MsgEthereumTx](#ethermint.evm.v1.MsgEthereumTx) | [MsgEthereumTxResponse](#ethermint.evm.v1.MsgEthereumTxResponse) | EthereumTx defines a method submitting Ethereum transactions. | |
| `EthereumTx` | [MsgEthereumTx](#ethermint.evm.v1.MsgEthereumTx) | [MsgEthereumTxResponse](#ethermint.evm.v1.MsgEthereumTxResponse) | EthereumTx defines a method submitting Ethereum transactions. | POST|/ethermint/evm/v1/ethereum_tx|

<!-- end services -->

Expand Down Expand Up @@ -753,6 +755,40 @@ method.



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

### QueryTraceBlockRequest
QueryTraceBlockRequest defines TraceTx request


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `txs` | [MsgEthereumTx](#ethermint.evm.v1.MsgEthereumTx) | repeated | txs messages in the block |
| `trace_config` | [TraceConfig](#ethermint.evm.v1.TraceConfig) | | TraceConfig holds extra parameters to trace functions. |
| `block_number` | [int64](#int64) | | block number |
| `block_hash` | [string](#string) | | block hex hash |
| `block_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | block time |






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

### QueryTraceBlockResponse
QueryTraceBlockResponse defines TraceBlock response


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






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

### QueryTraceTxRequest
Expand All @@ -764,6 +800,10 @@ QueryTraceTxRequest defines TraceTx request
| `msg` | [MsgEthereumTx](#ethermint.evm.v1.MsgEthereumTx) | | msgEthereumTx for the requested transaction |
| `tx_index` | [uint64](#uint64) | | transaction index |
| `trace_config` | [TraceConfig](#ethermint.evm.v1.TraceConfig) | | TraceConfig holds extra parameters to trace functions. |
| `predecessors` | [MsgEthereumTx](#ethermint.evm.v1.MsgEthereumTx) | repeated | the predecessor transactions included in the same block need to be replayed first to get correct context for tracing. |
| `block_number` | [int64](#int64) | | block number of requested transaction |
| `block_hash` | [string](#string) | | block hex hash of requested transaction |
| `block_time` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | block time of requested transaction |



Expand Down Expand Up @@ -874,6 +914,7 @@ Query defines the gRPC querier service.
| `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|
| `TraceBlock` | [QueryTraceBlockRequest](#ethermint.evm.v1.QueryTraceBlockRequest) | [QueryTraceBlockResponse](#ethermint.evm.v1.QueryTraceBlockResponse) | TraceBlock implements the `debug_traceBlockByNumber` and `debug_traceBlockByHash` rpc api | GET|/ethermint/evm/v1/trace_block|

<!-- end services -->

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ require (
golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af
google.golang.org/grpc v1.41.0
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v2 v2.4.0
)

Expand Down
35 changes: 35 additions & 0 deletions proto/ethermint/evm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "cosmos/base/query/v1beta1/pagination.proto";
import "google/api/annotations.proto";
import "ethermint/evm/v1/evm.proto";
import "ethermint/evm/v1/tx.proto";
import "google/protobuf/timestamp.proto";

option go_package = "github.com/tharsis/ethermint/x/evm/types";

Expand Down Expand Up @@ -65,6 +66,11 @@ service Query {
rpc TraceTx(QueryTraceTxRequest) returns (QueryTraceTxResponse) {
option (google.api.http).get = "/ethermint/evm/v1/trace_tx";
}

// TraceBlock implements the `debug_traceBlockByNumber` and `debug_traceBlockByHash` rpc api
rpc TraceBlock(QueryTraceBlockRequest) returns (QueryTraceBlockResponse) {
option (google.api.http).get = "/ethermint/evm/v1/trace_block";
}
}

// QueryAccountRequest is the request type for the Query/Account RPC method.
Expand Down Expand Up @@ -231,10 +237,39 @@ message QueryTraceTxRequest {
uint64 tx_index = 2;
// TraceConfig holds extra parameters to trace functions.
TraceConfig trace_config = 3;
// the predecessor transactions included in the same block
// need to be replayed first to get correct context for tracing.
repeated MsgEthereumTx predecessors = 4;
// block number of requested transaction
int64 block_number = 5;
// block hex hash of requested transaction
string block_hash = 6;
// block time of requested transaction
google.protobuf.Timestamp block_time = 7 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
}

// QueryTraceTxResponse defines TraceTx response
message QueryTraceTxResponse {
// response serialized in bytes
bytes data = 1;
}

// QueryTraceBlockRequest defines TraceTx request
message QueryTraceBlockRequest {
// txs messages in the block
repeated MsgEthereumTx txs = 1;
// TraceConfig holds extra parameters to trace functions.
TraceConfig trace_config = 3;
// block number
int64 block_number = 5;
// block hex hash
string block_hash = 6;
// block time
google.protobuf.Timestamp block_time = 7 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
}

// QueryTraceBlockResponse defines TraceBlock response
message QueryTraceBlockResponse {
bytes data = 1;
}

153 changes: 88 additions & 65 deletions rpc/ethereum/namespaces/debug/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"sync"
"time"

"github.com/tendermint/tendermint/types"
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"

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

Expand Down Expand Up @@ -77,6 +77,35 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (
return nil, errors.New("genesis is not traceable")
}

blk, err := a.backend.GetTendermintBlockByNumber(rpctypes.BlockNumber(transaction.Height))
if err != nil {
a.logger.Debug("block not found", "height", transaction.Height)
return nil, err
}

// check tx index is not out of bound
if uint32(len(blk.Block.Txs)) < transaction.Index {
a.logger.Debug("tx index out of bounds", "index", transaction.Index, "hash", hash.String(), "height", blk.Block.Height)
return nil, fmt.Errorf("transaction not included in block %v", blk.Block.Height)
}

// nolint: prealloc
var predecessors []*evmtypes.MsgEthereumTx
for _, txBz := range blk.Block.Txs[:transaction.Index] {
tx, err := a.clientCtx.TxConfig.TxDecoder()(txBz)
if err != nil {
a.logger.Debug("failed to decode transaction in block", "height", blk.Block.Height, "error", err.Error())
continue
}
msg := tx.GetMsgs()[0]
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
continue
}

predecessors = append(predecessors, ethMsg)
}

tx, err := a.clientCtx.TxConfig.TxDecoder()(transaction.Tx)
if err != nil {
a.logger.Debug("tx not found", "hash", hash)
Expand All @@ -90,15 +119,25 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (
}

traceTxRequest := evmtypes.QueryTraceTxRequest{
Msg: ethMessage,
TxIndex: uint64(transaction.Index),
Msg: ethMessage,
TxIndex: uint64(transaction.Index),
Predecessors: predecessors,
BlockNumber: blk.Block.Height,
BlockTime: blk.Block.Time,
BlockHash: common.Bytes2Hex(blk.BlockID.Hash),
}

if config != nil {
traceTxRequest.TraceConfig = config
}

traceResult, err := a.queryClient.TraceTx(rpctypes.ContextWithHeight(transaction.Height), &traceTxRequest)
// minus one to get the context of block beginning
contextHeight := transaction.Height - 1
if contextHeight < 1 {
// 0 is a special value in `ContextWithHeight`
contextHeight = 1
}
traceResult, err := a.queryClient.TraceTx(rpctypes.ContextWithHeight(contextHeight), &traceTxRequest)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -127,87 +166,71 @@ func (a *API) TraceBlockByNumber(height rpctypes.BlockNumber, config *evmtypes.T
return nil, err
}

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

// 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) {
func (a *API) traceBlock(height rpctypes.BlockNumber, config *evmtypes.TraceConfig, block *tmrpctypes.ResultBlock) ([]*evmtypes.TxTraceResult, error) {
txs := block.Block.Txs
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)
)
txDecoder := a.clientCtx.TxConfig.TxDecoder()

// nolint: prealloc
var txsMessages []*evmtypes.MsgEthereumTx
for i, tx := range txs {
decodedTx, err := txDecoder(tx)
if err != nil {
a.logger.Error("failed to decode transaction", "hash", txs[i].Hash(), "error", err.Error())
continue
}

threads := runtime.NumCPU()
if threads > txsLength {
threads = txsLength
messages := decodedTx.GetMsgs()
if len(messages) == 0 {
continue
}
ethMessage, ok := messages[0].(*evmtypes.MsgEthereumTx)
if !ok {
// Just considers Ethereum transactions
continue
}
txsMessages = append(txsMessages, ethMessage)
}

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}
}
}()
// minus one to get the context at the beginning of the block
contextHeight := height - 1
if contextHeight < 1 {
// 0 is a special value for `ContextWithHeight`.
contextHeight = 1
}
ctxWithHeight := rpctypes.ContextWithHeight(int64(contextHeight))

traceBlockRequest := &evmtypes.QueryTraceBlockRequest{
Txs: txsMessages,
TraceConfig: config,
BlockNumber: block.Block.Height,
BlockTime: block.Block.Time,
BlockHash: common.Bytes2Hex(block.BlockID.Hash),
}

for i := range txs {
jobs <- &evmtypes.TxTraceTask{Index: i}
res, err := a.queryClient.TraceBlock(ctxWithHeight, traceBlockRequest)
if err != nil {
return nil, err
}

close(jobs)
wg.Wait()
decodedResults := make([]*evmtypes.TxTraceResult, txsLength)
if err := json.Unmarshal(res.Data, &decodedResults); err != nil {
return nil, err
}

return results, nil
return decodedResults, nil
}

// BlockProfile turns on goroutine profiling for nsec seconds and writes profile data to
Expand Down
Loading

0 comments on commit 9f1478e

Please sign in to comment.