Skip to content

Commit

Permalink
Problem: tracer config is not aligned with go-ethereum (#382)
Browse files Browse the repository at this point in the history
* Problem: tracer config is not aligned with go-ethereum

for more info, see https://github.com/ethereum/go-ethereum/blob/v1.11.2/eth/tracers/native/call.go#L109

* add crosscheck

* Update CHANGELOG.md

Signed-off-by: mmsqe <[email protected]>

* keep json str

* keep json str

---------

Signed-off-by: mmsqe <[email protected]>
  • Loading branch information
mmsqe authored Dec 4, 2023
1 parent ebc71a0 commit ccb83e2
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 45 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (mempool) [#310](https://github.com/crypto-org-chain/ethermint/pull/310) disable vesting messages in check tx mode.
* (rpc) [#364](https://github.com/crypto-org-chain/ethermint/pull/364) Only use NextBaseFee as last item to avoid concurrent write in `eth_feeHistory`.
* (config) [#365](https://github.com/crypto-org-chain/ethermint/pull/365) Avoid redundant parse chainID from gensis when start server.
* (rpc) [#382](https://github.com/crypto-org-chain/ethermint/pull/382) Align tracer config with go-ethereum.

### Improvements

Expand Down
2 changes: 1 addition & 1 deletion proto/ethermint/evm/v1/trace_config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ message TraceConfig {
bool enable_return_data = 12 [(gogoproto.jsontag) = "enableReturnData"];
// tracer_json_config configures the tracer using a JSON string
string tracer_json_config = 13 [(gogoproto.jsontag) = "tracerConfig"];
}
}
4 changes: 2 additions & 2 deletions rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ type EVMBackend interface {
BloomStatus() (uint64, uint64)

// Tracing
TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (interface{}, error)
TraceBlock(height rpctypes.BlockNumber, config *evmtypes.TraceConfig, block *tmrpctypes.ResultBlock) ([]*evmtypes.TxTraceResult, error)
TraceTransaction(hash common.Hash, config *rpctypes.TraceConfig) (interface{}, error)
TraceBlock(height rpctypes.BlockNumber, config *rpctypes.TraceConfig, block *tmrpctypes.ResultBlock) ([]*evmtypes.TxTraceResult, error)
}

var _ BackendI = (*Backend)(nil)
Expand Down
17 changes: 13 additions & 4 deletions rpc/backend/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

// TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object.
func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (interface{}, error) {
func (b *Backend) TraceTransaction(hash common.Hash, config *rpctypes.TraceConfig) (interface{}, error) {
// Get transaction by hash
transaction, err := b.GetTxByEthHash(hash)
if err != nil {
Expand Down Expand Up @@ -104,7 +104,7 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi
}

if config != nil {
traceTxRequest.TraceConfig = config
traceTxRequest.TraceConfig = b.convertConfig(config)
}

// minus one to get the context of block beginning
Expand All @@ -129,11 +129,20 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi
return decodedResult, nil
}

func (b *Backend) convertConfig(config *rpctypes.TraceConfig) *evmtypes.TraceConfig {
if config == nil {
return &evmtypes.TraceConfig{}
}
cfg := config.TraceConfig
cfg.TracerJsonConfig = string(config.TracerConfig)
return &cfg
}

// 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 (b *Backend) TraceBlock(height rpctypes.BlockNumber,
config *evmtypes.TraceConfig,
config *rpctypes.TraceConfig,
block *tmrpctypes.ResultBlock,
) ([]*evmtypes.TxTraceResult, error) {
txs := block.Block.Txs
Expand Down Expand Up @@ -182,7 +191,7 @@ func (b *Backend) TraceBlock(height rpctypes.BlockNumber,

traceBlockRequest := &evmtypes.QueryTraceBlockRequest{
Txs: txsMessages,
TraceConfig: config,
TraceConfig: b.convertConfig(config),
BlockNumber: block.Block.Height,
BlockTime: block.Block.Time,
BlockHash: common.Bytes2Hex(block.BlockID.Hash),
Expand Down
7 changes: 4 additions & 3 deletions rpc/backend/tracing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/evmos/ethermint/crypto/ethsecp256k1"
"github.com/evmos/ethermint/indexer"
"github.com/evmos/ethermint/rpc/backend/mocks"
rpctypes "github.com/evmos/ethermint/rpc/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
)

Expand Down Expand Up @@ -208,15 +209,15 @@ func (suite *BackendTestSuite) TestTraceBlock() {
registerMock func()
expTraceResults []*evmtypes.TxTraceResult
resBlock *tmrpctypes.ResultBlock
config *evmtypes.TraceConfig
config *rpctypes.TraceConfig
expPass bool
}{
{
"pass - no transaction returning empty array",
func() {},
[]*evmtypes.TxTraceResult{},
&resBlockEmpty,
&evmtypes.TraceConfig{},
&rpctypes.TraceConfig{},
true,
},
{
Expand All @@ -229,7 +230,7 @@ func (suite *BackendTestSuite) TestTraceBlock() {
},
[]*evmtypes.TxTraceResult{},
&resBlockFilled,
&evmtypes.TraceConfig{},
&rpctypes.TraceConfig{},
false,
},
}
Expand Down
6 changes: 3 additions & 3 deletions rpc/namespaces/ethereum/debug/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ func NewAPI(

// TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object.
func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (interface{}, error) {
func (a *API) TraceTransaction(hash common.Hash, config *rpctypes.TraceConfig) (interface{}, error) {
a.logger.Debug("debug_traceTransaction", "hash", hash)
return a.backend.TraceTransaction(hash, config)
}

// 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) {
func (a *API) TraceBlockByNumber(height rpctypes.BlockNumber, config *rpctypes.TraceConfig) ([]*evmtypes.TxTraceResult, error) {
a.logger.Debug("debug_traceBlockByNumber", "height", height)
if height == 0 {
return nil, errors.New("genesis is not traceable")
Expand All @@ -100,7 +100,7 @@ func (a *API) TraceBlockByNumber(height rpctypes.BlockNumber, config *evmtypes.T

// TraceBlockByHash returns the structured logs created during the execution of
// EVM and returns them as a JSON object.
func (a *API) TraceBlockByHash(hash common.Hash, config *evmtypes.TraceConfig) ([]*evmtypes.TxTraceResult, error) {
func (a *API) TraceBlockByHash(hash common.Hash, config *rpctypes.TraceConfig) ([]*evmtypes.TxTraceResult, error) {
a.logger.Debug("debug_traceBlockByHash", "hash", hash)
// Get Tendermint Block
resBlock, err := a.backend.TendermintBlockByHash(hash)
Expand Down
7 changes: 7 additions & 0 deletions rpc/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
package types

import (
"encoding/json"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/evmos/ethermint/x/evm/statedb"
evmtypes "github.com/evmos/ethermint/x/evm/types"
)

// Copied the Account and StorageResult types since they are registered under an
Expand Down Expand Up @@ -139,3 +141,8 @@ type OneFeeHistory struct {
Reward []*big.Int // each element of the array will have the tip provided to miners for the percentile given
GasUsedRatio float64 // the ratio of gas used to the gas limit for each block
}

type TraceConfig struct {
evmtypes.TraceConfig
TracerConfig json.RawMessage `json:"tracerConfig"`
}
30 changes: 30 additions & 0 deletions tests/integration_tests/hardhat/contracts/TestMessageCall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Inner {
event TestEvent(uint256);
function test() public returns (uint256) {
emit TestEvent(42);
return 42;
}
}

// An contract that do lots of message calls
contract TestMessageCall {
Inner _inner;
constructor() public {
_inner = new Inner();
}

function test(uint iterations) public returns (uint256) {
uint256 n = 0;
for (uint i = 0; i < iterations; i++) {
n += _inner.test();
}
return n;
}

function inner() public view returns (address) {
return address(_inner);
}
}
2 changes: 2 additions & 0 deletions tests/integration_tests/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ def setup_geth(path, base_port):
str(base_port + 1),
"--miner.etherbase",
"0x57f96e6B86CdeFdB3d412547816a82E3E0EbF9D2",
"--http.api",
"eth,net,web3,debug",
]
print(*cmd)
proc = subprocess.Popen(
Expand Down
75 changes: 54 additions & 21 deletions tests/integration_tests/test_tracers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import itertools
import json
from concurrent.futures import ThreadPoolExecutor, as_completed

from web3 import Web3

Expand All @@ -9,8 +12,8 @@
from .utils import (
ADDRS,
CONTRACTS,
KEYS,
deploy_contract,
derive_new_account,
send_transaction,
w3_wait_for_new_blocks,
)
Expand All @@ -21,32 +24,62 @@ def test_tracers(ethermint_rpc_ws):
eth_rpc = w3.provider
gas_price = w3.eth.gas_price
tx = {"to": ADDRS["community"], "value": 100, "gasPrice": gas_price}
tx_hash = send_transaction(w3, tx, KEYS["validator"])["transactionHash"].hex()

tx_res = eth_rpc.make_request("debug_traceTransaction", [tx_hash])
tx_hash = send_transaction(w3, tx)["transactionHash"].hex()
method = "debug_traceTransaction"
tracer = {"tracer": "callTracer"}
tx_res = eth_rpc.make_request(method, [tx_hash])
assert tx_res["result"] == EXPECTED_STRUCT_TRACER, ""

tx_res = eth_rpc.make_request(
"debug_traceTransaction", [tx_hash, {"tracer": "callTracer"}]
)
tx_res = eth_rpc.make_request(method, [tx_hash, tracer])
assert tx_res["result"] == EXPECTED_CALLTRACERS, ""

tx_res = eth_rpc.make_request(
"debug_traceTransaction",
[tx_hash, {"tracer": "callTracer", "tracerConfig": "{'onlyTopCall':True}"}],
method,
[tx_hash, tracer | {"tracerConfig": {"onlyTopCall": True}}],
)
assert tx_res["result"] == EXPECTED_CALLTRACERS, ""

_, tx = deploy_contract(
w3,
CONTRACTS["TestERC20A"],
)
_, tx = deploy_contract(w3, CONTRACTS["TestERC20A"])
tx_hash = tx["transactionHash"].hex()

w3_wait_for_new_blocks(w3, 1)

tx_res = eth_rpc.make_request(
"debug_traceTransaction", [tx_hash, {"tracer": "callTracer"}]
)
tx_res = eth_rpc.make_request(method, [tx_hash, tracer])
tx_res["result"]["to"] = EXPECTED_CONTRACT_CREATE_TRACER["to"]
assert tx_res["result"] == EXPECTED_CONTRACT_CREATE_TRACER, ""


def test_crosscheck(ethermint, geth):
method = "debug_traceTransaction"
tracer = {"tracer": "callTracer"}
acc = derive_new_account(4)
sender = acc.address
# fund new sender to deploy contract with same address
fund = 3000000000000000000
tracers = [
[],
[tracer],
[tracer | {"tracerConfig": {"onlyTopCall": True}}],
[tracer | {"tracerConfig": {"withLog": True}}],
[tracer | {"tracerConfig": {"diffMode": True}}],
]
iterations = 1

def process(w3):
tx = {"to": sender, "value": fund, "gasPrice": w3.eth.gas_price}
send_transaction(w3, tx)
assert w3.eth.get_balance(sender, "latest") == fund
contract, _ = deploy_contract(w3, CONTRACTS["TestMessageCall"], key=acc.key)
tx = contract.functions.test(iterations).build_transaction()
tx_hash = send_transaction(w3, tx)["transactionHash"].hex()
res = []
call = w3.provider.make_request
with ThreadPoolExecutor(len(tracers)) as exec:
params = [([tx_hash] + cfg) for cfg in tracers]
exec_map = exec.map(call, itertools.repeat(method), params)
res = [json.dumps(resp["result"], sort_keys=True) for resp in exec_map]
return res

providers = [ethermint.w3, geth.w3]
with ThreadPoolExecutor(len(providers)) as exec:
tasks = [
exec.submit(process, w3) for w3 in providers
]
res = [future.result() for future in as_completed(tasks)]
assert len(res) == len(providers)
assert res[0] == res[1], res
1 change: 1 addition & 0 deletions tests/integration_tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"StateContract": "StateContract.sol",
"TestExploitContract": "TestExploitContract.sol",
"TestRevert": "TestRevert.sol",
"TestMessageCall": "TestMessageCall.sol",
}


Expand Down
18 changes: 7 additions & 11 deletions x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,13 +457,7 @@ func (k Keeper) TraceTx(c context.Context, req *types.QueryTraceTxRequest) (*typ
txConfig.TxIndex++
}

var tracerConfig json.RawMessage
if req.TraceConfig != nil && req.TraceConfig.TracerJsonConfig != "" {
// ignore error. default to no traceConfig
_ = json.Unmarshal([]byte(req.TraceConfig.TracerJsonConfig), &tracerConfig)
}

result, _, err := k.traceTx(ctx, cfg, txConfig, signer, tx, req.TraceConfig, false, tracerConfig)
result, _, err := k.traceTx(ctx, cfg, txConfig, signer, tx, req.TraceConfig, false)
if err != nil {
// error will be returned with detail status from traceTx
return nil, err
Expand Down Expand Up @@ -521,7 +515,7 @@ func (k Keeper) TraceBlock(c context.Context, req *types.QueryTraceBlockRequest)
ethTx := tx.AsTransaction()
txConfig.TxHash = ethTx.Hash()
txConfig.TxIndex = uint(i)
traceResult, logIndex, err := k.traceTx(ctx, cfg, txConfig, signer, ethTx, req.TraceConfig, true, nil)
traceResult, logIndex, err := k.traceTx(ctx, cfg, txConfig, signer, ethTx, req.TraceConfig, true)
if err != nil {
result.Error = err.Error()
} else {
Expand Down Expand Up @@ -550,7 +544,6 @@ func (k *Keeper) traceTx(
tx *ethtypes.Transaction,
traceConfig *types.TraceConfig,
commitMessage bool,
tracerJSONConfig json.RawMessage,
) (*interface{}, uint, error) {
// Assemble the structured logger or the JavaScript tracer
var (
Expand Down Expand Up @@ -589,9 +582,12 @@ func (k *Keeper) traceTx(
TxIndex: int(txConfig.TxIndex),
TxHash: txConfig.TxHash,
}

if traceConfig.Tracer != "" {
if tracer, err = tracers.DefaultDirectory.New(traceConfig.Tracer, tCtx, tracerJSONConfig); err != nil {
var cfg json.RawMessage
if traceConfig.TracerJsonConfig != "" {
cfg = json.RawMessage(traceConfig.TracerJsonConfig)
}
if tracer, err = tracers.DefaultDirectory.New(traceConfig.Tracer, tCtx, cfg); err != nil {
return nil, 0, status.Error(codes.Internal, err.Error())
}
}
Expand Down

0 comments on commit ccb83e2

Please sign in to comment.