From 08f3166868b91bfad4e63638013c4b352b776990 Mon Sep 17 00:00:00 2001 From: Dragan Milic Date: Tue, 23 Apr 2024 10:55:42 +0200 Subject: [PATCH 1/5] eth/tracers/native: fix flatCallTracer Stop() bug --- eth/tracers/native/call_flat.go | 1 + eth/tracers/native/call_flat_test.go | 45 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 eth/tracers/native/call_flat_test.go diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index f8d38ddd2d5b..fd5f820af1db 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -224,6 +224,7 @@ func (t *flatCallTracer) GetResult() (json.RawMessage, error) { // Stop terminates execution of the tracer at the first opportune moment. func (t *flatCallTracer) Stop(err error) { + t.reason = err t.tracer.Stop(err) } diff --git a/eth/tracers/native/call_flat_test.go b/eth/tracers/native/call_flat_test.go new file mode 100644 index 000000000000..c13ad4e34926 --- /dev/null +++ b/eth/tracers/native/call_flat_test.go @@ -0,0 +1,45 @@ +package native_test + +import ( + "encoding/json" + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" +) + +func TestCallFlatStop(t *testing.T) { + + ctx := &tracers.Context{} + + tracer, err := tracers.DefaultDirectory.New("flatCallTracer", ctx, json.RawMessage(`{}`)) + require.NoError(t, err) + + // this error should be returned by GetResult + stopError := errors.New("stop error") + + // simulate a transaction + tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil) + + tracer.OnTxStart(&tracing.VMContext{ + ChainConfig: params.MainnetChainConfig, + }, tx, common.Address{}) + + tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, nil, 0, big.NewInt(0)) + + // stop before the transaction is finished + tracer.Stop(stopError) + + tracer.OnTxEnd(&types.Receipt{GasUsed: 0}, nil) + + // check that the error is returned by GetResult + _, tracerError := tracer.GetResult() + require.Equal(t, stopError, tracerError) +} From 9fb41e990559292e79cd231212a31d4123d6fd9d Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 23 Apr 2024 13:50:15 +0200 Subject: [PATCH 2/5] handle interrupt --- eth/tracers/native/call_flat.go | 13 ++++++++++++- eth/tracers/native/call_flat_test.go | 15 +++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index fd5f820af1db..68cdb3749c93 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -23,6 +23,7 @@ import ( "math/big" "slices" "strings" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -115,6 +116,7 @@ type flatCallTracer struct { config flatCallTracerConfig ctx *tracers.Context // Holds tracer context data reason error // Textual reason for the interruption + interrupt atomic.Bool // Atomic flag to signal execution interruption activePrecompiles []common.Address // Updated on tx start based on given rules } @@ -169,6 +171,9 @@ func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to co // OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if t.interrupt.Load() { + return + } t.tracer.OnExit(depth, output, gasUsed, err, reverted) if depth == 0 { @@ -194,6 +199,9 @@ func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err er } func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { + if t.interrupt.Load() { + return + } t.tracer.OnTxStart(env, tx, from) // Update list of precompiles based on current block rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) @@ -201,6 +209,9 @@ func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction } func (t *flatCallTracer) OnTxEnd(receipt *types.Receipt, err error) { + if t.interrupt.Load() { + return + } t.tracer.OnTxEnd(receipt, err) } @@ -225,7 +236,7 @@ func (t *flatCallTracer) GetResult() (json.RawMessage, error) { // Stop terminates execution of the tracer at the first opportune moment. func (t *flatCallTracer) Stop(err error) { t.reason = err - t.tracer.Stop(err) + t.interrupt.Store(true) } // isPrecompiled returns whether the addr is a precompile. diff --git a/eth/tracers/native/call_flat_test.go b/eth/tracers/native/call_flat_test.go index c13ad4e34926..94d360877cca 100644 --- a/eth/tracers/native/call_flat_test.go +++ b/eth/tracers/native/call_flat_test.go @@ -1,7 +1,6 @@ package native_test import ( - "encoding/json" "errors" "math/big" "testing" @@ -16,17 +15,21 @@ import ( ) func TestCallFlatStop(t *testing.T) { - - ctx := &tracers.Context{} - - tracer, err := tracers.DefaultDirectory.New("flatCallTracer", ctx, json.RawMessage(`{}`)) + tracer, err := tracers.DefaultDirectory.New("flatCallTracer", &tracers.Context{}, nil) require.NoError(t, err) // this error should be returned by GetResult stopError := errors.New("stop error") // simulate a transaction - tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil) + tx := types.NewTx(&types.LegacyTx{ + Nonce: 0, + To: &common.Address{}, + Value: big.NewInt(0), + Gas: 0, + GasPrice: big.NewInt(0), + Data: nil, + }) tracer.OnTxStart(&tracing.VMContext{ ChainConfig: params.MainnetChainConfig, From a7d40e70e55a4ca1d8d10b062e01013d023d95bd Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 23 Apr 2024 14:00:46 +0200 Subject: [PATCH 3/5] forgot OnEnter --- eth/tracers/native/call_flat.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 68cdb3749c93..fd9b983d36ec 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -156,6 +156,9 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Trac // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if t.interrupt.Load() { + return + } t.tracer.OnEnter(depth, typ, from, to, input, gas, value) if depth == 0 { From 36ec4940e83653b574d4cbcf8260e34fc397b492 Mon Sep 17 00:00:00 2001 From: Dragan Milic Date: Tue, 23 Apr 2024 14:31:56 +0200 Subject: [PATCH 4/5] eth/tracers/native/call_flat_test.go: add license --- eth/tracers/native/call_flat_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/eth/tracers/native/call_flat_test.go b/eth/tracers/native/call_flat_test.go index 94d360877cca..d5481b868bcc 100644 --- a/eth/tracers/native/call_flat_test.go +++ b/eth/tracers/native/call_flat_test.go @@ -1,3 +1,19 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + package native_test import ( From 07f9406b459450f3f052e562239a77349e261c06 Mon Sep 17 00:00:00 2001 From: Dragan Milic Date: Tue, 23 Apr 2024 14:41:55 +0200 Subject: [PATCH 5/5] eth/tracers/native/call_flat.go: delegate reason to the wrapped call tracer --- eth/tracers/native/call_flat.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index fd9b983d36ec..ce0fb081143e 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -115,7 +115,6 @@ type flatCallTracer struct { tracer *callTracer config flatCallTracerConfig ctx *tracers.Context // Holds tracer context data - reason error // Textual reason for the interruption interrupt atomic.Bool // Atomic flag to signal execution interruption activePrecompiles []common.Address // Updated on tx start based on given rules } @@ -233,12 +232,12 @@ func (t *flatCallTracer) GetResult() (json.RawMessage, error) { if err != nil { return nil, err } - return res, t.reason + return res, t.tracer.reason } // Stop terminates execution of the tracer at the first opportune moment. func (t *flatCallTracer) Stop(err error) { - t.reason = err + t.tracer.Stop(err) t.interrupt.Store(true) }