forked from cerc-io/laconicd-deprecated
rpc: debug_traceBlockByNumber endpoint (#555)
* 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>
This commit is contained in:
co-authored by
Federico Kunze Küllmer
parent
ec7d8a37fd
commit
3f1eeb30b6
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"
|
||||
@@ -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 {
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user