From f198372d0bd335553d0b6f69466b88abf4cc4db5 Mon Sep 17 00:00:00 2001 From: vitaliy Date: Fri, 13 Sep 2024 14:26:03 +0300 Subject: [PATCH 1/4] Use hex for trace int values --- .../evm/jsonrpc/jsonrpctest/jsonrpc_test.go | 36 +++++++++++++++---- packages/evm/jsonrpc/tracer_call.go | 30 +++++++++------- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go index f5eb824915..0e308f0153 100644 --- a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go +++ b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go @@ -6,8 +6,10 @@ package jsonrpctest import ( "context" "encoding/json" + "fmt" "math/big" "slices" + "strconv" "strings" "testing" "time" @@ -518,6 +520,16 @@ func TestRPCCustomError(t *testing.T) { require.EqualValues(t, 42, args[0]) } +func Test1(t *testing.T) { + i := 123 + + s := fmt.Sprintf("%x", i) + s2 := strconv.FormatInt(int64(i), 16) + + fmt.Println(s) + fmt.Println(s2) +} + func TestRPCTraceTx(t *testing.T) { env := newSoloTestEnv(t) creator, creatorAddress := env.soloChain.NewEthereumAccountWithL2Funds() @@ -586,21 +598,25 @@ func TestRPCTraceTx(t *testing.T) { require.Equal(t, creatorAddress, trace1.From) require.Equal(t, contractAddress, *trace1.To) - require.Equal(t, big.NewInt(123), trace1.Value) + require.Equal(t, "0x7b", trace1.Value) expectedInput, err := contractABI.Pack("sendTo", common.Address{0x1}, big.NewInt(1)) require.NoError(t, err) require.Equal(t, expectedInput, trace1.Input) require.Empty(t, trace1.Error) require.Empty(t, trace1.RevertReason) + require.Equal(t, "0x0", trace1.Gas) + require.Equal(t, "0x0", trace1.GasUsed) require.Len(t, trace1.Calls, 1) trace2 := trace1.Calls[0] require.Equal(t, contractAddress, trace2.From) require.Equal(t, common.Address{0x1}, *trace2.To) - require.Equal(t, big.NewInt(1), trace2.Value) + require.Equal(t, "0x1", trace2.Value) require.Empty(t, trace2.Input) require.Empty(t, trace2.Error) require.Empty(t, trace2.RevertReason) + require.Contains(t, trace2.Gas, "0x") + require.Contains(t, trace2.GasUsed, "0x") } func TestRPCTraceBlock(t *testing.T) { @@ -681,39 +697,47 @@ func TestRPCTraceBlock(t *testing.T) { require.Equal(t, creatorAddress, trace1.From) require.Equal(t, contractAddress, *trace1.To) - require.Equal(t, big.NewInt(123), trace1.Value) + require.Equal(t, "0x7b", trace1.Value) expectedInput, err := contractABI.Pack("sendTo", common.Address{0x1}, big.NewInt(2)) require.NoError(t, err) require.Equal(t, expectedInput, trace1.Input) require.Empty(t, trace1.Error) require.Empty(t, trace1.RevertReason) + require.Equal(t, "0x0", trace1.Gas) + require.Equal(t, "0x0", trace1.GasUsed) require.Len(t, trace1.Calls, 1) innerCall1 := trace1.Calls[0] require.Equal(t, contractAddress, innerCall1.From) require.Equal(t, common.Address{0x1}, *innerCall1.To) - require.Equal(t, big.NewInt(2), innerCall1.Value) + require.Equal(t, "0x2", innerCall1.Value) require.Empty(t, innerCall1.Input) require.Empty(t, innerCall1.Error) require.Empty(t, innerCall1.RevertReason) + require.Contains(t, innerCall1.Gas, "0x") + require.Contains(t, innerCall1.GasUsed, "0x") require.Equal(t, creatorAddress2, trace2.From) require.Equal(t, contractAddress, *trace2.To) - require.Equal(t, big.NewInt(321), trace2.Value) + require.Equal(t, "0x141", trace2.Value) expectedInput, err = contractABI.Pack("sendTo", common.Address{0x2}, big.NewInt(3)) require.NoError(t, err) require.Equal(t, expectedInput, trace2.Input) require.Empty(t, trace2.Error) require.Empty(t, trace2.RevertReason) + require.Equal(t, "0x0", trace2.Gas) + require.Equal(t, "0x0", trace2.GasUsed) require.Len(t, trace2.Calls, 1) innerCall2 := trace2.Calls[0] require.Equal(t, contractAddress, innerCall2.From) require.Equal(t, common.Address{0x2}, *innerCall2.To) - require.Equal(t, big.NewInt(3), innerCall2.Value) + require.Equal(t, "0x3", innerCall2.Value) require.Empty(t, innerCall2.Input) require.Empty(t, innerCall2.Error) require.Empty(t, innerCall2.RevertReason) + require.Contains(t, innerCall2.Gas, "0x") + require.Contains(t, innerCall2.GasUsed, "0x") } func TestRPCBlockReceipt(t *testing.T) { diff --git a/packages/evm/jsonrpc/tracer_call.go b/packages/evm/jsonrpc/tracer_call.go index cd05d5a29c..881bf5908f 100644 --- a/packages/evm/jsonrpc/tracer_call.go +++ b/packages/evm/jsonrpc/tracer_call.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "math/big" + "strconv" "sync/atomic" "github.com/ethereum/go-ethereum/accounts/abi" @@ -35,8 +36,8 @@ type callLog struct { type CallFrame struct { Type vm.OpCode `json:"-"` From common.Address `json:"from"` - Gas uint64 `json:"gas"` - GasUsed uint64 `json:"gasUsed"` + Gas string `json:"gas"` + GasUsed string `json:"gasUsed"` To *common.Address `json:"to,omitempty" rlp:"optional"` Input []byte `json:"input" rlp:"optional"` Output []byte `json:"output,omitempty" rlp:"optional"` @@ -46,7 +47,7 @@ type CallFrame struct { Logs []callLog `json:"logs,omitempty" rlp:"optional"` // Placed at end on purpose. The RLP will be decoded to 0 instead of // nil if there are non-empty elements after in the struct. - Value *big.Int `json:"value,omitempty" rlp:"optional"` + Value string `json:"value,omitempty" rlp:"optional"` revertedSnapshot bool } @@ -161,19 +162,24 @@ func (t *callTracer) OnEnter(depth int, typ byte, from common.Address, to common toCopy := to call := CallFrame{ - Type: vm.OpCode(typ), - From: from, - To: &toCopy, - Input: common.CopyBytes(input), - Gas: gas, - Value: value, + Type: vm.OpCode(typ), + From: from, + To: &toCopy, + Input: common.CopyBytes(input), + Gas: intToHex(int64(gas)), + Value: intToHex(value.Int64()), + GasUsed: "0x0", } if depth == 0 { - call.Gas = t.gasLimit + call.Gas = intToHex(int64(t.gasLimit)) } t.callstack = append(t.callstack, call) } +func intToHex(i int64) string { + return "0x" + strconv.FormatInt(i, 16) +} + // OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { @@ -196,7 +202,7 @@ func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, t.callstack = t.callstack[:size-1] size-- - call.GasUsed = gasUsed + call.GasUsed = intToHex(int64(gasUsed)) call.processOutput(output, err, reverted) // Nest call into parent. t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) @@ -218,7 +224,7 @@ func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) { if err != nil { return } - t.callstack[0].GasUsed = receipt.GasUsed + t.callstack[0].GasUsed = intToHex(int64(receipt.GasUsed)) if t.config.WithLog { // Logs are not emitted when the call fails clearFailedLogs(&t.callstack[0], false) From 8ad70afd473af6230e13fa634bf67a25689e8933 Mon Sep 17 00:00:00 2001 From: vitaliy Date: Fri, 13 Sep 2024 14:40:02 +0300 Subject: [PATCH 2/4] Cleanup --- packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go index 0e308f0153..8d2b2892b5 100644 --- a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go +++ b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go @@ -6,10 +6,8 @@ package jsonrpctest import ( "context" "encoding/json" - "fmt" "math/big" "slices" - "strconv" "strings" "testing" "time" @@ -520,16 +518,6 @@ func TestRPCCustomError(t *testing.T) { require.EqualValues(t, 42, args[0]) } -func Test1(t *testing.T) { - i := 123 - - s := fmt.Sprintf("%x", i) - s2 := strconv.FormatInt(int64(i), 16) - - fmt.Println(s) - fmt.Println(s2) -} - func TestRPCTraceTx(t *testing.T) { env := newSoloTestEnv(t) creator, creatorAddress := env.soloChain.NewEthereumAccountWithL2Funds() From 5b371c706d6a44f1f6afb2112b526c1b0cf7b05a Mon Sep 17 00:00:00 2001 From: vitaliy Date: Tue, 17 Sep 2024 17:19:14 +0300 Subject: [PATCH 3/4] Add test for tracing EVM deposit --- .../evm/jsonrpc/jsonrpctest/jsonrpc_test.go | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go index 8d2b2892b5..e66a0e8978 100644 --- a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go +++ b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go @@ -607,6 +607,43 @@ func TestRPCTraceTx(t *testing.T) { require.Contains(t, trace2.GasUsed, "0x") } +func TestRPCTraceEvmDeposit(t *testing.T) { + env := newSoloTestEnv(t) + wallet, _ := env.solo.NewKeyPairWithFunds() + _, evmAddr := env.soloChain.NewEthereumAccountWithL2Funds() + + err := env.soloChain.TransferAllowanceTo( + isc.NewAssetsBaseTokens(1000), + isc.NewEthereumAddressAgentID(env.soloChain.ChainID, evmAddr), + wallet) + + block := env.BlockByNumber(nil) + require.NoError(t, err) + txs := block.Transactions() + tx := txs[0] + + require.Equal(t, evmAddr, *tx.To()) + + rc, err := env.TxReceipt(txs[0].Hash()) + require.NoError(t, err) + require.EqualValues(t, types.ReceiptStatusSuccessful, rc.Status) + + var res1 json.RawMessage + err = env.RawClient.CallContext( + context.Background(), + &res1, + "debug_traceTransaction", + tx.Hash().Hex(), + tracers.TraceConfig{TracerConfig: []byte(`{"tracer": "callTracer"}`)}, + ) + require.NoError(t, err) + + var trace1 []jsonrpc.CallFrame + err = json.Unmarshal(res1, &trace1) + require.NoError(t, err) + require.Len(t, trace1, 0) +} + func TestRPCTraceBlock(t *testing.T) { env := newSoloTestEnv(t) creator, creatorAddress := env.soloChain.NewEthereumAccountWithL2Funds() From 6125e4468647055b51b631712590ee4feb7de94b Mon Sep 17 00:00:00 2001 From: vitaliy Date: Tue, 17 Sep 2024 18:26:11 +0300 Subject: [PATCH 4/4] Fix trace field types --- .../evm/jsonrpc/jsonrpctest/jsonrpc_test.go | 36 +++++++++---------- packages/evm/jsonrpc/tracer_call.go | 30 +++++++--------- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go index e66a0e8978..9ad48a584c 100644 --- a/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go +++ b/packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go @@ -586,25 +586,25 @@ func TestRPCTraceTx(t *testing.T) { require.Equal(t, creatorAddress, trace1.From) require.Equal(t, contractAddress, *trace1.To) - require.Equal(t, "0x7b", trace1.Value) + require.Equal(t, "0x7b", trace1.Value.String()) expectedInput, err := contractABI.Pack("sendTo", common.Address{0x1}, big.NewInt(1)) require.NoError(t, err) require.Equal(t, expectedInput, trace1.Input) require.Empty(t, trace1.Error) require.Empty(t, trace1.RevertReason) - require.Equal(t, "0x0", trace1.Gas) - require.Equal(t, "0x0", trace1.GasUsed) + require.Equal(t, "0x0", trace1.Gas.String()) + require.Equal(t, "0x0", trace1.GasUsed.String()) require.Len(t, trace1.Calls, 1) trace2 := trace1.Calls[0] require.Equal(t, contractAddress, trace2.From) require.Equal(t, common.Address{0x1}, *trace2.To) - require.Equal(t, "0x1", trace2.Value) + require.Equal(t, "0x1", trace2.Value.String()) require.Empty(t, trace2.Input) require.Empty(t, trace2.Error) require.Empty(t, trace2.RevertReason) - require.Contains(t, trace2.Gas, "0x") - require.Contains(t, trace2.GasUsed, "0x") + require.Contains(t, trace2.Gas.String(), "0x") + require.Contains(t, trace2.GasUsed.String(), "0x") } func TestRPCTraceEvmDeposit(t *testing.T) { @@ -722,47 +722,47 @@ func TestRPCTraceBlock(t *testing.T) { require.Equal(t, creatorAddress, trace1.From) require.Equal(t, contractAddress, *trace1.To) - require.Equal(t, "0x7b", trace1.Value) + require.Equal(t, "0x7b", trace1.Value.String()) expectedInput, err := contractABI.Pack("sendTo", common.Address{0x1}, big.NewInt(2)) require.NoError(t, err) require.Equal(t, expectedInput, trace1.Input) require.Empty(t, trace1.Error) require.Empty(t, trace1.RevertReason) - require.Equal(t, "0x0", trace1.Gas) - require.Equal(t, "0x0", trace1.GasUsed) + require.Equal(t, "0x0", trace1.Gas.String()) + require.Equal(t, "0x0", trace1.GasUsed.String()) require.Len(t, trace1.Calls, 1) innerCall1 := trace1.Calls[0] require.Equal(t, contractAddress, innerCall1.From) require.Equal(t, common.Address{0x1}, *innerCall1.To) - require.Equal(t, "0x2", innerCall1.Value) + require.Equal(t, "0x2", innerCall1.Value.String()) require.Empty(t, innerCall1.Input) require.Empty(t, innerCall1.Error) require.Empty(t, innerCall1.RevertReason) - require.Contains(t, innerCall1.Gas, "0x") - require.Contains(t, innerCall1.GasUsed, "0x") + require.Contains(t, innerCall1.Gas.String(), "0x") + require.Contains(t, innerCall1.GasUsed.String(), "0x") require.Equal(t, creatorAddress2, trace2.From) require.Equal(t, contractAddress, *trace2.To) - require.Equal(t, "0x141", trace2.Value) + require.Equal(t, "0x141", trace2.Value.String()) expectedInput, err = contractABI.Pack("sendTo", common.Address{0x2}, big.NewInt(3)) require.NoError(t, err) require.Equal(t, expectedInput, trace2.Input) require.Empty(t, trace2.Error) require.Empty(t, trace2.RevertReason) - require.Equal(t, "0x0", trace2.Gas) - require.Equal(t, "0x0", trace2.GasUsed) + require.Equal(t, "0x0", trace2.Gas.String()) + require.Equal(t, "0x0", trace2.GasUsed.String()) require.Len(t, trace2.Calls, 1) innerCall2 := trace2.Calls[0] require.Equal(t, contractAddress, innerCall2.From) require.Equal(t, common.Address{0x2}, *innerCall2.To) - require.Equal(t, "0x3", innerCall2.Value) + require.Equal(t, "0x3", innerCall2.Value.String()) require.Empty(t, innerCall2.Input) require.Empty(t, innerCall2.Error) require.Empty(t, innerCall2.RevertReason) - require.Contains(t, innerCall2.Gas, "0x") - require.Contains(t, innerCall2.GasUsed, "0x") + require.Contains(t, innerCall2.Gas.String(), "0x") + require.Contains(t, innerCall2.GasUsed.String(), "0x") } func TestRPCBlockReceipt(t *testing.T) { diff --git a/packages/evm/jsonrpc/tracer_call.go b/packages/evm/jsonrpc/tracer_call.go index 881bf5908f..03acf914f1 100644 --- a/packages/evm/jsonrpc/tracer_call.go +++ b/packages/evm/jsonrpc/tracer_call.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "math/big" - "strconv" "sync/atomic" "github.com/ethereum/go-ethereum/accounts/abi" @@ -36,8 +35,8 @@ type callLog struct { type CallFrame struct { Type vm.OpCode `json:"-"` From common.Address `json:"from"` - Gas string `json:"gas"` - GasUsed string `json:"gasUsed"` + Gas hexutil.Uint64 `json:"gas"` + GasUsed hexutil.Uint64 `json:"gasUsed"` To *common.Address `json:"to,omitempty" rlp:"optional"` Input []byte `json:"input" rlp:"optional"` Output []byte `json:"output,omitempty" rlp:"optional"` @@ -47,7 +46,7 @@ type CallFrame struct { Logs []callLog `json:"logs,omitempty" rlp:"optional"` // Placed at end on purpose. The RLP will be decoded to 0 instead of // nil if there are non-empty elements after in the struct. - Value string `json:"value,omitempty" rlp:"optional"` + Value hexutil.Big `json:"value,omitempty" rlp:"optional"` revertedSnapshot bool } @@ -162,24 +161,19 @@ func (t *callTracer) OnEnter(depth int, typ byte, from common.Address, to common toCopy := to call := CallFrame{ - Type: vm.OpCode(typ), - From: from, - To: &toCopy, - Input: common.CopyBytes(input), - Gas: intToHex(int64(gas)), - Value: intToHex(value.Int64()), - GasUsed: "0x0", + Type: vm.OpCode(typ), + From: from, + To: &toCopy, + Input: common.CopyBytes(input), + Gas: hexutil.Uint64(gas), + Value: hexutil.Big(*value), } if depth == 0 { - call.Gas = intToHex(int64(t.gasLimit)) + call.Gas = hexutil.Uint64(t.gasLimit) } t.callstack = append(t.callstack, call) } -func intToHex(i int64) string { - return "0x" + strconv.FormatInt(i, 16) -} - // OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { @@ -202,7 +196,7 @@ func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, t.callstack = t.callstack[:size-1] size-- - call.GasUsed = intToHex(int64(gasUsed)) + call.GasUsed = hexutil.Uint64(gasUsed) call.processOutput(output, err, reverted) // Nest call into parent. t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) @@ -224,7 +218,7 @@ func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) { if err != nil { return } - t.callstack[0].GasUsed = intToHex(int64(receipt.GasUsed)) + t.callstack[0].GasUsed = hexutil.Uint64(receipt.GasUsed) if t.config.WithLog { // Logs are not emitted when the call fails clearFailedLogs(&t.callstack[0], false)