Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: debug_traceCall RPC support #384

Closed
Closed
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
ee2b893
Problem: Missing debug_traceCall RPC
calvinaco Oct 25, 2023
b58b94b
Fix inconsistent TraceCall signature
calvinaco Oct 25, 2023
cb1e146
Fix linting issue
calvinaco Oct 25, 2023
42962e0
Fix linting issue
calvinaco Oct 26, 2023
4b5eee4
Fixed a typo
calvinaco Oct 26, 2023
99be34f
Add integration tests for testing
calvinaco Oct 27, 2023
89d5a0d
Add more integration tests
calvinaco Nov 2, 2023
dab9d62
Merge branch 'develop' of github.com:crypto-org-chain/ethermint into …
XinyuCRO Nov 2, 2023
e2839c5
fix: compile
XinyuCRO Nov 2, 2023
c4a2692
fix: remove debug parameter
XinyuCRO Nov 13, 2023
c33839f
chore: update geth
XinyuCRO Nov 13, 2023
63ff4a7
test: update test
XinyuCRO Nov 13, 2023
2e70c9d
fix: balance & nonce not correct in prestateTracer
XinyuCRO Nov 16, 2023
275a7a2
fix: TracerJSONConfig type is not correct
XinyuCRO Nov 16, 2023
411e641
fix: debug_traceTransaction integration tests
XinyuCRO Nov 17, 2023
3d99b09
tests: add bigramTracers integration tests
XinyuCRO Nov 17, 2023
6d0c272
tests: add more js tracers
XinyuCRO Nov 17, 2023
31ae03c
tests: add custom js tracers
XinyuCRO Nov 17, 2023
0857f84
wip: integration tests
XinyuCRO Nov 17, 2023
befc089
Merge branch 'develop' of github.com:crypto-org-chain/ethermint into …
XinyuCRO Nov 17, 2023
c5445fa
tests: add no gas limit call to debug_traceCall
XinyuCRO Nov 17, 2023
d8eef39
feat: add stateOverrides to debug_traceCall
XinyuCRO Nov 20, 2023
c89e579
chore: update geth
XinyuCRO Nov 21, 2023
7ff5add
Merge branch 'develop' of github.com:crypto-org-chain/ethermint into …
XinyuCRO Nov 24, 2023
a391289
fix: bypass ExecutionReverted error early return, return correspondin…
XinyuCRO Nov 27, 2023
4feeb1d
tests: update tracer tests
XinyuCRO Nov 27, 2023
b779425
Merge branch 'develop' of github.com:crypto-org-chain/ethermint into …
XinyuCRO Dec 7, 2023
b7e8e25
Merge branch 'develop' of github.com:crypto-org-chain/ethermint into …
XinyuCRO Jan 3, 2024
7821453
chore: add missing changes
XinyuCRO Jan 3, 2024
0a1a705
test: fix debugTraceCall unit tests
XinyuCRO Jan 4, 2024
d47b0ea
chore: revert changes
XinyuCRO Jan 4, 2024
86fdf3f
fix: safeInt
XinyuCRO Jan 4, 2024
9ff1eb4
chore: remove unused code
XinyuCRO Jan 4, 2024
ce86161
chore: update comment
XinyuCRO Jan 4, 2024
77107a4
chore: lint
XinyuCRO Jan 4, 2024
da3082a
fix: lint
XinyuCRO Jan 4, 2024
5b9fc34
fix python lint
yihuang Jan 4, 2024
7a8a460
fix: tracer config
XinyuCRO Jan 4, 2024
8a93be0
test: fix integration tests
XinyuCRO Jan 4, 2024
f480d4a
Merge branch 'feature/debug_tracecall-debug' of github.com:XinyuCRO/e…
XinyuCRO Jan 4, 2024
d377b86
Update rpc/backend/backend.go
XinyuCRO Jan 4, 2024
9a4bd5e
Update rpc/backend/tracing.go
XinyuCRO Jan 4, 2024
b98c280
Update rpc/namespaces/ethereum/debug/api.go
XinyuCRO Jan 4, 2024
883865c
Update rpc/backend/tracing.go
XinyuCRO Jan 4, 2024
a1a5c44
Merge branch 'feature/debug_tracecall-debug' of github.com:XinyuCRO/e…
XinyuCRO Jan 4, 2024
6e7469b
fix: only update balance and nonce in tracing RPC calls
XinyuCRO Jan 4, 2024
a7113f7
fix isort
yihuang Jan 4, 2024
7299413
fix python lint
yihuang Jan 4, 2024
ae45b0b
python linter
yihuang Jan 4, 2024
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
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ func (app *EthermintApp) RegisterNodeService(clientCtx client.Context) {
}

// RegisterSwaggerAPI registers swagger route with API Server
func RegisterSwaggerAPI(ctx client.Context, rtr *mux.Router) {
func RegisterSwaggerAPI(_ client.Context, rtr *mux.Router) {
root, err := fs.Sub(docs.SwaggerUI, "swagger-ui")
if err != nil {
panic(err)
Expand Down
31 changes: 31 additions & 0 deletions proto/ethermint/evm/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ service Query {
option (google.api.http).get = "/ethermint/evm/v1/trace_block";
}

// TraceCall implements the `debug_traceCall` rpc api
rpc TraceCall(QueryTraceCallRequest) returns (QueryTraceCallResponse) {
option (google.api.http).get = "/ethermint/evm/v1/trace_call";
}

// BaseFee queries the base fee of the parent block of the current block,
// it's similar to feemarket module's method, but also checks london hardfork status.
rpc BaseFee(QueryBaseFeeRequest) returns (QueryBaseFeeResponse) {
Expand Down Expand Up @@ -272,6 +277,32 @@ message QueryTraceTxResponse {
bytes data = 1;
}

// QueryTraceCallRequest defines TraceCall request
message QueryTraceCallRequest {
// args uses the same json format as the json rpc api.
bytes args = 1;
// gas_cap defines the default gas cap to be used
uint64 gas_cap = 2;
// proposer_address of the requested block in hex format
bytes proposer_address = 3 [(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.ConsAddress"];
// trace_config holds extra parameters to trace functions.
TraceConfig trace_config = 4;
// block_number of requested transaction
int64 block_number = 5;
// block_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];
// chain_id is the the eip155 chain id parsed from the requested block header
int64 chain_id = 8;
}

// QueryTraceCallResponse defines TraceCallResponse
message QueryTraceCallResponse {
// data is the response serialized in bytes
bytes data = 1;
}

// QueryTraceBlockRequest defines TraceTx request
message QueryTraceBlockRequest {
// txs is an array of messages in the block
Expand Down
13 changes: 13 additions & 0 deletions proto/ethermint/evm/v1/trace_config_v0.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ import "ethermint/evm/v1/chain_config_v0.proto";

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


// OverrideAccount indicates the overriding fields of account during the execution of
// a message call.
message V0OverrideAccount {
uint64 nonce = 1;
string code = 2;
string balance = 3;
map<string, string> state = 4;
map<string, string> state_diff = 5 [(gogoproto.jsontag) = "stateDiff"];
}

// V0TraceConfig holds extra parameters to trace functions.
message V0TraceConfig {
// DEPRECATED: DisableMemory and DisableReturnData have been renamed to
Expand Down Expand Up @@ -36,4 +47,6 @@ message V0TraceConfig {
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"];
// temporary state modifications to Geth in order to simulate the effects of eth_call
map<string, V0OverrideAccount> state_overrides = 14 [(gogoproto.jsontag) = "stateOverrides"];
}
1 change: 1 addition & 0 deletions rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ type EVMBackend interface {
// Tracing
TraceTransaction(hash common.Hash, config *rpctypes.TraceConfig) (interface{}, error)
TraceBlock(height rpctypes.BlockNumber, config *rpctypes.TraceConfig, block *tmrpctypes.ResultBlock) ([]*evmtypes.TxTraceResult, error)
TraceCall(args evmtypes.TransactionArgs, blockNr rpctypes.BlockNumberOrHash, config *rpctypes.TraceConfig) (interface{}, error)
}

var _ BackendI = (*Backend)(nil)
Expand Down
12 changes: 12 additions & 0 deletions rpc/backend/evm_query_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,15 @@ func RegisterBalanceError(queryClient *mocks.EVMQueryClient, addr common.Address
queryClient.On("Balance", rpc.ContextWithHeight(height), &evmtypes.QueryBalanceRequest{Address: addr.String()}).
Return(nil, errortypes.ErrInvalidRequest)
}

// TraceCall
func RegisterTraceCall(queryClient *mocks.EVMQueryClient, request *evmtypes.QueryTraceCallRequest, response *evmtypes.QueryTraceCallResponse) {
queryClient.On("TraceCall", rpc.ContextWithHeight(request.BlockNumber), request).
Return(response, nil)
}

func RegisterTraceCallError(queryClient *mocks.EVMQueryClient, request *evmtypes.QueryTraceCallRequest) {
ctx, _ := context.WithCancel(rpc.ContextWithHeight(1))
queryClient.On("TraceCall", ctx, request).
Return(nil, errortypes.ErrInvalidRequest)
}
30 changes: 30 additions & 0 deletions rpc/backend/mocks/evm_query_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions rpc/backend/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,58 @@ func (b *Backend) TraceBlock(height rpctypes.BlockNumber,

return decodedResults, nil
}

// TraceCall returns the structured logs created during the execution of EVM call
// and returns them as a JSON object.
func (b *Backend) TraceCall(
args evmtypes.TransactionArgs, blockNrOrHash rpctypes.BlockNumberOrHash, config *rpctypes.TraceConfig,
) (interface{}, error) {
bz, err := json.Marshal(&args)
if err != nil {
return nil, err
}
blockNr, err := b.BlockNumberFromTendermint(blockNrOrHash)
if err != nil {
return nil, err
}
blk, err := b.TendermintBlockByNumber(blockNr)
if err != nil {
// the error message imitates geth behavior
return nil, errors.New("header not found")
}

traceCallRequest := evmtypes.QueryTraceCallRequest{
Args: bz,
GasCap: b.RPCGasCap(),
ProposerAddress: sdk.ConsAddress(blk.Block.ProposerAddress),
BlockNumber: blk.Block.Height,
BlockHash: common.Bytes2Hex(blk.BlockID.Hash),
BlockTime: blk.Block.Time,
ChainId: b.chainID.Int64(),
}

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

// minus one to get the context of block beginning
contextHeight := blk.Block.Height - 1
if contextHeight < 1 {
// 0 is a special value in `ContextWithHeight`
contextHeight = 1
}
traceResult, err := b.queryClient.TraceCall(rpctypes.ContextWithHeight(contextHeight), &traceCallRequest)
if err != nil {
return nil, err
}

// 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{}
err = json.Unmarshal(traceResult.Data, &decodedResult)
if err != nil {
return nil, err
}

return decodedResult, nil
}
73 changes: 73 additions & 0 deletions rpc/backend/tracing_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package backend

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

dbm "github.com/cometbft/cometbft-db"
abci "github.com/cometbft/cometbft/abci/types"
Expand All @@ -11,11 +13,13 @@ import (
tmtypes "github.com/cometbft/cometbft/types"
"github.com/cosmos/cosmos-sdk/crypto"
"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/crypto/ethsecp256k1"
"github.com/evmos/ethermint/indexer"
"github.com/evmos/ethermint/rpc/backend/mocks"
rpctypes "github.com/evmos/ethermint/rpc/types"
"github.com/evmos/ethermint/tests"
evmtypes "github.com/evmos/ethermint/x/evm/types"
)

Expand Down Expand Up @@ -251,3 +255,72 @@ func (suite *BackendTestSuite) TestTraceBlock() {
})
}
}

func (suite *BackendTestSuite) TestDebugTraceCall() {
_, bz := suite.buildEthereumTx()
gasPrice := (*hexutil.Big)(big.NewInt(1))
toAddr := tests.GenerateAddress()
chainID := (*hexutil.Big)(suite.backend.chainID)
callArgs := evmtypes.TransactionArgs{
From: nil,
To: &toAddr,
Gas: nil,
GasPrice: nil,
MaxFeePerGas: gasPrice,
MaxPriorityFeePerGas: gasPrice,
Value: gasPrice,
Input: nil,
Data: nil,
AccessList: nil,
ChainID: chainID,
}
argsBz, err := json.Marshal(callArgs)
suite.Require().NoError(err)

blockNum := rpctypes.NewBlockNumber(big.NewInt(1))

testCases := []struct {
name string
registerMock func()
blockNum rpctypes.BlockNumberOrHash
callArgs evmtypes.TransactionArgs
expEthTx interface{}
expPass bool
}{
{
"pass",
func() {
client := suite.backend.clientCtx.Client.(*mocks.Client)
queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterBlock(client, 1, bz)
RegisterTraceCall(
queryClient,
&evmtypes.QueryTraceCallRequest{Args: argsBz, ChainId: suite.backend.chainID.Int64(), BlockNumber: 1},
&evmtypes.QueryTraceCallResponse{Data: []byte("{}")},
)
},
rpctypes.BlockNumberOrHash{
BlockNumber: &blockNum,
},
callArgs,
map[string]interface{}{},
true,
},
}

for _, tc := range testCases {
suite.Run(fmt.Sprintf("case %s", tc.name), func() {
suite.SetupTest()
tc.registerMock()

result, err := suite.backend.TraceCall(tc.callArgs, tc.blockNum, nil)

if tc.expPass {
suite.Require().Equal(tc.expEthTx, result)
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
11 changes: 11 additions & 0 deletions rpc/namespaces/ethereum/debug/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ func (a *API) TraceBlockByHash(hash common.Hash, config *rpctypes.TraceConfig) (
return a.backend.TraceBlock(rpctypes.BlockNumber(resBlock.Block.Height), config, resBlock)
}

// TraceCall returns the structured logs created during the execution of EVM call
// and returns them as a JSON object.
func (a *API) TraceCall(
args evmtypes.TransactionArgs,
blockNrOrHash rpctypes.BlockNumberOrHash,
config *rpctypes.TraceConfig,
) (interface{}, error) {
a.logger.Debug("debug_traceCall", "args", args.String(), "block number or hash", blockNrOrHash)
return a.backend.TraceCall(args, blockNrOrHash, config)
}

// 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
6 changes: 5 additions & 1 deletion tests/integration_tests/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ def setup_custom_ethermint(
if chain_binary is not None:
cmd = cmd[:1] + ["--cmd", chain_binary] + cmd[1:]
print(*cmd)
subprocess.run(cmd, check=True)
try:
subprocess.run(cmd, check=True)
except subprocess.CalledProcessError as e:
print("Error {}: {}".format(e.returncode, e.output))
raise e
if post_init is not None:
post_init(path, base_port, config)
proc = subprocess.Popen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
)


def test_trace_blk(ethermint):
def test_traceblock(ethermint):
w3 = ethermint.w3
cli = ethermint.cosmos_cli()
acc = derive_new_account(3)
Expand Down
Loading
Loading