Skip to content

Commit

Permalink
fix(zetacore): allow object for tracerConfig (#3622)
Browse files Browse the repository at this point in the history
* fix(zetacore): allow object for tracerConfig

* add empty test case

* use explicit error return

* changelog
  • Loading branch information
gartnera authored Mar 4, 2025
1 parent 06e66fb commit 5041002
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 12 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
* [3509](https://github.com/zeta-chain/node/pull/3509) - schedule Bitcoin TSS keysign on interval to avoid TSS keysign spam
* [3517](https://github.com/zeta-chain/node/pull/3517) - remove duplicate gateway event appending to fix false positive on multiple events in same tx
* [3602](https://github.com/zeta-chain/node/pull/3602) - hardcode gas limits to avoid estimate gas calls
* [3622](https://github.com/zeta-chain/node/pull/3622) - allow object for tracerConfig in `debug_traceTransaction` RPC

### Tests

Expand Down
4 changes: 2 additions & 2 deletions rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,10 @@ type EVMBackend interface {
BloomStatus() (uint64, uint64)

// Tracing
TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (interface{}, error)
TraceTransaction(hash common.Hash, config *rpctypes.TraceConfig) (interface{}, error)
TraceBlock(
height rpctypes.BlockNumber,
config *evmtypes.TraceConfig,
config *rpctypes.TraceConfig,
block *tmrpctypes.ResultBlock,
) ([]*evmtypes.TxTraceResult, error)
}
Expand Down
44 changes: 40 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 @@ -80,7 +80,10 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi
}

if config != nil {
traceTxRequest.TraceConfig = config
traceTxRequest.TraceConfig, err = convertConfig(config)
if err != nil {
return nil, err
}
}

// minus one to get the context of block beginning
Expand Down Expand Up @@ -109,7 +112,7 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi
// 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 All @@ -135,9 +138,14 @@ func (b *Backend) TraceBlock(height rpctypes.BlockNumber,
}
ctxWithHeight := rpctypes.ContextWithHeight(int64(contextHeight))

traceConfig, err := convertConfig(config)
if err != nil {
return nil, err
}

traceBlockRequest := &evmtypes.QueryTraceBlockRequest{
Txs: msgs,
TraceConfig: config,
TraceConfig: traceConfig,
BlockNumber: block.Block.Height,
BlockTime: block.Block.Time,
BlockHash: common.Bytes2Hex(block.BlockID.Hash),
Expand All @@ -157,3 +165,31 @@ func (b *Backend) TraceBlock(height rpctypes.BlockNumber,

return decodedResults, nil
}

func convertConfig(config *rpctypes.TraceConfig) (*evmtypes.TraceConfig, error) {
if config == nil {
return &evmtypes.TraceConfig{}, nil
}

cfg := config.TraceConfig

if config.TracerConfig != nil {
switch v := config.TracerConfig.(type) {
case string:
// It's already a string, use it directly
cfg.TracerJsonConfig = v
case map[string]interface{}:
// this is the compliant style
// we need to encode it to a string before passing it to the ethermint side
jsonBytes, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("unable to encode traceConfig to JSON: %w", err)
}
cfg.TracerJsonConfig = string(jsonBytes)
default:
return nil, errors.New("unexpected traceConfig type")
}
}

return &cfg, nil
}
88 changes: 88 additions & 0 deletions rpc/backend/tracing_convert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package backend

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/require"
rpctypes "github.com/zeta-chain/node/rpc/types"
)

const expectedConvertedValue = `{"onlyTopCall":false}`

const brokenStyleRaw = `
{
"tracer": "callTracer",
"tracerConfig": "{\"onlyTopCall\":false}"
}
`

const compliantStyleRaw = `
{
"tracer": "callTracer",
"tracerConfig": {"onlyTopCall":false}
}
`

const emptyStyleRaw = `
{
"tracer": "callTracer"
}
`

const invalidStyleRaw = `
{
"tracer": "callTracer",
"tracerConfig": []
}
`

func TestConvertConfig(t *testing.T) {
tests := []struct {
name string
input string
expectError bool
expected string
}{
{
name: "broken style",
input: brokenStyleRaw,
expectError: false,
expected: expectedConvertedValue,
},
{
name: "compliant style",
input: compliantStyleRaw,
expectError: false,
expected: expectedConvertedValue,
},
{
name: "empty style",
input: emptyStyleRaw,
expectError: false,
expected: "",
},
{
name: "invalid style",
input: invalidStyleRaw,
expectError: true,
expected: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rpcConfig := &rpctypes.TraceConfig{}
err := json.Unmarshal([]byte(tt.input), rpcConfig)
require.NoError(t, err)

ethermintConfig, err := convertConfig(rpcConfig)
if tt.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.expected, ethermintConfig.TracerJsonConfig)
})
}
}
7 changes: 4 additions & 3 deletions rpc/backend/tracing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/zeta-chain/ethermint/crypto/ethsecp256k1"
"github.com/zeta-chain/ethermint/indexer"
evmtypes "github.com/zeta-chain/ethermint/x/evm/types"
rpctypes "github.com/zeta-chain/node/rpc/types"

"github.com/zeta-chain/node/rpc/backend/mocks"
)
Expand Down Expand Up @@ -272,15 +273,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 @@ -293,7 +294,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 @@ -72,7 +72,7 @@ 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)
}
Expand All @@ -81,7 +81,7 @@ func (a *API) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (
// EVM and returns them as a JSON object.
func (a *API) TraceBlockByNumber(
height rpctypes.BlockNumber,
config *evmtypes.TraceConfig,
config *rpctypes.TraceConfig,
) ([]*evmtypes.TxTraceResult, error) {
a.logger.Debug("debug_traceBlockByNumber", "height", height)
if height == 0 {
Expand All @@ -99,7 +99,7 @@ func (a *API) TraceBlockByNumber(

// 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
6 changes: 6 additions & 0 deletions rpc/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
evmtypes "github.com/zeta-chain/ethermint/x/evm/types"
)

// Copied the Account and StorageResult types since they are registered under an
Expand Down Expand Up @@ -115,3 +116,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 interface{} `json:"tracerConfig"`
}

0 comments on commit 5041002

Please sign in to comment.