From 916d6a441a866cb618ae826c220866de118899f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 14 Nov 2023 15:02:24 +0300 Subject: [PATCH 01/50] params: release Geth v1.15.5 --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index bcab461a4358..5fb9631f1b3b 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 1 // Major version component of the current release - VersionMinor = 13 // Minor version component of the current release - VersionPatch = 5 // Patch version component of the current release - VersionMeta = "unstable" // Version metadata to append to the version string + VersionMajor = 1 // Major version component of the current release + VersionMinor = 13 // Minor version component of the current release + VersionPatch = 5 // Patch version component of the current release + VersionMeta = "stable" // Version metadata to append to the version string ) // Version holds the textual version string. From 7f8926e716cbee8d5c679a82025bd035defb6427 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Tue, 6 Feb 2024 13:06:08 -0500 Subject: [PATCH 02/50] Backporting `extended-tracer` branch We backported branch `extended-tracer` at commit 3078dfe80be0f4cb3eacb439810e69975eb75148 back to v1.13.5 of Geth. To achieve the backport, we did: - git checkout -b extended-tracer-squashed - git reset --hard 3078dfe80be0f4cb3eacb439810e69975eb75148 - git reset --mixed fc380f52ef9778e988266f776b9593ce719cf79d # This was the latest merged commit not from `extended-tracer` branch - git commit -A -m "" - git checkout v1.13.5 - git checkout -b extended-tracer-backport-v1.13.5 - git cherry-pick extended-tracer-squashed --- accounts/abi/bind/backends/simulated.go | 2 +- cmd/evm/internal/t8ntool/execution.go | 19 +- cmd/evm/internal/t8ntool/flags.go | 8 + cmd/evm/internal/t8ntool/tracewriter.go | 92 +++++++++ cmd/evm/internal/t8ntool/transition.go | 19 +- cmd/geth/chaincmd.go | 1 + cmd/geth/config.go | 13 ++ cmd/geth/main.go | 2 + cmd/utils/flags.go | 17 +- consensus/beacon/consensus.go | 6 +- consensus/ethash/consensus.go | 6 +- consensus/misc/dao.go | 4 +- core/blockchain.go | 193 ++++++++++++------ core/blockchain_test.go | 7 +- core/chain_makers.go | 3 +- core/evm.go | 5 +- core/genesis.go | 40 +++- core/state/metadata.go | 66 ++++++ core/state/state_object.go | 22 +- core/state/state_test.go | 14 +- core/state/statedb.go | 69 ++++++- core/state/statedb_fuzz_test.go | 2 +- core/state/statedb_test.go | 54 ++--- core/state/sync_test.go | 2 +- core/state/trie_prefetcher_test.go | 6 +- core/state_processor.go | 31 ++- core/state_transition.go | 34 ++- core/txpool/blobpool/blobpool_test.go | 34 +-- core/txpool/legacypool/legacypool2_test.go | 12 +- core/txpool/legacypool/legacypool_test.go | 12 +- core/vm/contract.go | 5 +- core/vm/contracts.go | 5 +- core/vm/contracts_test.go | 8 +- core/vm/errors.go | 123 +++++++++++ core/vm/evm.go | 176 +++++++++------- core/vm/instructions.go | 49 ++++- core/vm/interface.go | 5 +- core/vm/interpreter.go | 17 +- core/vm/logger.go | 84 +++++++- core/vm/operations_acl.go | 2 +- core/vm/runtime/runtime.go | 9 + core/vm/runtime/runtime_test.go | 12 +- eth/api_debug_test.go | 2 +- eth/backend.go | 1 + eth/ethconfig/config.go | 4 + eth/tracers/api.go | 38 ++-- eth/tracers/api_test.go | 1 - eth/tracers/directory/live.go | 30 +++ eth/tracers/{native => directory}/noop.go | 55 +++-- eth/tracers/{ => directory}/tracers.go | 40 +--- eth/tracers/directory/util.go | 47 +++++ eth/tracers/directory/util_test.go | 60 ++++++ eth/tracers/internal/tracetest/README.md | 10 + .../internal/tracetest/calltrace_test.go | 75 ++++--- .../internal/tracetest/flat_calltrace_test.go | 16 +- eth/tracers/internal/tracetest/makeTest.js | 48 +++++ .../internal/tracetest/prestate_test.go | 11 +- .../testdata/call_tracer/inner_instafail.json | 12 +- .../callcode_precompiled_fail_hide.json | 17 +- .../call_tracer_flat/inner_instafail.json | 16 +- .../nested_create_inerror.json | 15 +- .../call_tracer_flat/selfdestruct.json | 17 +- .../skip_no_balance_error.json | 15 +- .../frontier_create_outofstorage.json | 189 +++++++++++++++++ .../prestate_tracer/create_create.json | 62 ++++++ eth/tracers/internal/tracetest/util.go | 53 ----- eth/tracers/js/goja.go | 90 +++++--- eth/tracers/js/tracer_test.go | 37 ++-- eth/tracers/live/printer.go | 134 ++++++++++++ eth/tracers/logger/access_list_tracer.go | 21 +- eth/tracers/logger/logger.go | 61 ++---- eth/tracers/logger/logger_json.go | 19 +- eth/tracers/logger/logger_test.go | 3 +- eth/tracers/native/4byte.go | 21 +- eth/tracers/native/call.go | 114 +++++------ eth/tracers/native/call_flat.go | 46 +++-- eth/tracers/native/mux.go | 84 ++++++-- eth/tracers/native/prestate.go | 160 ++++++--------- eth/tracers/tracers_test.go | 38 ---- internal/ethapi/api.go | 14 +- light/odr_test.go | 2 +- miner/worker.go | 4 +- tests/state_test_util.go | 4 +- 83 files changed, 2137 insertions(+), 839 deletions(-) create mode 100644 cmd/evm/internal/t8ntool/tracewriter.go create mode 100644 core/state/metadata.go create mode 100644 eth/tracers/directory/live.go rename eth/tracers/{native => directory}/noop.go (50%) rename eth/tracers/{ => directory}/tracers.go (77%) create mode 100644 eth/tracers/directory/util.go create mode 100644 eth/tracers/directory/util_test.go create mode 100644 eth/tracers/internal/tracetest/README.md create mode 100644 eth/tracers/internal/tracetest/makeTest.js create mode 100644 eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json create mode 100644 eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json create mode 100644 eth/tracers/live/printer.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index a26ee12e0ad1..3bb0e8651b91 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -681,7 +681,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // Set infinite balance to the fake caller account. from := stateDB.GetOrNewStateObject(call.From) - from.SetBalance(math.MaxBig256) + from.SetBalance(math.MaxBig256, state.BalanceChangeUnspecified) // Execute the call. msg := &core.Message{ diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 5cac5f07f886..5b9a2e417c91 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -187,7 +187,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil { evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) - core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb) + core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb, nil) } var blobGasUsed uint64 @@ -234,6 +234,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, ) evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) + if tracer != nil { + tracer.CaptureTxStart(evm, tx, msg.From) + } // (ret []byte, usedGas uint64, failed bool, err error) msgResult, err := core.ApplyMessage(evm, msg, gaspool) if err != nil { @@ -241,6 +244,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) gaspool.SetGas(prevGas) + if tracer != nil { + tracer.CaptureTxEnd(nil, err) + } continue } includedTxs = append(includedTxs, tx) @@ -282,6 +288,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, //receipt.BlockNumber receipt.TransactionIndex = uint(txIndex) receipts = append(receipts, receipt) + if tracer != nil { + tracer.CaptureTxEnd(receipt, nil) + } } txIndex++ @@ -307,15 +316,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) reward.Mul(reward, blockReward) reward.Div(reward, big.NewInt(8)) - statedb.AddBalance(ommer.Address, reward) + statedb.AddBalance(ommer.Address, reward, state.BalanceIncreaseRewardMineUncle) } - statedb.AddBalance(pre.Env.Coinbase, minerReward) + statedb.AddBalance(pre.Env.Coinbase, minerReward, state.BalanceIncreaseRewardMineBlock) } // Apply withdrawals for _, w := range pre.Env.Withdrawals { // Amount is in gwei, turn into wei amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) - statedb.AddBalance(w.Address, amount) + statedb.AddBalance(w.Address, amount, state.BalanceIncreaseWithdrawal) } // Commit block root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber)) @@ -358,7 +367,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance) + statedb.SetBalance(addr, a.Balance, state.BalanceIncreaseGenesisBalance) for k, v := range a.Storage { statedb.SetState(addr, k, v) } diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go index de19dbc851ef..16c9cd2e891d 100644 --- a/cmd/evm/internal/t8ntool/flags.go +++ b/cmd/evm/internal/t8ntool/flags.go @@ -30,6 +30,14 @@ var ( Name: "trace", Usage: "Output full trace logs to files .jsonl", } + TraceTracerFlag = &cli.StringFlag{ + Name: "trace.tracer", + Usage: "Configures the use of a custom tracer, e.g native or js tracers. Examples are callTracer and 4byteTracer. These tracers emit results into files as trace--.json", + } + TraceTracerConfigFlag = &cli.StringFlag{ + Name: "trace.jsonconfig", + Usage: "The configurations for the custom tracer specified by --trace.tracer. If provided, must be in JSON format", + } TraceDisableMemoryFlag = &cli.BoolFlag{ Name: "trace.nomemory", Value: true, diff --git a/cmd/evm/internal/t8ntool/tracewriter.go b/cmd/evm/internal/t8ntool/tracewriter.go new file mode 100644 index 000000000000..8a850f1d9812 --- /dev/null +++ b/cmd/evm/internal/t8ntool/tracewriter.go @@ -0,0 +1,92 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package t8ntool + +import ( + "encoding/json" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/log" +) + +// traceWriter is an vm.EVMLogger which also holds an inner logger/tracer. +// When the TxEnd event happens, the inner tracer result is written to the file, and +// the file is closed. +type traceWriter struct { + inner vm.EVMLogger + f io.WriteCloser +} + +// Compile-time interface check +var _ = vm.EVMLogger((*traceWriter)(nil)) + +func (t *traceWriter) CaptureTxEnd(receipt *types.Receipt, err error) { + t.inner.CaptureTxEnd(receipt, err) + defer t.f.Close() + + if tracer, ok := t.inner.(directory.Tracer); ok { + result, err := tracer.GetResult() + if err != nil { + log.Warn("Error in tracer", "err", err) + return + } + err = json.NewEncoder(t.f).Encode(result) + if err != nil { + log.Warn("Error writing tracer output", "err", err) + return + } + } +} + +func (t *traceWriter) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + t.inner.CaptureTxStart(env, tx, from) +} +func (t *traceWriter) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + t.inner.CaptureStart(from, to, create, input, gas, value) +} + +func (t *traceWriter) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { + t.inner.CaptureEnd(output, gasUsed, err, reverted) +} + +func (t *traceWriter) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + t.inner.CaptureEnter(typ, from, to, input, gas, value) +} + +func (t *traceWriter) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { + t.inner.CaptureExit(output, gasUsed, err, reverted) +} + +func (t *traceWriter) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + t.inner.CaptureState(pc, op, gas, cost, scope, rData, depth, err) +} +func (t *traceWriter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { + t.inner.CaptureFault(pc, op, gas, cost, scope, depth, err) +} + +func (t *traceWriter) CaptureKeccakPreimage(hash common.Hash, data []byte) { + t.inner.CaptureKeccakPreimage(hash, data) +} + +func (t *traceWriter) OnGasChange(old, new uint64, reason vm.GasChangeReason) { + t.inner.OnGasChange(old, new, reason) +} diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index d517592e5cfb..e41ff313e0a7 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -133,9 +134,21 @@ func Transition(ctx *cli.Context) error { prevFile = traceFile return logger.NewJSONLogger(logConfig, traceFile), nil } - } else { - getTracer = func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error) { - return nil, nil + } else if ctx.IsSet(TraceTracerFlag.Name) { + var config json.RawMessage + if ctx.IsSet(TraceTracerConfigFlag.Name) { + config = []byte(ctx.String(TraceTracerConfigFlag.Name)) + } + getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) { + traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String()))) + if err != nil { + return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) + } + tracer, err := directory.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config) + if err != nil { + return nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err)) + } + return &traceWriter{tracer, traceFile}, nil } } // We need to load three things: alloc, env and transactions. May be either in diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 5663963e3cf4..2627c6caf369 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -97,6 +97,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, utils.TxLookupLimitFlag, + utils.VMTraceFlag, utils.TransactionHistoryFlag, utils.StateHistoryFlag, }, utils.DatabaseFlags), diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 027dac7bd60a..fa1ec6f279da 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -37,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/version" @@ -178,6 +179,18 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { v := ctx.Uint64(utils.OverrideVerkle.Name) cfg.Eth.OverrideVerkle = &v } + + if ctx.IsSet(utils.VMTraceFlag.Name) { + if name := ctx.String(utils.VMTraceFlag.Name); name != "" { + t, err := directory.LiveDirectory.New(name) + if err != nil { + utils.Fatalf("Failed to create tracer %q: %v", name, err) + } + + cfg.Eth.VMTracer = t + } + } + backend, eth := utils.RegisterEthService(stack, &cfg.Eth) // Create gauge with geth system and build information diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 2d4fe3dc060c..7aee35753dfd 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -43,6 +43,7 @@ import ( // Force-load the tracer engines to trigger registration _ "github.com/ethereum/go-ethereum/eth/tracers/js" + _ "github.com/ethereum/go-ethereum/eth/tracers/live" _ "github.com/ethereum/go-ethereum/eth/tracers/native" "github.com/urfave/cli/v2" @@ -136,6 +137,7 @@ var ( utils.DeveloperGasLimitFlag, utils.DeveloperPeriodFlag, utils.VMEnableDebugFlag, + utils.VMTraceFlag, utils.NetworkIdFlag, utils.EthStatsURLFlag, utils.NoCompactionFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e9a7c7c110f9..6878a46e3912 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -51,6 +51,7 @@ import ( "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/remotedb" "github.com/ethereum/go-ethereum/ethstats" @@ -532,6 +533,11 @@ var ( Usage: "Record information useful for VM and contract debugging", Category: flags.VMCategory, } + VMTraceFlag = &cli.StringFlag{ + Name: "vmtrace", + Usage: "Name of tracer which should record internal VM operations (costly)", + Category: flags.VMCategory, + } // API options. RPCGlobalGasCapFlag = &cli.Uint64Flag{ @@ -2186,12 +2192,21 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh cache.TrieDirtyLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100 } vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} - + if ctx.IsSet(VMTraceFlag.Name) { + if name := ctx.String(VMTraceFlag.Name); name != "" { + t, err := directory.LiveDirectory.New(name) + if err != nil { + Fatalf("Failed to create tracer %q: %v", name, err) + } + vmcfg.Tracer = t + } + } // Disable transaction indexing/unindexing by default. chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil) if err != nil { Fatalf("Can't create BlockChain: %v", err) } + return chain, chainDb } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index e856f4e6cead..9ab9026fa8e2 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -347,9 +347,9 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine and processes withdrawals on top. -func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, stateDB *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { if !beacon.IsPoSHeader(header) { - beacon.ethone.Finalize(chain, header, state, txs, uncles, nil) + beacon.ethone.Finalize(chain, header, stateDB, txs, uncles, nil) return } // Withdrawals processing. @@ -357,7 +357,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // Convert amount from gwei to wei. amount := new(big.Int).SetUint64(w.Amount) amount = amount.Mul(amount, big.NewInt(params.GWei)) - state.AddBalance(w.Address, amount) + stateDB.AddBalance(w.Address, amount, state.BalanceIncreaseWithdrawal) } // No block reward which is issued by consensus layer instead. } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 8eb9863da1e2..5c00e5c7bcf3 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -547,7 +547,7 @@ var ( // AccumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. -func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { +func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, header *types.Header, uncles []*types.Header) { // Select the correct block reward based on chain progression blockReward := FrontierBlockReward if config.IsByzantium(header.Number) { @@ -564,10 +564,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, big8) - state.AddBalance(uncle.Coinbase, r) + stateDB.AddBalance(uncle.Coinbase, r, state.BalanceIncreaseRewardMineUncle) r.Div(blockReward, big32) reward.Add(reward, r) } - state.AddBalance(header.Coinbase, reward) + stateDB.AddBalance(header.Coinbase, reward, state.BalanceIncreaseRewardMineBlock) } diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go index 96995616de56..bc393a9cbced 100644 --- a/consensus/misc/dao.go +++ b/consensus/misc/dao.go @@ -80,7 +80,7 @@ func ApplyDAOHardFork(statedb *state.StateDB) { // Move every DAO account and extra-balance account funds into the refund contract for _, addr := range params.DAODrainList() { - statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr)) - statedb.SetBalance(addr, new(big.Int)) + statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), state.BalanceIncreaseDaoContract) + statedb.SetBalance(addr, new(big.Int), state.BalanceDecreaseDaoAccount) } } diff --git a/core/blockchain.go b/core/blockchain.go index f458da82573e..f9278d190888 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -185,6 +185,20 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig { return &config } +// BlockchainLogger is used to collect traces during chain processing. +// Please make a copy of the referenced types if you intend to retain them. +type BlockchainLogger interface { + vm.EVMLogger + state.StateLogger + // OnBlockStart is called before executing `block`. + // `td` is the total difficulty prior to `block`. + OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header, chainConfig *params.ChainConfig) + OnBlockEnd(err error) + OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc) + OnBeaconBlockRootStart(root common.Hash) + OnBeaconBlockRootEnd() +} + // BlockChain represents the canonical chain given a database with a genesis // block. The Blockchain manages chain imports, reverts, chain reorganisations. // @@ -258,6 +272,7 @@ type BlockChain struct { processor Processor // Block transaction processor interface forker *ForkChoice vmConfig vm.Config + logger BlockchainLogger } // NewBlockChain returns a fully initialised block chain using information @@ -269,7 +284,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } // Open trie database with provided config triedb := trie.NewDatabase(db, cacheConfig.triedbConfig()) - + var logger BlockchainLogger + if vmConfig.Tracer != nil { + l, ok := vmConfig.Tracer.(BlockchainLogger) + if !ok { + return nil, errors.New("only extended tracers are supported for live mode") + } + logger = l + } // Setup the genesis block, commit the provided genesis specification // to database if the genesis block is not present yet, or load the // stored one from database. @@ -301,6 +323,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks), engine: engine, vmConfig: vmConfig, + logger: logger, } bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit)) bc.forker = NewForkChoice(bc, shouldPreserve) @@ -427,6 +450,21 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } } + if bc.logger != nil { + if block := bc.CurrentBlock(); block.Number.Uint64() == 0 { + alloc, err := getGenesisState(bc.db, block.Hash()) + if err != nil { + return nil, fmt.Errorf("failed to get genesis state: %w", err) + } + + if alloc == nil { + return nil, fmt.Errorf("live blockchain tracer requires genesis alloc to be set") + } + + bc.logger.OnGenesisBlock(bc.genesisBlock, alloc) + } + } + // Load any existing snapshot, regenerating it if loading failed if bc.cacheConfig.SnapshotLimit > 0 { // If the chain was rewound past the snapshot persistent layer (causing @@ -1765,6 +1803,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) return it.index, err } stats.processed++ + if bc.logger != nil { + bc.logger.OnBlockStart(block, bc.GetTd(block.ParentHash(), block.NumberU64()-1), bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), bc.chainConfig) + bc.logger.OnBlockEnd(nil) + } // We can assume that logs are empty here, since the only way for consecutive // Clique blocks to have the same state is if there are no transactions. @@ -1782,6 +1824,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) if err != nil { return it.index, err } + statedb.SetLogger(bc.logger) // Enable prefetching to pull in trie node paths while processing transactions statedb.StartPrefetcher("chain") @@ -1795,7 +1838,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps) go func(start time.Time, followup *types.Block, throwaway *state.StateDB) { - bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt) + vmCfg := bc.vmConfig + vmCfg.Tracer = nil + bc.prefetcher.Prefetch(followup, throwaway, vmCfg, &followupInterrupt) blockPrefetchExecuteTimer.Update(time.Since(start)) if followupInterrupt.Load() { @@ -1805,68 +1850,15 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) } } - // Process block using the parent state as reference point - pstart := time.Now() - receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) - if err != nil { - bc.reportBlock(block, receipts, err) - followupInterrupt.Store(true) - return it.index, err - } - ptime := time.Since(pstart) - - vstart := time.Now() - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { - bc.reportBlock(block, receipts, err) - followupInterrupt.Store(true) - return it.index, err - } - vtime := time.Since(vstart) - proctime := time.Since(start) // processing + validation - - // Update the metrics touched during block processing and validation - accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing) - storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing) - snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing) - snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing) - accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) - storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) - accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) - storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation) - triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing - trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update - trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read - trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read - blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing - blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation - - // Write the block to the chain and get the status. - var ( - wstart = time.Now() - status WriteStatus - ) - if !setHead { - // Don't set the head, only insert the block - err = bc.writeBlockWithState(block, receipts, statedb) - } else { - status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false) - } + // The traced section of block import. + res, err := bc.processBlock(block, statedb, start, setHead) followupInterrupt.Store(true) if err != nil { return it.index, err } - // Update the metrics touched during block commit - accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them - storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them - snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them - triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them - - blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits) - blockInsertTimer.UpdateSince(start) - // Report the import stats before returning the various results stats.processed++ - stats.usedGas += usedGas + stats.usedGas += res.usedGas var snapDiffItems, snapBufItems common.StorageSize if bc.snaps != nil { @@ -1878,11 +1870,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) if !setHead { // After merge we expect few side chains. Simply count // all blocks the CL gives us for GC processing time - bc.gcproc += proctime - + bc.gcproc += res.procTime return it.index, nil // Direct block insertion of a single block } - switch status { + switch res.status { case CanonStatTy: log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), @@ -1892,7 +1883,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) lastCanon = block // Only count canonical blocks for GC processing time - bc.gcproc += proctime + bc.gcproc += res.procTime case SideStatTy: log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), @@ -1929,6 +1920,84 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) return it.index, err } +// blockProcessingResult is a summary of block processing +// used for updating the stats. +type blockProcessingResult struct { + usedGas uint64 + procTime time.Duration + status WriteStatus +} + +// processBlock executes and validates the given block. If there was no error +// it writes the block and associated state to database. +func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { + if bc.logger != nil { + td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) + bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), bc.chainConfig) + defer func() { + bc.logger.OnBlockEnd(blockEndErr) + }() + } + + // Process block using the parent state as reference point + pstart := time.Now() + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) + if err != nil { + bc.reportBlock(block, receipts, err) + return nil, err + } + ptime := time.Since(pstart) + + vstart := time.Now() + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { + bc.reportBlock(block, receipts, err) + return nil, err + } + vtime := time.Since(vstart) + proctime := time.Since(start) // processing + validation + + // Update the metrics touched during block processing and validation + accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing) + storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing) + snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing) + snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing) + accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation) + storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation) + accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation) + storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation) + triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing + trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update + trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read + trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read + blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing + blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation + + // Write the block to the chain and get the status. + var ( + wstart = time.Now() + status WriteStatus + ) + if !setHead { + // Don't set the head, only insert the block + err = bc.writeBlockWithState(block, receipts, statedb) + } else { + status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false) + } + if err != nil { + return nil, err + } + // Update the metrics touched during block commit + accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them + storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them + snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them + triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them + + blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits) + blockInsertTimer.UpdateSince(start) + + return &blockProcessingResult{usedGas: usedGas, procTime: proctime, status: status}, nil +} + // insertSideChain is called when an import batch hits upon a pruned ancestor // error, which happens when a sidechain with a sufficiently old fork-block is // found. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index bc6f8112f015..5f9892902b60 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -36,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -3248,7 +3247,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout), + //Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -3330,7 +3329,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout), + //Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -4682,7 +4681,7 @@ func TestEIP3651(t *testing.T) { b.AddTx(tx) }) - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{ /*Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)*/ }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 31c111b73e06..2949162bfe61 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -100,7 +100,7 @@ func (b *BlockGen) SetParentBeaconRoot(root common.Hash) { blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase) vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{}) ) - ProcessBeaconBlockRoot(root, vmenv, b.statedb) + ProcessBeaconBlockRoot(root, vmenv, b.statedb, nil) } // addTx adds a transaction to the generated block. If no coinbase has @@ -406,6 +406,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse // then generate chain on top. func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) { db := rawdb.NewMemoryDatabase() + triedb := trie.NewDatabase(db, trie.HashDefaults) defer triedb.Close() _, err := genesis.Commit(db, triedb) diff --git a/core/evm.go b/core/evm.go index c4801dc797db..8b724ac2cfe2 100644 --- a/core/evm.go +++ b/core/evm.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" ) @@ -135,6 +136,6 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { // Transfer subtracts amount from sender and adds amount to recipient using the given Db func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { - db.SubBalance(sender, amount) - db.AddBalance(recipient, amount) + db.SubBalance(sender, amount, state.BalanceChangeTransfer) + db.AddBalance(recipient, amount, state.BalanceChangeTransfer) } diff --git a/core/genesis.go b/core/genesis.go index 1045815fab90..edd323f67d84 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -131,7 +131,7 @@ func (ga *GenesisAlloc) hash() (common.Hash, error) { } for addr, account := range *ga { if account.Balance != nil { - statedb.AddBalance(addr, account.Balance) + statedb.AddBalance(addr, account.Balance, state.BalanceIncreaseGenesisBalance) } statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) @@ -152,7 +152,7 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas } for addr, account := range *ga { if account.Balance != nil { - statedb.AddBalance(addr, account.Balance) + statedb.AddBalance(addr, account.Balance, state.BalanceIncreaseGenesisBalance) } statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) @@ -179,6 +179,39 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas return nil } +func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc GenesisAlloc, err error) { + blob := rawdb.ReadGenesisStateSpec(db, blockhash) + if len(blob) != 0 { + if err := alloc.UnmarshalJSON(blob); err != nil { + return nil, err + } + + return alloc, nil + } + + // Genesis allocation is missing and there are several possibilities: + // the node is legacy which doesn't persist the genesis allocation or + // the persisted allocation is just lost. + // - supported networks(mainnet, testnets), recover with defined allocations + // - private network, can't recover + var genesis *Genesis + switch blockhash { + case params.MainnetGenesisHash: + genesis = DefaultGenesisBlock() + case params.GoerliGenesisHash: + genesis = DefaultGoerliGenesisBlock() + case params.SepoliaGenesisHash: + genesis = DefaultSepoliaGenesisBlock() + case params.HoleskyGenesisHash: + genesis = DefaultHoleskyGenesisBlock() + } + if genesis != nil { + return genesis.Alloc, nil + } + + return nil, nil +} + // GenesisAccount is an account in the state of the genesis block. type GenesisAccount struct { Code []byte `json:"code,omitempty"` @@ -287,6 +320,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen } else { log.Info("Writing custom genesis block") } + applyOverrides(genesis.Config) block, err := genesis.Commit(db, triedb) if err != nil { @@ -505,6 +539,8 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block // MustCommit writes the genesis block and state to db, panicking on error. // The block is committed as the canonical head block. +// Note the state changes will be committed in hash-based scheme, use Commit +// if path-scheme is preferred. func (g *Genesis) MustCommit(db ethdb.Database, triedb *trie.Database) *types.Block { block, err := g.Commit(db, triedb) if err != nil { diff --git a/core/state/metadata.go b/core/state/metadata.go new file mode 100644 index 000000000000..6978e6707559 --- /dev/null +++ b/core/state/metadata.go @@ -0,0 +1,66 @@ +// Copyright 2023 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 state + +// BalanceChangeReason is used to indicate the reason for a balance change, useful +// for tracing and reporting. +type BalanceChangeReason byte + +const ( + BalanceChangeUnspecified BalanceChangeReason = 0 + + // Issuance + // BalanceIncreaseRewardMineUncle is a reward for mining an uncle block. + BalanceIncreaseRewardMineUncle BalanceChangeReason = 1 + // BalanceIncreaseRewardMineBlock is a reward for mining a block. + BalanceIncreaseRewardMineBlock BalanceChangeReason = 2 + // BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain. + BalanceIncreaseWithdrawal BalanceChangeReason = 3 + // BalanceIncreaseGenesisBalance is ether allocated at the genesis block. + BalanceIncreaseGenesisBalance BalanceChangeReason = 4 + + // Transaction fees + // BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance. + BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5 + // BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction. + // Part of this gas will be burnt as per EIP-1559 rules. + BalanceDecreaseGasBuy BalanceChangeReason = 6 + // BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution. + BalanceIncreaseGasReturn BalanceChangeReason = 7 + + // DAO fork + // BalanceIncreaseDaoContract is ether sent to the DAO refund contract. + BalanceIncreaseDaoContract BalanceChangeReason = 8 + // BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract. + BalanceDecreaseDaoAccount BalanceChangeReason = 9 + + // BalanceChangeTransfer is ether transferred via a call. + // it is a decrease for the sender and an increase for the recipient. + BalanceChangeTransfer BalanceChangeReason = 10 + // BalanceChangeTouchAccount is a transfer of zero value. It is only there to + // touch-create an account. + BalanceChangeTouchAccount BalanceChangeReason = 11 + + // BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account. + BalanceIncreaseSelfdestruct BalanceChangeReason = 12 + // BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct. + BalanceDecreaseSelfdestruct BalanceChangeReason = 13 + // BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed + // account within the same tx (captured at end of tx). + // Note it doesn't account for a self-destruct which appoints itself as recipient. + BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 +) diff --git a/core/state/state_object.go b/core/state/state_object.go index d42d2c34d877..f9cc0109a286 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -239,6 +239,9 @@ func (s *stateObject) SetState(key, value common.Hash) { key: key, prevalue: prev, }) + if s.db.logger != nil { + s.db.logger.OnStorageChange(s.address, key, prev, value) + } s.setState(key, value) } @@ -401,7 +404,7 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. -func (s *stateObject) AddBalance(amount *big.Int) { +func (s *stateObject) AddBalance(amount *big.Int, reason BalanceChangeReason) { // EIP161: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. if amount.Sign() == 0 { @@ -410,23 +413,26 @@ func (s *stateObject) AddBalance(amount *big.Int) { } return } - s.SetBalance(new(big.Int).Add(s.Balance(), amount)) + s.SetBalance(new(big.Int).Add(s.Balance(), amount), reason) } // SubBalance removes amount from s's balance. // It is used to remove funds from the origin account of a transfer. -func (s *stateObject) SubBalance(amount *big.Int) { +func (s *stateObject) SubBalance(amount *big.Int, reason BalanceChangeReason) { if amount.Sign() == 0 { return } - s.SetBalance(new(big.Int).Sub(s.Balance(), amount)) + s.SetBalance(new(big.Int).Sub(s.Balance(), amount), reason) } -func (s *stateObject) SetBalance(amount *big.Int) { +func (s *stateObject) SetBalance(amount *big.Int, reason BalanceChangeReason) { s.db.journal.append(balanceChange{ account: &s.address, prev: new(big.Int).Set(s.data.Balance), }) + if s.db.logger != nil { + s.db.logger.OnBalanceChange(s.address, s.Balance(), amount, reason) + } s.setBalance(amount) } @@ -504,6 +510,9 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { prevhash: s.CodeHash(), prevcode: prevcode, }) + if s.db.logger != nil { + s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), prevcode, codeHash, code) + } s.setCode(codeHash, code) } @@ -518,6 +527,9 @@ func (s *stateObject) SetNonce(nonce uint64) { account: &s.address, prev: s.data.Nonce, }) + if s.db.logger != nil { + s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce) + } s.setNonce(nonce) } diff --git a/core/state/state_test.go b/core/state/state_test.go index 2553133dea55..26a2838977ed 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -49,11 +49,11 @@ func TestDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22)) + obj1.AddBalance(big.NewInt(22), BalanceChangeUnspecified) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44)) + obj3.SetBalance(big.NewInt(44), BalanceChangeUnspecified) // write some of them to the trie s.state.updateStateObject(obj1) @@ -103,13 +103,13 @@ func TestIterativeDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22)) + obj1.AddBalance(big.NewInt(22), BalanceChangeUnspecified) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44)) + obj3.SetBalance(big.NewInt(44), BalanceChangeUnspecified) obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00})) - obj4.AddBalance(big.NewInt(1337)) + obj4.AddBalance(big.NewInt(1337), BalanceChangeUnspecified) // write some of them to the trie s.state.updateStateObject(obj1) @@ -205,7 +205,7 @@ func TestSnapshot2(t *testing.T) { // db, trie are already non-empty values so0 := state.getStateObject(stateobjaddr0) - so0.SetBalance(big.NewInt(42)) + so0.SetBalance(big.NewInt(42), BalanceChangeUnspecified) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.selfDestructed = false @@ -217,7 +217,7 @@ func TestSnapshot2(t *testing.T) { // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) - so1.SetBalance(big.NewInt(52)) + so1.SetBalance(big.NewInt(52), BalanceChangeUnspecified) so1.SetNonce(53) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.selfDestructed = true diff --git a/core/state/statedb.go b/core/state/statedb.go index 195e463c28f9..618b1cf042a9 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -47,6 +47,24 @@ type revision struct { journalIndex int } +// StateLogger is used to collect state update traces from EVM transaction +// execution. +// The following hooks are invoked post execution. I.e. looking up state +// after the hook should reflect the new value. +// Note that reference types are actual VM data structures; make copies +// if you need to retain them beyond the current call. +type StateLogger interface { + OnBalanceChange(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) + OnNonceChange(addr common.Address, prev, new uint64) + OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) + OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) + OnLog(log *types.Log) + // OnNewAccount is called when a new account is created. + // Reset indicates an account existed at that address + // which will be replaced. + OnNewAccount(addr common.Address, reset bool) +} + // StateDB structs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: @@ -63,6 +81,7 @@ type StateDB struct { prefetcher *triePrefetcher trie Trie hasher crypto.KeccakState + logger StateLogger snaps *snapshot.Tree // Nil if snapshot is not available snap snapshot.Snapshot // Nil if snapshot is not available @@ -105,6 +124,9 @@ type StateDB struct { // Preimages occurred seen by VM in the scope of block. preimages map[common.Hash][]byte + // Enabled precompile contracts + precompiles map[common.Address]struct{} + // Per-transaction access list accessList *accessList @@ -161,6 +183,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) stateObjectsDestruct: make(map[common.Address]*types.StateAccount), logs: make(map[common.Hash][]*types.Log), preimages: make(map[common.Hash][]byte), + precompiles: make(map[common.Address]struct{}), journal: newJournal(), accessList: newAccessList(), transientStorage: newTransientStorage(), @@ -172,6 +195,11 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) return sdb, nil } +// SetLogger sets the logger for account update hooks. +func (s *StateDB) SetLogger(l StateLogger) { + s.logger = l +} + // StartPrefetcher initializes a new trie prefetcher to pull in nodes from the // state trie concurrently while the state is mutated so that when we reach the // commit phase, most of the needed data is already hot. @@ -212,6 +240,9 @@ func (s *StateDB) AddLog(log *types.Log) { log.TxHash = s.thash log.TxIndex = uint(s.txIndex) log.Index = s.logSize + if s.logger != nil { + s.logger.OnLog(log) + } s.logs[s.thash] = append(s.logs[s.thash], log) s.logSize++ } @@ -373,25 +404,25 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason BalanceChangeReason) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.AddBalance(amount) + stateObject.AddBalance(amount, reason) } } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason BalanceChangeReason) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SubBalance(amount) + stateObject.SubBalance(amount, reason) } } -func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { +func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason BalanceChangeReason) { stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetBalance(amount) + stateObject.SetBalance(amount, reason) } } @@ -447,11 +478,18 @@ func (s *StateDB) SelfDestruct(addr common.Address) { if stateObject == nil { return } + var ( + prev = stateObject.Balance() + n = new(big.Int) + ) s.journal.append(selfDestructChange{ account: &addr, prev: stateObject.selfDestructed, prevbalance: new(big.Int).Set(stateObject.Balance()), }) + if s.logger != nil && prev.Sign() > 0 { + s.logger.OnBalanceChange(addr, prev, n, BalanceDecreaseSelfdestruct) + } stateObject.markSelfdestructed() stateObject.data.Balance = new(big.Int) } @@ -628,6 +666,13 @@ func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! newobj = newObject(s, addr, nil) + if s.logger != nil { + // Precompiled contracts are touched during a call. + // Make sure we avoid emitting a new account event for them. + if _, ok := s.precompiles[addr]; !ok { + s.logger.OnNewAccount(addr, prev != nil) + } + } if prev == nil { s.journal.append(createObjectChange{account: &addr}) } else { @@ -837,6 +882,10 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) { obj.deleted = true + // If ether was sent to account post-selfdestruct it is burnt. + if bal := obj.Balance(); bal.Sign() != 0 && s.logger != nil { + s.logger.OnBalanceChange(obj.address, bal, new(big.Int), BalanceDecreaseSelfdestructBurn) + } // We need to maintain account deletions explicitly (will remain // set indefinitely). Note only the first occurred self-destruct // event is tracked. @@ -1342,6 +1391,14 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d s.transientStorage = newTransientStorage() } +// PrepareBlock prepares the statedb for execution of a block. It tracks +// the addresses of enabled precompiles for debugging purposes. +func (s *StateDB) PrepareBlock(precompiles []common.Address) { + for _, addr := range precompiles { + s.precompiles[addr] = struct{}{} + } +} + // AddAddressToAccessList adds the given address to the access list func (s *StateDB) AddAddressToAccessList(addr common.Address) { if s.accessList.AddAddress(addr) { diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index c4704257c73f..592d34b466c5 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -60,7 +60,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0])) + s.SetBalance(addr, big.NewInt(a.args[0]), BalanceChangeUnspecified) }, args: make([]int64, 1), }, diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index ad829a0c8f09..f6074917e35a 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -56,7 +56,7 @@ func TestUpdateLeaks(t *testing.T) { // Update it with some accounts for i := byte(0); i < 255; i++ { addr := common.BytesToAddress([]byte{i}) - state.AddBalance(addr, big.NewInt(int64(11*i))) + state.AddBalance(addr, big.NewInt(int64(11*i)), BalanceChangeUnspecified) state.SetNonce(addr, uint64(42*i)) if i%2 == 0 { state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) @@ -91,7 +91,7 @@ func TestIntermediateLeaks(t *testing.T) { finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) modify := func(state *StateDB, addr common.Address, i, tweak byte) { - state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) + state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)), BalanceChangeUnspecified) state.SetNonce(addr, uint64(42*i+tweak)) if i%2 == 0 { state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) @@ -167,7 +167,7 @@ func TestCopy(t *testing.T) { for i := byte(0); i < 255; i++ { obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(big.NewInt(int64(i))) + obj.AddBalance(big.NewInt(int64(i)), BalanceChangeUnspecified) orig.updateStateObject(obj) } orig.Finalise(false) @@ -184,9 +184,9 @@ func TestCopy(t *testing.T) { copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - origObj.AddBalance(big.NewInt(2 * int64(i))) - copyObj.AddBalance(big.NewInt(3 * int64(i))) - ccopyObj.AddBalance(big.NewInt(4 * int64(i))) + origObj.AddBalance(big.NewInt(2*int64(i)), BalanceChangeUnspecified) + copyObj.AddBalance(big.NewInt(3*int64(i)), BalanceChangeUnspecified) + ccopyObj.AddBalance(big.NewInt(4*int64(i)), BalanceChangeUnspecified) orig.updateStateObject(origObj) copy.updateStateObject(copyObj) @@ -266,14 +266,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0])) + s.SetBalance(addr, big.NewInt(a.args[0]), BalanceChangeUnspecified) }, args: make([]int64, 1), }, { name: "AddBalance", fn: func(a testAction, s *StateDB) { - s.AddBalance(addr, big.NewInt(a.args[0])) + s.AddBalance(addr, big.NewInt(a.args[0]), BalanceChangeUnspecified) }, args: make([]int64, 1), }, @@ -538,7 +538,7 @@ func TestTouchDelete(t *testing.T) { s.state, _ = New(root, s.state.db, s.state.snaps) snapshot := s.state.Snapshot() - s.state.AddBalance(common.Address{}, new(big.Int)) + s.state.AddBalance(common.Address{}, new(big.Int), BalanceChangeUnspecified) if len(s.state.journal.dirties) != 1 { t.Fatal("expected one dirty state object") @@ -554,7 +554,7 @@ func TestTouchDelete(t *testing.T) { func TestCopyOfCopy(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.HexToAddress("aaaa") - state.SetBalance(addr, big.NewInt(42)) + state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) if got := state.Copy().GetBalance(addr).Uint64(); got != 42 { t.Fatalf("1st copy fail, expected 42, got %v", got) @@ -577,9 +577,9 @@ func TestCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -650,9 +650,9 @@ func TestCopyCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -719,9 +719,9 @@ func TestCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -768,7 +768,7 @@ func TestDeleteCreateRevert(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.BytesToAddress([]byte("so")) - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) root, _ := state.Commit(0, false) state, _ = New(root, state.db, state.snaps) @@ -778,7 +778,7 @@ func TestDeleteCreateRevert(t *testing.T) { state.Finalise(true) id := state.Snapshot() - state.SetBalance(addr, big.NewInt(2)) + state.SetBalance(addr, big.NewInt(2), BalanceChangeUnspecified) state.RevertToSnapshot(id) // Commit the entire state and make sure we don't crash and have the correct state @@ -820,10 +820,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) { state, _ := New(types.EmptyRootHash, db, nil) addr := common.BytesToAddress([]byte("so")) { - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) state.SetCode(addr, []byte{1, 2, 3}) a2 := common.BytesToAddress([]byte("another")) - state.SetBalance(a2, big.NewInt(100)) + state.SetBalance(a2, big.NewInt(100), BalanceChangeUnspecified) state.SetCode(a2, []byte{1, 2, 4}) root, _ = state.Commit(0, false) t.Logf("root: %x", root) @@ -848,7 +848,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { t.Errorf("expected %d, got %d", exp, got) } // Modify the state - state.SetBalance(addr, big.NewInt(2)) + state.SetBalance(addr, big.NewInt(2), BalanceChangeUnspecified) root, err := state.Commit(0, false) if err == nil { t.Fatalf("expected error, got root :%x", root) @@ -1116,13 +1116,13 @@ func TestResetObject(t *testing.T) { slotB = common.HexToHash("0x2") ) // Initialize account with balance and storage in first transaction. - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) state.SetState(addr, slotA, common.BytesToHash([]byte{0x1})) state.IntermediateRoot(true) // Reset account and mutate balance and storages state.CreateAccount(addr) - state.SetBalance(addr, big.NewInt(2)) + state.SetBalance(addr, big.NewInt(2), BalanceChangeUnspecified) state.SetState(addr, slotB, common.BytesToHash([]byte{0x2})) root, _ := state.Commit(0, true) @@ -1148,7 +1148,7 @@ func TestDeleteStorage(t *testing.T) { addr = common.HexToAddress("0x1") ) // Initialize account and populate storage - state.SetBalance(addr, big.NewInt(1)) + state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) state.CreateAccount(addr) for i := 0; i < 1000; i++ { slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32()) diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 6196e77817e2..9ccb1475eab1 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -60,7 +60,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *trie.Database, com obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) acc := &testAccount{address: common.BytesToAddress([]byte{i})} - obj.AddBalance(big.NewInt(int64(11 * i))) + obj.AddBalance(big.NewInt(int64(11*i)), BalanceChangeUnspecified) acc.balance = big.NewInt(int64(11 * i)) obj.SetNonce(uint64(42 * i)) diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index b190567e92bc..a376c790c3d7 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -34,9 +34,9 @@ func filledStateDB() *StateDB { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie for i := 0; i < 100; i++ { sk := common.BigToHash(big.NewInt(int64(i))) state.SetState(addr, sk, sk) // Change the storage trie diff --git a/core/state_processor.go b/core/state_processor.go index 9a4333f72330..b468dce3afdb 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -67,6 +67,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg allLogs []*types.Log gp = new(GasPool).AddGas(block.GasLimit()) ) + // Mutate the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) @@ -74,11 +75,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg var ( context = NewEVMBlockContext(header, p.bc, nil) vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) + rules = vmenv.ChainConfig().Rules(context.BlockNumber, context.Random != nil, context.Time) signer = types.MakeSigner(p.config, header.Number, header.Time) ) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) + ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb, p.bc.logger) } + statedb.PrepareBlock(vm.ActivePrecompiles(rules)) // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) @@ -86,7 +89,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.SetTxContext(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) + + receipt, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -104,7 +108,16 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return receipts, allLogs, *usedGas, nil } -func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { +// ApplyTransactionWithEVM attempts to apply a transaction to the given state database +// and uses the input parameters for its environment similar to ApplyTransaction. However, +// this method takes an already created EVM instance as input. +func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { + if evm.Config.Tracer != nil { + evm.Config.Tracer.CaptureTxStart(evm, tx, msg.From) + defer func() { + evm.Config.Tracer.CaptureTxEnd(receipt, err) + }() + } // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -126,7 +139,7 @@ func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, sta // Create a new receipt for the transaction, storing the intermediate root and gas used // by the tx. - receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas} + receipt = &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas} if result.Failed() { receipt.Status = types.ReceiptStatusFailed } else { @@ -167,12 +180,18 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo blockContext := NewEVMBlockContext(header, bc, author) txContext := NewEVMTxContext(msg) vmenv := vm.NewEVM(blockContext, txContext, statedb, config, cfg) - return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) + return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv) } // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // contract. This method is exported to be used in tests. -func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) { +func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB, logger BlockchainLogger) { + if logger != nil { + logger.OnBeaconBlockRootStart(beaconRoot) + defer func() { + logger.OnBeaconBlockRootEnd() + }() + } // If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with // the new root msg := &Message{ diff --git a/core/state_transition.go b/core/state_transition.go index 612fdd781379..d73446744325 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" @@ -138,6 +139,8 @@ type Message struct { AccessList types.AccessList BlobGasFeeCap *big.Int BlobHashes []common.Hash + // Distinguishes type-2 txes + DynamicFee bool // When SkipAccountChecks is true, the message nonce is not checked against the // account nonce in state. It also disables checking that the sender is an EOA. @@ -160,6 +163,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In SkipAccountChecks: false, BlobHashes: tx.BlobHashes(), BlobGasFeeCap: tx.BlobGasFeeCap(), + DynamicFee: tx.Type() == 2 || tx.Type() == 3, } // If baseFee provided, set gasPrice to effectiveGasPrice. if baseFee != nil { @@ -257,10 +261,15 @@ func (st *StateTransition) buyGas() error { if err := st.gp.SubGas(st.msg.GasLimit); err != nil { return err } + + if st.evm.Config.Tracer != nil { + st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasChangeTxInitialBalance) + } + st.gasRemaining += st.msg.GasLimit st.initialGas = st.msg.GasLimit - st.state.SubBalance(st.msg.From, mgval) + st.state.SubBalance(st.msg.From, mgval, state.BalanceDecreaseGasBuy) return nil } @@ -368,13 +377,6 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, err } - if tracer := st.evm.Config.Tracer; tracer != nil { - tracer.CaptureTxStart(st.initialGas) - defer func() { - tracer.CaptureTxEnd(st.gasRemaining) - }() - } - var ( msg = st.msg sender = vm.AccountRef(msg.From) @@ -390,6 +392,9 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if st.gasRemaining < gas { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } + if t := st.evm.Config.Tracer; t != nil { + t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeTxIntrinsicGas) + } st.gasRemaining -= gas // Check clause 6 @@ -438,7 +443,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } else { fee := new(big.Int).SetUint64(st.gasUsed()) fee.Mul(fee, effectiveTip) - st.state.AddBalance(st.evm.Context.Coinbase, fee) + st.state.AddBalance(st.evm.Context.Coinbase, fee, state.BalanceIncreaseRewardTransactionFee) } return &ExecutionResult{ @@ -454,11 +459,20 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { if refund > st.state.GetRefund() { refund = st.state.GetRefund() } + + if st.evm.Config.Tracer != nil && refund > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds) + } + st.gasRemaining += refund // Return ETH for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) - st.state.AddBalance(st.msg.From, remaining) + st.state.AddBalance(st.msg.From, remaining, state.BalanceIncreaseGasReturn) + + if st.evm.Config.Tracer != nil && st.gasRemaining > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxLeftOverReturned) + } // Also return remaining gas to the block gas counter so it is // available for the next transaction. diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index 8914301e14c3..da830c4a77eb 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -512,17 +512,17 @@ func TestOpenDrops(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3) - statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000)) + statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2) - statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000)) - statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000)) + statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000), state.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -637,7 +637,7 @@ func TestOpenIndex(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr, big.NewInt(1_000_000_000)) + statedb.AddBalance(addr, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -737,9 +737,9 @@ func TestOpenHeap(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) + statedb.AddBalance(addr1, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr2, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr3, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -817,9 +817,9 @@ func TestOpenCap(t *testing.T) { for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} { // Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) + statedb.AddBalance(addr1, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr2, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr3, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -1210,7 +1210,7 @@ func TestAdd(t *testing.T) { addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey) // Seed the state database with this acocunt - statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance)) + statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance), state.BalanceChangeUnspecified) statedb.SetNonce(addrs[acc], seed.nonce) // Sign the seed transactions and store them in the data store diff --git a/core/txpool/legacypool/legacypool2_test.go b/core/txpool/legacypool/legacypool2_test.go index a73c1bb8a772..bf4f52b035b4 100644 --- a/core/txpool/legacypool/legacypool2_test.go +++ b/core/txpool/legacypool/legacypool2_test.go @@ -49,7 +49,7 @@ func fillPool(t testing.TB, pool *LegacyPool) { nonExecutableTxs := types.Transactions{} for i := 0; i < 384; i++ { key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000)) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000), state.BalanceChangeUnspecified) // Add executable ones for j := 0; j < int(pool.config.AccountSlots); j++ { executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key)) @@ -91,7 +91,7 @@ func TestTransactionFutureAttack(t *testing.T) { // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops { key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) futureTxs := types.Transactions{} for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key)) @@ -128,7 +128,7 @@ func TestTransactionFuture1559(t *testing.T) { // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops { key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) futureTxs := types.Transactions{} for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key)) @@ -182,7 +182,7 @@ func TestTransactionZAttack(t *testing.T) { for j := 0; j < int(pool.config.GlobalQueue); j++ { futureTxs := types.Transactions{} key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key)) pool.addRemotesSync(futureTxs) } @@ -190,7 +190,7 @@ func TestTransactionZAttack(t *testing.T) { overDraftTxs := types.Transactions{} { key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) for j := 0; j < int(pool.config.GlobalSlots); j++ { overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key)) } @@ -227,7 +227,7 @@ func BenchmarkFutureAttack(b *testing.B) { fillPool(b, pool) key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) futureTxs := types.Transactions{} for n := 0; n < b.N; n++ { diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index a8f3dd7d8624..a175df032438 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -255,7 +255,7 @@ func (c *testChain) State() (*state.StateDB, error) { c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) // simulate that the new head block included tx0 and tx1 c.statedb.SetNonce(c.address, 2) - c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether)) + c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether), state.BalanceChangeUnspecified) *c.trigger = false } return stdb, nil @@ -275,7 +275,7 @@ func TestStateChangeDuringReset(t *testing.T) { ) // setup pool with 2 transaction in it - statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether)) + statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether), state.BalanceChangeUnspecified) blockchain := &testChain{newTestBlockChain(params.TestChainConfig, 1000000000, statedb, new(event.Feed)), address, &trigger} tx0 := transaction(0, 100000, key) @@ -309,7 +309,7 @@ func TestStateChangeDuringReset(t *testing.T) { func testAddBalance(pool *LegacyPool, addr common.Address, amount *big.Int) { pool.mu.Lock() - pool.currentState.AddBalance(addr, amount) + pool.currentState.AddBalance(addr, amount, state.BalanceChangeUnspecified) pool.mu.Unlock() } @@ -470,7 +470,7 @@ func TestChainFork(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000)) + statedb.AddBalance(addr, big.NewInt(100000000000000), state.BalanceChangeUnspecified) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) <-pool.requestReset(nil, nil) @@ -499,7 +499,7 @@ func TestDoubleNonce(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000)) + statedb.AddBalance(addr, big.NewInt(100000000000000), state.BalanceChangeUnspecified) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) <-pool.requestReset(nil, nil) @@ -2618,7 +2618,7 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) { for i := 0; i < b.N; i++ { key, _ := crypto.GenerateKey() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000)) + pool.currentState.AddBalance(account, big.NewInt(1000000), state.BalanceChangeUnspecified) tx := transaction(uint64(0), 100000, key) batches[i] = tx } diff --git a/core/vm/contract.go b/core/vm/contract.go index e4b03bd74fec..f7dae713d17b 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -159,10 +159,13 @@ func (c *Contract) Caller() common.Address { } // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas uint64) (ok bool) { +func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) (ok bool) { if c.Gas < gas { return false } + if logger != nil && reason != GasChangeIgnored { + logger.OnGasChange(c.Gas, c.Gas-gas, reason) + } c.Gas -= gas return true } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 574bb9bef6a8..bb5dc039f0bc 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -168,11 +168,14 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger EVMLogger) (ret []byte, remainingGas uint64, err error) { gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas } + if logger != nil { + logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract) + } suppliedGas -= gasCost output, err := p.Run(input) return output, suppliedGas, err diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go index f40e2c8f9ea3..04aa8a116ddd 100644 --- a/core/vm/contracts_test.go +++ b/core/vm/contracts_test.go @@ -98,7 +98,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) { in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - if res, _, err := RunPrecompiledContract(p, in, gas); err != nil { + if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil { t.Error(err) } else if common.Bytes2Hex(res) != test.Expected { t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res)) @@ -120,7 +120,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) { gas := p.RequiredGas(in) - 1 t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := RunPrecompiledContract(p, in, gas, nil) if err.Error() != "out of gas" { t.Errorf("Expected error [out of gas], got [%v]", err) } @@ -137,7 +137,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing in := common.Hex2Bytes(test.Input) gas := p.RequiredGas(in) t.Run(test.Name, func(t *testing.T) { - _, _, err := RunPrecompiledContract(p, in, gas) + _, _, err := RunPrecompiledContract(p, in, gas, nil) if err.Error() != test.ExpectedError { t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err) } @@ -169,7 +169,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) { bench.ResetTimer() for i := 0; i < bench.N; i++ { copy(data, in) - res, _, err = RunPrecompiledContract(p, data, reqGas) + res, _, err = RunPrecompiledContract(p, data, reqGas, nil) } bench.StopTimer() elapsed := uint64(time.Since(start)) diff --git a/core/vm/errors.go b/core/vm/errors.go index fbbf19e178bf..9f6605f030f5 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -19,6 +19,7 @@ package vm import ( "errors" "fmt" + "math" ) // List evm execution errors @@ -71,3 +72,125 @@ type ErrInvalidOpCode struct { } func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) } + +// rpcError is the same interface as the one defined in rpc/errors.go +// but we do not want to depend on rpc package here so we redefine it. +// +// It's used to ensure that the VMError implements the RPC error interface. +type rpcError interface { + Error() string // returns the message + ErrorCode() int // returns the code +} + +var _ rpcError = (*VMError)(nil) + +// VMError wraps a VM error with an additional stable error code. The error +// field is the original error that caused the VM error and must be one of the +// VM error defined at the top of this file. +// +// If the error is not one of the known error above, the error code will be +// set to VMErrorCodeUnknown. +type VMError struct { + error + code int +} + +func VMErrorFromErr(err error) error { + if err == nil { + return nil + } + + return &VMError{ + error: err, + code: vmErrorCodeFromErr(err), + } +} + +func (e *VMError) Error() string { + return e.error.Error() +} + +func (e *VMError) Unwrap() error { + return e.error +} + +func (e *VMError) ErrorCode() int { + return e.code +} + +const ( + // We start the error code at 1 so that we can use 0 later for some possible extension. There + // is no unspecified value for the code today because it should always be set to a valid value + // that could be VMErrorCodeUnknown if the error is not mapped to a known error code. + + VMErrorCodeOutOfGas = 1 + iota + VMErrorCodeCodeStoreOutOfGas + VMErrorCodeDepth + VMErrorCodeInsufficientBalance + VMErrorCodeContractAddressCollision + VMErrorCodeExecutionReverted + VMErrorCodeMaxInitCodeSizeExceeded + VMErrorCodeMaxCodeSizeExceeded + VMErrorCodeInvalidJump + VMErrorCodeWriteProtection + VMErrorCodeReturnDataOutOfBounds + VMErrorCodeGasUintOverflow + VMErrorCodeInvalidCode + VMErrorCodeNonceUintOverflow + VMErrorCodeStackUnderflow + VMErrorCodeStackOverflow + VMErrorCodeInvalidOpCode + + // VMErrorCodeUnknown explicitly marks an error as unknown, this is useful when error is converted + // from an actual `error` in which case if the mapping is not known, we can use this value to indicate that. + VMErrorCodeUnknown = math.MaxInt - 1 +) + +func vmErrorCodeFromErr(err error) int { + switch { + case errors.Is(err, ErrOutOfGas): + return VMErrorCodeOutOfGas + case errors.Is(err, ErrCodeStoreOutOfGas): + return VMErrorCodeCodeStoreOutOfGas + case errors.Is(err, ErrDepth): + return VMErrorCodeDepth + case errors.Is(err, ErrInsufficientBalance): + return VMErrorCodeInsufficientBalance + case errors.Is(err, ErrContractAddressCollision): + return VMErrorCodeContractAddressCollision + case errors.Is(err, ErrExecutionReverted): + return VMErrorCodeExecutionReverted + case errors.Is(err, ErrMaxInitCodeSizeExceeded): + return VMErrorCodeMaxInitCodeSizeExceeded + case errors.Is(err, ErrMaxCodeSizeExceeded): + return VMErrorCodeMaxCodeSizeExceeded + case errors.Is(err, ErrInvalidJump): + return VMErrorCodeInvalidJump + case errors.Is(err, ErrWriteProtection): + return VMErrorCodeWriteProtection + case errors.Is(err, ErrReturnDataOutOfBounds): + return VMErrorCodeReturnDataOutOfBounds + case errors.Is(err, ErrGasUintOverflow): + return VMErrorCodeGasUintOverflow + case errors.Is(err, ErrInvalidCode): + return VMErrorCodeInvalidCode + case errors.Is(err, ErrNonceUintOverflow): + return VMErrorCodeNonceUintOverflow + + default: + // Dynamic errors + if v := (*ErrStackUnderflow)(nil); errors.As(err, &v) { + return VMErrorCodeStackUnderflow + } + + if v := (*ErrStackOverflow)(nil); errors.As(err, &v) { + return VMErrorCodeStackOverflow + } + + if v := (*ErrInvalidOpCode)(nil); errors.As(err, &v) { + return VMErrorCodeInvalidOpCode + } + + return VMErrorCodeUnknown + } +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 088b18aaa4ff..4960d08c777e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -17,10 +17,12 @@ package vm import ( + "errors" "math/big" "sync/atomic" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -177,6 +179,13 @@ func (evm *EVM) Interpreter() *EVMInterpreter { // the necessary steps to create accounts and reverses the state in case of an // execution error or failed value transfer. func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + // Capture the tracer start/end events in debug mode + if evm.Config.Tracer != nil { + evm.captureBegin(evm.depth == 0, CALL, caller.Address(), addr, input, gas, value) + defer func(startGas uint64) { + evm.captureEnd(evm.depth == 0, CALL, startGas, leftOverGas, ret, err) + }(gas) + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -187,44 +196,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } snapshot := evm.StateDB.Snapshot() p, isPrecompile := evm.precompile(addr) - debug := evm.Config.Tracer != nil if !evm.StateDB.Exist(addr) { if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 { - // Calling a non existing account, don't do anything, but ping the tracer - if debug { - if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) - evm.Config.Tracer.CaptureEnd(ret, 0, nil) - } else { - evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) - evm.Config.Tracer.CaptureExit(ret, 0, nil) - } - } + // Calling a non-existing account, don't do anything. return nil, gas, nil } evm.StateDB.CreateAccount(addr) } evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) - // Capture the tracer start/end events in debug mode - if debug { - if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) - defer func(startGas uint64) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err) - }(gas) - } else { - // Handle tracer events for entering and exiting a call frame - evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) - defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) - }(gas) - } - } - if isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -242,11 +225,15 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } } // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally + // above we revert to the snapshot and consume any gas remaining. Additionally, // when we're in homestead this also counts for code storage gas errors. if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + gas = 0 } // TODO: consider clearing up unused snapshots: @@ -264,6 +251,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // CallCode differs from Call in the sense that it executes the given address' // code with the caller as context. func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { + // Invoke tracer hooks that signal entering/exiting a call frame + if evm.Config.Tracer != nil { + evm.captureBegin(false, CALLCODE, caller.Address(), addr, input, gas, value) + defer func(startGas uint64) { + evm.captureEnd(false, CALLCODE, startGas, leftOverGas, ret, err) + }(gas) + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -277,17 +271,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, } var snapshot = evm.StateDB.Snapshot() - // Invoke tracer hooks that signal entering/exiting a call frame - if evm.Config.Tracer != nil { - evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) - defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) - }(gas) - } - // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { addrCopy := addr // Initialise a new contract and set the code that is to be used by the EVM. @@ -300,6 +286,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + gas = 0 } } @@ -312,27 +302,26 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // DelegateCall differs from CallCode in the sense that it executes the given address' // code with the caller as context and the caller is set to the caller of the caller. func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { - // Fail if we're trying to execute above the call depth limit - if evm.depth > int(params.CallCreateDepth) { - return nil, gas, ErrDepth - } - var snapshot = evm.StateDB.Snapshot() - // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { // NOTE: caller must, at all times be a contract. It should never happen // that caller is something other than a Contract. parent := caller.(*Contract) // DELEGATECALL inherits value from parent call - evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) + evm.captureBegin(false, DELEGATECALL, caller.Address(), addr, input, gas, parent.value) defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) + evm.captureEnd(false, DELEGATECALL, startGas, leftOverGas, ret, err) }(gas) } + // Fail if we're trying to execute above the call depth limit + if evm.depth > int(params.CallCreateDepth) { + return nil, gas, ErrDepth + } + var snapshot = evm.StateDB.Snapshot() // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { addrCopy := addr // Initialise a new contract and make initialise the delegate values @@ -344,6 +333,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } gas = 0 } } @@ -355,6 +347,13 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // Opcodes that attempt to perform such modifications will result in exceptions // instead of performing the modifications. func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { + // Invoke tracer hooks that signal entering/exiting a call frame + if evm.Config.Tracer != nil { + evm.captureBegin(false, STATICCALL, caller.Address(), addr, input, gas, nil) + defer func(startGas uint64) { + evm.captureEnd(false, STATICCALL, startGas, leftOverGas, ret, err) + }(gas) + } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -370,18 +369,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential // future scenarios - evm.StateDB.AddBalance(addr, big0) - - // Invoke tracer hooks that signal entering/exiting a call frame - if evm.Config.Tracer != nil { - evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil) - defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) - }(gas) - } + evm.StateDB.AddBalance(addr, new(big.Int), state.BalanceChangeTouchAccount) if p, isPrecompile := evm.precompile(addr); isPrecompile { - ret, gas, err = RunPrecompiledContract(p, input, gas) + ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) } else { // At this point, we use a copy of address. If we don't, the go compiler will // leak the 'contract' to the outer scope, and make allocation for 'contract' @@ -400,6 +391,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + gas = 0 } } @@ -419,7 +414,13 @@ func (c *codeAndHash) Hash() common.Hash { } // create creates a new contract using code as deployment code. -func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) { +func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { + if evm.Config.Tracer != nil { + evm.captureBegin(evm.depth == 0, typ, caller.Address(), address, codeAndHash.code, gas, value) + defer func(startGas uint64) { + evm.captureEnd(evm.depth == 0, typ, startGas, leftOverGas, ret, err) + }(gas) + } // Depth check execution. Fail if we're trying to execute above the // limit. if evm.depth > int(params.CallCreateDepth) { @@ -441,6 +442,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Ensure there's no existing contract already at the designated address contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state @@ -456,15 +461,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contract := NewContract(caller, AccountRef(address), value, gas) contract.SetCodeOptionalHash(&address, codeAndHash) - if evm.Config.Tracer != nil { - if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) - } else { - evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) - } - } - - ret, err := evm.interpreter.Run(contract, nil, false) + ret, err = evm.interpreter.Run(contract, nil, false) // Check whether the max code size has been exceeded, assign err if the case. if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize { @@ -482,7 +479,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // by the error checking condition below. if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas) { + if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCallCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -490,22 +487,15 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } // When an error was returned by the EVM or when setting the creation code - // above we revert to the snapshot and consume any gas remaining. Additionally + // above we revert to the snapshot and consume any gas remaining. Additionally, // when we're in homestead this also counts for code storage gas errors. if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas) + contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeCallFailedExecution) } } - if evm.Config.Tracer != nil { - if evm.depth == 0 { - evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err) - } else { - evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err) - } - } return ret, address, contract.Gas, err } @@ -527,3 +517,35 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } + +func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) { + tracer := evm.Config.Tracer + + if isRoot { + tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) + } else { + tracer.CaptureEnter(typ, from, to, input, startGas, value) + } + + tracer.OnGasChange(0, startGas, GasChangeCallInitialBalance) +} + +func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) { + tracer := evm.Config.Tracer + + if leftOverGas != 0 { + tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) + } + var reverted bool + if err != nil { + reverted = true + } + if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) { + reverted = false + } + if isRoot { + tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + } else { + tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 56ff3502011c..b9c5b153b731 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -18,6 +18,7 @@ package vm import ( "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -247,7 +248,9 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if evm.Config.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.CaptureKeccakPreimage(common.BytesToHash(interpreter.hasherBuf[:]), data) + } size.SetBytes(interpreter.hasherBuf[:]) return nil, nil } @@ -591,7 +594,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // reuse size int for stackvalue stackvalue := size - scope.Contract.UseGas(gas) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 if !value.IsZero() { @@ -611,6 +614,11 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b stackvalue.SetBytes(addr.Bytes()) } scope.Stack.push(&stackvalue) + + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { @@ -634,7 +642,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ) // Apply EIP150 gas -= gas / 64 - scope.Contract.UseGas(gas) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation2) // reuse size int for stackvalue stackvalue := size //TODO: use uint256.Int instead of converting with toBig() @@ -651,6 +659,11 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] stackvalue.SetBytes(addr.Bytes()) } scope.Stack.push(&stackvalue) + + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { @@ -696,6 +709,11 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -731,6 +749,11 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -759,6 +782,11 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -787,6 +815,11 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -822,11 +855,11 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, state.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) - tracer.CaptureExit([]byte{}, 0, nil) + tracer.CaptureExit([]byte{}, 0, nil, false) } return nil, errStopToken } @@ -837,12 +870,12 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon } beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance) - interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) + interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, state.BalanceDecreaseSelfdestruct) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, state.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) - tracer.CaptureExit([]byte{}, 0, nil) + tracer.CaptureExit([]byte{}, 0, nil, false) } return nil, errStopToken } diff --git a/core/vm/interface.go b/core/vm/interface.go index 26814d3d2f0e..e96e4502fb10 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" ) @@ -28,8 +29,8 @@ import ( type StateDB interface { CreateAccount(common.Address) - SubBalance(common.Address, *big.Int) - AddBalance(common.Address, *big.Int) + SubBalance(common.Address, *big.Int, state.BalanceChangeReason) + AddBalance(common.Address, *big.Int, state.BalanceChangeReason) GetBalance(common.Address) *big.Int GetNonce(common.Address) uint64 diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 28da2e80e60e..3eff0d25dbda 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -158,9 +158,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { if err != nil { if !logged { - in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) } else { - in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err) + in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) } } }() @@ -185,9 +185,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - if !contract.UseGas(cost) { + if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeIgnored) { return nil, ErrOutOfGas } + if operation.dynamicGas != nil { // All ops with a dynamic memory usage also has a dynamic gas cost. var memorySize uint64 @@ -211,21 +212,25 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( var dynamicCost uint64 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // for tracing - if err != nil || !contract.UseGas(dynamicCost) { + if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeIgnored) { return nil, ErrOutOfGas } + // Do tracing before memory expansion if debug { - in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode) + in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } if memorySize > 0 { mem.Resize(memorySize) } } else if debug { - in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode) + in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } + // execute the operation res, err = operation.execute(&pc, in, callContext) if err != nil { diff --git a/core/vm/logger.go b/core/vm/logger.go index 2667908a84d1..e86bb5d98e27 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" ) // EVMLogger is used to collect execution traces from an EVM transaction @@ -29,15 +30,88 @@ import ( // if you need to retain them beyond the current call. type EVMLogger interface { // Transaction level - CaptureTxStart(gasLimit uint64) - CaptureTxEnd(restGas uint64) + // Call simulations don't come with a valid signature. `from` field + // to be used for address of the caller. + CaptureTxStart(evm *EVM, tx *types.Transaction, from common.Address) + CaptureTxEnd(receipt *types.Receipt, err error) // Top call frame - CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) - CaptureEnd(output []byte, gasUsed uint64, err error) + CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) + // CaptureEnd is invoked when the processing of the top call ends. + // See docs for `CaptureExit` for info on the `reverted` parameter. + CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) // Rest of call frames CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) - CaptureExit(output []byte, gasUsed uint64, err error) + // CaptureExit is invoked when the processing of a message ends. + // `revert` is true when there was an error during the execution. + // Exceptionally, before the homestead hardfork a contract creation that + // ran out of gas when attempting to persist the code to database did not + // count as a call failure and did not cause a revert of the call. This will + // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. + CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) // Opcode level CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) + CaptureKeccakPreimage(hash common.Hash, data []byte) + // Misc + OnGasChange(old, new uint64, reason GasChangeReason) } + +// GasChangeReason is used to indicate the reason for a gas change, useful +// for tracing and reporting. +// +// There is essentially two types of gas changes, those that can be emitted once per transaction +// and those that can be emitted on a call basis, so possibly multiple times per transaction. +// +// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted +// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis. +type GasChangeReason byte + +const ( + GasChangeUnspecified GasChangeReason = iota + + // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per transaction. + GasChangeTxInitialBalance + // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is + // always exactly one of those per transaction. + GasChangeTxIntrinsicGas + // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) + // this generates an increase in gas. There is at most one of such gas change per transaction. + GasChangeTxRefunds + // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned + // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas + // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. + // There is at most one of such gas change per transaction. + GasChangeTxLeftOverReturned + + // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per call. + GasChangeCallInitialBalance + // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always + // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even + // will be emitted. + GasChangeCallLeftOverReturned + // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it + // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child. + // If there was no gas left to be refunded, no such even will be emitted. + GasChangeCallLeftOverRefunded + // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE. + GasChangeCallContractCreation + // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2. + GasChangeCallContractCreation2 + // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. + GasChangeCallCodeStorage + // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was + // performed can be check by `CaptureState` handling. + GasChangeCallOpCode + // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. + GasChangeCallPrecompiledContract + // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules. + GasChangeCallStorageColdAccess + // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. + GasChangeCallFailedExecution + + // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as + // it will be "manually" tracked by a direct emit of the gas change event. + GasChangeIgnored GasChangeReason = 0xFF +) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 04c6409ebd86..55c9bf34b952 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -169,7 +169,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call - if !contract.UseGas(coldCost) { + if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeCallStorageColdAccess) { return 0, ErrOutOfGas } } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index d10457e7faa9..65fbd289df0c 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -122,6 +122,9 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { sender = vm.AccountRef(cfg.Origin) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) + if cfg.EVMConfig.Tracer != nil { + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) @@ -155,6 +158,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { sender = vm.AccountRef(cfg.Origin) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) + if cfg.EVMConfig.Tracer != nil { + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) @@ -183,6 +189,9 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er statedb = cfg.State rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) + if cfg.EVMConfig.Tracer != nil { + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 796d3b443414..6e244759b857 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -32,7 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "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/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/params" @@ -330,7 +330,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) cfg.GasLimit = gas if len(tracerCode) > 0 { - tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(tracerCode, new(directory.Context), nil) if err != nil { b.Fatal(err) } @@ -811,7 +811,7 @@ func TestRuntimeJSTracer(t *testing.T) { byte(vm.PUSH1), 0, byte(vm.RETURN), } - depressedCode := []byte{ + suicideCode := []byte{ byte(vm.PUSH1), 0xaa, byte(vm.SELFDESTRUCT), } @@ -824,9 +824,9 @@ func TestRuntimeJSTracer(t *testing.T) { statedb.SetCode(common.HexToAddress("0xcc"), calleeCode) statedb.SetCode(common.HexToAddress("0xdd"), calleeCode) statedb.SetCode(common.HexToAddress("0xee"), calleeCode) - statedb.SetCode(common.HexToAddress("0xff"), depressedCode) + statedb.SetCode(common.HexToAddress("0xff"), suicideCode) - tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil) if err != nil { t.Fatal(err) } @@ -861,7 +861,7 @@ func TestJSTracerCreateTx(t *testing.T) { code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)} statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil) if err != nil { t.Fatal(err) } diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index 3d3444a87163..b93afbab06f2 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -72,7 +72,7 @@ func TestAccountRange(t *testing.T) { hash := common.HexToHash(fmt.Sprintf("%x", i)) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) addrs[i] = addr - sdb.SetBalance(addrs[i], big.NewInt(1)) + sdb.SetBalance(addrs[i], big.NewInt(1), state.BalanceChangeUnspecified) if _, ok := m[addr]; ok { t.Fatalf("bad") } else { diff --git a/eth/backend.go b/eth/backend.go index 09559f0ac116..1ba7552d76eb 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -192,6 +192,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { var ( vmConfig = vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, + Tracer: config.VMTracer, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 5e8f58efdb35..3ff1ae0013e6 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/ethdb" @@ -151,6 +152,9 @@ type Config struct { // Enables tracking of SHA3 preimages in the VM EnablePreimageRecording bool + // Enables VM tracing + VMTracer vm.EVMLogger + // Miscellaneous options DocRoot string `toml:"-"` diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 300d904a9970..30f0b23a56dc 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "os" "runtime" "sync" @@ -35,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -270,7 +272,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // Trace all the transactions contained within for i, tx := range task.block.Transactions() { msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee()) - txctx := &Context{ + txctx := &directory.Context{ BlockHash: task.block.Hash(), BlockNumber: task.block.Number(), TxIndex: i, @@ -282,8 +284,6 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) break } - // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - task.statedb.Finalise(api.backend.ChainConfig().IsEIP158(task.block.Number())) task.results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res} } // Tracing state is used up, queue it for de-referencing. Note the @@ -589,7 +589,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac // process that generates states in one thread and traces txes // in separate worker threads. if config != nil && config.Tracer != nil && *config.Tracer != "" { - if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS { + if isJS := directory.DefaultDirectory.IsJS(*config.Tracer); isJS { return api.traceBlockParallel(ctx, block, statedb, config) } } @@ -597,7 +597,6 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac var ( txs = block.Transactions() blockHash = block.Hash() - is158 = api.backend.ChainConfig().IsEIP158(block.Number()) blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) results = make([]*txTraceResult, len(txs)) @@ -605,7 +604,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac for i, tx := range txs { // Generate the next state snapshot fast without tracing msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) - txctx := &Context{ + txctx := &directory.Context{ BlockHash: blockHash, BlockNumber: block.Number(), TxIndex: i, @@ -616,9 +615,6 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac return nil, err } results[i] = &txTraceResult{TxHash: tx.Hash(), Result: res} - // Finalize the state so any modifications are written to the trie - // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - statedb.Finalise(is158) } return results, nil } @@ -648,7 +644,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat // Fetch and execute the next transaction trace tasks for task := range jobs { msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee()) - txctx := &Context{ + txctx := &directory.Context{ BlockHash: blockHash, BlockNumber: block.Number(), TxIndex: task.index, @@ -788,7 +784,9 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.SetTxContext(tx.Hash(), i) - _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) + vmConf.Tracer.CaptureTxStart(vmenv, tx, msg.From) + vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) + vmConf.Tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) if writer != nil { writer.Flush() } @@ -851,7 +849,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * } defer release() - txctx := &Context{ + txctx := &directory.Context{ BlockHash: blockHash, BlockNumber: block.Number(), TxIndex: int(index), @@ -916,18 +914,17 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, msg, new(Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, msg, new(directory.Context), vmctx, statedb, traceConfig) } // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { var ( - tracer Tracer - err error - timeout = defaultTraceTimeout - txContext = core.NewEVMTxContext(message) + tracer directory.Tracer + err error + timeout = defaultTraceTimeout ) if config == nil { config = &TraceConfig{} @@ -935,12 +932,13 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte // Default tracer is the struct logger tracer = logger.NewStructLogger(config.Config) if config.Tracer != nil { - tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) + tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) if err != nil { return nil, err } } - vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true}) + vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true}) + statedb.SetLogger(tracer) // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 0f78af9a01e5..52db08e9de8c 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -562,7 +562,6 @@ func TestTracingWithOverrides(t *testing.T) { From: &accounts[0].addr, // BLOCKNUMBER PUSH1 MSTORE Input: newRPCBytes(common.Hex2Bytes("4360005260206000f3")), - //&hexutil.Bytes{0x43}, // blocknumber }, config: &TraceCallConfig{ BlockOverrides: ðapi.BlockOverrides{Number: (*hexutil.Big)(big.NewInt(0x1337))}, diff --git a/eth/tracers/directory/live.go b/eth/tracers/directory/live.go new file mode 100644 index 000000000000..dc6927b14228 --- /dev/null +++ b/eth/tracers/directory/live.go @@ -0,0 +1,30 @@ +package directory + +import ( + "errors" + + "github.com/ethereum/go-ethereum/core" +) + +type ctorFunc func() (core.BlockchainLogger, error) + +// LiveDirectory is the collection of tracers which can be used +// during normal block import operations. +var LiveDirectory = liveDirectory{elems: make(map[string]ctorFunc)} + +type liveDirectory struct { + elems map[string]ctorFunc +} + +// Register registers a tracer constructor by name. +func (d *liveDirectory) Register(name string, f ctorFunc) { + d.elems[name] = f +} + +// New instantiates a tracer by name. +func (d *liveDirectory) New(name string) (core.BlockchainLogger, error) { + if f, ok := d.elems[name]; ok { + return f() + } + return nil, errors.New("not found") +} diff --git a/eth/tracers/native/noop.go b/eth/tracers/directory/noop.go similarity index 50% rename from eth/tracers/native/noop.go rename to eth/tracers/directory/noop.go index 3beecd8abfed..e784b6c8462e 100644 --- a/eth/tracers/native/noop.go +++ b/eth/tracers/directory/noop.go @@ -14,64 +14,85 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package native +package directory import ( "encoding/json" "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers" ) func init() { - tracers.DefaultDirectory.Register("noopTracer", newNoopTracer, false) + DefaultDirectory.Register("noopTracer", newNoopTracer, false) } -// noopTracer is a go implementation of the Tracer interface which +// NoopTracer is a go implementation of the Tracer interface which // performs no action. It's mostly useful for testing purposes. -type noopTracer struct{} +type NoopTracer struct{} // newNoopTracer returns a new noop tracer. -func newNoopTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { - return &noopTracer{}, nil +func newNoopTracer(ctx *Context, _ json.RawMessage) (Tracer, error) { + return &NoopTracer{}, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *noopTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *NoopTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { +func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *noopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *NoopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *noopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { +func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { } +// CaptureKeccakPreimage is called during the KECCAK256 opcode. +func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} + +// OnGasChange is called when gas is either consumed or refunded. +func (t *NoopTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {} + // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *noopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *noopTracer) CaptureExit(output []byte, gasUsed uint64, err error) { +func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { +} + +func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {} + +func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} + +func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { } -func (*noopTracer) CaptureTxStart(gasLimit uint64) {} +func (*NoopTracer) OnNonceChange(a common.Address, prev, new uint64) {} + +func (*NoopTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { +} + +func (*NoopTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {} + +func (*NoopTracer) OnLog(log *types.Log) {} -func (*noopTracer) CaptureTxEnd(restGas uint64) {} +func (*NoopTracer) OnNewAccount(a common.Address, reset bool) {} // GetResult returns an empty json object. -func (t *noopTracer) GetResult() (json.RawMessage, error) { +func (t *NoopTracer) GetResult() (json.RawMessage, error) { return json.RawMessage(`{}`), nil } // Stop terminates execution of the tracer at the first opportune moment. -func (t *noopTracer) Stop(err error) { +func (t *NoopTracer) Stop(err error) { } diff --git a/eth/tracers/tracers.go b/eth/tracers/directory/tracers.go similarity index 77% rename from eth/tracers/tracers.go rename to eth/tracers/directory/tracers.go index 7b43b7cf834a..b16d9d2fe69d 100644 --- a/eth/tracers/tracers.go +++ b/eth/tracers/directory/tracers.go @@ -14,16 +14,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -// Package tracers is a manager for transaction tracing engines. -package tracers +// Package directory provides functionality to lookup tracers by name. +// It also includes utility functions that are imported by the other +// tracing packages. +package directory import ( "encoding/json" - "errors" - "fmt" "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" ) @@ -36,10 +37,13 @@ type Context struct { TxHash common.Hash // Hash of the transaction being traced (zero if dangling call) } -// Tracer interface extends vm.EVMLogger and additionally -// allows collecting the tracing result. +// The set of methods that must be exposed by a tracer +// for it to be available through the RPC interface. +// This involves a method to retrieve results and one to +// stop tracing. type Tracer interface { vm.EVMLogger + state.StateLogger GetResult() (json.RawMessage, error) // Stop terminates execution of the tracer at the first opportune moment. Stop(err error) @@ -97,27 +101,3 @@ func (d *directory) IsJS(name string) bool { // JS eval will execute JS code return true } - -const ( - memoryPadLimit = 1024 * 1024 -) - -// GetMemoryCopyPadded returns offset + size as a new slice. -// It zero-pads the slice if it extends beyond memory bounds. -func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) { - if offset < 0 || size < 0 { - return nil, errors.New("offset or size must not be negative") - } - if int(offset+size) < m.Len() { // slice fully inside memory - return m.GetCopy(offset, size), nil - } - paddingNeeded := int(offset+size) - m.Len() - if paddingNeeded > memoryPadLimit { - return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) - } - cpy := make([]byte, size) - if overlap := int64(m.Len()) - offset; overlap > 0 { - copy(cpy, m.GetPtr(offset, overlap)) - } - return cpy, nil -} diff --git a/eth/tracers/directory/util.go b/eth/tracers/directory/util.go new file mode 100644 index 000000000000..92de735291d1 --- /dev/null +++ b/eth/tracers/directory/util.go @@ -0,0 +1,47 @@ +// Copyright 2023 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 directory + +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/core/vm" +) + +const ( + memoryPadLimit = 1024 * 1024 +) + +// GetMemoryCopyPadded returns offset + size as a new slice. +// It zero-pads the slice if it extends beyond memory bounds. +func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) { + if offset < 0 || size < 0 { + return nil, errors.New("offset or size must not be negative") + } + if int(offset+size) < m.Len() { // slice fully inside memory + return m.GetCopy(offset, size), nil + } + paddingNeeded := int(offset+size) - m.Len() + if paddingNeeded > memoryPadLimit { + return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) + } + cpy := make([]byte, size) + if overlap := int64(m.Len()) - offset; overlap > 0 { + copy(cpy, m.GetPtr(offset, overlap)) + } + return cpy, nil +} diff --git a/eth/tracers/directory/util_test.go b/eth/tracers/directory/util_test.go new file mode 100644 index 000000000000..c72096708da3 --- /dev/null +++ b/eth/tracers/directory/util_test.go @@ -0,0 +1,60 @@ +// Copyright 2023 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 directory + +import ( + "testing" + + "github.com/ethereum/go-ethereum/core/vm" +) + +func TestMemCopying(t *testing.T) { + for i, tc := range []struct { + memsize int64 + offset int64 + size int64 + wantErr string + wantSize int + }{ + {0, 0, 100, "", 100}, // Should pad up to 100 + {0, 100, 0, "", 0}, // No need to pad (0 size) + {100, 50, 100, "", 100}, // Should pad 100-150 + {100, 50, 5, "", 5}, // Wanted range fully within memory + {100, -50, 0, "offset or size must not be negative", 0}, // Errror + {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror + {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror + + } { + mem := vm.NewMemory() + mem.Resize(uint64(tc.memsize)) + cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size) + if want := tc.wantErr; want != "" { + if err == nil { + t.Fatalf("test %d: want '%v' have no error", i, want) + } + if have := err.Error(); want != have { + t.Fatalf("test %d: want '%v' have '%v'", i, want, have) + } + continue + } + if err != nil { + t.Fatalf("test %d: unexpected error: %v", i, err) + } + if want, have := tc.wantSize, len(cpy); have != want { + t.Fatalf("test %d: want %v have %v", i, want, have) + } + } +} diff --git a/eth/tracers/internal/tracetest/README.md b/eth/tracers/internal/tracetest/README.md new file mode 100644 index 000000000000..8c3d5d275f2c --- /dev/null +++ b/eth/tracers/internal/tracetest/README.md @@ -0,0 +1,10 @@ +# Filling test cases + +To fill test cases for the built-in tracers, the `makeTest.js` script can be used. Given a transaction on a dev/test network, `makeTest.js` will fetch its prestate and then traces with the given configuration. +In the Geth console do: + +```terminal +let tx = '0x...' +loadScript('makeTest.js') +makeTest(tx, { tracer: 'callTracer' }) +``` \ No newline at end of file diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 5c74baacd1c8..e518874549d7 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -18,6 +18,7 @@ package tracetest import ( "encoding/json" + "fmt" "math/big" "os" "path/filepath" @@ -31,7 +32,8 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "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/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" @@ -142,19 +144,22 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { ) triedb.Close() - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } + statedb.SetLogger(tracer) evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) msg, err := core.TransactionToMessage(tx, signer, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } + tracer.CaptureTxStart(evm, tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("failed to execute transaction: %v", err) } + tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected. res, err := tracer.GetResult() if err != nil { @@ -246,7 +251,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), nil) if err != nil { b.Fatalf("failed to create call tracer: %v", err) } @@ -265,13 +270,13 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { func TestInternals(t *testing.T) { var ( + config = params.MainnetChainConfig to = common.HexToAddress("0x00000000000000000000000000000000deadbeef") - origin = common.HexToAddress("0x00000000000000000000000000000000feed") - txContext = vm.TxContext{ - Origin: origin, - GasPrice: big.NewInt(1), - } - context = vm.BlockContext{ + originHex = "0x71562b71999873db5b286df957af199ec94617f7" + origin = common.HexToAddress(originHex) + signer = types.LatestSigner(config) + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, Coinbase: common.Address{}, @@ -279,10 +284,11 @@ func TestInternals(t *testing.T) { Time: 5, Difficulty: big.NewInt(0x30000), GasLimit: uint64(6000000), + BaseFee: new(big.Int), } ) - mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer { - tr, err := tracers.DefaultDirectory.New(name, nil, cfg) + mkTracer := func(name string, cfg json.RawMessage) directory.Tracer { + tr, err := directory.DefaultDirectory.New(name, nil, cfg) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } @@ -292,7 +298,7 @@ func TestInternals(t *testing.T) { for _, tc := range []struct { name string code []byte - tracer tracers.Tracer + tracer directory.Tracer want string }{ { @@ -306,13 +312,13 @@ func TestInternals(t *testing.T) { byte(vm.CALL), }, tracer: mkTracer("callTracer", nil), - want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x13880","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0xe01a","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`, + want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x54d8","to":"0x00000000000000000000000000000000deadbeef","input":"0x","calls":[{"from":"0x00000000000000000000000000000000deadbeef","gas":"0xe01a","gasUsed":"0x0","to":"0x00000000000000000000000000000000000000ff","input":"0x","value":"0x0","type":"CALL"}],"value":"0x0","type":"CALL"}`, originHex), }, { name: "Stack depletion in LOG0", code: []byte{byte(vm.LOG3)}, tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)), - want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x13880","gasUsed":"0x13880","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`, + want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x13880","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`, originHex), }, { name: "Mem expansion in LOG0", @@ -325,7 +331,7 @@ func TestInternals(t *testing.T) { byte(vm.LOG0), }, tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)), - want: `{"from":"0x000000000000000000000000000000000000feed","gas":"0x13880","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[{"address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","position":"0x0"}],"value":"0x0","type":"CALL"}`, + want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x5b9e","to":"0x00000000000000000000000000000000deadbeef","input":"0x","logs":[{"address":"0x00000000000000000000000000000000deadbeef","topics":[],"data":"0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","position":"0x0"}],"value":"0x0","type":"CALL"}`, originHex), }, { // Leads to OOM on the prestate tracer @@ -344,7 +350,7 @@ func TestInternals(t *testing.T) { byte(vm.LOG0), }, tracer: mkTracer("prestateTracer", nil), - want: `{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x000000000000000000000000000000000000feed":{"balance":"0x1c6bf52647880"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"}}`, + want: fmt.Sprintf(`{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600164ffffffffff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex), }, { // CREATE2 which requires padding memory by prestate tracer @@ -363,7 +369,7 @@ func TestInternals(t *testing.T) { byte(vm.LOG0), }, tracer: mkTracer("prestateTracer", nil), - want: `{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x000000000000000000000000000000000000feed":{"balance":"0x1c6bf52647880"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600160ff60016000f560ff6000a0"},"0x91ff9a805d36f54e3e272e230f3e3f5c1b330804":{"balance":"0x0"}}`, + want: fmt.Sprintf(`{"0x0000000000000000000000000000000000000000":{"balance":"0x0"},"0x00000000000000000000000000000000deadbeef":{"balance":"0x0","code":"0x6001600052600160ff60016000f560ff6000a0"},"%s":{"balance":"0x1c6bf52634000"}}`, originHex), }, } { t.Run(tc.name, func(t *testing.T) { @@ -377,22 +383,31 @@ func TestInternals(t *testing.T) { }, }, false, rawdb.HashScheme) defer triedb.Close() - - evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Tracer: tc.tracer}) - msg := &core.Message{ - To: &to, - From: origin, - Value: big.NewInt(0), - GasLimit: 80000, - GasPrice: big.NewInt(0), - GasFeeCap: big.NewInt(0), - GasTipCap: big.NewInt(0), - SkipAccountChecks: false, + statedb.SetLogger(tc.tracer) + tx, err := types.SignNewTx(key, signer, &types.LegacyTx{ + To: &to, + Value: big.NewInt(0), + Gas: 80000, + GasPrice: big.NewInt(1), + }) + if err != nil { + t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err) + } + txContext := vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) - if _, err := st.TransitionDb(); err != nil { + evm := vm.NewEVM(context, txContext, statedb, config, vm.Config{Tracer: tc.tracer}) + msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0)) + if err != nil { + t.Fatalf("test %v: failed to create message: %v", tc.name, err) + } + tc.tracer.CaptureTxStart(evm, tx, msg.From) + vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + if err != nil { t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err) } + tc.tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected res, err := tc.tracer.GetResult() if err != nil { diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 423167b13ccd..4b56dfd6b9b1 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -16,11 +16,9 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" - - // Force-load the native, to trigger registration - "github.com/ethereum/go-ethereum/eth/tracers" ) // flatCallTrace is the result of a callTracerParity run. @@ -104,21 +102,23 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string defer triedb.Close() // Create the tracer, the EVM environment and run it - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) if err != nil { return fmt.Errorf("failed to create call tracer: %v", err) } + statedb.SetLogger(tracer) evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) msg, err := core.TransactionToMessage(tx, signer, nil) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - - if _, err = st.TransitionDb(); err != nil { + tracer.CaptureTxStart(evm, tx, msg.From) + vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + if err != nil { return fmt.Errorf("failed to execute transaction: %v", err) } + tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the etalon res, err := tracer.GetResult() @@ -130,7 +130,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to unmarshal trace result: %v", err) } if !jsonEqualFlat(ret, test.Result) { - t.Logf("tracer name: %s", tracerName) + t.Logf("test %s failed", filename) // uncomment this for easier debugging // have, _ := json.MarshalIndent(ret, "", " ") diff --git a/eth/tracers/internal/tracetest/makeTest.js b/eth/tracers/internal/tracetest/makeTest.js new file mode 100644 index 000000000000..306f10719009 --- /dev/null +++ b/eth/tracers/internal/tracetest/makeTest.js @@ -0,0 +1,48 @@ +// makeTest generates a test for the configured tracer by running +// a prestate reassembled and a call trace run, assembling all the +// gathered information into a test case. +var makeTest = function(tx, traceConfig) { + // Generate the genesis block from the block, transaction and prestate data + var block = eth.getBlock(eth.getTransaction(tx).blockHash); + var genesis = eth.getBlock(block.parentHash); + + delete genesis.gasUsed; + delete genesis.logsBloom; + delete genesis.parentHash; + delete genesis.receiptsRoot; + delete genesis.sha3Uncles; + delete genesis.size; + delete genesis.transactions; + delete genesis.transactionsRoot; + delete genesis.uncles; + + genesis.gasLimit = genesis.gasLimit.toString(); + genesis.number = genesis.number.toString(); + genesis.timestamp = genesis.timestamp.toString(); + + genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer"}); + for (var key in genesis.alloc) { + var nonce = genesis.alloc[key].nonce; + if (nonce) { + genesis.alloc[key].nonce = nonce.toString(); + } + } + genesis.config = admin.nodeInfo.protocols.eth.config; + + // Generate the call trace and produce the test input + var result = debug.traceTransaction(tx, traceConfig); + delete result.time; + + console.log(JSON.stringify({ + genesis: genesis, + context: { + number: block.number.toString(), + difficulty: block.difficulty, + timestamp: block.timestamp.toString(), + gasLimit: block.gasLimit.toString(), + miner: block.miner, + }, + input: eth.getRawTransaction(tx), + result: result, + }, null, 2)); +} \ No newline at end of file diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index b4fa5b627269..9a9189e3994d 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -29,7 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "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/eth/tracers/directory" "github.com/ethereum/go-ethereum/tests" ) @@ -112,19 +112,22 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { ) defer triedb.Close() - tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig) + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } + statedb.SetLogger(tracer) evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) msg, err := core.TransactionToMessage(tx, signer, nil) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) - if _, err = st.TransitionDb(); err != nil { + tracer.CaptureTxStart(evm, tx, msg.From) + vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + if err != nil { t.Fatalf("failed to execute transaction: %v", err) } + tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected res, err := tracer.GetResult() if err != nil { diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json index 9b45b52fe9ad..ed3688a942e1 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer/inner_instafail.json @@ -56,6 +56,16 @@ "value": "0x0", "gas": "0x1f97e", "gasUsed": "0x72de", - "input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000" + "input": "0x2e1a7d4d00000000000000000000000000000000000000000000000014d1120d7b160000", + "calls": [{ + "from":"0x6c06b16512b332e6cd8293a2974872674716ce18", + "gas":"0x8fc", + "gasUsed":"0x0", + "to":"0x66fdfd05e46126a07465ad24e40cc0597bc1ef31", + "input":"0x", + "error":"insufficient balance for transfer", + "value":"0x14d1120d7b160000", + "type":"CALL" + }] } } diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json index c796804a4bcd..a2386ea9c713 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/callcode_precompiled_fail_hide.json @@ -63,12 +63,27 @@ "address": "0x5f8a7e007172ba80afbff1b15f800eb0b260f224" }, "traceAddress": [], - "subtraces": 0, + "subtraces": 1, "transactionPosition": 74, "transactionHash": "0x5ef60b27ac971c22a7d484e546e50093ca62300c8986d165154e47773764b6a4", "blockNumber": 1555279, "blockHash": "0xd6c98d1b87dfa92a210d99bad2873adaf0c9e51fe43addc63fd9cca03a5c6f46", "time": "209.346µs" + }, + { + "action": { + "balance": "0x0", + "callType": "callcode", + "from": "0x5f8a7e007172ba80afbff1b15f800eb0b260f224", + "gas": "0xaf64", + "to": "0x0000000000000000000000000000000000000004", + "value": "0x13" + }, + "error": "insufficient balance for transfer", + "result": {}, + "subtraces": 0, + "traceAddress": [0], + "type": "call" } ] } diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_instafail.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_instafail.json index 4de08f2ccaf1..611e50e2c046 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_instafail.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/inner_instafail.json @@ -64,9 +64,23 @@ "gasUsed": "0x72de", "output": "0x" }, - "subtraces": 0, + "subtraces": 1, "traceAddress": [], "type": "call" + }, + { + "action": { + "callType": "call", + "from": "0x6c06b16512b332e6cd8293a2974872674716ce18", + "gas": "0x8fc", + "to": "0x66fdfd05e46126a07465ad24e40cc0597bc1ef31", + "value": "0x14d1120d7b160000" + }, + "error": "insufficient balance for transfer", + "result": {}, + "subtraces": 0, + "traceAddress": [0], + "type": "call" } ] } diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json index 28e96684b2df..f3a7d9a94610 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/nested_create_inerror.json @@ -70,12 +70,25 @@ "output": "0x" }, "traceAddress": [], - "subtraces": 0, + "subtraces": 1, "transactionPosition": 26, "transactionHash": "0xcb1090fa85d2a3da8326b75333e92b3dca89963c895d9c981bfdaa64643135e4", "blockNumber": 839247, "blockHash": "0xce7ff7d84ca97f0f89d6065e2c12409a795c9f607cdb14aef0713cad5d7e311c", "time": "182.267µs" + }, + { + "action": { + "from": "0x76554b33410b6d90b7dc889bfed0451ad195f27e", + "gas": "0x25a18", + "init": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value": "0xa" + }, + "error": "insufficient balance for transfer", + "result": {}, + "subtraces": 0, + "traceAddress": [0], + "type": "create" } ] } \ No newline at end of file diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json index 74fd87cc6c4d..3c5d6d9f2b07 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/selfdestruct.json @@ -63,13 +63,26 @@ "address": "0x1d99a1a3efa9181f540f9e24fa6e4e08eb7844ca" }, "traceAddress": [], - "subtraces": 1, + "subtraces": 2, "transactionPosition": 14, "transactionHash": "0xdd76f02407e2f8329303ba688e111cae4f7008ad0d14d6e42c5698424ea36d79", "blockNumber": 1555146, "blockHash": "0xafb4f1dd27b9054c805acb81a88ed04384788cb31d84164c21874935c81e5c7e", "time": "187.145µs" }, + { + "action": { + "from": "0x1d99a1a3efa9181f540f9e24fa6e4e08eb7844ca", + "gas": "0x50ac", + "init": "0x5a", + "value": "0x1" + }, + "error": "insufficient balance for transfer", + "result": {}, + "subtraces": 0, + "traceAddress": [0], + "type": "create" + }, { "type": "suicide", "action": { @@ -79,7 +92,7 @@ }, "result": null, "traceAddress": [ - 0 + 1 ], "subtraces": 0, "transactionPosition": 14, diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json index 96060d554539..6911ed4b32af 100644 --- a/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_flat/skip_no_balance_error.json @@ -59,12 +59,25 @@ }, "error": "out of gas", "traceAddress": [], - "subtraces": 0, + "subtraces": 1, "transactionPosition": 16, "transactionHash": "0x384487e5ae8d2997aece8e28403d393cb9752425e6de358891bed981c5af1c05", "blockNumber": 1555285, "blockHash": "0x93231d8e9662adb4c5c703583a92c7b3112cd5448f43ab4fa1f0f00a0183ed3f", "time": "665.278µs" + }, + { + "action": { + "from": "0xf84bf5189ccd19f5897739756d214fa0dc099e0d", + "gas": "0x1d5c", + "init": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "value": "0xc350" + }, + "error": "insufficient balance for transfer", + "result": {}, + "subtraces": 0, + "traceAddress": [0], + "type": "create" } ] } \ No newline at end of file diff --git a/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json new file mode 100644 index 000000000000..c46fe080f7f2 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/call_tracer_withLog/frontier_create_outofstorage.json @@ -0,0 +1,189 @@ +{ + "genesis": { + "difficulty": "7797655526461", + "extraData": "0xd583010203844765746885676f312e35856c696e7578", + "gasLimit": "3141592", + "hash": "0x4ad333086cb86a6d261329504c9e1ca4d571212f56d6635dd213b700e1e85a6f", + "miner": "0x2a65aca4d5fc5b5c859090a6c34d164135398226", + "mixHash": "0xdaca4c8bd9a6e6707059736633543ebf50f97c07700a9ed55859b97275c19ea5", + "nonce": "0x894c15d74e8ae8bd", + "number": "469666", + "stateRoot": "0xf9c50965ffae3f99310483a7836c545a025cc680303adaf3671dbeef99edf03a", + "timestamp": "1446318401", + "totalDifficulty": "2462705215747880313", + "alloc": { + "0x0000000000000000000000000000000000000004": { + "balance": "0x0" + }, + "0x0047a8033cc6d6ca2ed5044674fd421f44884de8": { + "balance": "0x44f5ced08fe37cf7", + "nonce": "872" + }, + "0x1d11e5eae3112dbd44f99266872ff1d07c77dce8": { + "balance": "0x0", + "code": "0x60606040526000357c01000000000000000000000000000000000000000000000000000000009004806338cc48311461004f578063767800de14610088578063d1d80fdf146100c15761004d565b005b61005c60048050506100ff565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61009560048050506100d9565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100d7600480803590602001909190505061012e565b005b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905061012b565b90565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561018a57610002565b80600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055505b5056", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f631e3b3aafa084bc51c714825aacf505d2059be" + } + }, + "0xe48430c4e88a929bba0ee3dce284866a9937b609": { + "balance": "0x26758774d51d8677a", + "nonce": "261" + }, + "0xf631e3b3aafa084bc51c714825aacf505d2059be": { + "balance": "0x0", + "code": "0x606060405236156100da5760e060020a600035046323dc42e781146100ff5780632ef3accc146101a5578063385928321461021d57806345362978146102b65780634c7737951461034a578063524f38891461035c5780635c242c59146103ad578063772286591461044a5780637e1c42051461052457806381ade30714610601578063a2ec191a14610696578063adf59f991461071b578063ae815843146107b5578063bf1fe4201461084f578063de4b326214610890578063e8025731146108d4578063e839e65e14610970578063fbf8041814610a44575b610b20604051600160a060020a03331690600090349082818181858883f15050505050565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050505050505060008260006000610c0b83335b60006113fd8362030d40846101f5565b6040805160206004803580820135601f8101849004840285018401909552848452610b22949193602493909291840191908190840183828082843750949650509335935050505060006113fd8383335b600160a060020a03811660009081526003602052604081205460ff168114156114b557610b57565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050933593505050506000610d5885858585610841565b6040805160206004803580820135601f8101849004840285018401909552848452610b2294919360249390929184019190819084018382808284375050604080516020601f8935808c01359182018390048302840183019094528083529799986044989297509290920194509250829150840183828082843750949650505050505050600082600060006114068333610195565b610b34600154600160a060020a031681565b6040805160206004803580820135601f8101849004840285018401909552848452610b2294919360249390929184019190819084018382808284375094965050505050505060006114008233610195565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050933593505050505b60008360006000610d5f8333610195565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897608497919650602491909101945090925082915084018382808284375094965050505050505060008360006000610cb88333610195565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897608497919650602491909101945090925082915084018382808284375094965050933593505050505b60008460006000610f288333610195565b6040805160206004803580820135601f8101849004840285018401909552848452610b2294919360249390929184019190819084018382808284375050604080516020601f8935808c0135918201839004830284018301909452808352979998604498929750929092019450925082915084018382808284375094965050505050505060006113fd6000848462030d40610439565b6040805160206004803580820135601f8101849004840285018401909552848452610b209491936024939092918401919081908401838280828437509496505093359350505050600254600090600160a060020a039081163391909116148015906107115750600154600160a060020a039081163390911614155b156111fd57610002565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760649791965060249190910194509092508291508401838280828437509496505050505050506000610c0484848462030d40610439565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050933593505050505b6000610d5885858585610439565b610b20600435600254600160a060020a039081163391909116148015906108865750600154600160a060020a039081163390911614155b156114b057610002565b610b20600435600254600090600160a060020a039081163391909116148015906108ca5750600154600160a060020a039081163390911614155b1561134d57610002565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760649791965060249190910194509092508291508401838280828437509496505093359350505050600083600060006111618333610195565b6040805160206004803580820135601f8101849004840285018401909552848452610b2294919360249390929184019190819084018382808284375050604080516020601f8935808c01359182018390048302840183019094528083529799986044989297509290920194509250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a01909352828252969897606497919650602491909101945090925082915084018382808284375094965050505050505060008360006000610b5e8333610195565b60408051602060046024803582810135601f8101859004850286018501909652858552610b229583359593946044949392909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a0190935282825296989760849791965060249190910194509092508291508401838280828437509496505093359350505050600084600060006112a68333610195565b005b60408051918252519081900360200190f35b60408051600160a060020a03929092168252519081900360200190f35b93505050505b9392505050565b91508160001415610b8d57600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f15050503484900392508211159050610bee57604051600160a060020a03331690600090839082818181858883f150505050505b610b51600088888862030d406105f0565b610002565b9050610b57565b91508160001415610c3a57600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f15050503484900392508211159050610c9b57604051600160a060020a03331690600090839082818181858883f150505050505b610b5187878762030d40610439565b93505050505b949350505050565b91508160001415610ce757600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f15050503484900392508211159050610d4857604051600160a060020a03331690600090839082818181858883f150505050505b610caa8888888862030d406105f0565b9050610cb0565b91508160001415610d8e57600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f15050503484900392508211159050610def57604051600160a060020a03331690600090839082818181858883f150505050505b604080516000805442810183528351928390036020908101842060019290920183558184528381018d9052608084018a905260a09484018581528c51958501959095528b519198507f1f28d876aff267c3302a63cd25ebcca53e6f60691049df42275b6d06ab455c679489948e948e948e948e9492606085019260c086019289810192829185918391869190600490601f850104600302600f01f150905090810190601f168015610eb45780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610f0d5780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390a150610cb0915050565b91508160001415610f5757600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f15050503484900392508211159050610fb857604051600160a060020a03331690600090839082818181858883f150505050505b60006000505442016040518082815260200191505060405180910390209350835060006000818150548092919060010191905055507f4e65aab8959da44521dc50a6ce3dfbd65016d8cfab70a47ea7541458206c4d5b848a8a8a8a8a604051808781526020018681526020018060200180602001806020018581526020018481038452888181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561108e5780820380516001836020036101000a031916815260200191505b508481038352878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110e75780820380516001836020036101000a031916815260200191505b508481038252868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156111405780820380516001836020036101000a031916815260200191505b50995050505050505050505060405180910390a15050505b95945050505050565b9150816000141561119057600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f150505034849003925082111590506111f157604051600160a060020a03331690600090839082818181858883f150505050505b610caa88888888610439565b82604051808280519060200190808383829060006004602084601f0104600302600f01f1506007805491909301849003909320600184018084559095508594509192918391508280158290116112765781836000526020600020918201910161127691905b808211156112a25760008155600101611262565b505050815481101561000257600091825260208083209091019290925591825260069052604090205550565b5090565b915081600014156112d557600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f1505050348490039250821115905061133657604051600160a060020a03331690600090839082818181858883f150505050505b61134389898989896105f0565b9350505050611158565b50600481905560005b6007548110156113f957600780546006916000918490811015610002575080547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6888501548352602093909352604082205485029260059291908590811015610002579082527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688018150548152602081019190915260400160002055600101611356565b5050565b90505b92915050565b9150816000141561143557600160a060020a0333166000908152600360205260409020805460ff191660011790555b34829010610bff5760405173f65b3b60010d57d0bb8478aa6ced15fe720621b490600090849082818181858883f1505050348490039250821115905061149657604051600160a060020a03331690600090839082818181858883f150505050505b6114a66000878762030d40610439565b9350505050611400565b600855565b6005600050600085604051808280519060200190808383829060006004602084601f0104600302600f01f1509091018290039091208352505060209190915260409020546008548402019050610b5756", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000004", + "0xde5cab2c6836c23f6388364c9a0e20bd1c8c7e6c3b5d0339cd8a2f7c4b36208c": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0xf65b3b60010d57d0bb8478aa6ced15fe720621b4": { + "balance": "0x2c52a97273d2164" + } + }, + "config": { + "chainId": 1, + "homesteadBlock": 1150000, + "daoForkBlock": 1920000, + "daoForkSupport": true, + "eip150Block": 2463000, + "eip155Block": 2675000, + "eip158Block": 2675000, + "byzantiumBlock": 4370000, + "constantinopleBlock": 7280000, + "petersburgBlock": 7280000, + "istanbulBlock": 9069000, + "muirGlacierBlock": 9200000, + "berlinBlock": 12244000, + "londonBlock": 12965000, + "arrowGlacierBlock": 13773000, + "grayGlacierBlock": 15050000, + "shanghaiTime": 1681338455, + "terminalTotalDifficulty": 7797655526461000, + "terminalTotalDifficultyPassed": true, + "ethash": {} + } + }, + "context": { + "number": "469667", + "difficulty": "7793848077478", + "timestamp": "1446318425", + "gasLimit": "3141592", + "miner": "0xe48430c4e88a929bba0ee3dce284866a9937b609" + }, + "input": "0xf91ec7820368850ba43b7400831b77408080b91e72606060405260018054600160a060020a0319163317905561036f600360609081527f55524c0000000000000000000000000000000000000000000000000000000000608052610120604052604c60a09081527f6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f707560c0527f626c69632f5469636b65723f706169723d455448584254292e726573756c742e60e0527f58455448585842542e632e3000000000000000000000000000000000000000006101005261037d919062030d417f38cc483100000000000000000000000000000000000000000000000000000000610120908152600090731d11e5eae3112dbd44f99266872ff1d07c77dce89081906338cc4831906101249060209060048188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc8887604051837c010000000000000000000000000000000000000000000000000000000002815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156102255780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f11561000257505060405180517f385928320000000000000000000000000000000000000000000000000000000082526004828101888152606484018a90526080602485018181528d5160848701528d519496508a958e958e958e9594604484019360a40192909181908490829085908e906020601f850104600302600f01f150905090810190601f1680156102e65780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561033f5780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f11561000257505060405151979650505050505050565b611af2806103806000396000f35b5056606060405236156100985760e060020a6000350463056e1059811461009a57806327dc297e14610391578063346b306a146103e257806341c0e1b51461075e578063489306eb146107855780635731f35714610a5e57806365a4dfb314610de05780637975c56e14611179578063a2e6204514611458578063ae152cf414611528578063b77644751461181b578063d594877014611876575b005b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561025e5780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103085780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103615780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f115610002575050604051519350610dd892505050565b60408051602060248035600481810135601f81018590048502860185019096528585526100989581359591946044949293909201918190840183828082843750949650505050505050611a2761187a565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156105d15780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106795780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106d25780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561072b5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b9392505050565b610098600154600160a060020a03908116339091161415611a255733600160a060020a0316ff5b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109345780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150600087876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109d75780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610a305780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f115610002575050604051519695505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610c535780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610cfa5780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610d535780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610dac5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b949350505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663fbf80418600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc89876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610fe45780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015089898989896040518760e060020a028152600401808681526020018060200180602001806020018581526020018481038452888181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110935780820380516001836020036101000a031916815260200191505b508481038352878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110ec5780820380516001836020036101000a031916815260200191505b508481038252868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156111455780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038185886185025a03f115610002575050604051519998505050505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561132e5780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f11561000257505050604051805190602001508787876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156113d05780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156114295780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6100985b611aef604060405190810160405280600381526020017f55524c0000000000000000000000000000000000000000000000000000000000815260200150608060405190810160405280604c81526020017f6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f707581526020017f626c69632f5469636b65723f706169723d455448584254292e726573756c742e81526020017f58455448585842542e632e30000000000000000000000000000000000000000081526020015062030d416115ae565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f810183900483028401830190945283835297999860449892975091909101945090925082915084018382808284375094965050933593505050505b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156116e75780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117925780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117eb5780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6040805160028054602060018216156101000260001901909116829004601f81018290048202840182019094528383526119679390830182828015611a1d5780601f106119f257610100808354040283529160200191611a1d565b6119d55b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604080518051855473ffffffffffffffffffffffffffffffffffffffff1916178086557f4c7737950000000000000000000000000000000000000000000000000000000082529151600160a060020a03929092169250634c773795916004828101926020929190829003018188876161da5a03f115610002575050604051519250505090565b60408051918252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156119c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311611a0057829003601f168201915b505050505081565b565b600160a060020a031633600160a060020a0316141515611a4657610002565b8060026000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611aad57805160ff19168380011785555b50611add9291505b80821115611ae75760008155600101611a99565b82800160010185558215611a91579182015b82811115611a91578251826000505591602001919060010190611abf565b5050611aeb61145c565b5090565b5050565b50561ca083d25971e732af3acb0a223dea6258f37407bf2d075fd852a83312238fca7cdea01f5a1189b054e947a0a140c565c4fc829b584e7348c9a7f65a890fe688e8b67f", + "tracerConfig": { + "withLog": true + }, + "result": { + "from": "0x0047a8033cc6d6ca2ed5044674fd421f44884de8", + "gas": "0x1b7740", + "gasUsed": "0x9274f", + "input": "0x606060405260018054600160a060020a0319163317905561036f600360609081527f55524c0000000000000000000000000000000000000000000000000000000000608052610120604052604c60a09081527f6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f707560c0527f626c69632f5469636b65723f706169723d455448584254292e726573756c742e60e0527f58455448585842542e632e3000000000000000000000000000000000000000006101005261037d919062030d417f38cc483100000000000000000000000000000000000000000000000000000000610120908152600090731d11e5eae3112dbd44f99266872ff1d07c77dce89081906338cc4831906101249060209060048188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc8887604051837c010000000000000000000000000000000000000000000000000000000002815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156102255780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f11561000257505060405180517f385928320000000000000000000000000000000000000000000000000000000082526004828101888152606484018a90526080602485018181528d5160848701528d519496508a958e958e958e9594604484019360a40192909181908490829085908e906020601f850104600302600f01f150905090810190601f1680156102e65780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561033f5780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f11561000257505060405151979650505050505050565b611af2806103806000396000f35b5056606060405236156100985760e060020a6000350463056e1059811461009a57806327dc297e14610391578063346b306a146103e257806341c0e1b51461075e578063489306eb146107855780635731f35714610a5e57806365a4dfb314610de05780637975c56e14611179578063a2e6204514611458578063ae152cf414611528578063b77644751461181b578063d594877014611876575b005b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561025e5780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103085780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156103615780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f115610002575050604051519350610dd892505050565b60408051602060248035600481810135601f81018590048502860185019096528585526100989581359591946044949293909201918190840183828082843750949650505050505050611a2761187a565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156105d15780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106795780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156106d25780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561072b5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b9392505050565b610098600154600160a060020a03908116339091161415611a255733600160a060020a0316ff5b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f8101839004830284018301909452838352979998604498929750919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109345780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150600087876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156109d75780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610a305780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f115610002575050604051519695505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166377228659600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889886040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610c535780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f1156100025750505060405180519060200150888888886040518660e060020a028152600401808581526020018060200180602001806020018481038452878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610cfa5780820380516001836020036101000a031916815260200191505b508481038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610d535780820380516001836020036101000a031916815260200191505b508481038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610dac5780820380516001836020036101000a031916815260200191505b5097505050505050505060206040518083038185886185025a03f1156100025750506040515193505050505b949350505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024919091019450909250829150840183828082843750949650509335935050505060006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663fbf80418600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc89876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f168015610fe45780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015089898989896040518760e060020a028152600401808681526020018060200180602001806020018581526020018481038452888181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110935780820380516001836020036101000a031916815260200191505b508481038352878181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156110ec5780820380516001836020036101000a031916815260200191505b508481038252868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156111455780820380516001836020036101000a031916815260200191505b509850505050505050505060206040518083038185886185025a03f115610002575050604051519998505050505050505050565b60408051602060248035600481810135601f81018590048502860185019096528585526119559581359591946044949293909201918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976064979196506024919091019450909250829150840183828082843750506040805160e060020a6338cc48310281529051959760009750731d11e5eae3112dbd44f99266872ff1d07c77dce8968796506338cc4831955082820194506020935091829003018188876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a031663adf59f99600060009054906101000a9004600160a060020a0316600160a060020a031663524f3889876040518260e060020a02815260040180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f16801561132e5780820380516001836020036101000a031916815260200191505b50925050506020604051808303816000876161da5a03f11561000257505050604051805190602001508787876040518560e060020a0281526004018084815260200180602001806020018381038352858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156113d05780820380516001836020036101000a031916815260200191505b508381038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156114295780820380516001836020036101000a031916815260200191505b509550505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6100985b611aef604060405190810160405280600381526020017f55524c0000000000000000000000000000000000000000000000000000000000815260200150608060405190810160405280604c81526020017f6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f707581526020017f626c69632f5469636b65723f706169723d455448584254292e726573756c742e81526020017f58455448585842542e632e30000000000000000000000000000000000000000081526020015062030d416115ae565b6040805160206004803580820135601f8101849004840285018401909552848452611955949193602493909291840191908190840183828082843750506040805160208835808b0135601f810183900483028401830190945283835297999860449892975091909101945090925082915084018382808284375094965050933593505050505b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f1156100025750505060405180519060200150600060006101000a815481600160a060020a0302191690830217905550600060009054906101000a9004600160a060020a0316600160a060020a03166338592832600060009054906101000a9004600160a060020a0316600160a060020a0316632ef3accc88876040518360e060020a02815260040180806020018381526020018281038252848181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156116e75780820380516001836020036101000a031916815260200191505b5093505050506020604051808303816000876161da5a03f115610002575050506040518051906020015060008888886040518660e060020a0281526004018085815260200180602001806020018481526020018381038352868181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117925780820380516001836020036101000a031916815260200191505b508381038252858181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156117eb5780820380516001836020036101000a031916815260200191505b50965050505050505060206040518083038185886185025a03f11561000257505060405151935061075792505050565b6040805160028054602060018216156101000260001901909116829004601f81018290048202840182019094528383526119679390830182828015611a1d5780601f106119f257610100808354040283529160200191611a1d565b6119d55b60006000731d11e5eae3112dbd44f99266872ff1d07c77dce8905080600160a060020a03166338cc48316040518160e060020a0281526004018090506020604051808303816000876161da5a03f115610002575050604080518051855473ffffffffffffffffffffffffffffffffffffffff1916178086557f4c7737950000000000000000000000000000000000000000000000000000000082529151600160a060020a03929092169250634c773795916004828101926020929190829003018188876161da5a03f115610002575050604051519250505090565b60408051918252519081900360200190f35b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600302600f01f150905090810190601f1680156119c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b820191906000526020600020905b815481529060010190602001808311611a0057829003601f168201915b505050505081565b565b600160a060020a031633600160a060020a0316141515611a4657610002565b8060026000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611aad57805160ff19168380011785555b50611add9291505b80821115611ae75760008155600101611a99565b82800160010185558215611a91579182015b82811115611a91578251826000505591602001919060010190611abf565b5050611aeb61145c565b5090565b5050565b5056", + "error": "contract creation code storage out of gas", + "calls": [ + { + "from": "0xc24431c1a1147456414355b1f1769de450e524da", + "gas": "0x12c54b", + "gasUsed": "0x106", + "to": "0x1d11e5eae3112dbd44f99266872ff1d07c77dce8", + "input": "0x38cc4831", + "output": "0x000000000000000000000000f631e3b3aafa084bc51c714825aacf505d2059be", + "value": "0x0", + "type": "CALL" + }, + { + "from": "0xc24431c1a1147456414355b1f1769de450e524da", + "gas": "0x12", + "gasUsed": "0x12", + "to": "0x0000000000000000000000000000000000000004", + "input": "0x55524c", + "output": "0x55524c", + "value": "0x0", + "type": "CALL" + }, + { + "from": "0xc24431c1a1147456414355b1f1769de450e524da", + "gas": "0x127270", + "gasUsed": "0x26b", + "to": "0xf631e3b3aafa084bc51c714825aacf505d2059be", + "input": "0x2ef3accc00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000030d41000000000000000000000000000000000000000000000000000000000000000355524c0000000000000000000000000000000000000000000000000000000000", + "output": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value": "0x0", + "type": "CALL" + }, + { + "from": "0xc24431c1a1147456414355b1f1769de450e524da", + "gas": "0x12", + "gasUsed": "0x12", + "to": "0x0000000000000000000000000000000000000004", + "input": "0x55524c", + "output": "0x55524c", + "value": "0x0", + "type": "CALL" + }, + { + "from": "0xc24431c1a1147456414355b1f1769de450e524da", + "gas": "0x18", + "gasUsed": "0x18", + "to": "0x0000000000000000000000000000000000000004", + "input": "0x6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f7075626c69632f5469636b65723f706169723d455448584254292e726573756c742e58455448585842542e632e30", + "output": "0x6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f7075626c69632f5469636b65723f706169723d455448584254292e726573756c742e58455448585842542e632e30", + "value": "0x0", + "type": "CALL" + }, + { + "from": "0xc24431c1a1147456414355b1f1769de450e524da", + "gas": "0x124995", + "gasUsed": "0x78f5", + "to": "0xf631e3b3aafa084bc51c714825aacf505d2059be", + "input": "0x385928320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000030d41000000000000000000000000000000000000000000000000000000000000000355524c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f7075626c69632f5469636b65723f706169723d455448584254292e726573756c742e58455448585842542e632e300000000000000000000000000000000000000000", + "output": "0x55bc8431ce52389ac668a9b14a0943290cb7263732251186e960bc8b249b5f32", + "calls": [ + { + "from": "0xf631e3b3aafa084bc51c714825aacf505d2059be", + "gas": "0x0", + "gasUsed": "0x0", + "to": "0xf65b3b60010d57d0bb8478aa6ced15fe720621b4", + "input": "0x", + "value": "0x0", + "type": "CALL" + }, + { + "from": "0xf631e3b3aafa084bc51c714825aacf505d2059be", + "gas": "0x12", + "gasUsed": "0x12", + "to": "0x0000000000000000000000000000000000000004", + "input": "0x55524c", + "output": "0x55524c", + "value": "0x0", + "type": "CALL" + }, + { + "from": "0xf631e3b3aafa084bc51c714825aacf505d2059be", + "gas": "0x18", + "gasUsed": "0x18", + "to": "0x0000000000000000000000000000000000000004", + "input": "0x6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f7075626c69632f5469636b65723f706169723d455448584254292e726573756c742e58455448585842542e632e30", + "output": "0x6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f7075626c69632f5469636b65723f706169723d455448584254292e726573756c742e58455448585842542e632e30", + "value": "0x0", + "type": "CALL" + } + ], + "logs":[ + { + "address": "0xf631e3b3aafa084bc51c714825aacf505d2059be", + "topics": ["0x1f28d876aff267c3302a63cd25ebcca53e6f60691049df42275b6d06ab455c67"], + "data":"0x55bc8431ce52389ac668a9b14a0943290cb7263732251186e960bc8b249b5f32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000030d41000000000000000000000000000000000000000000000000000000000000000355524c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c6a736f6e2868747470733a2f2f6170692e6b72616b656e2e636f6d2f302f7075626c69632f5469636b65723f706169723d455448584254292e726573756c742e58455448585842542e632e300000000000000000000000000000000000000000", + "position":"0x3" + } + ], + "value": "0x0", + "type": "CALL" + } + ], + "value": "0x0", + "type": "CREATE" + } +} \ No newline at end of file diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json new file mode 100644 index 000000000000..909a1eabe38e --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_create.json @@ -0,0 +1,62 @@ +{ + "genesis": { + "baseFeePerGas": "875000000", + "difficulty": "0", + "extraData": "0xd983010d05846765746888676f312e32312e318664617277696e", + "gasLimit": "11511229", + "hash": "0xd462585c6c5a3b3bf14850ebcde71b6615b9aaf6541403f9a0457212dd0502e0", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0xfa51e868d6a7c0728f18800e4cc8d4cc1c87430cc9975e947eb6c9c03599b4e2", + "nonce": "0x0000000000000000", + "number": "1", + "stateRoot": "0xd2ebe0a7f3572ffe3e5b4c78147376d3fca767f236e4dd23f9151acfec7cb0d1", + "timestamp": "1699617692", + "totalDifficulty": "0", + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "alloc": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x5208" + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "balance": "0x8ac7230489e80000" + } + }, + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true, + "isDev": true + } + }, + "context": { + "number": "2", + "difficulty": "0", + "timestamp": "1699617847", + "gasLimit": "11522469", + "miner": "0x0000000000000000000000000000000000000000" + }, + "input": "0x02f902b48205398084b2d05e0085011b1f3f8083031ca88080b90258608060405234801561001057600080fd5b5060405161001d906100e3565b604051809103906000f080158015610039573d6000803e3d6000fd5b50600080546001600160a01b0319166001600160a01b039290921691821781556040517fc66247bafd1305823857fb4c3e651e684d918df8554ef560bbbcb025fdd017039190a26000546040516360fe47b160e01b8152600560048201526001600160a01b03909116906360fe47b190602401600060405180830381600087803b1580156100c657600080fd5b505af11580156100da573d6000803e3d6000fd5b505050506100ef565b60ca8061018e83390190565b6091806100fd6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806380de699314602d575b600080fd5b600054603f906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f3fea2646970667358221220dab781465e7f4cf20304cc388130a763508e20edd25b4bc8ea8f57743a0de8da64736f6c634300081700336080604052348015600f57600080fd5b5060ac8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146049575b600080fd5b60476042366004605e565b600055565b005b60005460405190815260200160405180910390f35b600060208284031215606f57600080fd5b503591905056fea264697066735822122049e09da6320793487d58eaa7b97f802618a062cbc35f08ca1ce92c17349141f864736f6c63430008170033c080a01d4fce93ad08bf413052645721f20e6136830cf5a2759fa57e76a134e90899a7a0399a72832d52118991dc04c4f9e1c0fec3d5e441ad7d4b055f0cf03130d8f815", + "result": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x5208" + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "balance": "0x8ac7230489e80000" + } + } +} \ No newline at end of file diff --git a/eth/tracers/internal/tracetest/util.go b/eth/tracers/internal/tracetest/util.go index 95d292c9240b..1913f352ddb7 100644 --- a/eth/tracers/internal/tracetest/util.go +++ b/eth/tracers/internal/tracetest/util.go @@ -9,59 +9,6 @@ import ( _ "github.com/ethereum/go-ethereum/eth/tracers/native" ) -// To generate a new callTracer test, copy paste the makeTest method below into -// a Geth console and call it with a transaction hash you which to export. - -/* -// makeTest generates a callTracer test by running a prestate reassembled and a -// call trace run, assembling all the gathered information into a test case. -var makeTest = function(tx, rewind) { - // Generate the genesis block from the block, transaction and prestate data - var block = eth.getBlock(eth.getTransaction(tx).blockHash); - var genesis = eth.getBlock(block.parentHash); - - delete genesis.gasUsed; - delete genesis.logsBloom; - delete genesis.parentHash; - delete genesis.receiptsRoot; - delete genesis.sha3Uncles; - delete genesis.size; - delete genesis.transactions; - delete genesis.transactionsRoot; - delete genesis.uncles; - - genesis.gasLimit = genesis.gasLimit.toString(); - genesis.number = genesis.number.toString(); - genesis.timestamp = genesis.timestamp.toString(); - - genesis.alloc = debug.traceTransaction(tx, {tracer: "prestateTracer", rewind: rewind}); - for (var key in genesis.alloc) { - var nonce = genesis.alloc[key].nonce; - if (nonce) { - genesis.alloc[key].nonce = nonce.toString(); - } - } - genesis.config = admin.nodeInfo.protocols.eth.config; - - // Generate the call trace and produce the test input - var result = debug.traceTransaction(tx, {tracer: "callTracer", rewind: rewind}); - delete result.time; - - console.log(JSON.stringify({ - genesis: genesis, - context: { - number: block.number.toString(), - difficulty: block.difficulty, - timestamp: block.timestamp.toString(), - gasLimit: block.gasLimit.toString(), - miner: block.miner, - }, - input: eth.getRawTransaction(tx), - result: result, - }, null, 2)); -} -*/ - // camel converts a snake cased input string into a camel cased output. func camel(str string) string { pieces := strings.Split(str, "_") diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index d22d140988fe..c3a95082022f 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -23,12 +23,13 @@ import ( "math/big" "github.com/dop251/goja" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers" jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers" ) @@ -41,16 +42,16 @@ func init() { if err != nil { panic(err) } - type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error) + type ctorFn = func(*directory.Context, json.RawMessage) (directory.Tracer, error) lookup := func(code string) ctorFn { - return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { + return func(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { return newJsTracer(code, ctx, cfg) } } for name, code := range assetTracers { - tracers.DefaultDirectory.Register(name, lookup(code), true) + directory.DefaultDirectory.Register(name, lookup(code), true) } - tracers.DefaultDirectory.RegisterJSEval(newJsTracer) + directory.DefaultDirectory.RegisterJSEval(newJsTracer) } // bigIntProgram is compiled once and the exported function mostly invoked to convert @@ -95,6 +96,8 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b // jsTracer is an implementation of the Tracer interface which evaluates // JS functions on the relevant EVM hooks. It uses Goja as its JS engine. type jsTracer struct { + directory.NoopTracer + vm *goja.Runtime env *vm.EVM toBig toBigFn // Converts a hex string into a JS bigint @@ -104,7 +107,6 @@ type jsTracer struct { activePrecompiles []common.Address // List of active precompiles at current block traceStep bool // True if tracer object exposes a `step()` method traceFrame bool // True if tracer object exposes the `enter()` and `exit()` methods - gasLimit uint64 // Amount of gas bought for the whole tx err error // Any error that should stop tracing obj *goja.Object // Trace object @@ -134,7 +136,7 @@ type jsTracer struct { // The methods `result` and `fault` are required to be present. // The methods `step`, `enter`, and `exit` are optional, but note that // `enter` and `exit` always go together. -func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { vm := goja.New() // By default field names are exported to JS as is, i.e. capitalized. vm.SetFieldNameMapper(goja.UncapFieldNameMapper()) @@ -143,7 +145,7 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer ctx: make(map[string]goja.Value), } if ctx == nil { - ctx = new(tracers.Context) + ctx = new(directory.Context) } if ctx.BlockHash != (common.Hash{}) { t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes()) @@ -212,46 +214,73 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer // CaptureTxStart implements the Tracer interface and is invoked at the beginning of // transaction processing. -func (t *jsTracer) CaptureTxStart(gasLimit uint64) { - t.gasLimit = gasLimit +func (t *jsTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + t.env = env + // Need statedb access for db object + db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} + t.dbValue = db.setupObject() + // Update list of precompiles based on current block + rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) + t.activePrecompiles = vm.ActivePrecompiles(rules) + t.ctx["block"] = t.vm.ToValue(t.env.Context.BlockNumber.Uint64()) + t.ctx["gas"] = t.vm.ToValue(tx.Gas()) + gasPriceBig, err := t.toBig(t.vm, env.TxContext.GasPrice.String()) + if err != nil { + t.err = err + t.env.Cancel() + return + } + t.ctx["gasPrice"] = gasPriceBig } // CaptureTxEnd implements the Tracer interface and is invoked at the end of // transaction processing. -func (t *jsTracer) CaptureTxEnd(restGas uint64) { - t.ctx["gasUsed"] = t.vm.ToValue(t.gasLimit - restGas) +func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + if err != nil { + // Don't override vm error + if _, ok := t.ctx["error"]; !ok { + t.ctx["error"] = t.vm.ToValue(err.Error()) + } + return + } + t.ctx["gasUsed"] = t.vm.ToValue(receipt.GasUsed) } // CaptureStart implements the Tracer interface to initialize the tracing operation. -func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.env = env - db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} - t.dbValue = db.setupObject() +func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + cancel := func(err error) { + t.err = err + t.env.Cancel() + } if create { t.ctx["type"] = t.vm.ToValue("CREATE") } else { t.ctx["type"] = t.vm.ToValue("CALL") } - t.ctx["from"] = t.vm.ToValue(from.Bytes()) - t.ctx["to"] = t.vm.ToValue(to.Bytes()) - t.ctx["input"] = t.vm.ToValue(input) - t.ctx["gas"] = t.vm.ToValue(t.gasLimit) - gasPriceBig, err := t.toBig(t.vm, env.TxContext.GasPrice.String()) + fromVal, err := t.toBuf(t.vm, from.Bytes()) if err != nil { - t.err = err + cancel(err) return } - t.ctx["gasPrice"] = gasPriceBig + t.ctx["from"] = fromVal + toVal, err := t.toBuf(t.vm, to.Bytes()) + if err != nil { + cancel(err) + return + } + t.ctx["to"] = toVal + inputVal, err := t.toBuf(t.vm, input) + if err != nil { + cancel(err) + return + } + t.ctx["input"] = inputVal valueBig, err := t.toBig(t.vm, value.String()) if err != nil { t.err = err return } t.ctx["value"] = valueBig - t.ctx["block"] = t.vm.ToValue(env.Context.BlockNumber.Uint64()) - // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) - t.activePrecompiles = vm.ActivePrecompiles(rules) } // CaptureState implements the Tracer interface to trace a single step of VM execution. @@ -292,8 +321,7 @@ func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { - t.ctx["output"] = t.vm.ToValue(output) +func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { if err != nil { t.ctx["error"] = t.vm.ToValue(err.Error()) } @@ -325,7 +353,7 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) { +func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { if !t.traceFrame { return } @@ -572,7 +600,7 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) { if end < begin || begin < 0 { return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end) } - slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin) + slice, err := directory.GetMemoryCopyPadded(mo.memory, begin, end-begin) if err != nil { return nil, err } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index bf6427faf673..11c44fbcc4ab 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -26,8 +26,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "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/eth/tracers/directory" "github.com/ethereum/go-ethereum/params" ) @@ -60,7 +61,7 @@ func testCtx() *vmContext { return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} } -func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { +func runTrace(tracer directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { var ( env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer}) gasLimit uint64 = 31000 @@ -73,12 +74,12 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon contract.Code = contractCode } - tracer.CaptureTxStart(gasLimit) - tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) + tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) + tracer.CaptureStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value) ret, err := env.Interpreter().Run(contract, []byte{}, false) - tracer.CaptureEnd(ret, startGas-contract.Gas, err) + tracer.CaptureEnd(ret, startGas-contract.Gas, err, true) // Rest gas assumes no refund - tracer.CaptureTxEnd(contract.Gas) + tracer.CaptureTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) if err != nil { return nil, err } @@ -180,11 +181,12 @@ func TestHaltBetweenSteps(t *testing.T) { if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) + tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{}), common.Address{}) + tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) @@ -205,8 +207,9 @@ func TestNoStepExec(t *testing.T) { t.Fatal(err) } env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) - tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) - tracer.CaptureEnd(nil, 0, nil) + tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{}), common.Address{}) + tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) + tracer.CaptureEnd(nil, 0, nil, false) ret, err := tracer.GetResult() if err != nil { t.Fatal(err) @@ -261,14 +264,14 @@ func TestIsPrecompile(t *testing.T) { func TestEnterExit(t *testing.T) { // test that either both or none of enter() and exit() are defined - if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(directory.Context), nil); err == nil { t.Fatal("tracer creation should've failed without exit() definition") } - if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil { + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(directory.Context), nil); err != nil { t.Fatal(err) } // test that the enter and exit method are correctly invoked and the values passed - tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil) + tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(directory.Context), nil) if err != nil { t.Fatal(err) } @@ -276,7 +279,7 @@ func TestEnterExit(t *testing.T) { Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) - tracer.CaptureExit([]byte{}, 400, nil) + tracer.CaptureExit([]byte{}, 400, nil, false) have, err := tracer.GetResult() if err != nil { @@ -290,7 +293,7 @@ func TestEnterExit(t *testing.T) { func TestSetup(t *testing.T) { // Test empty config - _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil) + _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(directory.Context), nil) if err != nil { t.Error(err) } @@ -300,12 +303,12 @@ func TestSetup(t *testing.T) { t.Fatal(err) } // Test no setup func - _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg) + _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(directory.Context), cfg) if err != nil { t.Fatal(err) } // Test config value - tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg) + tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(directory.Context), cfg) if err != nil { t.Fatal(err) } diff --git a/eth/tracers/live/printer.go b/eth/tracers/live/printer.go new file mode 100644 index 000000000000..fc6e75900535 --- /dev/null +++ b/eth/tracers/live/printer.go @@ -0,0 +1,134 @@ +package live + +import ( + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/params" +) + +func init() { + directory.LiveDirectory.Register("printer", newPrinter) +} + +type Printer struct{} + +func newPrinter() (core.BlockchainLogger, error) { + return &Printer{}, nil +} + +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. +func (p *Printer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + fmt.Printf("CaptureStart: from=%v, to=%v, create=%v, input=%s, gas=%v, value=%v\n", from, to, create, hexutil.Bytes(input), gas, value) +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (p *Printer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { + fmt.Printf("CaptureEnd: output=%s, gasUsed=%v, err=%v\n", hexutil.Bytes(output), gasUsed, err) +} + +// CaptureState implements the EVMLogger interface to trace a single step of VM execution. +func (p *Printer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { + //fmt.Printf("CaptureState: pc=%v, op=%v, gas=%v, cost=%v, scope=%v, rData=%v, depth=%v, err=%v\n", pc, op, gas, cost, scope, rData, depth, err) +} + +// CaptureFault implements the EVMLogger interface to trace an execution fault. +func (p *Printer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { + fmt.Printf("CaptureFault: pc=%v, op=%v, gas=%v, cost=%v, depth=%v, err=%v\n", pc, op, gas, cost, depth, err) +} + +// CaptureKeccakPreimage is called during the KECCAK256 opcode. +func (p *Printer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} + +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (p *Printer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + fmt.Printf("CaptureEnter: typ=%v, from=%v, to=%v, input=%s, gas=%v, value=%v\n", typ, from, to, hexutil.Bytes(input), gas, value) +} + +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. +func (p *Printer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { + fmt.Printf("CaptureExit: output=%s, gasUsed=%v, err=%v\n", hexutil.Bytes(output), gasUsed, err) +} + +func (p *Printer) OnBeaconBlockRootStart(root common.Hash) {} +func (p *Printer) OnBeaconBlockRootEnd() {} + +func (p *Printer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + buf, err := json.Marshal(tx) + if err != nil { + fmt.Printf("err: %v\n", err) + return + } + fmt.Printf("CaptureTxStart: tx=%s\n", buf) +} + +func (p *Printer) CaptureTxEnd(receipt *types.Receipt, err error) { + if err != nil { + fmt.Printf("CaptureTxEnd err: %v\n", err) + return + } + buf, err := json.Marshal(receipt) + if err != nil { + fmt.Printf("err: %v\n", err) + return + } + fmt.Printf("CaptureTxEnd: receipt=%s\n", buf) +} + +func (p *Printer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, _ *params.ChainConfig) { + if finalized != nil && safe != nil { + fmt.Printf("OnBlockStart: b=%v, td=%v, finalized=%v, safe=%v\n", b.NumberU64(), td, finalized.Number.Uint64(), safe.Number.Uint64()) + } else { + fmt.Printf("OnBlockStart: b=%v, td=%v\n", b.NumberU64(), td) + } +} + +func (p *Printer) OnBlockEnd(err error) { + fmt.Printf("OnBlockEnd: err=%v\n", err) +} + +func (p *Printer) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) { + fmt.Printf("OnGenesisBlock: b=%v, allocLength=%d\n", b.NumberU64(), len(alloc)) +} + +func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { + fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new) +} + +func (p *Printer) OnNonceChange(a common.Address, prev, new uint64) { + fmt.Printf("OnNonceChange: a=%v, prev=%v, new=%v\n", a, prev, new) +} + +func (p *Printer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { + fmt.Printf("OnCodeChange: a=%v, prevCodeHash=%v, prev=%s, codeHash=%v, code=%s\n", a, prevCodeHash, hexutil.Bytes(prev), codeHash, hexutil.Bytes(code)) +} + +func (p *Printer) OnStorageChange(a common.Address, k, prev, new common.Hash) { + fmt.Printf("OnStorageChange: a=%v, k=%v, prev=%v, new=%v\n", a, k, prev, new) +} + +func (p *Printer) OnLog(l *types.Log) { + buf, err := json.Marshal(l) + if err != nil { + fmt.Printf("err: %v\n", err) + return + } + fmt.Printf("OnLog: l=%s\n", buf) +} + +func (p *Printer) OnNewAccount(a common.Address, reset bool) { + fmt.Printf("OnNewAccount: a=%v\n", a) +} + +func (p *Printer) OnGasChange(old, new uint64, reason vm.GasChangeReason) { + fmt.Printf("OnGasChange: old=%v, new=%v, diff=%v\n", old, new, new-old) +} diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 766ee4e4b95c..7df66066b55b 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -17,11 +17,10 @@ package logger import ( - "math/big" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) // accessList is an accumulator for the set of accounts and storage slots an EVM @@ -103,6 +102,7 @@ func (al accessList) accessList() types.AccessList { // AccessListTracer is a tracer that accumulates touched accounts and storage // slots into an internal set. type AccessListTracer struct { + directory.NoopTracer excl map[common.Address]struct{} // Set of account to exclude from the list list accessList // Set of accounts and storage slots touched } @@ -132,9 +132,6 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi } } -func (a *AccessListTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { -} - // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { stack := scope.Stack @@ -158,20 +155,6 @@ func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6 } } -func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { -} - -func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {} - -func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {} - -func (*AccessListTracer) CaptureTxStart(gasLimit uint64) {} - -func (*AccessListTracer) CaptureTxEnd(restGas uint64) {} - // AccessList returns the current accesslist maintained by the tracer. func (a *AccessListTracer) AccessList() types.AccessList { return a.list.accessList() diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 4c9b910a27f1..ae64dc86253b 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -106,15 +107,15 @@ func (s *StructLog) ErrorString() string { // a track record of modified storage which is used in reporting snapshots of the // contract their storage. type StructLogger struct { + directory.NoopTracer cfg Config env *vm.EVM - storage map[common.Address]Storage - logs []StructLog - output []byte - err error - gasLimit uint64 - usedGas uint64 + storage map[common.Address]Storage + logs []StructLog + output []byte + err error + usedGas uint64 interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption @@ -139,11 +140,6 @@ func (l *StructLogger) Reset() { l.err = nil } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (l *StructLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - l.env = env -} - // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. @@ -212,13 +208,8 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s l.logs = append(l.logs, log) } -// CaptureFault implements the EVMLogger interface to trace an execution fault -// while running an opcode. -func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { -} - // CaptureEnd is called after the call finishes to finalize the tracing. -func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { +func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { l.output = output l.err = err if l.cfg.Debug { @@ -229,12 +220,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { } } -func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) { -} - func (l *StructLogger) GetResult() (json.RawMessage, error) { // Tracing aborted if l.reason != nil { @@ -261,12 +246,19 @@ func (l *StructLogger) Stop(err error) { l.interrupt.Store(true) } -func (l *StructLogger) CaptureTxStart(gasLimit uint64) { - l.gasLimit = gasLimit +func (l *StructLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + l.env = env } -func (l *StructLogger) CaptureTxEnd(restGas uint64) { - l.usedGas = l.gasLimit - restGas +func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) { + if err != nil { + // Don't override vm error + if l.err == nil { + l.err = err + } + return + } + l.usedGas = receipt.GasUsed } // StructLogs returns the captured log entries. @@ -326,6 +318,7 @@ func WriteLogs(writer io.Writer, logs []*types.Log) { } type mdLogger struct { + directory.NoopTracer out io.Writer cfg *Config env *vm.EVM @@ -341,8 +334,7 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { return l } -func (t *mdLogger) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.env = env +func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { if !create { fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", from.String(), to.String(), @@ -384,20 +376,11 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } -func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { +func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", output, gasUsed, err) } -func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { -} - -func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} - -func (*mdLogger) CaptureTxStart(gasLimit uint64) {} - -func (*mdLogger) CaptureTxEnd(restGas uint64) {} - // ExecutionResult groups all structured logs emitted by the EVM // while replaying a transaction in debug mode as well as transaction // execution status, the amount of gas used and the return value diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index a2cb4cd9fc59..1bf88f8899ea 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -19,14 +19,16 @@ package logger import ( "encoding/json" "io" - "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) type JSONLogger struct { + directory.NoopTracer encoder *json.Encoder cfg *Config env *vm.EVM @@ -42,10 +44,6 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { return l } -func (l *JSONLogger) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - l.env = env -} - func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.ScopeContext, depth int, err error) { // TODO: Add rData to this interface as well l.CaptureState(pc, op, gas, cost, scope, nil, depth, err) @@ -79,7 +77,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco } // CaptureEnd is triggered at end of execution. -func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { +func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { type endLog struct { Output string `json:"output"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` @@ -92,11 +90,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg}) } -func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + l.env = env } - -func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} - -func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {} - -func (l *JSONLogger) CaptureTxEnd(restGas uint64) {} diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index 3192a15cbab8..a244e3f9ca1b 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -60,7 +60,8 @@ func TestStoreCapture(t *testing.T) { ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash - logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil) + logger.CaptureTxStart(env, nil, common.Address{}) + logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil) _, err := env.Interpreter().Run(contract, []byte{}, false) if err != nil { t.Fatal(err) diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 5a2c4f91115f..c7c45cfaa429 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -23,12 +23,13 @@ import ( "sync/atomic" "github.com/ethereum/go-ethereum/common" + "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/eth/tracers/directory" ) func init() { - tracers.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false) + directory.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false) } // fourByteTracer searches for 4byte-identifiers, and collects them for post-processing. @@ -46,7 +47,8 @@ func init() { // 0xc281d19e-0: 1 // } type fourByteTracer struct { - noopTracer + directory.NoopTracer + env *vm.EVM ids map[string]int // ids aggregates the 4byte ids found interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption @@ -55,7 +57,7 @@ type fourByteTracer struct { // newFourByteTracer returns a native go tracer which collects // 4 byte-identifiers of a tx, and implements vm.EVMLogger. -func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) { +func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (directory.Tracer, error) { t := &fourByteTracer{ ids: make(map[string]int), } @@ -78,13 +80,16 @@ func (t *fourByteTracer) store(id []byte, size int) { t.ids[key] += 1 } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *fourByteTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *fourByteTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + t.env = env // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) + rules := t.env.ChainConfig().Rules(t.env.Context.BlockNumber, t.env.Context.Random != nil, t.env.Context.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) +} - // Save the outer calldata also +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. +func (t *fourByteTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + // Save the outer calldata if len(input) >= 4 { t.store(input[0:4], len(input)-4) } diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index f85cf6206a72..43bd0198da73 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -25,15 +25,15 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "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/log" + "github.com/ethereum/go-ethereum/eth/tracers/directory" ) //go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go func init() { - tracers.DefaultDirectory.Register("callTracer", newCallTracer, false) + directory.DefaultDirectory.Register("callTracer", newCallTracer, false) } type callLog struct { @@ -59,7 +59,8 @@ 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 *big.Int `json:"value,omitempty" rlp:"optional"` + revertedSnapshot bool } func (f callFrame) TypeString() string { @@ -67,16 +68,17 @@ func (f callFrame) TypeString() string { } func (f callFrame) failed() bool { - return len(f.Error) > 0 + return len(f.Error) > 0 && f.revertedSnapshot } -func (f *callFrame) processOutput(output []byte, err error) { +func (f *callFrame) processOutput(output []byte, err error, reverted bool) { output = common.CopyBytes(output) if err == nil { f.Output = output return } f.Error = err.Error() + f.revertedSnapshot = reverted if f.Type == vm.CREATE || f.Type == vm.CREATE2 { f.To = nil } @@ -102,10 +104,11 @@ type callFrameMarshaling struct { } type callTracer struct { - noopTracer + directory.NoopTracer callstack []callFrame config callTracerConfig gasLimit uint64 + depth int interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption } @@ -117,7 +120,7 @@ type callTracerConfig struct { // newCallTracer returns a native go tracer which tracks // call frames of a tx, and implements vm.EVMLogger. -func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config callTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -130,7 +133,7 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, e } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *callTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { toCopy := to t.callstack[0] = callFrame{ Type: vm.CALL, @@ -146,63 +149,17 @@ func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { - t.callstack[0].processOutput(output, err) +func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { + t.callstack[0].processOutput(output, err, reverted) } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - // skip if the previous op caused an error - if err != nil { - return - } - // Only logs need to be captured via opcode processing - if !t.config.WithLog { - return - } - // Avoid processing nested calls when only caring about top call - if t.config.OnlyTopCall && depth > 0 { - return - } - // Skip if tracing was interrupted - if t.interrupt.Load() { - return - } - switch op { - case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4: - size := int(op - vm.LOG0) - - stack := scope.Stack - stackData := stack.Data() - - // Don't modify the stack - mStart := stackData[len(stackData)-1] - mSize := stackData[len(stackData)-2] - topics := make([]common.Hash, size) - for i := 0; i < size; i++ { - topic := stackData[len(stackData)-2-(i+1)] - topics[i] = common.Hash(topic.Bytes32()) - } - - data, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(mStart.Uint64()), int64(mSize.Uint64())) - if err != nil { - // mSize was unrealistically large - log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "callTracer", "offset", mStart, "size", mSize) - return - } - - log := callLog{ - Address: scope.Contract.Address(), - Topics: topics, - Data: hexutil.Bytes(data), - Position: hexutil.Uint(len(t.callstack[len(t.callstack)-1].Calls)), - } - t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log) - } } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + t.depth++ if t.config.OnlyTopCall { return } @@ -225,7 +182,8 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common. // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { +func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { + t.depth-- if t.config.OnlyTopCall { return } @@ -239,22 +197,48 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { size -= 1 call.GasUsed = gasUsed - call.processOutput(output, err) + call.processOutput(output, err, reverted) t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) } -func (t *callTracer) CaptureTxStart(gasLimit uint64) { - t.gasLimit = gasLimit +func (t *callTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + t.gasLimit = tx.Gas() } -func (t *callTracer) CaptureTxEnd(restGas uint64) { - t.callstack[0].GasUsed = t.gasLimit - restGas +func (t *callTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + // Error happened during tx validation. + if err != nil { + return + } + t.callstack[0].GasUsed = receipt.GasUsed if t.config.WithLog { // Logs are not emitted when the call fails clearFailedLogs(&t.callstack[0], false) } } +func (t *callTracer) OnLog(log *types.Log) { + // Only logs need to be captured via opcode processing + if !t.config.WithLog { + return + } + // Avoid processing nested calls when only caring about top call + if t.config.OnlyTopCall && t.depth > 0 { + return + } + // Skip if tracing was interrupted + if t.interrupt.Load() { + return + } + l := callLog{ + Address: log.Address, + Topics: log.Topics, + Data: log.Data, + Position: hexutil.Uint(len(t.callstack[len(t.callstack)-1].Calls)), + } + t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, l) +} + // GetResult returns the json-encoded nested list of call traces, and any // error arising from the encoding or forceful termination (via `Stop`). func (t *callTracer) GetResult() (json.RawMessage, error) { @@ -266,7 +250,7 @@ func (t *callTracer) GetResult() (json.RawMessage, error) { if err != nil { return nil, err } - return json.RawMessage(res), t.reason + return res, t.reason } // Stop terminates execution of the tracer at the first opportune moment. diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 266ab9900146..26f114f44fe3 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -25,15 +25,16 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "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/eth/tracers/directory" ) //go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go //go:generate go run github.com/fjl/gencodec -type flatCallResult -field-override flatCallResultMarshaling -out gen_flatcallresult_json.go func init() { - tracers.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false) + directory.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false) } var parityErrorMapping = map[string]string{ @@ -108,11 +109,12 @@ type flatCallResultMarshaling struct { // flatCallTracer reports call frame information of a tx in a flat format, i.e. // as opposed to the nested format of `callTracer`. type flatCallTracer struct { + directory.NoopTracer tracer *callTracer config flatCallTracerConfig - ctx *tracers.Context // Holds tracer context data - reason error // Textual reason for the interruption - activePrecompiles []common.Address // Updated on CaptureStart based on given rules + ctx *directory.Context // Holds tracer context data + reason error // Textual reason for the interruption + activePrecompiles []common.Address // Updated on CaptureStart based on given rules } type flatCallTracerConfig struct { @@ -121,7 +123,7 @@ type flatCallTracerConfig struct { } // newFlatCallTracer returns a new flatCallTracer. -func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config flatCallTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -131,7 +133,7 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace // Create inner call tracer with default configuration, don't forward // the OnlyTopCall or WithLog to inner for now - tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, nil) + tracer, err := directory.DefaultDirectory.New("callTracer", ctx, nil) if err != nil { return nil, err } @@ -144,16 +146,13 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *flatCallTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.tracer.CaptureStart(env, from, to, create, input, gas, value) - // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) - t.activePrecompiles = vm.ActivePrecompiles(rules) +func (t *flatCallTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { + t.tracer.CaptureStart(from, to, create, input, gas, value) } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { - t.tracer.CaptureEnd(output, gasUsed, err) +func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { + t.tracer.CaptureEnd(output, gasUsed, err, reverted) } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. @@ -179,8 +178,8 @@ func (t *flatCallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to com // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { - t.tracer.CaptureExit(output, gasUsed, err) +func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { + t.tracer.CaptureExit(output, gasUsed, err, reverted) // Parity traces don't include CALL/STATICCALLs to precompiles. // By default we remove them from the callstack. @@ -201,12 +200,15 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { } } -func (t *flatCallTracer) CaptureTxStart(gasLimit uint64) { - t.tracer.CaptureTxStart(gasLimit) +func (t *flatCallTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + t.tracer.CaptureTxStart(env, tx, from) + // Update list of precompiles based on current block + rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) + t.activePrecompiles = vm.ActivePrecompiles(rules) } -func (t *flatCallTracer) CaptureTxEnd(restGas uint64) { - t.tracer.CaptureTxEnd(restGas) +func (t *flatCallTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + t.tracer.CaptureTxEnd(receipt, err) } // GetResult returns an empty json object. @@ -242,7 +244,7 @@ func (t *flatCallTracer) isPrecompiled(addr common.Address) bool { return false } -func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) { +func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *directory.Context) (output []flatCallFrame, err error) { var frame *flatCallFrame switch input.Type { case vm.CREATE, vm.CREATE2: @@ -341,7 +343,7 @@ func newFlatSelfdestruct(input *callFrame) *flatCallFrame { } } -func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) { +func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *directory.Context) { if ctx == nil { return } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index db8ddd64380d..1175cc768a3a 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -21,33 +21,35 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "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/eth/tracers/directory" ) func init() { - tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false) + directory.DefaultDirectory.Register("muxTracer", newMuxTracer, false) } // muxTracer is a go implementation of the Tracer interface which // runs multiple tracers in one go. type muxTracer struct { names []string - tracers []tracers.Tracer + tracers []directory.Tracer } // newMuxTracer returns a new mux tracer. -func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config map[string]json.RawMessage if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { return nil, err } } - objects := make([]tracers.Tracer, 0, len(config)) + objects := make([]directory.Tracer, 0, len(config)) names := make([]string, 0, len(config)) for k, v := range config { - t, err := tracers.DefaultDirectory.New(k, ctx, v) + t, err := directory.DefaultDirectory.New(k, ctx, v) if err != nil { return nil, err } @@ -59,16 +61,16 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, er } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *muxTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { - t.CaptureStart(env, from, to, create, input, gas, value) + t.CaptureStart(from, to, create, input, gas, value) } } // CaptureEnd is called after the call finishes to finalize the tracing. -func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { +func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { for _, t := range t.tracers { - t.CaptureEnd(output, gasUsed, err) + t.CaptureEnd(output, gasUsed, err, reverted) } } @@ -86,6 +88,20 @@ func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scop } } +// CaptureKeccakPreimage is called during the KECCAK256 opcode. +func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { + for _, t := range t.tracers { + t.CaptureKeccakPreimage(hash, data) + } +} + +// CaptureGasConsumed is called when gas is consumed. +func (t *muxTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) { + for _, t := range t.tracers { + t.OnGasChange(old, new, reason) + } +} + // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { @@ -95,21 +111,57 @@ func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.A // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error) { +func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { + for _, t := range t.tracers { + t.CaptureExit(output, gasUsed, err, reverted) + } +} + +func (t *muxTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + for _, t := range t.tracers { + t.CaptureTxStart(env, tx, from) + } +} + +func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + for _, t := range t.tracers { + t.CaptureTxEnd(receipt, err) + } +} + +func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { + for _, t := range t.tracers { + t.OnBalanceChange(a, prev, new, reason) + } +} + +func (t *muxTracer) OnNonceChange(a common.Address, prev, new uint64) { + for _, t := range t.tracers { + t.OnNonceChange(a, prev, new) + } +} + +func (t *muxTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { + for _, t := range t.tracers { + t.OnCodeChange(a, prevCodeHash, prev, codeHash, code) + } +} + +func (t *muxTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) { for _, t := range t.tracers { - t.CaptureExit(output, gasUsed, err) + t.OnStorageChange(a, k, prev, new) } } -func (t *muxTracer) CaptureTxStart(gasLimit uint64) { +func (t *muxTracer) OnLog(log *types.Log) { for _, t := range t.tracers { - t.CaptureTxStart(gasLimit) + t.OnLog(log) } } -func (t *muxTracer) CaptureTxEnd(restGas uint64) { +func (t *muxTracer) OnNewAccount(a common.Address, reset bool) { for _, t := range t.tracers { - t.CaptureTxEnd(restGas) + t.OnNewAccount(a, reset) } } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 82451c40a65f..8608ab69dcac 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -24,25 +24,27 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/log" ) //go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go func init() { - tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false) + directory.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false) } -type state = map[common.Address]*account +type stateMap = map[common.Address]*account type account struct { Balance *big.Int `json:"balance,omitempty"` Code []byte `json:"code,omitempty"` Nonce uint64 `json:"nonce,omitempty"` Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + empty bool } func (a *account) exists() bool { @@ -55,13 +57,11 @@ type accountMarshaling struct { } type prestateTracer struct { - noopTracer + directory.NoopTracer env *vm.EVM - pre state - post state - create bool + pre stateMap + post stateMap to common.Address - gasLimit uint64 // Amount of gas bought for the whole tx config prestateTracerConfig interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption @@ -73,7 +73,7 @@ type prestateTracerConfig struct { DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications } -func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { var config prestateTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -81,57 +81,14 @@ func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace } } return &prestateTracer{ - pre: state{}, - post: state{}, + pre: stateMap{}, + post: stateMap{}, config: config, created: make(map[common.Address]bool), deleted: make(map[common.Address]bool), }, nil } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *prestateTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.env = env - t.create = create - t.to = to - - t.lookupAccount(from) - t.lookupAccount(to) - t.lookupAccount(env.Context.Coinbase) - - // The recipient balance includes the value transferred. - toBal := new(big.Int).Sub(t.pre[to].Balance, value) - t.pre[to].Balance = toBal - - // The sender balance is after reducing: value and gasLimit. - // We need to re-add them to get the pre-tx balance. - fromBal := new(big.Int).Set(t.pre[from].Balance) - gasPrice := env.TxContext.GasPrice - consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit)) - fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas)) - t.pre[from].Balance = fromBal - t.pre[from].Nonce-- - - if create && t.config.DiffMode { - t.created[to] = true - } -} - -// CaptureEnd is called after the call finishes to finalize the tracing. -func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error) { - if t.config.DiffMode { - return - } - - if t.create { - // Keep existing account prior to contract creation at that address - if s := t.pre[t.to]; s != nil && !s.exists() { - // Exclude newly created contract. - delete(t.pre, t.to) - } - } -} - // CaptureState implements the EVMLogger interface to trace a single step of VM execution. func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { if err != nil { @@ -166,7 +123,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, case stackLen >= 4 && op == vm.CREATE2: offset := stackData[stackLen-2] size := stackData[stackLen-3] - init, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64())) + init, err := directory.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64())) if err != nil { log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size) return @@ -179,15 +136,62 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, } } -func (t *prestateTracer) CaptureTxStart(gasLimit uint64) { - t.gasLimit = gasLimit +func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + t.env = env + if tx.To() == nil { + t.to = crypto.CreateAddress(from, env.StateDB.GetNonce(from)) + t.created[t.to] = true + } else { + t.to = *tx.To() + } + + t.lookupAccount(from) + t.lookupAccount(t.to) + t.lookupAccount(env.Context.Coinbase) } -func (t *prestateTracer) CaptureTxEnd(restGas uint64) { - if !t.config.DiffMode { +func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + if err != nil { return } + if t.config.DiffMode { + t.processDiffState() + } + // the new created contracts' prestate were empty, so delete them + for a := range t.created { + // the created contract maybe exists in statedb before the creating tx + if s := t.pre[a]; s != nil && s.empty { + delete(t.pre, a) + } + } +} +// GetResult returns the json-encoded nested list of call traces, and any +// error arising from the encoding or forceful termination (via `Stop`). +func (t *prestateTracer) GetResult() (json.RawMessage, error) { + var res []byte + var err error + if t.config.DiffMode { + res, err = json.Marshal(struct { + Post stateMap `json:"post"` + Pre stateMap `json:"pre"` + }{t.post, t.pre}) + } else { + res, err = json.Marshal(t.pre) + } + if err != nil { + return nil, err + } + return json.RawMessage(res), t.reason +} + +// Stop terminates execution of the tracer at the first opportune moment. +func (t *prestateTracer) Stop(err error) { + t.reason = err + t.interrupt.Store(true) +} + +func (t *prestateTracer) processDiffState() { for addr, state := range t.pre { // The deleted account's state is pruned from `post` but kept in `pre` if _, ok := t.deleted[addr]; ok { @@ -237,38 +241,6 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) { delete(t.pre, addr) } } - // the new created contracts' prestate were empty, so delete them - for a := range t.created { - // the created contract maybe exists in statedb before the creating tx - if s := t.pre[a]; s != nil && !s.exists() { - delete(t.pre, a) - } - } -} - -// GetResult returns the json-encoded nested list of call traces, and any -// error arising from the encoding or forceful termination (via `Stop`). -func (t *prestateTracer) GetResult() (json.RawMessage, error) { - var res []byte - var err error - if t.config.DiffMode { - res, err = json.Marshal(struct { - Post state `json:"post"` - Pre state `json:"pre"` - }{t.post, t.pre}) - } else { - res, err = json.Marshal(t.pre) - } - if err != nil { - return nil, err - } - return json.RawMessage(res), t.reason -} - -// Stop terminates execution of the tracer at the first opportune moment. -func (t *prestateTracer) Stop(err error) { - t.reason = err - t.interrupt.Store(true) } // lookupAccount fetches details of an account and adds it to the prestate @@ -278,12 +250,16 @@ func (t *prestateTracer) lookupAccount(addr common.Address) { return } - t.pre[addr] = &account{ + acc := &account{ Balance: t.env.StateDB.GetBalance(addr), Nonce: t.env.StateDB.GetNonce(addr), Code: t.env.StateDB.GetCode(addr), Storage: make(map[common.Hash]common.Hash), } + if !acc.exists() { + acc.empty = true + } + t.pre[addr] = acc } // lookupStorage fetches the requested storage slot and adds diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index b4989ec98445..ddfdb81ec661 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -111,41 +111,3 @@ func BenchmarkTransactionTrace(b *testing.B) { tracer.Reset() } } - -func TestMemCopying(t *testing.T) { - for i, tc := range []struct { - memsize int64 - offset int64 - size int64 - wantErr string - wantSize int - }{ - {0, 0, 100, "", 100}, // Should pad up to 100 - {0, 100, 0, "", 0}, // No need to pad (0 size) - {100, 50, 100, "", 100}, // Should pad 100-150 - {100, 50, 5, "", 5}, // Wanted range fully within memory - {100, -50, 0, "offset or size must not be negative", 0}, // Errror - {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror - {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror - - } { - mem := vm.NewMemory() - mem.Resize(uint64(tc.memsize)) - cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size) - if want := tc.wantErr; want != "" { - if err == nil { - t.Fatalf("test %d: want '%v' have no error", i, want) - } - if have := err.Error(); want != have { - t.Fatalf("test %d: want '%v' have '%v'", i, want, have) - } - continue - } - if err != nil { - t.Fatalf("test %d: unexpected error: %v", i, err) - } - if want, have := tc.wantSize, len(cpy); have != want { - t.Fatalf("test %d: want %v have %v", i, want, have) - } - } -} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 38a7924124a6..4f792260ede3 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -945,41 +945,41 @@ type OverrideAccount struct { type StateOverride map[common.Address]OverrideAccount // Apply overrides the fields of specified accounts into the given state. -func (diff *StateOverride) Apply(state *state.StateDB) error { +func (diff *StateOverride) Apply(statedb *state.StateDB) error { if diff == nil { return nil } for addr, account := range *diff { // Override account nonce. if account.Nonce != nil { - state.SetNonce(addr, uint64(*account.Nonce)) + statedb.SetNonce(addr, uint64(*account.Nonce)) } // Override account(contract) code. if account.Code != nil { - state.SetCode(addr, *account.Code) + statedb.SetCode(addr, *account.Code) } // Override account balance. if account.Balance != nil { - state.SetBalance(addr, (*big.Int)(*account.Balance)) + statedb.SetBalance(addr, (*big.Int)(*account.Balance), state.BalanceChangeUnspecified) } if account.State != nil && account.StateDiff != nil { return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) } // Replace entire state if caller requires. if account.State != nil { - state.SetStorage(addr, *account.State) + statedb.SetStorage(addr, *account.State) } // Apply state diff into specified accounts. if account.StateDiff != nil { for key, value := range *account.StateDiff { - state.SetState(addr, key, value) + statedb.SetState(addr, key, value) } } } // Now finalize the changes. Finalize is normally performed between transactions. // By using finalize, the overrides are semantically behaving as // if they were created in a transaction just before the tracing occur. - state.Finalise(false) + statedb.Finalise(false) return nil } diff --git a/light/odr_test.go b/light/odr_test.go index c415d73e7ef2..44450fc2ff74 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -201,7 +201,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain } // Perform read-only call. - st.SetBalance(testBankAddress, math.MaxBig256) + st.SetBalance(testBankAddress, math.MaxBig256, state.BalanceChangeUnspecified) msg := &core.Message{ From: testBankAddress, To: &testContractAddr, diff --git a/miner/worker.go b/miner/worker.go index f68070281454..a5ea9571a9c8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -784,7 +784,7 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction) (*typ snap = env.state.Snapshot() gp = env.gasPool.Gas() ) - receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) + receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) if err != nil { env.state.RevertToSnapshot(snap) env.gasPool.SetGas(gp) @@ -976,7 +976,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { if header.ParentBeaconRoot != nil { context := core.NewEVMBlockContext(header, w.chain, nil) vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}) - core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) + core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state, nil) } return env, nil } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 745a3c6b287a..d959165e541a 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -298,7 +298,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // - the coinbase self-destructed, or // - there are only 'bad' transactions, which aren't executed. In those cases, // the coinbase gets no txfee, so isn't created, and thus needs to be touched - statedb.AddBalance(block.Coinbase(), new(big.Int)) + statedb.AddBalance(block.Coinbase(), new(big.Int), state.BalanceChangeUnspecified) // Commit state mutations into database. root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number())) @@ -322,7 +322,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance) + statedb.SetBalance(addr, a.Balance, state.BalanceChangeUnspecified) for k, v := range a.Storage { statedb.SetState(addr, k, v) } From 9f71824d8c37f1ff97fea635d3503e2bae37481a Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Tue, 6 Feb 2024 15:36:12 -0500 Subject: [PATCH 03/50] Backported commits from `extended-tracer` - https://github.com/s1na/go-ethereum/commit/6c44a594f4423664cc363ea4e9e50223a4597139 - https://github.com/s1na/go-ethereum/commit/188cd4182e3dee1c7ac5fc91b0c29cde2c2eb48f - https://github.com/s1na/go-ethereum/commit/f3c0a89b574c34dc88349c0b3df9d8b1e7e707ab - https://github.com/s1na/go-ethereum/commit/95b50299a5c4f96ccdd44fe73d543f28629e1938 - https://github.com/s1na/go-ethereum/commit/2cc09548bb86736718ff4c897c1b30e495ac31a6 --- core/blockchain.go | 10 ++- core/state/statedb.go | 20 +----- core/state_processor.go | 2 - eth/tracers/live/noop.go | 98 ++++++++++++++++++++++++++ eth/tracers/live/printer.go | 134 ------------------------------------ 5 files changed, 107 insertions(+), 157 deletions(-) create mode 100644 eth/tracers/live/noop.go delete mode 100644 eth/tracers/live/printer.go diff --git a/core/blockchain.go b/core/blockchain.go index f9278d190888..42648345d55e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -190,9 +190,10 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig { type BlockchainLogger interface { vm.EVMLogger state.StateLogger + OnBlockchainInit(chainConfig *params.ChainConfig) // OnBlockStart is called before executing `block`. // `td` is the total difficulty prior to `block`. - OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header, chainConfig *params.ChainConfig) + OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header) OnBlockEnd(err error) OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc) OnBeaconBlockRootStart(root common.Hash) @@ -501,6 +502,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } rawdb.WriteChainConfig(db, genesisHash, chainConfig) } + if bc.logger != nil { + bc.logger.OnBlockchainInit(chainConfig) + } // Start tx indexer/unindexer if required. if txLookupLimit != nil { bc.txLookupLimit = *txLookupLimit @@ -1804,7 +1808,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) } stats.processed++ if bc.logger != nil { - bc.logger.OnBlockStart(block, bc.GetTd(block.ParentHash(), block.NumberU64()-1), bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), bc.chainConfig) + bc.logger.OnBlockStart(block, bc.GetTd(block.ParentHash(), block.NumberU64()-1), bc.CurrentFinalBlock(), bc.CurrentSafeBlock()) bc.logger.OnBlockEnd(nil) } @@ -1933,7 +1937,7 @@ type blockProcessingResult struct { func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { if bc.logger != nil { td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) - bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), bc.chainConfig) + bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock()) defer func() { bc.logger.OnBlockEnd(blockEndErr) }() diff --git a/core/state/statedb.go b/core/state/statedb.go index 618b1cf042a9..838cbbe9a9d6 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -124,9 +124,6 @@ type StateDB struct { // Preimages occurred seen by VM in the scope of block. preimages map[common.Hash][]byte - // Enabled precompile contracts - precompiles map[common.Address]struct{} - // Per-transaction access list accessList *accessList @@ -183,7 +180,6 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) stateObjectsDestruct: make(map[common.Address]*types.StateAccount), logs: make(map[common.Hash][]*types.Log), preimages: make(map[common.Hash][]byte), - precompiles: make(map[common.Address]struct{}), journal: newJournal(), accessList: newAccessList(), transientStorage: newTransientStorage(), @@ -667,11 +663,7 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! newobj = newObject(s, addr, nil) if s.logger != nil { - // Precompiled contracts are touched during a call. - // Make sure we avoid emitting a new account event for them. - if _, ok := s.precompiles[addr]; !ok { - s.logger.OnNewAccount(addr, prev != nil) - } + s.logger.OnNewAccount(addr, prev != nil) } if prev == nil { s.journal.append(createObjectChange{account: &addr}) @@ -883,7 +875,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { obj.deleted = true // If ether was sent to account post-selfdestruct it is burnt. - if bal := obj.Balance(); bal.Sign() != 0 && s.logger != nil { + if bal := obj.Balance(); s.logger != nil && obj.selfDestructed && bal.Sign() != 0 { s.logger.OnBalanceChange(obj.address, bal, new(big.Int), BalanceDecreaseSelfdestructBurn) } // We need to maintain account deletions explicitly (will remain @@ -1391,14 +1383,6 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d s.transientStorage = newTransientStorage() } -// PrepareBlock prepares the statedb for execution of a block. It tracks -// the addresses of enabled precompiles for debugging purposes. -func (s *StateDB) PrepareBlock(precompiles []common.Address) { - for _, addr := range precompiles { - s.precompiles[addr] = struct{}{} - } -} - // AddAddressToAccessList adds the given address to the access list func (s *StateDB) AddAddressToAccessList(addr common.Address) { if s.accessList.AddAddress(addr) { diff --git a/core/state_processor.go b/core/state_processor.go index b468dce3afdb..231f63f9690f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -75,13 +75,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg var ( context = NewEVMBlockContext(header, p.bc, nil) vmenv = vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) - rules = vmenv.ChainConfig().Rules(context.BlockNumber, context.Random != nil, context.Time) signer = types.MakeSigner(p.config, header.Number, header.Time) ) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb, p.bc.logger) } - statedb.PrepareBlock(vm.ActivePrecompiles(rules)) // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go new file mode 100644 index 000000000000..822fbbaea334 --- /dev/null +++ b/eth/tracers/live/noop.go @@ -0,0 +1,98 @@ +package live + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/params" +) + +func init() { + directory.LiveDirectory.Register("noop", newNoopTracer) +} + +// noop is a no-op live tracer. It's there to +// catch changes in the tracing interface, as well as +// for testing live tracing performance. Can be removed +// as soon as we have a real live tracer. +type noop struct{} + +func newNoopTracer() (core.BlockchainLogger, error) { + return &noop{}, nil +} + +// CaptureStart implements the EVMLogger interface to initialize the tracing operation. +func (t *noop) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +} + +// CaptureEnd is called after the call finishes to finalize the tracing. +func (t *noop) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { +} + +// CaptureState implements the EVMLogger interface to trace a single step of VM execution. +func (t *noop) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +} + +// CaptureFault implements the EVMLogger interface to trace an execution fault. +func (t *noop) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { +} + +// CaptureKeccakPreimage is called during the KECCAK256 opcode. +func (t *noop) CaptureKeccakPreimage(hash common.Hash, data []byte) {} + +// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *noop) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +} + +// CaptureExit is called when EVM exits a scope, even if the scope didn't +// execute any code. +func (t *noop) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { +} + +func (t *noop) OnBeaconBlockRootStart(root common.Hash) {} +func (t *noop) OnBeaconBlockRootEnd() {} + +func (t *noop) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +} + +func (t *noop) CaptureTxEnd(receipt *types.Receipt, err error) { +} + +func (t *noop) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header) { +} + +func (t *noop) OnBlockEnd(err error) { +} + +func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) { +} + +func (t *noop) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) { +} + +func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { +} + +func (t *noop) OnNonceChange(a common.Address, prev, new uint64) { +} + +func (t *noop) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { +} + +func (t *noop) OnStorageChange(a common.Address, k, prev, new common.Hash) { +} + +func (t *noop) OnLog(l *types.Log) { + +} + +func (t *noop) OnNewAccount(a common.Address, reset bool) { +} + +func (t *noop) OnGasChange(old, new uint64, reason vm.GasChangeReason) { +} diff --git a/eth/tracers/live/printer.go b/eth/tracers/live/printer.go deleted file mode 100644 index fc6e75900535..000000000000 --- a/eth/tracers/live/printer.go +++ /dev/null @@ -1,134 +0,0 @@ -package live - -import ( - "encoding/json" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers/directory" - "github.com/ethereum/go-ethereum/params" -) - -func init() { - directory.LiveDirectory.Register("printer", newPrinter) -} - -type Printer struct{} - -func newPrinter() (core.BlockchainLogger, error) { - return &Printer{}, nil -} - -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (p *Printer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - fmt.Printf("CaptureStart: from=%v, to=%v, create=%v, input=%s, gas=%v, value=%v\n", from, to, create, hexutil.Bytes(input), gas, value) -} - -// CaptureEnd is called after the call finishes to finalize the tracing. -func (p *Printer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { - fmt.Printf("CaptureEnd: output=%s, gasUsed=%v, err=%v\n", hexutil.Bytes(output), gasUsed, err) -} - -// CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (p *Printer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - //fmt.Printf("CaptureState: pc=%v, op=%v, gas=%v, cost=%v, scope=%v, rData=%v, depth=%v, err=%v\n", pc, op, gas, cost, scope, rData, depth, err) -} - -// CaptureFault implements the EVMLogger interface to trace an execution fault. -func (p *Printer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { - fmt.Printf("CaptureFault: pc=%v, op=%v, gas=%v, cost=%v, depth=%v, err=%v\n", pc, op, gas, cost, depth, err) -} - -// CaptureKeccakPreimage is called during the KECCAK256 opcode. -func (p *Printer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} - -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (p *Printer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { - fmt.Printf("CaptureEnter: typ=%v, from=%v, to=%v, input=%s, gas=%v, value=%v\n", typ, from, to, hexutil.Bytes(input), gas, value) -} - -// CaptureExit is called when EVM exits a scope, even if the scope didn't -// execute any code. -func (p *Printer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { - fmt.Printf("CaptureExit: output=%s, gasUsed=%v, err=%v\n", hexutil.Bytes(output), gasUsed, err) -} - -func (p *Printer) OnBeaconBlockRootStart(root common.Hash) {} -func (p *Printer) OnBeaconBlockRootEnd() {} - -func (p *Printer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { - buf, err := json.Marshal(tx) - if err != nil { - fmt.Printf("err: %v\n", err) - return - } - fmt.Printf("CaptureTxStart: tx=%s\n", buf) -} - -func (p *Printer) CaptureTxEnd(receipt *types.Receipt, err error) { - if err != nil { - fmt.Printf("CaptureTxEnd err: %v\n", err) - return - } - buf, err := json.Marshal(receipt) - if err != nil { - fmt.Printf("err: %v\n", err) - return - } - fmt.Printf("CaptureTxEnd: receipt=%s\n", buf) -} - -func (p *Printer) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, _ *params.ChainConfig) { - if finalized != nil && safe != nil { - fmt.Printf("OnBlockStart: b=%v, td=%v, finalized=%v, safe=%v\n", b.NumberU64(), td, finalized.Number.Uint64(), safe.Number.Uint64()) - } else { - fmt.Printf("OnBlockStart: b=%v, td=%v\n", b.NumberU64(), td) - } -} - -func (p *Printer) OnBlockEnd(err error) { - fmt.Printf("OnBlockEnd: err=%v\n", err) -} - -func (p *Printer) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) { - fmt.Printf("OnGenesisBlock: b=%v, allocLength=%d\n", b.NumberU64(), len(alloc)) -} - -func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { - fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new) -} - -func (p *Printer) OnNonceChange(a common.Address, prev, new uint64) { - fmt.Printf("OnNonceChange: a=%v, prev=%v, new=%v\n", a, prev, new) -} - -func (p *Printer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { - fmt.Printf("OnCodeChange: a=%v, prevCodeHash=%v, prev=%s, codeHash=%v, code=%s\n", a, prevCodeHash, hexutil.Bytes(prev), codeHash, hexutil.Bytes(code)) -} - -func (p *Printer) OnStorageChange(a common.Address, k, prev, new common.Hash) { - fmt.Printf("OnStorageChange: a=%v, k=%v, prev=%v, new=%v\n", a, k, prev, new) -} - -func (p *Printer) OnLog(l *types.Log) { - buf, err := json.Marshal(l) - if err != nil { - fmt.Printf("err: %v\n", err) - return - } - fmt.Printf("OnLog: l=%s\n", buf) -} - -func (p *Printer) OnNewAccount(a common.Address, reset bool) { - fmt.Printf("OnNewAccount: a=%v\n", a) -} - -func (p *Printer) OnGasChange(old, new uint64, reason vm.GasChangeReason) { - fmt.Printf("OnGasChange: old=%v, new=%v, diff=%v\n", old, new, new-old) -} From 31b3b7dca3d9562b41f121c54a60180562718c74 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 8 Feb 2024 15:57:12 -0500 Subject: [PATCH 04/50] rm OnNewAccount Backport of https://github.com/s1na/go-ethereum/commit/57cd0c30df8ca897a1ba1bd805822ee89e38869f --- core/state/statedb.go | 7 ------- eth/tracers/directory/noop.go | 2 -- eth/tracers/live/noop.go | 3 --- eth/tracers/native/mux.go | 6 ------ 4 files changed, 18 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 838cbbe9a9d6..0c2a5b5aefb0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -59,10 +59,6 @@ type StateLogger interface { OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) OnLog(log *types.Log) - // OnNewAccount is called when a new account is created. - // Reset indicates an account existed at that address - // which will be replaced. - OnNewAccount(addr common.Address, reset bool) } // StateDB structs within the ethereum protocol are used to store anything @@ -662,9 +658,6 @@ func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! newobj = newObject(s, addr, nil) - if s.logger != nil { - s.logger.OnNewAccount(addr, prev != nil) - } if prev == nil { s.journal.append(createObjectChange{account: &addr}) } else { diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index e784b6c8462e..5f6546cfbebc 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -86,8 +86,6 @@ func (*NoopTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) { func (*NoopTracer) OnLog(log *types.Log) {} -func (*NoopTracer) OnNewAccount(a common.Address, reset bool) {} - // GetResult returns an empty json object. func (t *NoopTracer) GetResult() (json.RawMessage, error) { return json.RawMessage(`{}`), nil diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index 822fbbaea334..2984c6ee8366 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -91,8 +91,5 @@ func (t *noop) OnLog(l *types.Log) { } -func (t *noop) OnNewAccount(a common.Address, reset bool) { -} - func (t *noop) OnGasChange(old, new uint64, reason vm.GasChangeReason) { } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 1175cc768a3a..70af63e752a3 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -159,12 +159,6 @@ func (t *muxTracer) OnLog(log *types.Log) { } } -func (t *muxTracer) OnNewAccount(a common.Address, reset bool) { - for _, t := range t.tracers { - t.OnNewAccount(a, reset) - } -} - // GetResult returns an empty json object. func (t *muxTracer) GetResult() (json.RawMessage, error) { resObject := make(map[string]json.RawMessage) From 7ff2ae6924aeed14b9b248483f27ceb4769d2153 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 8 Feb 2024 16:04:15 -0500 Subject: [PATCH 05/50] indicate Known block Backport of https://github.com/s1na/go-ethereum/commit/056f2292602b9f8ae59dd14611234ec7f20653bc --- core/blockchain.go | 10 +++++++--- eth/tracers/live/noop.go | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 42648345d55e..92e122cbaab9 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -193,7 +193,11 @@ type BlockchainLogger interface { OnBlockchainInit(chainConfig *params.ChainConfig) // OnBlockStart is called before executing `block`. // `td` is the total difficulty prior to `block`. - OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header) + // `skip` indicates processing of this previously known block + // will be skipped. OnBlockStart and OnBlockEnd will be emitted to + // convey how chain is progressing. E.g. known blocks will be skipped + // when node is started after a crash. + OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header, skip bool) OnBlockEnd(err error) OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc) OnBeaconBlockRootStart(root common.Hash) @@ -1808,7 +1812,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) } stats.processed++ if bc.logger != nil { - bc.logger.OnBlockStart(block, bc.GetTd(block.ParentHash(), block.NumberU64()-1), bc.CurrentFinalBlock(), bc.CurrentSafeBlock()) + bc.logger.OnBlockStart(block, bc.GetTd(block.ParentHash(), block.NumberU64()-1), bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), true) bc.logger.OnBlockEnd(nil) } @@ -1937,7 +1941,7 @@ type blockProcessingResult struct { func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { if bc.logger != nil { td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) - bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock()) + bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), false) defer func() { bc.logger.OnBlockEnd(blockEndErr) }() diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index 2984c6ee8366..beb532a42366 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -63,7 +63,7 @@ func (t *noop) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Ad func (t *noop) CaptureTxEnd(receipt *types.Receipt, err error) { } -func (t *noop) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header) { +func (t *noop) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, skip bool) { } func (t *noop) OnBlockEnd(err error) { From 97e9a8b31542af0ed2a7fa8345eff7d174bc648b Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Fri, 9 Feb 2024 11:02:39 +0100 Subject: [PATCH 06/50] fix blockchain -> logger circular import Backport of https://github.com/s1na/go-ethereum/commit/03206befb9a57d00411bba8e1ad7982a50710145 --- cmd/geth/config.go | 4 ++-- cmd/utils/flags.go | 4 ++-- core/blockchain_test.go | 7 ++++--- eth/tracers/directory/{live.go => live/dir.go} | 12 ++++++------ eth/tracers/live/noop.go | 4 ++-- 5 files changed, 16 insertions(+), 15 deletions(-) rename eth/tracers/directory/{live.go => live/dir.go} (55%) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index fa1ec6f279da..3f5303259b8f 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -37,7 +37,7 @@ import ( "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/version" @@ -182,7 +182,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { if ctx.IsSet(utils.VMTraceFlag.Name) { if name := ctx.String(utils.VMTraceFlag.Name); name != "" { - t, err := directory.LiveDirectory.New(name) + t, err := live.Directory.New(name) if err != nil { utils.Fatalf("Failed to create tracer %q: %v", name, err) } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 6878a46e3912..81f16458f8d6 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -51,7 +51,7 @@ import ( "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers" - "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb/remotedb" "github.com/ethereum/go-ethereum/ethstats" @@ -2194,7 +2194,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { - t, err := directory.LiveDirectory.New(name) + t, err := live.Directory.New(name) if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 5f9892902b60..bc6f8112f015 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -3247,7 +3248,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - //Tracer: logger.NewJSONLogger(nil, os.Stdout), + Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -3329,7 +3330,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - //Tracer: logger.NewJSONLogger(nil, os.Stdout), + Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -4681,7 +4682,7 @@ func TestEIP3651(t *testing.T) { b.AddTx(tx) }) - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{ /*Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)*/ }, nil, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } diff --git a/eth/tracers/directory/live.go b/eth/tracers/directory/live/dir.go similarity index 55% rename from eth/tracers/directory/live.go rename to eth/tracers/directory/live/dir.go index dc6927b14228..011d27f88746 100644 --- a/eth/tracers/directory/live.go +++ b/eth/tracers/directory/live/dir.go @@ -1,4 +1,4 @@ -package directory +package live import ( "errors" @@ -8,21 +8,21 @@ import ( type ctorFunc func() (core.BlockchainLogger, error) -// LiveDirectory is the collection of tracers which can be used +// Directory is the collection of tracers which can be used // during normal block import operations. -var LiveDirectory = liveDirectory{elems: make(map[string]ctorFunc)} +var Directory = directory{elems: make(map[string]ctorFunc)} -type liveDirectory struct { +type directory struct { elems map[string]ctorFunc } // Register registers a tracer constructor by name. -func (d *liveDirectory) Register(name string, f ctorFunc) { +func (d *directory) Register(name string, f ctorFunc) { d.elems[name] = f } // New instantiates a tracer by name. -func (d *liveDirectory) New(name string) (core.BlockchainLogger, error) { +func (d *directory) New(name string) (core.BlockchainLogger, error) { if f, ok := d.elems[name]; ok { return f() } diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index beb532a42366..4ff4153c6049 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -8,12 +8,12 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) func init() { - directory.LiveDirectory.Register("noop", newNoopTracer) + live.Directory.Register("noop", newNoopTracer) } // noop is a no-op live tracer. It's there to From ee96dd2c0388c139b8772d32fbd148654b77eee9 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Fri, 9 Feb 2024 11:13:35 +0100 Subject: [PATCH 07/50] live tracer err -> warn Backport of https://github.com/s1na/go-ethereum/commit/08cb623c2e27c562badcf55185e90f4eb761154a --- core/blockchain.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 92e122cbaab9..33c9b0f7c95a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -292,10 +292,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis var logger BlockchainLogger if vmConfig.Tracer != nil { l, ok := vmConfig.Tracer.(BlockchainLogger) - if !ok { - return nil, errors.New("only extended tracers are supported for live mode") + if ok { + logger = l + } else { + log.Warn("only extended tracers are supported for live mode") } - logger = l } // Setup the genesis block, commit the provided genesis specification // to database if the genesis block is not present yet, or load the From 2264a9c3fec4e97fbb9b7affc179462812f68895 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Fri, 9 Feb 2024 11:19:56 +0100 Subject: [PATCH 08/50] fix mdLogger Backport of https://github.com/s1na/go-ethereum/commit/cf6a31514b8c1b8502861fbe1773830172b1d98e --- eth/tracers/logger/logger.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index ae64dc86253b..3c22bd7886c5 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -334,6 +334,10 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { return l } +func (t *mdLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { + t.env = env +} + func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { if !create { fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", From d0fd1bd7f3cf6f4df70343b527ba2c84ffa7bc4a Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 22 Feb 2024 20:03:08 -0500 Subject: [PATCH 09/50] Fixed missing instrumentation for `txTrace` which was making a test fail --- eth/tracers/api.go | 71 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 11ede963ad0e..f098defb4ea6 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -279,7 +279,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config) if err != nil { task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -611,7 +611,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config) + res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config) if err != nil { return nil, err } @@ -651,7 +651,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat TxIndex: task.index, TxHash: txs[task.index].Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config) if err != nil { results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()} continue @@ -856,7 +856,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - return api.traceTx(ctx, msg, txctx, vmctx, statedb, config) + return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config) } // TraceCall lets you trace a given eth_call. It collects the structured logs @@ -921,22 +921,26 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if err != nil { return nil, err } - + tx, err := argsToTransaction(api.backend, &args, msg) + if err != nil { + return nil, err + } var traceConfig *TraceConfig if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, msg, new(directory.Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, tx, msg, new(directory.Context), vmctx, statedb, traceConfig) } // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb vm.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb vm.StateDB, config *TraceConfig) (interface{}, error) { var ( tracer directory.Tracer err error timeout = defaultTraceTimeout + usedGas uint64 ) if config == nil { config = &TraceConfig{} @@ -971,7 +975,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *direc // Call Prepare to clear out the statedb access list statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) - if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil { + if _, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv); err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } return tracer.GetResult() @@ -1036,3 +1040,54 @@ func overrideConfig(original *params.ChainConfig, override *params.ChainConfig) return copy, canon } + +// argsToTransaction produces a Transaction object given call arguments. +// The `msg` field must be converted from the same arguments. +func argsToTransaction(b Backend, args *ethapi.TransactionArgs, msg *core.Message) (*types.Transaction, error) { + chainID := b.ChainConfig().ChainID + if args.ChainID != nil { + if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 { + return nil, fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, chainID) + } + } + var data types.TxData + switch { + case args.MaxFeePerGas != nil: + al := types.AccessList{} + if args.AccessList != nil { + al = *args.AccessList + } + data = &types.DynamicFeeTx{ + To: args.To, + ChainID: chainID, + Nonce: msg.Nonce, + Gas: msg.GasLimit, + GasFeeCap: msg.GasFeeCap, + GasTipCap: msg.GasTipCap, + Value: msg.Value, + Data: msg.Data, + AccessList: al, + } + case args.AccessList != nil: + data = &types.AccessListTx{ + To: args.To, + ChainID: chainID, + Nonce: msg.Nonce, + Gas: msg.GasLimit, + GasPrice: msg.GasPrice, + Value: msg.Value, + Data: msg.Data, + AccessList: *args.AccessList, + } + default: + data = &types.LegacyTx{ + To: args.To, + Nonce: msg.Nonce, + Gas: msg.GasLimit, + GasPrice: msg.GasPrice, + Value: msg.Value, + Data: msg.Data, + } + } + return types.NewTx(data), nil +} From 4952d19a6c7a5700620bbecf926d9ac868e69d53 Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Thu, 7 Mar 2024 16:21:31 +0800 Subject: [PATCH 10/50] Add method deny list in geth http server --- rpc/server.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/rpc/server.go b/rpc/server.go index 2742adf07b82..b34cefc677a5 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -18,6 +18,7 @@ package rpc import ( "context" + "fmt" "io" "sync" "sync/atomic" @@ -48,6 +49,7 @@ type Server struct { mutex sync.Mutex codecs map[ServerCodec]struct{} + denyList map[string]struct{} run atomic.Bool batchItemLimit int batchResponseLimit int @@ -56,8 +58,9 @@ type Server struct { // NewServer creates a new server instance with no registered handlers. func NewServer() *Server { server := &Server{ - idgen: randomIDGenerator(), - codecs: make(map[ServerCodec]struct{}), + idgen: randomIDGenerator(), + codecs: make(map[ServerCodec]struct{}), + denyList: make(map[string]struct{}), } server.run.Store(true) // Register the default service providing meta information about the RPC service such @@ -86,6 +89,12 @@ func (s *Server) RegisterName(name string, receiver interface{}) error { return s.services.registerName(name, receiver) } +// RegisterDenyList add given method name to the deny list so that RPC requests that matches +// any of the methods in deny list will got rejected directly. +func (s *Server) RegisterDenyList(methodName string) { + s.denyList[methodName] = struct{}{} +} + // ServeCodec reads incoming requests from codec, calls the appropriate callback and writes // the response back using the given codec. It will block until the codec is closed or the // server is stopped. In either case the codec is closed. @@ -141,6 +150,7 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { defer h.close(io.EOF, nil) reqs, batch, err := codec.readBatch() + if err != nil { if err != io.EOF { resp := errorMessage(&invalidMessageError{"parse error"}) @@ -148,6 +158,15 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { } return } + // check deny list + for _, req := range reqs { + method := req.Method + if _, found := s.denyList[method]; found { + resp := errorMessage(&invalidMessageError{fmt.Sprintf("method %s is in deny list", method)}) + codec.writeJSON(ctx, resp, true) + return + } + } if batch { h.handleBatch(reqs) } else { From 8c577d58294d3b8e1d6dbbae3101c9036fbab3e3 Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Tue, 12 Mar 2024 13:39:24 +0800 Subject: [PATCH 11/50] Change error type to methodNotFoundError --- rpc/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc/server.go b/rpc/server.go index b34cefc677a5..5cee52d21bad 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -162,7 +162,7 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { for _, req := range reqs { method := req.Method if _, found := s.denyList[method]; found { - resp := errorMessage(&invalidMessageError{fmt.Sprintf("method %s is in deny list", method)}) + resp := errorMessage(&methodNotFoundError{fmt.Sprintf("method %s is in deny list", method)}) codec.writeJSON(ctx, resp, true) return } From c27c86e2955a1f1944b8deeda657691f122b1a39 Mon Sep 17 00:00:00 2001 From: yzang2019 Date: Tue, 12 Mar 2024 15:18:11 +0800 Subject: [PATCH 12/50] Fix error code --- rpc/server.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rpc/server.go b/rpc/server.go index 5cee52d21bad..a97c927757b5 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -18,7 +18,6 @@ package rpc import ( "context" - "fmt" "io" "sync" "sync/atomic" @@ -162,7 +161,7 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { for _, req := range reqs { method := req.Method if _, found := s.denyList[method]; found { - resp := errorMessage(&methodNotFoundError{fmt.Sprintf("method %s is in deny list", method)}) + resp := errorMessage(&methodNotFoundError{method: method}) codec.writeJSON(ctx, resp, true) return } From 318dc843145303db11f2631a8f9b281a26b77be8 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 14 Feb 2024 16:52:10 +0100 Subject: [PATCH 13/50] add config for tracers --- cmd/geth/chaincmd.go | 1 + cmd/geth/config.go | 7 ++++++- cmd/geth/main.go | 1 + cmd/utils/flags.go | 13 +++++++++++-- eth/tracers/directory/live/dir.go | 7 ++++--- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 2627c6caf369..5c53bd296291 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -98,6 +98,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`, utils.MetricsInfluxDBOrganizationFlag, utils.TxLookupLimitFlag, utils.VMTraceFlag, + utils.VMTraceConfigFlag, utils.TransactionHistoryFlag, utils.StateHistoryFlag, }, utils.DatabaseFlags), diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 3f5303259b8f..95d59a04fa0d 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -18,6 +18,7 @@ package main import ( "bufio" + "encoding/json" "errors" "fmt" "os" @@ -182,7 +183,11 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { if ctx.IsSet(utils.VMTraceFlag.Name) { if name := ctx.String(utils.VMTraceFlag.Name); name != "" { - t, err := live.Directory.New(name) + var config string + if ctx.IsSet(utils.VMTraceConfigFlag.Name) { + config = ctx.String(utils.VMTraceConfigFlag.Name) + } + t, err := live.Directory.New(name, json.RawMessage(config)) if err != nil { utils.Fatalf("Failed to create tracer %q: %v", name, err) } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 7aee35753dfd..bb3e49b2edae 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -138,6 +138,7 @@ var ( utils.DeveloperPeriodFlag, utils.VMEnableDebugFlag, utils.VMTraceFlag, + utils.VMTraceConfigFlag, utils.NetworkIdFlag, utils.EthStatsURLFlag, utils.NoCompactionFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 81f16458f8d6..2490e79fe5c8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -21,6 +21,7 @@ import ( "context" "crypto/ecdsa" "encoding/hex" + "encoding/json" "errors" "fmt" "math" @@ -538,7 +539,11 @@ var ( Usage: "Name of tracer which should record internal VM operations (costly)", Category: flags.VMCategory, } - + VMTraceConfigFlag = &cli.StringFlag{ + Name: "vmtrace.config", + Usage: "Tracer configuration (JSON)", + Category: flags.VMCategory, + } // API options. RPCGlobalGasCapFlag = &cli.Uint64Flag{ Name: "rpc.gascap", @@ -2194,7 +2199,11 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { - t, err := live.Directory.New(name) + var config string + if ctx.IsSet(VMTraceConfigFlag.Name) { + config = ctx.String(VMTraceConfigFlag.Name) + } + t, err := live.Directory.New(name, json.RawMessage(config)) if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) } diff --git a/eth/tracers/directory/live/dir.go b/eth/tracers/directory/live/dir.go index 011d27f88746..39d1e99caa2f 100644 --- a/eth/tracers/directory/live/dir.go +++ b/eth/tracers/directory/live/dir.go @@ -1,12 +1,13 @@ package live import ( + "encoding/json" "errors" "github.com/ethereum/go-ethereum/core" ) -type ctorFunc func() (core.BlockchainLogger, error) +type ctorFunc func(config json.RawMessage) (core.BlockchainLogger, error) // Directory is the collection of tracers which can be used // during normal block import operations. @@ -22,9 +23,9 @@ func (d *directory) Register(name string, f ctorFunc) { } // New instantiates a tracer by name. -func (d *directory) New(name string) (core.BlockchainLogger, error) { +func (d *directory) New(name string, config json.RawMessage) (core.BlockchainLogger, error) { if f, ok := d.elems[name]; ok { - return f() + return f(config) } return nil, errors.New("not found") } From d17940d2e73a72b82f67aeb4925fa3d0268d1ea5 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 14 Feb 2024 16:54:51 +0100 Subject: [PATCH 14/50] minor fix --- eth/tracers/live/noop.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index 4ff4153c6049..cc597adfc55e 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -1,6 +1,7 @@ package live import ( + "encoding/json" "math/big" "github.com/ethereum/go-ethereum/common" @@ -22,7 +23,7 @@ func init() { // as soon as we have a real live tracer. type noop struct{} -func newNoopTracer() (core.BlockchainLogger, error) { +func newNoopTracer(_ json.RawMessage) (core.BlockchainLogger, error) { return &noop{}, nil } From b210cc761dde5979ddf8d819bebd12bed7c0e5ab Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 15 Feb 2024 16:05:55 +0100 Subject: [PATCH 15/50] remove onBeaconBlockRoot events --- core/blockchain.go | 2 -- core/state_processor.go | 6 ------ eth/tracers/live/noop.go | 3 --- 3 files changed, 11 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 33c9b0f7c95a..74a226b78bde 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -200,8 +200,6 @@ type BlockchainLogger interface { OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header, skip bool) OnBlockEnd(err error) OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc) - OnBeaconBlockRootStart(root common.Hash) - OnBeaconBlockRootEnd() } // BlockChain represents the canonical chain given a database with a genesis diff --git a/core/state_processor.go b/core/state_processor.go index 231f63f9690f..76dd0fdb1e3c 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -184,12 +184,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // contract. This method is exported to be used in tests. func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB, logger BlockchainLogger) { - if logger != nil { - logger.OnBeaconBlockRootStart(beaconRoot) - defer func() { - logger.OnBeaconBlockRootEnd() - }() - } // If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with // the new root msg := &Message{ diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index cc597adfc55e..c565feced0c7 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -55,9 +55,6 @@ func (t *noop) CaptureEnter(typ vm.OpCode, from common.Address, to common.Addres func (t *noop) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { } -func (t *noop) OnBeaconBlockRootStart(root common.Hash) {} -func (t *noop) OnBeaconBlockRootEnd() {} - func (t *noop) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { } From 8ec4560e1724e948729e137ef99c77ad0ebda848 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 15 Feb 2024 16:22:59 +0100 Subject: [PATCH 16/50] refactor onBlockStart params, new skip method --- core/blockchain.go | 34 ++++++++++++++++++++++++++-------- eth/tracers/live/noop.go | 4 +++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 74a226b78bde..e08c939f1e97 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -185,6 +185,15 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig { return &config } +// BlockEvent is emitted upon tracing an incoming block. +// It contains the block as well as consensus related information. +type BlockEvent struct { + Block *types.Block + TD *big.Int + Finalized *types.Header + Safe *types.Header +} + // BlockchainLogger is used to collect traces during chain processing. // Please make a copy of the referenced types if you intend to retain them. type BlockchainLogger interface { @@ -193,12 +202,12 @@ type BlockchainLogger interface { OnBlockchainInit(chainConfig *params.ChainConfig) // OnBlockStart is called before executing `block`. // `td` is the total difficulty prior to `block`. - // `skip` indicates processing of this previously known block - // will be skipped. OnBlockStart and OnBlockEnd will be emitted to - // convey how chain is progressing. E.g. known blocks will be skipped - // when node is started after a crash. - OnBlockStart(block *types.Block, td *big.Int, finalized *types.Header, safe *types.Header, skip bool) + OnBlockStart(event BlockEvent) OnBlockEnd(err error) + // OnSkippedBlock indicates a block was skipped during processing + // due to it being known previously. This can happen e.g. when recovering + // from a crash. + OnSkippedBlock(event BlockEvent) OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc) } @@ -1811,8 +1820,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) } stats.processed++ if bc.logger != nil { - bc.logger.OnBlockStart(block, bc.GetTd(block.ParentHash(), block.NumberU64()-1), bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), true) - bc.logger.OnBlockEnd(nil) + bc.logger.OnSkippedBlock(BlockEvent{ + Block: block, + TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1), + Finalized: bc.CurrentFinalBlock(), + Safe: bc.CurrentSafeBlock(), + }) } // We can assume that logs are empty here, since the only way for consecutive @@ -1940,7 +1953,12 @@ type blockProcessingResult struct { func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { if bc.logger != nil { td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) - bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), false) + bc.logger.OnBlockStart(BlockEvent{ + Block: block, + TD: td, + Finalized: bc.CurrentFinalBlock(), + Safe: bc.CurrentSafeBlock(), + }) defer func() { bc.logger.OnBlockEnd(blockEndErr) }() diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index c565feced0c7..fe835e1d81ac 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -61,12 +61,14 @@ func (t *noop) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Ad func (t *noop) CaptureTxEnd(receipt *types.Receipt, err error) { } -func (t *noop) OnBlockStart(b *types.Block, td *big.Int, finalized, safe *types.Header, skip bool) { +func (t *noop) OnBlockStart(ev core.BlockEvent) { } func (t *noop) OnBlockEnd(err error) { } +func (t *noop) OnSkippedBlock(ev core.BlockEvent) {} + func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) { } From 4df6904051eb7b0573174e7191f37b6b671f56f0 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Fri, 16 Feb 2024 11:20:56 +0100 Subject: [PATCH 17/50] use struct for tracing events # Conflicts: # core/blockchain.go # core/state/state_object.go # core/state/statedb.go --- cmd/geth/config.go | 3 ++- cmd/utils/flags.go | 3 ++- core/blockchain.go | 25 +++++++++------------ core/state/state_object.go | 8 +++---- core/state/statedb.go | 11 ++++----- core/vm/interpreter.go | 8 ++++--- eth/backend.go | 1 + eth/ethconfig/config.go | 4 +++- eth/tracers/directory/live/dir.go | 37 +++++++++++++++++++++++++++++-- eth/tracers/live/noop.go | 17 ++++++++++++-- 10 files changed, 83 insertions(+), 34 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 95d59a04fa0d..d6ab18d8051a 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -192,7 +192,8 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { utils.Fatalf("Failed to create tracer %q: %v", name, err) } - cfg.Eth.VMTracer = t + cfg.Eth.VMTracer = t.VMLogger + cfg.Eth.LiveLogger = t } } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2490e79fe5c8..abff65a09f61 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2207,7 +2207,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) } - vmcfg.Tracer = t + vmcfg.LiveLogger = t + vmcfg.Tracer = t.VMLogger } } // Disable transaction indexing/unindexing by default. diff --git a/core/blockchain.go b/core/blockchain.go index e08c939f1e97..bb5a36ba447b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -39,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/syncx" @@ -284,7 +285,7 @@ type BlockChain struct { processor Processor // Block transaction processor interface forker *ForkChoice vmConfig vm.Config - logger BlockchainLogger + logger *live.LiveLogger } // NewBlockChain returns a fully initialised block chain using information @@ -296,15 +297,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } // Open trie database with provided config triedb := trie.NewDatabase(db, cacheConfig.triedbConfig()) - var logger BlockchainLogger - if vmConfig.Tracer != nil { - l, ok := vmConfig.Tracer.(BlockchainLogger) - if ok { - logger = l - } else { - log.Warn("only extended tracers are supported for live mode") - } - } + // Setup the genesis block, commit the provided genesis specification // to database if the genesis block is not present yet, or load the // stored one from database. @@ -336,7 +329,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks), engine: engine, vmConfig: vmConfig, - logger: logger, + logger: vmConfig.LiveLogger, } bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit)) bc.forker = NewForkChoice(bc, shouldPreserve) @@ -463,7 +456,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } } - if bc.logger != nil { + if bc.logger != nil && bc.logger.OnGenesisBlock != nil { if block := bc.CurrentBlock(); block.Number.Uint64() == 0 { alloc, err := getGenesisState(bc.db, block.Hash()) if err != nil { @@ -514,7 +507,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } rawdb.WriteChainConfig(db, genesisHash, chainConfig) } - if bc.logger != nil { + if bc.logger != nil && bc.logger.OnBlockchainInit != nil { bc.logger.OnBlockchainInit(chainConfig) } // Start tx indexer/unindexer if required. @@ -1819,7 +1812,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) return it.index, err } stats.processed++ - if bc.logger != nil { + if bc.logger != nil && bc.logger.OnSkippedBlock != nil { bc.logger.OnSkippedBlock(BlockEvent{ Block: block, TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1), @@ -1951,7 +1944,7 @@ type blockProcessingResult struct { // processBlock executes and validates the given block. If there was no error // it writes the block and associated state to database. func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { - if bc.logger != nil { + if bc.logger != nil && bc.logger.OnBlockStart != nil { td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) bc.logger.OnBlockStart(BlockEvent{ Block: block, @@ -1959,6 +1952,8 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s Finalized: bc.CurrentFinalBlock(), Safe: bc.CurrentSafeBlock(), }) + } + if bc.logger != nil && bc.logger.OnBlockEnd != nil { defer func() { bc.logger.OnBlockEnd(blockEndErr) }() diff --git a/core/state/state_object.go b/core/state/state_object.go index f9cc0109a286..b6decf77175d 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -239,7 +239,7 @@ func (s *stateObject) SetState(key, value common.Hash) { key: key, prevalue: prev, }) - if s.db.logger != nil { + if s.db.logger != nil && s.db.logger.OnStorageChange != nil { s.db.logger.OnStorageChange(s.address, key, prev, value) } s.setState(key, value) @@ -430,7 +430,7 @@ func (s *stateObject) SetBalance(amount *big.Int, reason BalanceChangeReason) { account: &s.address, prev: new(big.Int).Set(s.data.Balance), }) - if s.db.logger != nil { + if s.db.logger != nil && s.db.logger.OnBalanceChange != nil { s.db.logger.OnBalanceChange(s.address, s.Balance(), amount, reason) } s.setBalance(amount) @@ -510,7 +510,7 @@ func (s *stateObject) SetCode(codeHash common.Hash, code []byte) { prevhash: s.CodeHash(), prevcode: prevcode, }) - if s.db.logger != nil { + if s.db.logger != nil && s.db.logger.OnCodeChange != nil { s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), prevcode, codeHash, code) } s.setCode(codeHash, code) @@ -527,7 +527,7 @@ func (s *stateObject) SetNonce(nonce uint64) { account: &s.address, prev: s.data.Nonce, }) - if s.db.logger != nil { + if s.db.logger != nil && s.db.logger.OnNonceChange != nil { s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce) } s.setNonce(nonce) diff --git a/core/state/statedb.go b/core/state/statedb.go index 0c2a5b5aefb0..5eabbcaeb3e0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" @@ -77,7 +78,7 @@ type StateDB struct { prefetcher *triePrefetcher trie Trie hasher crypto.KeccakState - logger StateLogger + logger *live.LiveLogger snaps *snapshot.Tree // Nil if snapshot is not available snap snapshot.Snapshot // Nil if snapshot is not available @@ -188,7 +189,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) } // SetLogger sets the logger for account update hooks. -func (s *StateDB) SetLogger(l StateLogger) { +func (s *StateDB) SetLogger(l *live.LiveLogger) { s.logger = l } @@ -232,7 +233,7 @@ func (s *StateDB) AddLog(log *types.Log) { log.TxHash = s.thash log.TxIndex = uint(s.txIndex) log.Index = s.logSize - if s.logger != nil { + if s.logger != nil && s.logger.OnLog != nil { s.logger.OnLog(log) } s.logs[s.thash] = append(s.logs[s.thash], log) @@ -479,7 +480,7 @@ func (s *StateDB) SelfDestruct(addr common.Address) { prev: stateObject.selfDestructed, prevbalance: new(big.Int).Set(stateObject.Balance()), }) - if s.logger != nil && prev.Sign() > 0 { + if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 { s.logger.OnBalanceChange(addr, prev, n, BalanceDecreaseSelfdestruct) } stateObject.markSelfdestructed() @@ -868,7 +869,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { obj.deleted = true // If ether was sent to account post-selfdestruct it is burnt. - if bal := obj.Balance(); s.logger != nil && obj.selfDestructed && bal.Sign() != 0 { + if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 { s.logger.OnBalanceChange(obj.address, bal, new(big.Int), BalanceDecreaseSelfdestructBurn) } // We need to maintain account deletions explicitly (will remain diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 3eff0d25dbda..7750517e06fb 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -20,15 +20,17 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/log" ) // Config are the configuration options for the Interpreter type Config struct { Tracer EVMLogger // Opcode logger - NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) - EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages - ExtraEips []int // Additional EIPS that are to be enabled + LiveLogger *live.LiveLogger + NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) + EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages + ExtraEips []int // Additional EIPS that are to be enabled } // ScopeContext contains the things that are per-call, such as stack and memory, diff --git a/eth/backend.go b/eth/backend.go index 1ba7552d76eb..abb6a7e7c2d1 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -193,6 +193,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { vmConfig = vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, Tracer: config.VMTracer, + LiveLogger: config.LiveLogger, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 3ff1ae0013e6..b5e2f2368ccc 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" @@ -153,7 +154,8 @@ type Config struct { EnablePreimageRecording bool // Enables VM tracing - VMTracer vm.EVMLogger + VMTracer vm.EVMLogger + LiveLogger *live.LiveLogger // Miscellaneous options DocRoot string `toml:"-"` diff --git a/eth/tracers/directory/live/dir.go b/eth/tracers/directory/live/dir.go index 39d1e99caa2f..f6656ac79faf 100644 --- a/eth/tracers/directory/live/dir.go +++ b/eth/tracers/directory/live/dir.go @@ -3,11 +3,44 @@ package live import ( "encoding/json" "errors" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" ) -type ctorFunc func(config json.RawMessage) (core.BlockchainLogger, error) +type LiveLogger struct { + VMLogger vm.EVMLogger + + /* + - Chain events - + */ + OnBlockchainInit func(chainConfig *params.ChainConfig) + // OnBlockStart is called before executing `block`. + // `td` is the total difficulty prior to `block`. + OnBlockStart func(event core.BlockEvent) + OnBlockEnd func(err error) + // OnSkippedBlock indicates a block was skipped during processing + // due to it being known previously. This can happen e.g. when recovering + // from a crash. + OnSkippedBlock func(event core.BlockEvent) + OnGenesisBlock func(genesis *types.Block, alloc core.GenesisAlloc) + + /* + - State events - + */ + OnBalanceChange func(addr common.Address, prev, new *big.Int, reason state.BalanceChangeReason) + OnNonceChange func(addr common.Address, prev, new uint64) + OnCodeChange func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) + OnStorageChange func(addr common.Address, slot common.Hash, prev, new common.Hash) + OnLog func(log *types.Log) +} + +type ctorFunc func(config json.RawMessage) (*LiveLogger, error) // Directory is the collection of tracers which can be used // during normal block import operations. @@ -23,7 +56,7 @@ func (d *directory) Register(name string, f ctorFunc) { } // New instantiates a tracer by name. -func (d *directory) New(name string, config json.RawMessage) (core.BlockchainLogger, error) { +func (d *directory) New(name string, config json.RawMessage) (*LiveLogger, error) { if f, ok := d.elems[name]; ok { return f(config) } diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index fe835e1d81ac..f9b8e19def25 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -23,8 +23,21 @@ func init() { // as soon as we have a real live tracer. type noop struct{} -func newNoopTracer(_ json.RawMessage) (core.BlockchainLogger, error) { - return &noop{}, nil +func newNoopTracer(_ json.RawMessage) (*live.LiveLogger, error) { + t := &noop{} + return &live.LiveLogger{ + VMLogger: t, + OnBlockchainInit: t.OnBlockchainInit, + OnBlockStart: t.OnBlockStart, + OnBlockEnd: t.OnBlockEnd, + OnSkippedBlock: t.OnSkippedBlock, + OnGenesisBlock: t.OnGenesisBlock, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, + }, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. From 4e43718bd0257180710223b4dd3e2462871a50d0 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 21 Feb 2024 18:47:59 +0100 Subject: [PATCH 18/50] full struct-based tracing infra # Conflicts: # consensus/beacon/consensus.go # consensus/ethash/consensus.go # consensus/misc/dao.go # core/blockchain.go # core/evm.go # core/genesis.go # core/state/dump.go # core/state/state_object.go # core/state/statedb.go # core/state_transition.go # core/vm/evm.go # core/vm/instructions.go # core/vm/interface.go # internal/ethapi/api.go --- cmd/geth/config.go | 3 +- cmd/utils/flags.go | 3 +- consensus/beacon/consensus.go | 3 +- consensus/ethash/consensus.go | 5 +- consensus/misc/dao.go | 5 +- core/blockchain.go | 35 ++------ core/chain_makers.go | 2 +- core/evm.go | 6 +- core/genesis.go | 7 +- core/state/dump.go | 17 ++-- core/state/metadata.go | 66 -------------- core/state/state_object.go | 9 +- core/state/statedb.go | 30 ++----- core/state_processor.go | 24 ++++-- core/state_transition.go | 24 +++--- core/vm/contract.go | 5 +- core/vm/contracts.go | 7 +- core/vm/evm.go | 62 +++++++++----- core/vm/instructions.go | 28 +++--- core/vm/interface.go | 6 +- core/vm/interpreter.go | 60 ++++++++++--- core/vm/logger.go | 70 +-------------- core/vm/operations_acl.go | 3 +- eth/backend.go | 1 - eth/ethconfig/config.go | 4 +- eth/tracers/api.go | 12 +-- eth/tracers/directory/live/dir.go | 83 ++++++++++++++++-- eth/tracers/directory/noop.go | 40 ++++++--- eth/tracers/directory/tracers.go | 18 ++-- eth/tracers/js/goja.go | 45 ++++++---- eth/tracers/live/noop.go | 52 +++++++----- eth/tracers/logger/access_list_tracer.go | 15 +++- eth/tracers/logger/logger.go | 60 ++++++++----- eth/tracers/logger/logger_json.go | 30 ++++--- eth/tracers/native/4byte.go | 22 +++-- eth/tracers/native/call.go | 32 +++++-- eth/tracers/native/call_flat.go | 35 +++++--- eth/tracers/native/mux.go | 104 +++++++++++++++++------ eth/tracers/native/prestate.go | 28 ++++-- internal/ethapi/api.go | 5 +- miner/worker.go | 2 +- 41 files changed, 604 insertions(+), 464 deletions(-) delete mode 100644 core/state/metadata.go diff --git a/cmd/geth/config.go b/cmd/geth/config.go index d6ab18d8051a..95d59a04fa0d 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -192,8 +192,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { utils.Fatalf("Failed to create tracer %q: %v", name, err) } - cfg.Eth.VMTracer = t.VMLogger - cfg.Eth.LiveLogger = t + cfg.Eth.VMTracer = t } } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index abff65a09f61..2490e79fe5c8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2207,8 +2207,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) } - vmcfg.LiveLogger = t - vmcfg.Tracer = t.VMLogger + vmcfg.Tracer = t } } // Disable transaction indexing/unindexing by default. diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 9ab9026fa8e2..42bf2bd7f99f 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -357,7 +358,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // Convert amount from gwei to wei. amount := new(big.Int).SetUint64(w.Amount) amount = amount.Mul(amount, big.NewInt(params.GWei)) - stateDB.AddBalance(w.Address, amount, state.BalanceIncreaseWithdrawal) + stateDB.AddBalance(w.Address, amount, live.BalanceIncreaseWithdrawal) } // No block reward which is issued by consensus layer instead. } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 5c00e5c7bcf3..e09bfa10a007 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -564,10 +565,10 @@ func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, heade r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, big8) - stateDB.AddBalance(uncle.Coinbase, r, state.BalanceIncreaseRewardMineUncle) + stateDB.AddBalance(uncle.Coinbase, r, live.BalanceIncreaseRewardMineUncle) r.Div(blockReward, big32) reward.Add(reward, r) } - stateDB.AddBalance(header.Coinbase, reward, state.BalanceIncreaseRewardMineBlock) + stateDB.AddBalance(header.Coinbase, reward, live.BalanceIncreaseRewardMineBlock) } diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go index bc393a9cbced..1f97527588a5 100644 --- a/consensus/misc/dao.go +++ b/consensus/misc/dao.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -80,7 +81,7 @@ func ApplyDAOHardFork(statedb *state.StateDB) { // Move every DAO account and extra-balance account funds into the refund contract for _, addr := range params.DAODrainList() { - statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), state.BalanceIncreaseDaoContract) - statedb.SetBalance(addr, new(big.Int), state.BalanceDecreaseDaoAccount) + statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), live.BalanceIncreaseDaoContract) + statedb.SetBalance(addr, new(big.Int), live.BalanceDecreaseDaoAccount) } } diff --git a/core/blockchain.go b/core/blockchain.go index bb5a36ba447b..babfcaf8f2c8 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -186,30 +186,11 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig { return &config } -// BlockEvent is emitted upon tracing an incoming block. -// It contains the block as well as consensus related information. -type BlockEvent struct { - Block *types.Block - TD *big.Int - Finalized *types.Header - Safe *types.Header -} - -// BlockchainLogger is used to collect traces during chain processing. -// Please make a copy of the referenced types if you intend to retain them. -type BlockchainLogger interface { - vm.EVMLogger - state.StateLogger - OnBlockchainInit(chainConfig *params.ChainConfig) - // OnBlockStart is called before executing `block`. - // `td` is the total difficulty prior to `block`. - OnBlockStart(event BlockEvent) - OnBlockEnd(err error) - // OnSkippedBlock indicates a block was skipped during processing - // due to it being known previously. This can happen e.g. when recovering - // from a crash. - OnSkippedBlock(event BlockEvent) - OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc) +// txLookup is wrapper over transaction lookup along with the corresponding +// transaction object. +type txLookup struct { + lookup *rawdb.LegacyTxLookupEntry + transaction *types.Transaction } // BlockChain represents the canonical chain given a database with a genesis @@ -329,7 +310,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks), engine: engine, vmConfig: vmConfig, - logger: vmConfig.LiveLogger, + logger: vmConfig.Tracer, } bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit)) bc.forker = NewForkChoice(bc, shouldPreserve) @@ -1813,7 +1794,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) } stats.processed++ if bc.logger != nil && bc.logger.OnSkippedBlock != nil { - bc.logger.OnSkippedBlock(BlockEvent{ + bc.logger.OnSkippedBlock(live.BlockEvent{ Block: block, TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1), Finalized: bc.CurrentFinalBlock(), @@ -1946,7 +1927,7 @@ type blockProcessingResult struct { func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { if bc.logger != nil && bc.logger.OnBlockStart != nil { td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) - bc.logger.OnBlockStart(BlockEvent{ + bc.logger.OnBlockStart(live.BlockEvent{ Block: block, TD: td, Finalized: bc.CurrentFinalBlock(), diff --git a/core/chain_makers.go b/core/chain_makers.go index 2949162bfe61..dbaeb67f28a3 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -100,7 +100,7 @@ func (b *BlockGen) SetParentBeaconRoot(root common.Hash) { blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase) vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{}) ) - ProcessBeaconBlockRoot(root, vmenv, b.statedb, nil) + ProcessBeaconBlockRoot(root, vmenv, b.statedb) } // addTx adds a transaction to the generated block. If no coinbase has diff --git a/core/evm.go b/core/evm.go index 8b724ac2cfe2..515f8d78a9da 100644 --- a/core/evm.go +++ b/core/evm.go @@ -22,9 +22,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) // ChainContext supports retrieving headers and consensus parameters from the @@ -136,6 +136,6 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { // Transfer subtracts amount from sender and adds amount to recipient using the given Db func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { - db.SubBalance(sender, amount, state.BalanceChangeTransfer) - db.AddBalance(recipient, amount, state.BalanceChangeTransfer) + db.SubBalance(sender, amount, live.BalanceChangeTransfer) + db.AddBalance(recipient, amount, live.BalanceChangeTransfer) } diff --git a/core/genesis.go b/core/genesis.go index edd323f67d84..8e58bb134e6d 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -131,7 +132,7 @@ func (ga *GenesisAlloc) hash() (common.Hash, error) { } for addr, account := range *ga { if account.Balance != nil { - statedb.AddBalance(addr, account.Balance, state.BalanceIncreaseGenesisBalance) + statedb.AddBalance(addr, account.Balance, live.BalanceIncreaseGenesisBalance) } statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) @@ -152,7 +153,9 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas } for addr, account := range *ga { if account.Balance != nil { - statedb.AddBalance(addr, account.Balance, state.BalanceIncreaseGenesisBalance) + // This is not actually logged via tracer because OnGenesisBlock + // already captures the allocations. + statedb.AddBalance(addr, account.Balance, live.BalanceIncreaseGenesisBalance) } statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) diff --git a/core/state/dump.go b/core/state/dump.go index 9ce6cd394b88..1840e5b2205f 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -49,15 +49,14 @@ type DumpCollector interface { // DumpAccount represents an account in the state. type DumpAccount struct { - Balance string `json:"balance"` - Nonce uint64 `json:"nonce"` - Root hexutil.Bytes `json:"root"` - CodeHash hexutil.Bytes `json:"codeHash"` - Code hexutil.Bytes `json:"code,omitempty"` - Storage map[common.Hash]string `json:"storage,omitempty"` - Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode - SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key - + Balance string `json:"balance"` + Nonce uint64 `json:"nonce"` + Root hexutil.Bytes `json:"root"` + CodeHash hexutil.Bytes `json:"codeHash"` + Code hexutil.Bytes `json:"code,omitempty"` + Storage map[common.Hash]string `json:"storage,omitempty"` + Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode + AddressHash hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key } // Dump represents the full dump in a collected format, as one large map. diff --git a/core/state/metadata.go b/core/state/metadata.go deleted file mode 100644 index 6978e6707559..000000000000 --- a/core/state/metadata.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2023 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 state - -// BalanceChangeReason is used to indicate the reason for a balance change, useful -// for tracing and reporting. -type BalanceChangeReason byte - -const ( - BalanceChangeUnspecified BalanceChangeReason = 0 - - // Issuance - // BalanceIncreaseRewardMineUncle is a reward for mining an uncle block. - BalanceIncreaseRewardMineUncle BalanceChangeReason = 1 - // BalanceIncreaseRewardMineBlock is a reward for mining a block. - BalanceIncreaseRewardMineBlock BalanceChangeReason = 2 - // BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain. - BalanceIncreaseWithdrawal BalanceChangeReason = 3 - // BalanceIncreaseGenesisBalance is ether allocated at the genesis block. - BalanceIncreaseGenesisBalance BalanceChangeReason = 4 - - // Transaction fees - // BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance. - BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5 - // BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction. - // Part of this gas will be burnt as per EIP-1559 rules. - BalanceDecreaseGasBuy BalanceChangeReason = 6 - // BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution. - BalanceIncreaseGasReturn BalanceChangeReason = 7 - - // DAO fork - // BalanceIncreaseDaoContract is ether sent to the DAO refund contract. - BalanceIncreaseDaoContract BalanceChangeReason = 8 - // BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract. - BalanceDecreaseDaoAccount BalanceChangeReason = 9 - - // BalanceChangeTransfer is ether transferred via a call. - // it is a decrease for the sender and an increase for the recipient. - BalanceChangeTransfer BalanceChangeReason = 10 - // BalanceChangeTouchAccount is a transfer of zero value. It is only there to - // touch-create an account. - BalanceChangeTouchAccount BalanceChangeReason = 11 - - // BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account. - BalanceIncreaseSelfdestruct BalanceChangeReason = 12 - // BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct. - BalanceDecreaseSelfdestruct BalanceChangeReason = 13 - // BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed - // account within the same tx (captured at end of tx). - // Note it doesn't account for a self-destruct which appoints itself as recipient. - BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 -) diff --git a/core/state/state_object.go b/core/state/state_object.go index b6decf77175d..2c7ffbf8961d 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" @@ -404,7 +405,7 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. -func (s *stateObject) AddBalance(amount *big.Int, reason BalanceChangeReason) { +func (s *stateObject) AddBalance(amount *big.Int, reason live.BalanceChangeReason) { // EIP161: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. if amount.Sign() == 0 { @@ -418,14 +419,14 @@ func (s *stateObject) AddBalance(amount *big.Int, reason BalanceChangeReason) { // SubBalance removes amount from s's balance. // It is used to remove funds from the origin account of a transfer. -func (s *stateObject) SubBalance(amount *big.Int, reason BalanceChangeReason) { - if amount.Sign() == 0 { +func (s *stateObject) SubBalance(amount *big.Int, reason live.BalanceChangeReason) { + if amount.IsZero() { return } s.SetBalance(new(big.Int).Sub(s.Balance(), amount), reason) } -func (s *stateObject) SetBalance(amount *big.Int, reason BalanceChangeReason) { +func (s *stateObject) SetBalance(amount *big.Int, reason live.BalanceChangeReason) { s.db.journal.append(balanceChange{ account: &s.address, prev: new(big.Int).Set(s.data.Balance), diff --git a/core/state/statedb.go b/core/state/statedb.go index 5eabbcaeb3e0..27bca3d2e8c4 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -48,20 +48,6 @@ type revision struct { journalIndex int } -// StateLogger is used to collect state update traces from EVM transaction -// execution. -// The following hooks are invoked post execution. I.e. looking up state -// after the hook should reflect the new value. -// Note that reference types are actual VM data structures; make copies -// if you need to retain them beyond the current call. -type StateLogger interface { - OnBalanceChange(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) - OnNonceChange(addr common.Address, prev, new uint64) - OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) - OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) - OnLog(log *types.Log) -} - // StateDB structs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: @@ -397,23 +383,23 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason BalanceChangeReason) { - stateObject := s.GetOrNewStateObject(addr) +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason live.BalanceChangeReason) { + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.AddBalance(amount, reason) } } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason BalanceChangeReason) { - stateObject := s.GetOrNewStateObject(addr) +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason live.BalanceChangeReason) { + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SubBalance(amount, reason) } } -func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason BalanceChangeReason) { - stateObject := s.GetOrNewStateObject(addr) +func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason live.BalanceChangeReason) { + stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SetBalance(amount, reason) } @@ -481,7 +467,7 @@ func (s *StateDB) SelfDestruct(addr common.Address) { prevbalance: new(big.Int).Set(stateObject.Balance()), }) if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 { - s.logger.OnBalanceChange(addr, prev, n, BalanceDecreaseSelfdestruct) + s.logger.OnBalanceChange(addr, prev, n, live.BalanceDecreaseSelfdestruct) } stateObject.markSelfdestructed() stateObject.data.Balance = new(big.Int) @@ -870,7 +856,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // If ether was sent to account post-selfdestruct it is burnt. if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 { - s.logger.OnBalanceChange(obj.address, bal, new(big.Int), BalanceDecreaseSelfdestructBurn) + s.logger.OnBalanceChange(obj.address, bal, new(big.Int), live.BalanceDecreaseSelfdestructBurn) } // We need to maintain account deletions explicitly (will remain // set indefinitely). Note only the first occurred self-destruct diff --git a/core/state_processor.go b/core/state_processor.go index 76dd0fdb1e3c..776a10422391 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -78,7 +79,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg signer = types.MakeSigner(p.config, header.Number, header.Time) ) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { - ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb, p.bc.logger) + ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb) } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { @@ -110,11 +111,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { - if evm.Config.Tracer != nil { - evm.Config.Tracer.CaptureTxStart(evm, tx, msg.From) - defer func() { - evm.Config.Tracer.CaptureTxEnd(receipt, err) - }() + if evm.Config.Tracer != nil && evm.Config.Tracer.CaptureTxStart != nil { + evm.Config.Tracer.CaptureTxStart(&live.VMContext{ + ChainConfig: evm.ChainConfig(), + StateDB: statedb, + BlockNumber: evm.Context.BlockNumber, + Time: evm.Context.Time, + Coinbase: evm.Context.Coinbase, + Random: evm.Context.Random, + }, tx, msg.From) + if evm.Config.Tracer.CaptureTxEnd != nil { + defer func() { + evm.Config.Tracer.CaptureTxEnd(receipt, err) + }() + } } // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) @@ -183,7 +193,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root // contract. This method is exported to be used in tests. -func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB, logger BlockchainLogger) { +func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) { // If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with // the new root msg := &Message{ diff --git a/core/state_transition.go b/core/state_transition.go index d73446744325..f149d2a3a141 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -24,9 +24,9 @@ import ( "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -262,14 +262,14 @@ func (st *StateTransition) buyGas() error { return err } - if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasChangeTxInitialBalance) + if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil { + st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, live.GasChangeTxInitialBalance) } st.gasRemaining += st.msg.GasLimit st.initialGas = st.msg.GasLimit - st.state.SubBalance(st.msg.From, mgval, state.BalanceDecreaseGasBuy) + st.state.SubBalance(st.msg.From, mgval, live.BalanceDecreaseGasBuy) return nil } @@ -392,8 +392,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { if st.gasRemaining < gas { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } - if t := st.evm.Config.Tracer; t != nil { - t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeTxIntrinsicGas) + if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil { + t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, live.GasChangeTxIntrinsicGas) } st.gasRemaining -= gas @@ -443,7 +443,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } else { fee := new(big.Int).SetUint64(st.gasUsed()) fee.Mul(fee, effectiveTip) - st.state.AddBalance(st.evm.Context.Coinbase, fee, state.BalanceIncreaseRewardTransactionFee) + st.state.AddBalance(st.evm.Context.Coinbase, fee, live.BalanceIncreaseRewardTransactionFee) } return &ExecutionResult{ @@ -460,18 +460,18 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { refund = st.state.GetRefund() } - if st.evm.Config.Tracer != nil && refund > 0 { - st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds) + if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, live.GasChangeTxRefunds) } st.gasRemaining += refund // Return ETH for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) - st.state.AddBalance(st.msg.From, remaining, state.BalanceIncreaseGasReturn) + st.state.AddBalance(st.msg.From, remaining, live.BalanceIncreaseGasReturn) - if st.evm.Config.Tracer != nil && st.gasRemaining > 0 { - st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxLeftOverReturned) + if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, live.GasChangeTxLeftOverReturned) } // Also return remaining gas to the block gas counter so it is diff --git a/core/vm/contract.go b/core/vm/contract.go index f7dae713d17b..fbfc3b125d82 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/holiman/uint256" ) @@ -159,11 +160,11 @@ func (c *Contract) Caller() common.Address { } // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) (ok bool) { +func (c *Contract) UseGas(gas uint64, logger *live.LiveLogger, reason live.GasChangeReason) (ok bool) { if c.Gas < gas { return false } - if logger != nil && reason != GasChangeIgnored { + if logger != nil && logger.OnGasChange != nil && reason != live.GasChangeIgnored { logger.OnGasChange(c.Gas, c.Gas-gas, reason) } c.Gas -= gas diff --git a/core/vm/contracts.go b/core/vm/contracts.go index bb5dc039f0bc..799665f0cb9e 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/bls12381" "github.com/ethereum/go-ethereum/crypto/bn256" "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "golang.org/x/crypto/ripemd160" ) @@ -168,13 +169,13 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger EVMLogger) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *live.LiveLogger) (ret []byte, remainingGas uint64, err error) { gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas } - if logger != nil { - logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract) + if logger != nil && logger.OnGasChange != nil { + logger.OnGasChange(suppliedGas, suppliedGas-gasCost, live.GasChangeCallPrecompiledContract) } suppliedGas -= gasCost output, err := p.Run(input) diff --git a/core/vm/evm.go b/core/vm/evm.go index 4960d08c777e..b291bcfbc65f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -22,9 +22,9 @@ import ( "sync/atomic" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -230,8 +230,8 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { + evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) } gas = 0 @@ -286,8 +286,8 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { + evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) } gas = 0 @@ -333,8 +333,8 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { + evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) } gas = 0 } @@ -369,7 +369,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential // future scenarios - evm.StateDB.AddBalance(addr, new(big.Int), state.BalanceChangeTouchAccount) + evm.StateDB.AddBalance(addr, new(big.Int), live.BalanceChangeTouchAccount) if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) @@ -391,8 +391,8 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { + evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) } gas = 0 @@ -442,8 +442,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Ensure there's no existing contract already at the designated address contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { - if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) + if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { + evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) } return nil, common.Address{}, 0, ErrContractAddressCollision @@ -479,7 +479,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // by the error checking condition below. if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCallCodeStorage) { + if contract.UseGas(createDataGas, evm.Config.Tracer, live.GasChangeCallCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -492,7 +492,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeCallFailedExecution) + contract.UseGas(contract.Gas, evm.Config.Tracer, live.GasChangeCallFailedExecution) } } @@ -522,19 +522,23 @@ func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to co tracer := evm.Config.Tracer if isRoot { - tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) - } else { - tracer.CaptureEnter(typ, from, to, input, startGas, value) + if tracer.CaptureStart != nil { + tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) + } + } else if tracer.CaptureEnter != nil { + tracer.CaptureEnter(live.OpCode(typ), from, to, input, startGas, value) } - tracer.OnGasChange(0, startGas, GasChangeCallInitialBalance) + if tracer.OnGasChange != nil { + tracer.OnGasChange(0, startGas, live.GasChangeCallInitialBalance) + } } func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) { tracer := evm.Config.Tracer - if leftOverGas != 0 { - tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) + if leftOverGas != 0 && tracer.OnGasChange != nil { + tracer.OnGasChange(leftOverGas, 0, live.GasChangeCallLeftOverReturned) } var reverted bool if err != nil { @@ -544,8 +548,22 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas reverted = false } if isRoot { - tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) - } else { + if tracer.CaptureEnd != nil { + tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + } + } else if tracer.CaptureExit != nil { tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) } } + +func (evm *EVM) GetVMContext() *live.VMContext { + return &live.VMContext{ + Coinbase: evm.Context.Coinbase, + BlockNumber: evm.Context.BlockNumber, + Time: evm.Context.Time, + Random: evm.Context.Random, + GasPrice: evm.TxContext.GasPrice, + ChainConfig: evm.ChainConfig(), + StateDB: evm.StateDB, + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b9c5b153b731..31296aa1a564 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -18,9 +18,9 @@ package vm import ( "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -594,7 +594,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // reuse size int for stackvalue stackvalue := size - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, live.GasChangeCallContractCreation) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 if !value.IsZero() { @@ -616,7 +616,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -642,7 +642,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ) // Apply EIP150 gas -= gas / 64 - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation2) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, live.GasChangeCallContractCreation2) // reuse size int for stackvalue stackvalue := size //TODO: use uint256.Int instead of converting with toBig() @@ -661,7 +661,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -711,7 +711,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } if interpreter.evm.Config.Tracer != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -751,7 +751,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } if interpreter.evm.Config.Tracer != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -784,7 +784,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } if interpreter.evm.Config.Tracer != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -817,7 +817,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } if interpreter.evm.Config.Tracer != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -855,10 +855,10 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, state.BalanceIncreaseSelfdestruct) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) tracer.CaptureExit([]byte{}, 0, nil, false) } return nil, errStopToken @@ -870,11 +870,11 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon } beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, state.BalanceDecreaseSelfdestruct) - interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, state.BalanceIncreaseSelfdestruct) + interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, live.BalanceDecreaseSelfdestruct) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) tracer.CaptureExit([]byte{}, 0, nil, false) } return nil, errStopToken diff --git a/core/vm/interface.go b/core/vm/interface.go index e96e4502fb10..94aa015ed918 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,8 +20,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -29,8 +29,8 @@ import ( type StateDB interface { CreateAccount(common.Address) - SubBalance(common.Address, *big.Int, state.BalanceChangeReason) - AddBalance(common.Address, *big.Int, state.BalanceChangeReason) + SubBalance(common.Address, *big.Int, live.BalanceChangeReason) + AddBalance(common.Address, *big.Int, live.BalanceChangeReason) GetBalance(common.Address) *big.Int GetNonce(common.Address) uint64 diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7750517e06fb..5f86299637b7 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -22,12 +22,12 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" ) // Config are the configuration options for the Interpreter type Config struct { - Tracer EVMLogger // Opcode logger - LiveLogger *live.LiveLogger + Tracer *live.LiveLogger NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages ExtraEips []int // Additional EIPS that are to be enabled @@ -41,6 +41,30 @@ type ScopeContext struct { Contract *Contract } +func (ctx *ScopeContext) GetMemoryData() []byte { + return ctx.Memory.Data() +} + +func (ctx *ScopeContext) GetStackData() []uint256.Int { + return ctx.Stack.Data() +} + +func (ctx *ScopeContext) GetCaller() common.Address { + return ctx.Contract.Caller() +} + +func (ctx *ScopeContext) GetAddress() common.Address { + return ctx.Contract.Address() +} + +func (ctx *ScopeContext) GetCallValue() *uint256.Int { + return ctx.Contract.Value() +} + +func (ctx *ScopeContext) GetCallInput() []byte { + return ctx.Contract.Input +} + // EVMInterpreter represents an EVM interpreter type EVMInterpreter struct { evm *EVM @@ -160,9 +184,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { if err != nil { if !logged { - in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) - } else { - in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) + if in.evm.Config.Tracer.CaptureState != nil { + in.evm.Config.Tracer.CaptureState(pcCopy, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + } + } else if in.evm.Config.Tracer.CaptureFault != nil { + in.evm.Config.Tracer.CaptureFault(pcCopy, live.OpCode(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) } } }() @@ -187,7 +213,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeIgnored) { + if !contract.UseGas(cost, in.evm.Config.Tracer, live.GasChangeIgnored) { return nil, ErrOutOfGas } @@ -214,23 +240,31 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( var dynamicCost uint64 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // for tracing - if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeIgnored) { + if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, live.GasChangeIgnored) { return nil, ErrOutOfGas } // Do tracing before memory expansion if debug { - in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode) - in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) - logged = true + if in.evm.Config.Tracer.OnGasChange != nil { + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, live.GasChangeCallOpCode) + } + if in.evm.Config.Tracer.CaptureState != nil { + in.evm.Config.Tracer.CaptureState(pc, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + logged = true + } } if memorySize > 0 { mem.Resize(memorySize) } } else if debug { - in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode) - in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) - logged = true + if in.evm.Config.Tracer.OnGasChange != nil { + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, live.GasChangeCallOpCode) + } + if in.evm.Config.Tracer.CaptureState != nil { + in.evm.Config.Tracer.CaptureState(pc, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + logged = true + } } // execute the operation diff --git a/core/vm/logger.go b/core/vm/logger.go index e86bb5d98e27..269aed509cfa 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -16,19 +16,12 @@ package vm -import ( - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - // EVMLogger is used to collect execution traces from an EVM transaction // execution. CaptureState is called for each step of the VM with the // current VM state. // Note that reference types are actual VM data structures; make copies // if you need to retain them beyond the current call. -type EVMLogger interface { +/*type EVMLogger interface { // Transaction level // Call simulations don't come with a valid signature. `from` field // to be used for address of the caller. @@ -55,63 +48,4 @@ type EVMLogger interface { // Misc OnGasChange(old, new uint64, reason GasChangeReason) } - -// GasChangeReason is used to indicate the reason for a gas change, useful -// for tracing and reporting. -// -// There is essentially two types of gas changes, those that can be emitted once per transaction -// and those that can be emitted on a call basis, so possibly multiple times per transaction. -// -// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted -// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis. -type GasChangeReason byte - -const ( - GasChangeUnspecified GasChangeReason = iota - - // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only - // one such gas change per transaction. - GasChangeTxInitialBalance - // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is - // always exactly one of those per transaction. - GasChangeTxIntrinsicGas - // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) - // this generates an increase in gas. There is at most one of such gas change per transaction. - GasChangeTxRefunds - // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned - // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas - // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. - // There is at most one of such gas change per transaction. - GasChangeTxLeftOverReturned - - // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only - // one such gas change per call. - GasChangeCallInitialBalance - // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always - // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even - // will be emitted. - GasChangeCallLeftOverReturned - // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it - // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child. - // If there was no gas left to be refunded, no such even will be emitted. - GasChangeCallLeftOverRefunded - // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE. - GasChangeCallContractCreation - // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2. - GasChangeCallContractCreation2 - // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. - GasChangeCallCodeStorage - // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was - // performed can be check by `CaptureState` handling. - GasChangeCallOpCode - // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. - GasChangeCallPrecompiledContract - // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules. - GasChangeCallStorageColdAccess - // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. - GasChangeCallFailedExecution - - // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as - // it will be "manually" tracked by a direct emit of the gas change event. - GasChangeIgnored GasChangeReason = 0xFF -) +*/ diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 55c9bf34b952..e9f26a88c140 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -169,7 +170,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call - if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeCallStorageColdAccess) { + if !contract.UseGas(coldCost, evm.Config.Tracer, live.GasChangeCallStorageColdAccess) { return 0, ErrOutOfGas } } diff --git a/eth/backend.go b/eth/backend.go index abb6a7e7c2d1..1ba7552d76eb 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -193,7 +193,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { vmConfig = vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, Tracer: config.VMTracer, - LiveLogger: config.LiveLogger, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index b5e2f2368ccc..11e43e41b1b9 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -29,7 +29,6 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/tracers/directory/live" @@ -154,8 +153,7 @@ type Config struct { EnablePreimageRecording bool // Enables VM tracing - VMTracer vm.EVMLogger - LiveLogger *live.LiveLogger + VMTracer *live.LiveLogger // Miscellaneous options DocRoot string `toml:"-"` diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 30f0b23a56dc..422b672086d8 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -777,14 +777,14 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Swap out the noop logger to the standard tracer writer = bufio.NewWriter(dump) vmConf = vm.Config{ - Tracer: logger.NewJSONLogger(&logConfig, writer), + Tracer: logger.NewJSONLogger(&logConfig, writer).GetLogger(), EnablePreimageRecording: true, } } // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.SetTxContext(tx.Hash(), i) - vmConf.Tracer.CaptureTxStart(vmenv, tx, msg.From) + vmConf.Tracer.CaptureTxStart(vmenv.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) vmConf.Tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) if writer != nil { @@ -922,7 +922,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc // be tracer dependent. func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { var ( - tracer directory.Tracer + tracer *directory.Tracer err error timeout = defaultTraceTimeout ) @@ -930,15 +930,15 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *direc config = &TraceConfig{} } // Default tracer is the struct logger - tracer = logger.NewStructLogger(config.Config) + tracer = logger.NewStructLogger(config.Config).GetTracer() if config.Tracer != nil { tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) if err != nil { return nil, err } } - vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true}) - statedb.SetLogger(tracer) + vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.LiveLogger, NoBaseFee: true}) + statedb.SetLogger(tracer.LiveLogger) // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { diff --git a/eth/tracers/directory/live/dir.go b/eth/tracers/directory/live/dir.go index f6656ac79faf..9e89c0f36cc8 100644 --- a/eth/tracers/directory/live/dir.go +++ b/eth/tracers/directory/live/dir.go @@ -6,15 +6,82 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" ) +type ScopeContext interface { + GetMemoryData() []byte + GetStackData() []uint256.Int + GetCaller() common.Address + GetAddress() common.Address + GetCallValue() *uint256.Int + GetCallInput() []byte +} + +type StateDB interface { + GetBalance(common.Address) *uint256.Int + GetNonce(common.Address) uint64 + GetCode(common.Address) []byte + GetState(common.Address, common.Hash) common.Hash + Exist(common.Address) bool + GetRefund() uint64 +} + +type VMContext struct { + Coinbase common.Address + BlockNumber *big.Int + Time uint64 + Random *common.Hash + // Effective tx gas price + GasPrice *big.Int + ChainConfig *params.ChainConfig + StateDB StateDB +} + +// BlockEvent is emitted upon tracing an incoming block. +// It contains the block as well as consensus related information. +type BlockEvent struct { + Block *types.Block + TD *big.Int + Finalized *types.Header + Safe *types.Header +} + +// OpCode is an EVM opcode +// TODO: provide utils for consumers +type OpCode byte + type LiveLogger struct { - VMLogger vm.EVMLogger + /* + - VM events - + */ + // Transaction level + // Call simulations don't come with a valid signature. `from` field + // to be used for address of the caller. + CaptureTxStart func(vm *VMContext, tx *types.Transaction, from common.Address) + CaptureTxEnd func(receipt *types.Receipt, err error) + // Top call frame + CaptureStart func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) + // CaptureEnd is invoked when the processing of the top call ends. + // See docs for `CaptureExit` for info on the `reverted` parameter. + CaptureEnd func(output []byte, gasUsed uint64, err error, reverted bool) + // Rest of call frames + CaptureEnter func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) + // CaptureExit is invoked when the processing of a message ends. + // `revert` is true when there was an error during the execution. + // Exceptionally, before the homestead hardfork a contract creation that + // ran out of gas when attempting to persist the code to database did not + // count as a call failure and did not cause a revert of the call. This will + // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. + CaptureExit func(output []byte, gasUsed uint64, err error, reverted bool) + // Opcode level + CaptureState func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, rData []byte, depth int, err error) + CaptureFault func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, depth int, err error) + CaptureKeccakPreimage func(hash common.Hash, data []byte) + // Misc + OnGasChange func(old, new uint64, reason GasChangeReason) /* - Chain events - @@ -22,18 +89,18 @@ type LiveLogger struct { OnBlockchainInit func(chainConfig *params.ChainConfig) // OnBlockStart is called before executing `block`. // `td` is the total difficulty prior to `block`. - OnBlockStart func(event core.BlockEvent) + OnBlockStart func(event BlockEvent) OnBlockEnd func(err error) // OnSkippedBlock indicates a block was skipped during processing // due to it being known previously. This can happen e.g. when recovering // from a crash. - OnSkippedBlock func(event core.BlockEvent) - OnGenesisBlock func(genesis *types.Block, alloc core.GenesisAlloc) + OnSkippedBlock func(event BlockEvent) + OnGenesisBlock func(genesis *types.Block, alloc types.GenesisAlloc) /* - State events - */ - OnBalanceChange func(addr common.Address, prev, new *big.Int, reason state.BalanceChangeReason) + OnBalanceChange func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) OnNonceChange func(addr common.Address, prev, new uint64) OnCodeChange func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) OnStorageChange func(addr common.Address, slot common.Hash, prev, new common.Hash) diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index 5f6546cfbebc..9c28770494b2 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -21,9 +21,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) func init() { @@ -35,8 +34,29 @@ func init() { type NoopTracer struct{} // newNoopTracer returns a new noop tracer. -func newNoopTracer(ctx *Context, _ json.RawMessage) (Tracer, error) { - return &NoopTracer{}, nil +func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) { + t := &NoopTracer{} + return &Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: t.CaptureTxStart, + CaptureTxEnd: t.CaptureTxEnd, + CaptureStart: t.CaptureStart, + CaptureEnd: t.CaptureEnd, + CaptureEnter: t.CaptureEnter, + CaptureExit: t.CaptureExit, + CaptureState: t.CaptureState, + CaptureFault: t.CaptureFault, + CaptureKeccakPreimage: t.CaptureKeccakPreimage, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, + }, + GetResult: t.GetResult, + Stop: t.Stop, + }, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. @@ -48,21 +68,21 @@ func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *NoopTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *NoopTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { +func (t *NoopTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, _ live.ScopeContext, depth int, err error) { } // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} // OnGasChange is called when gas is either consumed or refunded. -func (t *NoopTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {} +func (t *NoopTracer) OnGasChange(old, new uint64, reason live.GasChangeReason) {} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *NoopTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } // CaptureExit is called when EVM exits a scope, even if the scope didn't @@ -70,11 +90,11 @@ func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common. func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { } -func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {} +func (*NoopTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {} func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} -func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { +func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) { } func (*NoopTracer) OnNonceChange(a common.Address, prev, new uint64) {} diff --git a/eth/tracers/directory/tracers.go b/eth/tracers/directory/tracers.go index b16d9d2fe69d..4953c46c5683 100644 --- a/eth/tracers/directory/tracers.go +++ b/eth/tracers/directory/tracers.go @@ -24,8 +24,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) // Context contains some contextual infos for a transaction execution that is not @@ -41,16 +40,15 @@ type Context struct { // for it to be available through the RPC interface. // This involves a method to retrieve results and one to // stop tracing. -type Tracer interface { - vm.EVMLogger - state.StateLogger - GetResult() (json.RawMessage, error) +type Tracer struct { + *live.LiveLogger + GetResult func() (json.RawMessage, error) // Stop terminates execution of the tracer at the first opportune moment. - Stop(err error) + Stop func(err error) } -type ctorFn func(*Context, json.RawMessage) (Tracer, error) -type jsCtorFn func(string, *Context, json.RawMessage) (Tracer, error) +type ctorFn func(*Context, json.RawMessage) (*Tracer, error) +type jsCtorFn func(string, *Context, json.RawMessage) (*Tracer, error) type elem struct { ctor ctorFn @@ -83,7 +81,7 @@ func (d *directory) RegisterJSEval(f jsCtorFn) { // New returns a new instance of a tracer, by iterating through the // registered lookups. Name is either name of an existing tracer // or an arbitrary JS code. -func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (Tracer, error) { +func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (*Tracer, error) { if elem, ok := d.elems[name]; ok { return elem.ctor(ctx, cfg) } diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index c3a95082022f..f6ed3ed229de 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -25,6 +25,7 @@ import ( "github.com/dop251/goja" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -42,9 +43,9 @@ func init() { if err != nil { panic(err) } - type ctorFn = func(*directory.Context, json.RawMessage) (directory.Tracer, error) + type ctorFn = func(*directory.Context, json.RawMessage) (*directory.Tracer, error) lookup := func(code string) ctorFn { - return func(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { + return func(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { return newJsTracer(code, ctx, cfg) } } @@ -99,7 +100,7 @@ type jsTracer struct { directory.NoopTracer vm *goja.Runtime - env *vm.EVM + env *live.VMContext toBig toBigFn // Converts a hex string into a JS bigint toBuf toBufFn // Converts a []byte into a JS buffer fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte @@ -136,7 +137,7 @@ type jsTracer struct { // The methods `result` and `fault` are required to be present. // The methods `step`, `enter`, and `exit` are optional, but note that // `enter` and `exit` always go together. -func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { +func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { vm := goja.New() // By default field names are exported to JS as is, i.e. capitalized. vm.SetFieldNameMapper(goja.UncapFieldNameMapper()) @@ -209,22 +210,36 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (dire t.frameValue = t.frame.setupObject() t.frameResultValue = t.frameResult.setupObject() t.logValue = t.log.setupObject() - return t, nil + + return &directory.Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: t.CaptureTxStart, + CaptureTxEnd: t.CaptureTxEnd, + CaptureStart: t.CaptureStart, + CaptureEnd: t.CaptureEnd, + CaptureEnter: t.CaptureEnter, + CaptureExit: t.CaptureExit, + CaptureState: t.CaptureState, + CaptureFault: t.CaptureFault, + }, + GetResult: t.GetResult, + Stop: t.Stop, + }, nil } // CaptureTxStart implements the Tracer interface and is invoked at the beginning of // transaction processing. -func (t *jsTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (t *jsTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { t.env = env // Need statedb access for db object db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} t.dbValue = db.setupObject() // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) + rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) - t.ctx["block"] = t.vm.ToValue(t.env.Context.BlockNumber.Uint64()) + t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64()) t.ctx["gas"] = t.vm.ToValue(tx.Gas()) - gasPriceBig, err := t.toBig(t.vm, env.TxContext.GasPrice.String()) + gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String()) if err != nil { t.err = err t.env.Cancel() @@ -284,7 +299,7 @@ func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create b } // CaptureState implements the Tracer interface to trace a single step of VM execution. -func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *jsTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { if !t.traceStep { return } @@ -293,7 +308,7 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope } log := t.log - log.op.op = op + log.op.op = vm.OpCode(op) log.memory.memory = scope.Memory log.stack.stack = scope.Stack log.contract.contract = scope.Contract @@ -309,7 +324,7 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope } // CaptureFault implements the Tracer interface to trace an execution fault -func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (t *jsTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { if t.err != nil { return } @@ -328,7 +343,7 @@ func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *jsTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { if !t.traceFrame { return } @@ -336,7 +351,7 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad return } - t.frame.typ = typ.String() + t.frame.typ = vm.OpCode(typ).String() t.frame.from = from t.frame.to = to t.frame.input = common.CopyBytes(input) @@ -681,7 +696,7 @@ func (s *stackObj) setupObject() *goja.Object { } type dbObj struct { - db vm.StateDB + db live.StateDB vm *goja.Runtime toBig toBigFn toBuf toBufFn diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index f9b8e19def25..e099ef9ac514 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -5,10 +5,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -26,17 +23,26 @@ type noop struct{} func newNoopTracer(_ json.RawMessage) (*live.LiveLogger, error) { t := &noop{} return &live.LiveLogger{ - VMLogger: t, - OnBlockchainInit: t.OnBlockchainInit, - OnBlockStart: t.OnBlockStart, - OnBlockEnd: t.OnBlockEnd, - OnSkippedBlock: t.OnSkippedBlock, - OnGenesisBlock: t.OnGenesisBlock, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, + CaptureTxStart: t.CaptureTxStart, + CaptureTxEnd: t.CaptureTxEnd, + CaptureStart: t.CaptureStart, + CaptureEnd: t.CaptureEnd, + CaptureEnter: t.CaptureEnter, + CaptureExit: t.CaptureExit, + CaptureState: t.CaptureState, + CaptureFault: t.CaptureFault, + CaptureKeccakPreimage: t.CaptureKeccakPreimage, + OnGasChange: t.OnGasChange, + OnBlockchainInit: t.OnBlockchainInit, + OnBlockStart: t.OnBlockStart, + OnBlockEnd: t.OnBlockEnd, + OnSkippedBlock: t.OnSkippedBlock, + OnGenesisBlock: t.OnGenesisBlock, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, }, nil } @@ -49,18 +55,18 @@ func (t *noop) CaptureEnd(output []byte, gasUsed uint64, err error, reverted boo } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *noop) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *noop) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *noop) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { +func (t *noop) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, _ live.ScopeContext, depth int, err error) { } // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *noop) CaptureKeccakPreimage(hash common.Hash, data []byte) {} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *noop) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *noop) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } // CaptureExit is called when EVM exits a scope, even if the scope didn't @@ -68,27 +74,27 @@ func (t *noop) CaptureEnter(typ vm.OpCode, from common.Address, to common.Addres func (t *noop) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { } -func (t *noop) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (t *noop) CaptureTxStart(vm *live.VMContext, tx *types.Transaction, from common.Address) { } func (t *noop) CaptureTxEnd(receipt *types.Receipt, err error) { } -func (t *noop) OnBlockStart(ev core.BlockEvent) { +func (t *noop) OnBlockStart(ev live.BlockEvent) { } func (t *noop) OnBlockEnd(err error) { } -func (t *noop) OnSkippedBlock(ev core.BlockEvent) {} +func (t *noop) OnSkippedBlock(ev live.BlockEvent) {} func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) { } -func (t *noop) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) { +func (t *noop) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { } -func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { +func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) { } func (t *noop) OnNonceChange(a common.Address, prev, new uint64) { @@ -104,5 +110,5 @@ func (t *noop) OnLog(l *types.Log) { } -func (t *noop) OnGasChange(old, new uint64, reason vm.GasChangeReason) { +func (t *noop) OnGasChange(old, new uint64, reason live.GasChangeReason) { } diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 7df66066b55b..fdc990dfee25 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) // accessList is an accumulator for the set of accounts and storage slots an EVM @@ -132,14 +133,20 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi } } +func (a *AccessListTracer) GetLogger() *live.LiveLogger { + return &live.LiveLogger{ + CaptureState: a.CaptureState, + } +} + // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. -func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - stack := scope.Stack - stackData := stack.Data() +func (a *AccessListTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { + stackData := scope.GetStackData() stackLen := len(stackData) + op := vm.OpCode(opcode) if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 { slot := common.Hash(stackData[stackLen-1].Bytes32()) - a.list.addSlot(scope.Contract.Address(), slot) + a.list.addSlot(scope.GetAddress(), slot) } if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 { addr := common.Address(stackData[stackLen-1].Bytes20()) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 3c22bd7886c5..3e8a68e6e5a8 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -109,7 +110,7 @@ func (s *StructLog) ErrorString() string { type StructLogger struct { directory.NoopTracer cfg Config - env *vm.EVM + env *live.VMContext storage map[common.Address]Storage logs []StructLog @@ -132,6 +133,19 @@ func NewStructLogger(cfg *Config) *StructLogger { return logger } +func (l *StructLogger) GetTracer() *directory.Tracer { + return &directory.Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: l.CaptureTxStart, + CaptureTxEnd: l.CaptureTxEnd, + CaptureEnd: l.CaptureEnd, + CaptureState: l.CaptureState, + }, + GetResult: l.GetResult, + Stop: l.Stop, + } +} + // Reset clears the data held by the logger. func (l *StructLogger) Reset() { l.storage = make(map[common.Address]Storage) @@ -143,7 +157,7 @@ func (l *StructLogger) Reset() { // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (l *StructLogger) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { // If tracing was interrupted, set the error and stop if l.interrupt.Load() { return @@ -153,49 +167,49 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s return } - memory := scope.Memory - stack := scope.Stack - contract := scope.Contract + op := vm.OpCode(opcode) + memory := scope.GetMemoryData() + stack := scope.GetStackData() // Copy a snapshot of the current memory state to a new buffer var mem []byte if l.cfg.EnableMemory { - mem = make([]byte, len(memory.Data())) - copy(mem, memory.Data()) + mem = make([]byte, len(memory)) + copy(mem, memory) } // Copy a snapshot of the current stack state to a new buffer var stck []uint256.Int if !l.cfg.DisableStack { - stck = make([]uint256.Int, len(stack.Data())) - for i, item := range stack.Data() { + stck = make([]uint256.Int, len(stack)) + for i, item := range stack { stck[i] = item } } - stackData := stack.Data() - stackLen := len(stackData) + contractAddr := scope.GetAddress() + stackLen := len(stack) // Copy a snapshot of the current storage to a new container var storage Storage if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) { // initialise new changed values storage container for this contract // if not present. - if l.storage[contract.Address()] == nil { - l.storage[contract.Address()] = make(Storage) + if l.storage[contractAddr] == nil { + l.storage[contractAddr] = make(Storage) } // capture SLOAD opcodes and record the read entry in the local storage if op == vm.SLOAD && stackLen >= 1 { var ( - address = common.Hash(stackData[stackLen-1].Bytes32()) - value = l.env.StateDB.GetState(contract.Address(), address) + address = common.Hash(stack[stackLen-1].Bytes32()) + value = l.env.StateDB.GetState(contractAddr, address) ) - l.storage[contract.Address()][address] = value - storage = l.storage[contract.Address()].Copy() + l.storage[contractAddr][address] = value + storage = l.storage[contractAddr].Copy() } else if op == vm.SSTORE && stackLen >= 2 { // capture SSTORE opcodes and record the written entry in the local storage. var ( - value = common.Hash(stackData[stackLen-2].Bytes32()) - address = common.Hash(stackData[stackLen-1].Bytes32()) + value = common.Hash(stack[stackLen-2].Bytes32()) + address = common.Hash(stack[stackLen-1].Bytes32()) ) - l.storage[contract.Address()][address] = value - storage = l.storage[contract.Address()].Copy() + l.storage[contractAddr][address] = value + storage = l.storage[contractAddr].Copy() } } var rdata []byte @@ -204,7 +218,7 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s copy(rdata, rData) } // create a new snapshot of the EVM. - log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} + log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err} l.logs = append(l.logs, log) } @@ -246,7 +260,7 @@ func (l *StructLogger) Stop(err error) { l.interrupt.Store(true) } -func (l *StructLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (l *StructLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { l.env = env } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 1bf88f8899ea..fecfa0cb73dd 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -25,13 +25,14 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) type JSONLogger struct { directory.NoopTracer encoder *json.Encoder cfg *Config - env *vm.EVM + env *live.VMContext } // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects @@ -44,31 +45,40 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { return l } -func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (l *JSONLogger) GetLogger() *live.LiveLogger { + return &live.LiveLogger{ + CaptureTxStart: l.CaptureTxStart, + CaptureEnd: l.CaptureEnd, + CaptureState: l.CaptureState, + CaptureFault: l.CaptureFault, + } +} + +func (l *JSONLogger) CaptureFault(pc uint64, op live.OpCode, gas uint64, cost uint64, scope live.ScopeContext, depth int, err error) { // TODO: Add rData to this interface as well l.CaptureState(pc, op, gas, cost, scope, nil, depth, err) } // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - memory := scope.Memory - stack := scope.Stack +func (l *JSONLogger) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { + memory := scope.GetMemoryData() + stack := scope.GetStackData() log := StructLog{ Pc: pc, - Op: op, + Op: vm.OpCode(op), Gas: gas, GasCost: cost, - MemorySize: memory.Len(), + MemorySize: len(memory), Depth: depth, RefundCounter: l.env.StateDB.GetRefund(), Err: err, } if l.cfg.EnableMemory { - log.Memory = memory.Data() + log.Memory = memory } if !l.cfg.DisableStack { - log.Stack = stack.Data() + log.Stack = stack } if l.cfg.EnableReturnData { log.ReturnData = rData @@ -90,6 +100,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, revert l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg}) } -func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (l *JSONLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { l.env = env } diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index c7c45cfaa429..aa2dbd906a13 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) func init() { @@ -48,7 +49,6 @@ func init() { // } type fourByteTracer struct { directory.NoopTracer - env *vm.EVM ids map[string]int // ids aggregates the 4byte ids found interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption @@ -57,11 +57,19 @@ type fourByteTracer struct { // newFourByteTracer returns a native go tracer which collects // 4 byte-identifiers of a tx, and implements vm.EVMLogger. -func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (directory.Tracer, error) { +func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (*directory.Tracer, error) { t := &fourByteTracer{ ids: make(map[string]int), } - return t, nil + return &directory.Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: t.CaptureTxStart, + CaptureStart: t.CaptureStart, + CaptureEnter: t.CaptureEnter, + }, + GetResult: t.GetResult, + Stop: t.Stop, + }, nil } // isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go @@ -80,10 +88,9 @@ func (t *fourByteTracer) store(id []byte, size int) { t.ids[key] += 1 } -func (t *fourByteTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { - t.env = env +func (t *fourByteTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { // Update list of precompiles based on current block - rules := t.env.ChainConfig().Rules(t.env.Context.BlockNumber, t.env.Context.Random != nil, t.env.Context.Time) + rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) } @@ -96,7 +103,7 @@ func (t *fourByteTracer) CaptureStart(from common.Address, to common.Address, cr } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *fourByteTracer) CaptureEnter(opcode live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { // Skip if tracing was interrupted if t.interrupt.Load() { return @@ -104,6 +111,7 @@ func (t *fourByteTracer) CaptureEnter(op vm.OpCode, from common.Address, to comm if len(input) < 4 { return } + op := vm.OpCode(opcode) // primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT if op != vm.DELEGATECALL && op != vm.STATICCALL && op != vm.CALL && op != vm.CALLCODE { diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 43bd0198da73..74a13038d9f3 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) //go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go @@ -120,7 +121,28 @@ type callTracerConfig struct { // newCallTracer returns a native go tracer which tracks // call frames of a tx, and implements vm.EVMLogger. -func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { +func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { + t, err := newCallTracerObject(ctx, cfg) + if err != nil { + return nil, err + } + return &directory.Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: t.CaptureTxStart, + CaptureTxEnd: t.CaptureTxEnd, + CaptureStart: t.CaptureStart, + CaptureEnd: t.CaptureEnd, + CaptureEnter: t.CaptureEnter, + CaptureExit: t.CaptureExit, + CaptureState: t.CaptureState, + OnLog: t.OnLog, + }, + GetResult: t.GetResult, + Stop: t.Stop, + }, nil +} + +func newCallTracerObject(ctx *directory.Context, cfg json.RawMessage) (*callTracer, error) { var config callTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -154,11 +176,11 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *callTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *callTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { t.depth++ if t.config.OnlyTopCall { return @@ -170,7 +192,7 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common. toCopy := to call := callFrame{ - Type: typ, + Type: vm.OpCode(typ), From: from, To: &toCopy, Input: common.CopyBytes(input), @@ -201,7 +223,7 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, rever t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) } -func (t *callTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (t *callTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { t.gasLimit = tx.Gas() } diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 26f114f44fe3..4cd5308e4054 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) //go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go @@ -123,7 +124,7 @@ type flatCallTracerConfig struct { } // newFlatCallTracer returns a new flatCallTracer. -func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { +func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { var config flatCallTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -133,16 +134,26 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.T // Create inner call tracer with default configuration, don't forward // the OnlyTopCall or WithLog to inner for now - tracer, err := directory.DefaultDirectory.New("callTracer", ctx, nil) + t, err := newCallTracerObject(ctx, nil) if err != nil { return nil, err } - t, ok := tracer.(*callTracer) - if !ok { - return nil, errors.New("internal error: embedded tracer has wrong type") - } - return &flatCallTracer{tracer: t, ctx: ctx, config: config}, nil + ft := &flatCallTracer{tracer: t, ctx: ctx, config: config} + return &directory.Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: ft.CaptureTxStart, + CaptureTxEnd: ft.CaptureTxEnd, + CaptureStart: ft.CaptureStart, + CaptureEnd: ft.CaptureEnd, + CaptureEnter: ft.CaptureEnter, + CaptureExit: ft.CaptureExit, + CaptureState: ft.CaptureState, + CaptureFault: ft.CaptureFault, + }, + Stop: ft.Stop, + GetResult: ft.GetResult, + }, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. @@ -156,17 +167,17 @@ func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, re } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *flatCallTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *flatCallTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err) } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *flatCallTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (t *flatCallTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err) } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *flatCallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *flatCallTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { t.tracer.CaptureEnter(typ, from, to, input, gas, value) // Child calls must have a value, even if it's zero. @@ -200,10 +211,10 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, r } } -func (t *flatCallTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (t *flatCallTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { t.tracer.CaptureTxStart(env, tx, from) // Update list of precompiles based on current block - rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time) + rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 70af63e752a3..501f59838557 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -21,10 +21,9 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) func init() { @@ -35,18 +34,18 @@ func init() { // runs multiple tracers in one go. type muxTracer struct { names []string - tracers []directory.Tracer + tracers []*directory.Tracer } // newMuxTracer returns a new mux tracer. -func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { +func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { var config map[string]json.RawMessage if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { return nil, err } } - objects := make([]directory.Tracer, 0, len(config)) + objects := make([]*directory.Tracer, 0, len(config)) names := make([]string, 0, len(config)) for k, v := range config { t, err := directory.DefaultDirectory.New(k, ctx, v) @@ -57,55 +56,90 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer names = append(names, k) } - return &muxTracer{names: names, tracers: objects}, nil + t := &muxTracer{names: names, tracers: objects} + return &directory.Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: t.CaptureTxStart, + CaptureTxEnd: t.CaptureTxEnd, + CaptureStart: t.CaptureStart, + CaptureEnd: t.CaptureEnd, + CaptureEnter: t.CaptureEnter, + CaptureExit: t.CaptureExit, + CaptureState: t.CaptureState, + CaptureFault: t.CaptureFault, + CaptureKeccakPreimage: t.CaptureKeccakPreimage, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, + }, + GetResult: t.GetResult, + Stop: t.Stop, + }, nil } // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { - t.CaptureStart(from, to, create, input, gas, value) + if t.CaptureStart != nil { + t.CaptureStart(from, to, create, input, gas, value) + } } } // CaptureEnd is called after the call finishes to finalize the tracing. func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { for _, t := range t.tracers { - t.CaptureEnd(output, gasUsed, err, reverted) + if t.CaptureEnd != nil { + t.CaptureEnd(output, gasUsed, err, reverted) + } } } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *muxTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *muxTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { for _, t := range t.tracers { - t.CaptureState(pc, op, gas, cost, scope, rData, depth, err) + if t.CaptureState != nil { + t.CaptureState(pc, op, gas, cost, scope, rData, depth, err) + } } } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (t *muxTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { for _, t := range t.tracers { - t.CaptureFault(pc, op, gas, cost, scope, depth, err) + if t.CaptureFault != nil { + t.CaptureFault(pc, op, gas, cost, scope, depth, err) + } } } // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { for _, t := range t.tracers { - t.CaptureKeccakPreimage(hash, data) + if t.CaptureKeccakPreimage != nil { + t.CaptureKeccakPreimage(hash, data) + } } } // CaptureGasConsumed is called when gas is consumed. -func (t *muxTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) { +func (t *muxTracer) OnGasChange(old, new uint64, reason live.GasChangeReason) { for _, t := range t.tracers { - t.OnGasChange(old, new, reason) + if t.OnGasChange != nil { + t.OnGasChange(old, new, reason) + } } } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *muxTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { - t.CaptureEnter(typ, from, to, input, gas, value) + if t.CaptureEnter != nil { + t.CaptureEnter(typ, from, to, input, gas, value) + } } } @@ -113,49 +147,65 @@ func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.A // execute any code. func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { for _, t := range t.tracers { - t.CaptureExit(output, gasUsed, err, reverted) + if t.CaptureExit != nil { + t.CaptureExit(output, gasUsed, err, reverted) + } } } -func (t *muxTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (t *muxTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { for _, t := range t.tracers { - t.CaptureTxStart(env, tx, from) + if t.CaptureTxStart != nil { + t.CaptureTxStart(env, tx, from) + } } } func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) { for _, t := range t.tracers { - t.CaptureTxEnd(receipt, err) + if t.CaptureTxEnd != nil { + t.CaptureTxEnd(receipt, err) + } } } -func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { +func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) { for _, t := range t.tracers { - t.OnBalanceChange(a, prev, new, reason) + if t.OnBalanceChange != nil { + t.OnBalanceChange(a, prev, new, reason) + } } } func (t *muxTracer) OnNonceChange(a common.Address, prev, new uint64) { for _, t := range t.tracers { - t.OnNonceChange(a, prev, new) + if t.OnNonceChange != nil { + t.OnNonceChange(a, prev, new) + } } } func (t *muxTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { for _, t := range t.tracers { - t.OnCodeChange(a, prevCodeHash, prev, codeHash, code) + if t.OnCodeChange != nil { + t.OnCodeChange(a, prevCodeHash, prev, codeHash, code) + } } } func (t *muxTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) { for _, t := range t.tracers { - t.OnStorageChange(a, k, prev, new) + if t.OnStorageChange != nil { + t.OnStorageChange(a, k, prev, new) + } } } func (t *muxTracer) OnLog(log *types.Log) { for _, t := range t.tracers { - t.OnLog(log) + if t.OnLog != nil { + t.OnLog(log) + } } } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 8608ab69dcac..88cd0009fa75 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/log" ) @@ -58,7 +59,7 @@ type accountMarshaling struct { type prestateTracer struct { directory.NoopTracer - env *vm.EVM + env *live.VMContext pre stateMap post stateMap to common.Address @@ -73,24 +74,33 @@ type prestateTracerConfig struct { DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications } -func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) { +func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Tracer, error) { var config prestateTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { return nil, err } } - return &prestateTracer{ + t := &prestateTracer{ pre: stateMap{}, post: stateMap{}, config: config, created: make(map[common.Address]bool), deleted: make(map[common.Address]bool), + } + return &directory.Tracer{ + LiveLogger: &live.LiveLogger{ + CaptureTxStart: t.CaptureTxStart, + CaptureTxEnd: t.CaptureTxEnd, + CaptureState: t.CaptureState, + }, + GetResult: t.GetResult, + Stop: t.Stop, }, nil } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *prestateTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { if err != nil { return } @@ -98,10 +108,10 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, if t.interrupt.Load() { return } - stack := scope.Stack - stackData := stack.Data() + op := vm.OpCode(opcode) + stackData := scope.GetStackData() stackLen := len(stackData) - caller := scope.Contract.Address() + caller := scope.GetAddress() switch { case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE): slot := common.Hash(stackData[stackLen-1].Bytes32()) @@ -136,7 +146,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, } } -func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (t *prestateTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { t.env = env if tx.To() == nil { t.to = crypto.CreateAddress(from, env.StateDB.GetNonce(from)) @@ -147,7 +157,7 @@ func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from t.lookupAccount(from) t.lookupAccount(t.to) - t.lookupAccount(env.Context.Coinbase) + t.lookupAccount(env.Coinbase) } func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 4f792260ede3..39d79413bd7b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -40,6 +40,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" @@ -960,7 +961,7 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error { } // Override account balance. if account.Balance != nil { - statedb.SetBalance(addr, (*big.Int)(*account.Balance), state.BalanceChangeUnspecified) + statedb.SetBalance(addr, (*big.Int)(*account.Balance), live.BalanceChangeUnspecified) } if account.State != nil && account.StateDiff != nil { return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) @@ -1639,7 +1640,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) - config := vm.Config{Tracer: tracer, NoBaseFee: true} + config := vm.Config{Tracer: tracer.GetLogger(), NoBaseFee: true} vmenv, _ := b.GetEVM(ctx, msg, statedb, header, &config, nil) res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) if err != nil { diff --git a/miner/worker.go b/miner/worker.go index a5ea9571a9c8..95190d5c5d69 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -976,7 +976,7 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { if header.ParentBeaconRoot != nil { context := core.NewEVMBlockContext(header, w.chain, nil) vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}) - core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state, nil) + core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) } return env, nil } From e0b96540b7a3c7ad3d9040fc6665c18366eb84b6 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 21 Feb 2024 18:54:15 +0100 Subject: [PATCH 19/50] Add canceler interface --- core/vm/evm.go | 1 + eth/tracers/directory/live/dir.go | 7 +++++++ eth/tracers/js/goja.go | 6 +++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index b291bcfbc65f..a3fd2da531db 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -565,5 +565,6 @@ func (evm *EVM) GetVMContext() *live.VMContext { GasPrice: evm.TxContext.GasPrice, ChainConfig: evm.ChainConfig(), StateDB: evm.StateDB, + VM: evm, } } diff --git a/eth/tracers/directory/live/dir.go b/eth/tracers/directory/live/dir.go index 9e89c0f36cc8..fca365414185 100644 --- a/eth/tracers/directory/live/dir.go +++ b/eth/tracers/directory/live/dir.go @@ -29,6 +29,12 @@ type StateDB interface { GetRefund() uint64 } +// Canceler is an interface that wraps the Cancel method. +// It allows loggers to cancel EVM processing. +type Canceler interface { + Cancel() +} + type VMContext struct { Coinbase common.Address BlockNumber *big.Int @@ -38,6 +44,7 @@ type VMContext struct { GasPrice *big.Int ChainConfig *params.ChainConfig StateDB StateDB + VM Canceler } // BlockEvent is emitted upon tracing an incoming block. diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index f6ed3ed229de..3e0a4b592f3f 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -242,7 +242,7 @@ func (t *jsTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, fr gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String()) if err != nil { t.err = err - t.env.Cancel() + t.env.VM.Cancel() return } t.ctx["gasPrice"] = gasPriceBig @@ -265,7 +265,7 @@ func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) { func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { cancel := func(err error) { t.err = err - t.env.Cancel() + t.env.VM.Cancel() } if create { t.ctx["type"] = t.vm.ToValue("CREATE") @@ -408,7 +408,7 @@ func (t *jsTracer) onError(context string, err error) { t.err = wrapError(context, err) // `env` is set on CaptureStart which comes before any JS execution. // So it should be non-nil. - t.env.Cancel() + t.env.VM.Cancel() } func wrapError(context string, err error) error { From 6013241175129e75c3176af069904995f6533730 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 27 Feb 2024 16:32:21 +0100 Subject: [PATCH 20/50] fix memory copy util func --- eth/tracers/directory/util.go | 30 ++++++++++++++++++++++-------- eth/tracers/directory/util_test.go | 2 +- eth/tracers/native/prestate.go | 2 +- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/eth/tracers/directory/util.go b/eth/tracers/directory/util.go index 92de735291d1..25f95642c882 100644 --- a/eth/tracers/directory/util.go +++ b/eth/tracers/directory/util.go @@ -18,8 +18,6 @@ package directory import ( "errors" "fmt" - - "github.com/ethereum/go-ethereum/core/vm" ) const ( @@ -28,20 +26,36 @@ const ( // GetMemoryCopyPadded returns offset + size as a new slice. // It zero-pads the slice if it extends beyond memory bounds. -func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) { +func GetMemoryCopyPadded(m []byte, offset, size int64) ([]byte, error) { if offset < 0 || size < 0 { return nil, errors.New("offset or size must not be negative") } - if int(offset+size) < m.Len() { // slice fully inside memory - return m.GetCopy(offset, size), nil + length := int64(len(m)) + if offset+size < length { // slice fully inside memory + return memoryCopy(m, offset, size), nil } - paddingNeeded := int(offset+size) - m.Len() + paddingNeeded := offset + size - length if paddingNeeded > memoryPadLimit { return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) } cpy := make([]byte, size) - if overlap := int64(m.Len()) - offset; overlap > 0 { - copy(cpy, m.GetPtr(offset, overlap)) + if overlap := length - offset; overlap > 0 { + copy(cpy, m[offset:offset+overlap]) } return cpy, nil } + +func memoryCopy(m []byte, offset, size int64) (cpy []byte) { + if size == 0 { + return nil + } + + if len(m) > int(offset) { + cpy = make([]byte, size) + copy(cpy, m[offset:offset+size]) + + return + } + + return +} diff --git a/eth/tracers/directory/util_test.go b/eth/tracers/directory/util_test.go index c72096708da3..904c749d06a3 100644 --- a/eth/tracers/directory/util_test.go +++ b/eth/tracers/directory/util_test.go @@ -40,7 +40,7 @@ func TestMemCopying(t *testing.T) { } { mem := vm.NewMemory() mem.Resize(uint64(tc.memsize)) - cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size) + cpy, err := GetMemoryCopyPadded(mem.Data(), tc.offset, tc.size) if want := tc.wantErr; want != "" { if err == nil { t.Fatalf("test %d: want '%v' have no error", i, want) diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 88cd0009fa75..7ac77ffaa976 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -133,7 +133,7 @@ func (t *prestateTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost u case stackLen >= 4 && op == vm.CREATE2: offset := stackData[stackLen-2] size := stackData[stackLen-3] - init, err := directory.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64())) + init, err := directory.GetMemoryCopyPadded(scope.GetMemoryData(), int64(offset.Uint64()), int64(size.Uint64())) if err != nil { log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size) return From f1027b7c0a5afae1c2ba04e3d3862a285ce593a9 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 27 Feb 2024 16:51:47 +0100 Subject: [PATCH 21/50] fix goja scope object wrappers --- eth/tracers/directory/util.go | 21 ++++++++++++++++- eth/tracers/js/goja.go | 43 ++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/eth/tracers/directory/util.go b/eth/tracers/directory/util.go index 25f95642c882..f5a2cd80d34d 100644 --- a/eth/tracers/directory/util.go +++ b/eth/tracers/directory/util.go @@ -18,6 +18,8 @@ package directory import ( "errors" "fmt" + + "github.com/holiman/uint256" ) const ( @@ -40,7 +42,7 @@ func GetMemoryCopyPadded(m []byte, offset, size int64) ([]byte, error) { } cpy := make([]byte, size) if overlap := length - offset; overlap > 0 { - copy(cpy, m[offset:offset+overlap]) + copy(cpy, MemoryPtr(m, offset, overlap)) } return cpy, nil } @@ -59,3 +61,20 @@ func memoryCopy(m []byte, offset, size int64) (cpy []byte) { return } + +func MemoryPtr(m []byte, offset, size int64) []byte { + if size == 0 { + return nil + } + + if len(m) > int(offset) { + return m[offset : offset+size] + } + + return nil +} + +// Back returns the n'th item in stack +func StackBack(st []uint256.Int, n int) *uint256.Int { + return &st[len(st)-n-1] +} diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 3e0a4b592f3f..62115f40a2b2 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers/directory" "github.com/ethereum/go-ethereum/eth/tracers/directory/live" + "github.com/holiman/uint256" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -309,9 +310,9 @@ func (t *jsTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, sco log := t.log log.op.op = vm.OpCode(op) - log.memory.memory = scope.Memory - log.stack.stack = scope.Stack - log.contract.contract = scope.Contract + log.memory.memory = scope.GetMemoryData() + log.stack.stack = scope.GetStackData() + log.contract.scope = scope log.pc = pc log.gas = gas log.cost = cost @@ -587,7 +588,7 @@ func (o *opObj) setupObject() *goja.Object { } type memoryObj struct { - memory *vm.Memory + memory []byte vm *goja.Runtime toBig toBigFn toBuf toBufFn @@ -638,14 +639,14 @@ func (mo *memoryObj) GetUint(addr int64) goja.Value { // getUint returns the 32 bytes at the specified address interpreted as a uint. func (mo *memoryObj) getUint(addr int64) (*big.Int, error) { - if mo.memory.Len() < int(addr)+32 || addr < 0 { - return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32) + if len(mo.memory) < int(addr)+32 || addr < 0 { + return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", len(mo.memory), addr, 32) } - return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil + return new(big.Int).SetBytes(directory.MemoryPtr(mo.memory, addr, 32)), nil } func (mo *memoryObj) Length() int { - return mo.memory.Len() + return len(mo.memory) } func (m *memoryObj) setupObject() *goja.Object { @@ -657,7 +658,7 @@ func (m *memoryObj) setupObject() *goja.Object { } type stackObj struct { - stack *vm.Stack + stack []uint256.Int vm *goja.Runtime toBig toBigFn } @@ -678,14 +679,14 @@ func (s *stackObj) Peek(idx int) goja.Value { // peek returns the nth-from-the-top element of the stack. func (s *stackObj) peek(idx int) (*big.Int, error) { - if len(s.stack.Data()) <= idx || idx < 0 { - return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx) + if len(s.stack) <= idx || idx < 0 { + return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack), idx) } - return s.stack.Back(idx).ToBig(), nil + return directory.StackBack(s.stack, idx).ToBig(), nil } func (s *stackObj) Length() int { - return len(s.stack.Data()) + return len(s.stack) } func (s *stackObj) setupObject() *goja.Object { @@ -788,14 +789,14 @@ func (do *dbObj) setupObject() *goja.Object { } type contractObj struct { - contract *vm.Contract - vm *goja.Runtime - toBig toBigFn - toBuf toBufFn + scope live.ScopeContext + vm *goja.Runtime + toBig toBigFn + toBuf toBufFn } func (co *contractObj) GetCaller() goja.Value { - caller := co.contract.Caller().Bytes() + caller := co.scope.GetCaller().Bytes() res, err := co.toBuf(co.vm, caller) if err != nil { co.vm.Interrupt(err) @@ -805,7 +806,7 @@ func (co *contractObj) GetCaller() goja.Value { } func (co *contractObj) GetAddress() goja.Value { - addr := co.contract.Address().Bytes() + addr := co.scope.GetAddress().Bytes() res, err := co.toBuf(co.vm, addr) if err != nil { co.vm.Interrupt(err) @@ -815,7 +816,7 @@ func (co *contractObj) GetAddress() goja.Value { } func (co *contractObj) GetValue() goja.Value { - value := co.contract.Value() + value := co.scope.GetCallValue() res, err := co.toBig(co.vm, value.String()) if err != nil { co.vm.Interrupt(err) @@ -825,7 +826,7 @@ func (co *contractObj) GetValue() goja.Value { } func (co *contractObj) GetInput() goja.Value { - input := common.CopyBytes(co.contract.Input) + input := common.CopyBytes(co.scope.GetCallInput()) res, err := co.toBuf(co.vm, input) if err != nil { co.vm.Interrupt(err) From 29ebfb30c371518acb4a0efe61a55dfb19be88ad Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Tue, 27 Feb 2024 20:31:01 +0100 Subject: [PATCH 22/50] fixes, rm t8ntool tracewriter # Conflicts: # cmd/evm/internal/t8ntool/execution.go # cmd/evm/internal/t8ntool/transition.go # core/vm/instructions.go # eth/tracers/internal/tracetest/calltrace_test.go # eth/tracers/internal/tracetest/flat_calltrace_test.go # eth/tracers/internal/tracetest/prestate_test.go # eth/tracers/js/tracer_test.go # eth/tracers/logger/logger_test.go # eth/tracers/tracers_test.go # tests/block_test_util.go # tests/state_test_util.go --- cmd/evm/blockrunner.go | 6 +- cmd/evm/internal/t8ntool/execution.go | 41 +++++++-- cmd/evm/internal/t8ntool/tracewriter.go | 92 ------------------- cmd/evm/internal/t8ntool/transition.go | 32 +++---- core/vm/instructions.go | 30 +++--- core/vm/interpreter.go | 6 ++ .../internal/tracetest/calltrace_test.go | 28 +++--- .../internal/tracetest/flat_calltrace_test.go | 13 +-- .../internal/tracetest/prestate_test.go | 16 ++-- eth/tracers/js/tracer_test.go | 17 ++-- eth/tracers/logger/logger_test.go | 4 +- eth/tracers/tracers_test.go | 4 +- tests/block_test_util.go | 3 +- tests/state_test_util.go | 5 +- 14 files changed, 115 insertions(+), 182 deletions(-) delete mode 100644 cmd/evm/internal/t8ntool/tracewriter.go diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index ff6557458628..9c2f4578c816 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -25,7 +25,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/tests" "github.com/urfave/cli/v2" @@ -50,7 +50,7 @@ func blockTestCmd(ctx *cli.Context) error { return errors.New("path-to-test argument required") } - var tracer vm.EVMLogger + var tracer *live.LiveLogger // Configure the EVM logger if ctx.Bool(MachineFlag.Name) { tracer = logger.NewJSONLogger(&logger.Config{ @@ -58,7 +58,7 @@ func blockTestCmd(ctx *cli.Context) error { DisableStack: ctx.Bool(DisableStackFlag.Name), DisableStorage: ctx.Bool(DisableStorageFlag.Name), EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - }, os.Stderr) + }, os.Stderr).GetLogger() } // Load the test content from the input file src, err := os.ReadFile(ctx.Args().First()) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 5b9a2e417c91..ea8069ba6621 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -17,7 +17,9 @@ package t8ntool import ( + "encoding/json" "fmt" + "io" "math/big" "github.com/ethereum/go-ethereum/common" @@ -31,6 +33,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -117,7 +121,7 @@ type rejectedTx struct { // Apply applies a set of transactions to a pre-state func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txIt txIterator, miningReward int64, - getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.EVMLogger, err error)) (*state.StateDB, *ExecutionResult, []byte, error) { + getTracerFn func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) { // Capture errors for BLOCKHASH operation, if we haven't been supplied the // required blockhashes var hashError error @@ -187,7 +191,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil { evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig) - core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb, nil) + core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb) } var blobGasUsed uint64 @@ -220,11 +224,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, } blobGasUsed += txBlobGas } - tracer, err := getTracerFn(txIndex, tx.Hash()) + tracer, traceOutput, err := getTracerFn(txIndex, tx.Hash()) if err != nil { return nil, nil, nil, err } - vmConfig.Tracer = tracer + vmConfig.Tracer = tracer.LiveLogger statedb.SetTxContext(tx.Hash(), txIndex) var ( @@ -235,7 +239,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) if tracer != nil { - tracer.CaptureTxStart(evm, tx, msg.From) + tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) } // (ret []byte, usedGas uint64, failed bool, err error) msgResult, err := core.ApplyMessage(evm, msg, gaspool) @@ -246,6 +250,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, gaspool.SetGas(prevGas) if tracer != nil { tracer.CaptureTxEnd(nil, err) + if err := writeTraceResult(tracer, traceOutput); err != nil { + log.Warn("Error writing tracer output", "err", err) + } } continue } @@ -289,7 +296,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, receipt.TransactionIndex = uint(txIndex) receipts = append(receipts, receipt) if tracer != nil { - tracer.CaptureTxEnd(receipt, nil) + tracer.LiveLogger.CaptureTxEnd(receipt, nil) + writeTraceResult(tracer, traceOutput) } } @@ -316,15 +324,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) reward.Mul(reward, blockReward) reward.Div(reward, big.NewInt(8)) - statedb.AddBalance(ommer.Address, reward, state.BalanceIncreaseRewardMineUncle) + statedb.AddBalance(ommer.Address, reward, live.BalanceIncreaseRewardMineUncle) } - statedb.AddBalance(pre.Env.Coinbase, minerReward, state.BalanceIncreaseRewardMineBlock) + statedb.AddBalance(pre.Env.Coinbase, minerReward, live.BalanceIncreaseRewardMineBlock) } // Apply withdrawals for _, w := range pre.Env.Withdrawals { // Amount is in gwei, turn into wei amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) - statedb.AddBalance(w.Address, amount, state.BalanceIncreaseWithdrawal) + statedb.AddBalance(w.Address, amount, live.BalanceIncreaseWithdrawal) } // Commit block root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber)) @@ -367,7 +375,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance, state.BalanceIncreaseGenesisBalance) + statedb.SetBalance(addr, a.Balance, live.BalanceIncreaseGenesisBalance) for k, v := range a.Storage { statedb.SetState(addr, k, v) } @@ -404,3 +412,16 @@ func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime } return ethash.CalcDifficulty(config, currentTime, parent) } + +func writeTraceResult(tracer *directory.Tracer, f io.WriteCloser) error { + defer f.Close() + result, err := tracer.GetResult() + if err != nil { + return err + } + err = json.NewEncoder(f).Encode(result) + if err != nil { + return err + } + return nil +} diff --git a/cmd/evm/internal/t8ntool/tracewriter.go b/cmd/evm/internal/t8ntool/tracewriter.go deleted file mode 100644 index 8a850f1d9812..000000000000 --- a/cmd/evm/internal/t8ntool/tracewriter.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package t8ntool - -import ( - "encoding/json" - "io" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers/directory" - "github.com/ethereum/go-ethereum/log" -) - -// traceWriter is an vm.EVMLogger which also holds an inner logger/tracer. -// When the TxEnd event happens, the inner tracer result is written to the file, and -// the file is closed. -type traceWriter struct { - inner vm.EVMLogger - f io.WriteCloser -} - -// Compile-time interface check -var _ = vm.EVMLogger((*traceWriter)(nil)) - -func (t *traceWriter) CaptureTxEnd(receipt *types.Receipt, err error) { - t.inner.CaptureTxEnd(receipt, err) - defer t.f.Close() - - if tracer, ok := t.inner.(directory.Tracer); ok { - result, err := tracer.GetResult() - if err != nil { - log.Warn("Error in tracer", "err", err) - return - } - err = json.NewEncoder(t.f).Encode(result) - if err != nil { - log.Warn("Error writing tracer output", "err", err) - return - } - } -} - -func (t *traceWriter) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { - t.inner.CaptureTxStart(env, tx, from) -} -func (t *traceWriter) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.inner.CaptureStart(from, to, create, input, gas, value) -} - -func (t *traceWriter) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { - t.inner.CaptureEnd(output, gasUsed, err, reverted) -} - -func (t *traceWriter) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { - t.inner.CaptureEnter(typ, from, to, input, gas, value) -} - -func (t *traceWriter) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { - t.inner.CaptureExit(output, gasUsed, err, reverted) -} - -func (t *traceWriter) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - t.inner.CaptureState(pc, op, gas, cost, scope, rData, depth, err) -} -func (t *traceWriter) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { - t.inner.CaptureFault(pc, op, gas, cost, scope, depth, err) -} - -func (t *traceWriter) CaptureKeccakPreimage(hash common.Hash, data []byte) { - t.inner.CaptureKeccakPreimage(hash, data) -} - -func (t *traceWriter) OnGasChange(old, new uint64, reason vm.GasChangeReason) { - t.inner.OnGasChange(old, new, reason) -} diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index e41ff313e0a7..135b89fc1b25 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "math/big" "os" "path" @@ -86,12 +87,7 @@ func Transition(ctx *cli.Context) error { glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name))) log.Root().SetHandler(glogger) - var ( - err error - tracer vm.EVMLogger - ) - var getTracer func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) - + var getTracer = func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error) { return nil, nil, nil } baseDir, err := createBasedir(ctx) if err != nil { return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err)) @@ -123,32 +119,36 @@ func Transition(ctx *cli.Context) error { prevFile.Close() } }() - getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) { - if prevFile != nil { - prevFile.Close() - } + getTracer = func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error) { traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String()))) if err != nil { - return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) + return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } prevFile = traceFile - return logger.NewJSONLogger(logConfig, traceFile), nil + logger := logger.NewJSONLogger(logConfig, traceFile).GetLogger() + tracer := &directory.Tracer{ + LiveLogger: logger, + // JSONLogger streams out result to file. + GetResult: func() (json.RawMessage, error) { return nil, nil }, + Stop: func(err error) {}, + } + return tracer, traceFile, nil } } else if ctx.IsSet(TraceTracerFlag.Name) { var config json.RawMessage if ctx.IsSet(TraceTracerConfigFlag.Name) { config = []byte(ctx.String(TraceTracerConfigFlag.Name)) } - getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) { + getTracer = func(txIndex int, txHash common.Hash) (*directory.Tracer, io.WriteCloser, error) { traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String()))) if err != nil { - return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) + return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } tracer, err := directory.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config) if err != nil { - return nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err)) + return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err)) } - return &traceWriter{tracer, traceFile}, nil + return tracer, traceFile, nil } } // We need to load three things: alloc, env and transactions. May be either in diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 31296aa1a564..3a681775bbcf 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -248,7 +248,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if evm.Config.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - if interpreter.evm.Config.Tracer != nil { + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.CaptureKeccakPreimage != nil { interpreter.evm.Config.Tracer.CaptureKeccakPreimage(common.BytesToHash(interpreter.hasherBuf[:]), data) } size.SetBytes(interpreter.hasherBuf[:]) @@ -615,7 +615,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } scope.Stack.push(&stackvalue) - if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } @@ -660,7 +660,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } scope.Stack.push(&stackvalue) - if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } @@ -710,7 +710,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } @@ -750,7 +750,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } @@ -783,7 +783,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } @@ -816,7 +816,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && returnGas > 0 { + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) } @@ -858,8 +858,12 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) - tracer.CaptureExit([]byte{}, 0, nil, false) + if tracer.CaptureEnter != nil { + tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + } + if tracer.CaptureExit != nil { + tracer.CaptureExit([]byte{}, 0, nil, false) + } } return nil, errStopToken } @@ -874,8 +878,12 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) - tracer.CaptureExit([]byte{}, 0, nil, false) + if tracer.CaptureEnter != nil { + tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + } + if tracer.CaptureExit != nil { + tracer.CaptureExit([]byte{}, 0, nil, false) + } } return nil, errStopToken } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 5f86299637b7..6da530cd0602 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -42,10 +42,16 @@ type ScopeContext struct { } func (ctx *ScopeContext) GetMemoryData() []byte { + if ctx.Memory == nil { + return nil + } return ctx.Memory.Data() } func (ctx *ScopeContext) GetStackData() []uint256.Int { + if ctx.Stack == nil { + return nil + } return ctx.Stack.Data() } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index e518874549d7..fcc56180d148 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -124,12 +124,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { } // Configure a blockchain with the given prestate var ( - signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - origin, _ = signer.Sender(tx) - txContext = vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } + signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -148,13 +143,14 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - statedb.SetLogger(tracer) - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + + statedb.SetLogger(tracer.LiveLogger) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - tracer.CaptureTxStart(evm, tx, msg.From) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) + tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("failed to execute transaction: %v", err) @@ -255,7 +251,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if err != nil { b.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) + evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, err = st.TransitionDb(); err != nil { @@ -287,7 +283,7 @@ func TestInternals(t *testing.T) { BaseFee: new(big.Int), } ) - mkTracer := func(name string, cfg json.RawMessage) directory.Tracer { + mkTracer := func(name string, cfg json.RawMessage) *directory.Tracer { tr, err := directory.DefaultDirectory.New(name, nil, cfg) if err != nil { t.Fatalf("failed to create call tracer: %v", err) @@ -298,7 +294,7 @@ func TestInternals(t *testing.T) { for _, tc := range []struct { name string code []byte - tracer directory.Tracer + tracer *directory.Tracer want string }{ { @@ -383,7 +379,7 @@ func TestInternals(t *testing.T) { }, }, false, rawdb.HashScheme) defer triedb.Close() - statedb.SetLogger(tc.tracer) + statedb.SetLogger(tc.tracer.LiveLogger) tx, err := types.SignNewTx(key, signer, &types.LegacyTx{ To: &to, Value: big.NewInt(0), @@ -397,12 +393,12 @@ func TestInternals(t *testing.T) { Origin: origin, GasPrice: tx.GasPrice(), } - evm := vm.NewEVM(context, txContext, statedb, config, vm.Config{Tracer: tc.tracer}) + evm := vm.NewEVM(context, txContext, statedb, config, vm.Config{Tracer: tc.tracer.LiveLogger}) msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0)) if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) } - tc.tracer.CaptureTxStart(evm, tx, msg.From) + tc.tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err) diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 4b56dfd6b9b1..7ae99d2b9d93 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -84,11 +84,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - origin, _ := signer.Sender(tx) - txContext := vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } context := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -106,14 +101,14 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string if err != nil { return fmt.Errorf("failed to create call tracer: %v", err) } - statedb.SetLogger(tracer) - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + statedb.SetLogger(tracer.LiveLogger) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } - tracer.CaptureTxStart(evm, tx, msg.From) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) + tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return fmt.Errorf("failed to execute transaction: %v", err) diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 9a9189e3994d..08e1eccc3c45 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -92,12 +92,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { } // Configure a blockchain with the given prestate var ( - signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - origin, _ = signer.Sender(tx) - txContext = vm.TxContext{ - Origin: origin, - GasPrice: tx.GasPrice(), - } + signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -116,13 +111,14 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to create call tracer: %v", err) } - statedb.SetLogger(tracer) - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + + statedb.SetLogger(tracer.LiveLogger) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - tracer.CaptureTxStart(evm, tx, msg.From) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) + tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("failed to execute transaction: %v", err) diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 11c44fbcc4ab..c5a3a7e2f85b 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/directory" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -61,9 +62,9 @@ func testCtx() *vmContext { return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} } -func runTrace(tracer directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { +func runTrace(tracer *directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { var ( - env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer}) + env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.LiveLogger}) gasLimit uint64 = 31000 startGas uint64 = 10000 value = big.NewInt(0) @@ -74,7 +75,7 @@ func runTrace(tracer directory.Tracer, vmctx *vmContext, chaincfg *params.ChainC contract.Code = contractCode } - tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) + tracer.CaptureTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) tracer.CaptureStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value) ret, err := env.Interpreter().Run(contract, []byte{}, false) tracer.CaptureEnd(ret, startGas-contract.Gas, err, true) @@ -184,8 +185,8 @@ func TestHaltBetweenSteps(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) - tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{}), common.Address{}) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.LiveLogger}) + tracer.CaptureTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") @@ -206,8 +207,8 @@ func TestNoStepExec(t *testing.T) { if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) - tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{}), common.Address{}) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.LiveLogger}) + tracer.CaptureTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) tracer.CaptureEnd(nil, 0, nil, false) ret, err := tracer.GetResult() @@ -278,7 +279,7 @@ func TestEnterExit(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) + tracer.CaptureEnter(live.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.CaptureExit([]byte{}, 400, nil, false) have, err := tracer.GetResult() diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index a244e3f9ca1b..cd70f1a97004 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -55,12 +55,12 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} func TestStoreCapture(t *testing.T) { var ( logger = NewStructLogger(nil) - env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger}) + env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.GetTracer().LiveLogger}) contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash - logger.CaptureTxStart(env, nil, common.Address{}) + logger.CaptureTxStart(env.GetVMContext(), nil, common.Address{}) logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil) _, err := env.Interpreter().Run(contract, []byte{}, false) if err != nil { diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index ddfdb81ec661..e3ce3963744a 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -89,8 +89,8 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer}) - msg, err := core.TransactionToMessage(tx, signer, nil) + evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.GetTracer().LiveLogger}) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index ad1d34fb2bc7..5a021b245a55 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -108,7 +109,7 @@ type btHeaderMarshaling struct { ExcessBlobGas *math.HexOrDecimal64 } -func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger) error { +func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *live.LiveLogger) error { config, ok := Forks[t.json.Network] if !ok { return UnsupportedForkError{t.json.Network} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index d959165e541a..25b50a79e3cd 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -298,7 +299,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // - the coinbase self-destructed, or // - there are only 'bad' transactions, which aren't executed. In those cases, // the coinbase gets no txfee, so isn't created, and thus needs to be touched - statedb.AddBalance(block.Coinbase(), new(big.Int), state.BalanceChangeUnspecified) + statedb.AddBalance(block.Coinbase(), new(big.Int), live.BalanceChangeUnspecified) // Commit state mutations into database. root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number())) @@ -322,7 +323,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance, state.BalanceChangeUnspecified) + statedb.SetBalance(addr, a.Balance, live.BalanceChangeUnspecified) for k, v := range a.Storage { statedb.SetState(addr, k, v) } From e1c669b3aa22aa5271ce9af0b1b97ea8d3b117c3 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 28 Feb 2024 19:20:17 +0100 Subject: [PATCH 23/50] moaar fixes # Conflicts: # core/state/state_test.go # core/state/statedb_fuzz_test.go # core/state/statedb_test.go # core/state/sync_test.go # core/state/trie_prefetcher_test.go # core/txpool/blobpool/blobpool_test.go # core/txpool/legacypool/legacypool2_test.go # core/txpool/legacypool/legacypool_test.go # eth/api_debug_test.go --- cmd/evm/internal/t8ntool/execution.go | 4 +- cmd/evm/runner.go | 7 +-- cmd/evm/staterunner.go | 4 +- core/blockchain_test.go | 6 +-- core/state/state_test.go | 15 +++--- core/state/statedb_fuzz_test.go | 3 +- core/state/statedb_test.go | 55 +++++++++++----------- core/state/sync_test.go | 3 +- core/state/trie_prefetcher_test.go | 7 +-- core/txpool/blobpool/blobpool_test.go | 35 +++++++------- core/txpool/legacypool/legacypool2_test.go | 23 ++++++++- core/txpool/legacypool/legacypool_test.go | 13 ++--- core/vm/runtime/runtime.go | 6 +-- core/vm/runtime/runtime_test.go | 10 ++-- eth/api_debug_test.go | 3 +- eth/tracers/logger/logger.go | 22 ++++++--- tests/state_test.go | 2 +- 17 files changed, 130 insertions(+), 88 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index ea8069ba6621..bfcd9fde7d06 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -228,7 +228,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if err != nil { return nil, nil, nil, err } - vmConfig.Tracer = tracer.LiveLogger + if vmConfig.Tracer != nil { + vmConfig.Tracer = tracer.LiveLogger + } statedb.SetTxContext(tx.Hash(), txIndex) var ( diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 45fc9853519f..e247f11f1810 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/params" @@ -116,7 +117,7 @@ func runCmd(ctx *cli.Context) error { } var ( - tracer vm.EVMLogger + tracer *live.LiveLogger debugLogger *logger.StructLogger statedb *state.StateDB chainConfig *params.ChainConfig @@ -127,10 +128,10 @@ func runCmd(ctx *cli.Context) error { blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests ) if ctx.Bool(MachineFlag.Name) { - tracer = logger.NewJSONLogger(logconfig, os.Stdout) + tracer = logger.NewJSONLogger(logconfig, os.Stdout).GetLogger() } else if ctx.Bool(DebugFlag.Name) { debugLogger = logger.NewStructLogger(logconfig) - tracer = debugLogger + tracer = debugLogger.GetTracer().LiveLogger } else { debugLogger = logger.NewStructLogger(logconfig) } diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 618ddf2ede13..7d969ebad2fc 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -61,10 +61,10 @@ func stateTestCmd(ctx *cli.Context) error { var cfg vm.Config switch { case ctx.Bool(MachineFlag.Name): - cfg.Tracer = logger.NewJSONLogger(config, os.Stderr) + cfg.Tracer = logger.NewJSONLogger(config, os.Stderr).GetLogger() case ctx.Bool(DebugFlag.Name): - cfg.Tracer = logger.NewStructLogger(config) + cfg.Tracer = logger.NewStructLogger(config).GetTracer().LiveLogger } // Load the test content from the input file if len(ctx.Args().First()) != 0 { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index bc6f8112f015..aa41ddab8c12 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3248,7 +3248,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout), + Tracer: logger.NewJSONLogger(nil, os.Stdout).GetLogger(), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -3330,7 +3330,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout), + Tracer: logger.NewJSONLogger(nil, os.Stdout).GetLogger(), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -4682,7 +4682,7 @@ func TestEIP3651(t *testing.T) { b.AddTx(tx) }) - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Logger()}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } diff --git a/core/state/state_test.go b/core/state/state_test.go index 26a2838977ed..fe65b28bb4aa 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" ) @@ -49,11 +50,11 @@ func TestDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22), BalanceChangeUnspecified) + obj1.AddBalance(big.NewInt(22), live.BalanceChangeUnspecified) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44), BalanceChangeUnspecified) + obj3.SetBalance(big.NewInt(44), live.BalanceChangeUnspecified) // write some of them to the trie s.state.updateStateObject(obj1) @@ -103,13 +104,13 @@ func TestIterativeDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22), BalanceChangeUnspecified) + obj1.AddBalance(big.NewInt(22), live.BalanceChangeUnspecified) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44), BalanceChangeUnspecified) + obj3.SetBalance(big.NewInt(44), live.BalanceChangeUnspecified) obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00})) - obj4.AddBalance(big.NewInt(1337), BalanceChangeUnspecified) + obj4.AddBalance(big.NewInt(1337), live.BalanceChangeUnspecified) // write some of them to the trie s.state.updateStateObject(obj1) @@ -205,7 +206,7 @@ func TestSnapshot2(t *testing.T) { // db, trie are already non-empty values so0 := state.getStateObject(stateobjaddr0) - so0.SetBalance(big.NewInt(42), BalanceChangeUnspecified) + so0.SetBalance(big.NewInt(42), live.BalanceChangeUnspecified) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.selfDestructed = false @@ -217,7 +218,7 @@ func TestSnapshot2(t *testing.T) { // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) - so1.SetBalance(big.NewInt(52), BalanceChangeUnspecified) + so1.SetBalance(big.NewInt(52), live.BalanceChangeUnspecified) so1.SetNonce(53) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.selfDestructed = true diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index 592d34b466c5..5a6009103f3c 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/triedb/pathdb" @@ -60,7 +61,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0]), BalanceChangeUnspecified) + s.SetBalance(addr, big.NewInt(a.args[0]), live.BalanceChangeUnspecified) }, args: make([]int64, 1), }, diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index f6074917e35a..0824c2b07e3f 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/triedb/hashdb" @@ -56,7 +57,7 @@ func TestUpdateLeaks(t *testing.T) { // Update it with some accounts for i := byte(0); i < 255; i++ { addr := common.BytesToAddress([]byte{i}) - state.AddBalance(addr, big.NewInt(int64(11*i)), BalanceChangeUnspecified) + state.AddBalance(addr, big.NewInt(int64(11*i)), live.BalanceChangeUnspecified) state.SetNonce(addr, uint64(42*i)) if i%2 == 0 { state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) @@ -91,7 +92,7 @@ func TestIntermediateLeaks(t *testing.T) { finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) modify := func(state *StateDB, addr common.Address, i, tweak byte) { - state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)), live.BalanceChangeUnspecified) state.SetNonce(addr, uint64(42*i+tweak)) if i%2 == 0 { state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) @@ -167,7 +168,7 @@ func TestCopy(t *testing.T) { for i := byte(0); i < 255; i++ { obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(big.NewInt(int64(i)), BalanceChangeUnspecified) + obj.AddBalance(big.NewInt(int64(i)), live.BalanceChangeUnspecified) orig.updateStateObject(obj) } orig.Finalise(false) @@ -184,9 +185,9 @@ func TestCopy(t *testing.T) { copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - origObj.AddBalance(big.NewInt(2*int64(i)), BalanceChangeUnspecified) - copyObj.AddBalance(big.NewInt(3*int64(i)), BalanceChangeUnspecified) - ccopyObj.AddBalance(big.NewInt(4*int64(i)), BalanceChangeUnspecified) + origObj.AddBalance(big.NewInt(2*int64(i)), live.BalanceChangeUnspecified) + copyObj.AddBalance(big.NewInt(3*int64(i)), live.BalanceChangeUnspecified) + ccopyObj.AddBalance(big.NewInt(4*int64(i)), live.BalanceChangeUnspecified) orig.updateStateObject(origObj) copy.updateStateObject(copyObj) @@ -266,14 +267,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0]), BalanceChangeUnspecified) + s.SetBalance(addr, big.NewInt(a.args[0]), live.BalanceChangeUnspecified) }, args: make([]int64, 1), }, { name: "AddBalance", fn: func(a testAction, s *StateDB) { - s.AddBalance(addr, big.NewInt(a.args[0]), BalanceChangeUnspecified) + s.AddBalance(addr, big.NewInt(a.args[0]), live.BalanceChangeUnspecified) }, args: make([]int64, 1), }, @@ -538,7 +539,7 @@ func TestTouchDelete(t *testing.T) { s.state, _ = New(root, s.state.db, s.state.snaps) snapshot := s.state.Snapshot() - s.state.AddBalance(common.Address{}, new(big.Int), BalanceChangeUnspecified) + s.state.AddBalance(common.Address{}, new(big.Int), live.BalanceChangeUnspecified) if len(s.state.journal.dirties) != 1 { t.Fatal("expected one dirty state object") @@ -554,7 +555,7 @@ func TestTouchDelete(t *testing.T) { func TestCopyOfCopy(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.HexToAddress("aaaa") - state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) if got := state.Copy().GetBalance(addr).Uint64(); got != 42 { t.Fatalf("1st copy fail, expected 42, got %v", got) @@ -577,9 +578,9 @@ func TestCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -650,9 +651,9 @@ func TestCopyCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -719,9 +720,9 @@ func TestCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -768,7 +769,7 @@ func TestDeleteCreateRevert(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.BytesToAddress([]byte("so")) - state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) root, _ := state.Commit(0, false) state, _ = New(root, state.db, state.snaps) @@ -778,7 +779,7 @@ func TestDeleteCreateRevert(t *testing.T) { state.Finalise(true) id := state.Snapshot() - state.SetBalance(addr, big.NewInt(2), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(2), live.BalanceChangeUnspecified) state.RevertToSnapshot(id) // Commit the entire state and make sure we don't crash and have the correct state @@ -820,10 +821,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) { state, _ := New(types.EmptyRootHash, db, nil) addr := common.BytesToAddress([]byte("so")) { - state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) state.SetCode(addr, []byte{1, 2, 3}) a2 := common.BytesToAddress([]byte("another")) - state.SetBalance(a2, big.NewInt(100), BalanceChangeUnspecified) + state.SetBalance(a2, big.NewInt(100), live.BalanceChangeUnspecified) state.SetCode(a2, []byte{1, 2, 4}) root, _ = state.Commit(0, false) t.Logf("root: %x", root) @@ -848,7 +849,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { t.Errorf("expected %d, got %d", exp, got) } // Modify the state - state.SetBalance(addr, big.NewInt(2), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(2), live.BalanceChangeUnspecified) root, err := state.Commit(0, false) if err == nil { t.Fatalf("expected error, got root :%x", root) @@ -1116,13 +1117,13 @@ func TestResetObject(t *testing.T) { slotB = common.HexToHash("0x2") ) // Initialize account with balance and storage in first transaction. - state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) state.SetState(addr, slotA, common.BytesToHash([]byte{0x1})) state.IntermediateRoot(true) // Reset account and mutate balance and storages state.CreateAccount(addr) - state.SetBalance(addr, big.NewInt(2), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(2), live.BalanceChangeUnspecified) state.SetState(addr, slotB, common.BytesToHash([]byte{0x2})) root, _ := state.Commit(0, true) @@ -1148,7 +1149,7 @@ func TestDeleteStorage(t *testing.T) { addr = common.HexToAddress("0x1") ) // Initialize account and populate storage - state.SetBalance(addr, big.NewInt(1), BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) state.CreateAccount(addr) for i := 0; i < 1000; i++ { slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32()) diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 9ccb1475eab1..86c790a16a04 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -60,7 +61,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *trie.Database, com obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) acc := &testAccount{address: common.BytesToAddress([]byte{i})} - obj.AddBalance(big.NewInt(int64(11*i)), BalanceChangeUnspecified) + obj.AddBalance(big.NewInt(int64(11*i)), live.BalanceChangeUnspecified) acc.balance = big.NewInt(int64(11 * i)) obj.SetNonce(uint64(42 * i)) diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index a376c790c3d7..2a1956392d57 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) func filledStateDB() *StateDB { @@ -34,9 +35,9 @@ func filledStateDB() *StateDB { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie for i := 0; i < 100; i++ { sk := common.BigToHash(big.NewInt(int64(i))) state.SetState(addr, sk, sk) // Change the storage trie diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index da830c4a77eb..a5848fdf0b51 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -39,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -512,17 +513,17 @@ func TestOpenDrops(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3) - statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2) - statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000), state.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000), state.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000), live.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -637,7 +638,7 @@ func TestOpenIndex(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -737,9 +738,9 @@ func TestOpenHeap(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr1, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) + statedb.AddBalance(addr2, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) + statedb.AddBalance(addr3, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -817,9 +818,9 @@ func TestOpenCap(t *testing.T) { for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} { // Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr1, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) + statedb.AddBalance(addr2, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) + statedb.AddBalance(addr3, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -1210,7 +1211,7 @@ func TestAdd(t *testing.T) { addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey) // Seed the state database with this acocunt - statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance), state.BalanceChangeUnspecified) + statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance), live.BalanceChangeUnspecified) statedb.SetNonce(addrs[acc], seed.nonce) // Sign the seed transactions and store them in the data store diff --git a/core/txpool/legacypool/legacypool2_test.go b/core/txpool/legacypool/legacypool2_test.go index bf4f52b035b4..621987b38476 100644 --- a/core/txpool/legacypool/legacypool2_test.go +++ b/core/txpool/legacypool/legacypool2_test.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/event" ) @@ -49,7 +50,11 @@ func fillPool(t testing.TB, pool *LegacyPool) { nonExecutableTxs := types.Transactions{} for i := 0; i < 384; i++ { key, _ := crypto.GenerateKey() +<<<<<<< HEAD pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000), state.BalanceChangeUnspecified) +======= + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(10000000000), live.BalanceChangeUnspecified) +>>>>>>> 8cc747f439 (moaar fixes) // Add executable ones for j := 0; j < int(pool.config.AccountSlots); j++ { executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key)) @@ -91,7 +96,11 @@ func TestTransactionFutureAttack(t *testing.T) { // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops { key, _ := crypto.GenerateKey() +<<<<<<< HEAD pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) +======= + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) +>>>>>>> 8cc747f439 (moaar fixes) futureTxs := types.Transactions{} for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key)) @@ -128,7 +137,11 @@ func TestTransactionFuture1559(t *testing.T) { // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops { key, _ := crypto.GenerateKey() +<<<<<<< HEAD pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) +======= + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) +>>>>>>> 8cc747f439 (moaar fixes) futureTxs := types.Transactions{} for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key)) @@ -182,7 +195,11 @@ func TestTransactionZAttack(t *testing.T) { for j := 0; j < int(pool.config.GlobalQueue); j++ { futureTxs := types.Transactions{} key, _ := crypto.GenerateKey() +<<<<<<< HEAD pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) +======= + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) +>>>>>>> 8cc747f439 (moaar fixes) futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key)) pool.addRemotesSync(futureTxs) } @@ -190,7 +207,11 @@ func TestTransactionZAttack(t *testing.T) { overDraftTxs := types.Transactions{} { key, _ := crypto.GenerateKey() +<<<<<<< HEAD pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) +======= + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) +>>>>>>> 8cc747f439 (moaar fixes) for j := 0; j < int(pool.config.GlobalSlots); j++ { overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key)) } @@ -227,7 +248,7 @@ func BenchmarkFutureAttack(b *testing.B) { fillPool(b, pool) key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), core/state/state_test.go.BalanceChangeUnspecified) futureTxs := types.Transactions{} for n := 0; n < b.N; n++ { diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index a175df032438..719b71bb34e5 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -255,7 +256,7 @@ func (c *testChain) State() (*state.StateDB, error) { c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) // simulate that the new head block included tx0 and tx1 c.statedb.SetNonce(c.address, 2) - c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether), state.BalanceChangeUnspecified) + c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether), core/state/state_test.go.BalanceChangeUnspecified) *c.trigger = false } return stdb, nil @@ -275,7 +276,7 @@ func TestStateChangeDuringReset(t *testing.T) { ) // setup pool with 2 transaction in it - statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether), state.BalanceChangeUnspecified) + statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether), core/state/state_test.go.BalanceChangeUnspecified) blockchain := &testChain{newTestBlockChain(params.TestChainConfig, 1000000000, statedb, new(event.Feed)), address, &trigger} tx0 := transaction(0, 100000, key) @@ -309,7 +310,7 @@ func TestStateChangeDuringReset(t *testing.T) { func testAddBalance(pool *LegacyPool, addr common.Address, amount *big.Int) { pool.mu.Lock() - pool.currentState.AddBalance(addr, amount, state.BalanceChangeUnspecified) + pool.currentState.AddBalance(addr, amount, core/state/state_test.go.BalanceChangeUnspecified) pool.mu.Unlock() } @@ -470,7 +471,7 @@ func TestChainFork(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr, big.NewInt(100000000000000), core/state/state_test.go.BalanceChangeUnspecified) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) <-pool.requestReset(nil, nil) @@ -499,7 +500,7 @@ func TestDoubleNonce(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000), state.BalanceChangeUnspecified) + statedb.AddBalance(addr, big.NewInt(100000000000000), core/state/state_test.go.BalanceChangeUnspecified) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) <-pool.requestReset(nil, nil) @@ -2618,7 +2619,7 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) { for i := 0; i < b.N; i++ { key, _ := crypto.GenerateKey() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000), state.BalanceChangeUnspecified) + pool.currentState.AddBalance(account, big.NewInt(1000000), live.BalanceChangeUnspecified) tx := transaction(uint64(0), 100000, key) batches[i] = tx } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 65fbd289df0c..aba137c50cff 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -123,7 +123,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil { - cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) @@ -159,7 +159,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil { - cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) @@ -190,7 +190,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil { - cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 6e244759b857..2997ca5351f4 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -335,7 +335,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode b.Fatal(err) } cfg.EVMConfig = vm.Config{ - Tracer: tracer, + Tracer: tracer.LiveLogger, } } var ( @@ -510,7 +510,7 @@ func TestEip2929Cases(t *testing.T) { code, ops) Execute(code, nil, &Config{ EVMConfig: vm.Config{ - Tracer: logger.NewMarkdownLogger(nil, os.Stdout), + Tracer: logger.NewMarkdownLogger(nil, os.Stdout).Logger(), ExtraEips: []int{2929}, }, }) @@ -663,7 +663,7 @@ func TestColdAccountAccessCost(t *testing.T) { tracer := logger.NewStructLogger(nil) Execute(tc.code, nil, &Config{ EVMConfig: vm.Config{ - Tracer: tracer, + Tracer: tracer.GetTracer().LiveLogger, }, }) have := tracer.StructLogs()[tc.step].GasCost @@ -834,7 +834,7 @@ func TestRuntimeJSTracer(t *testing.T) { GasLimit: 1000000, State: statedb, EVMConfig: vm.Config{ - Tracer: tracer, + Tracer: tracer.LiveLogger, }}) if err != nil { t.Fatal("didn't expect error", err) @@ -868,7 +868,7 @@ func TestJSTracerCreateTx(t *testing.T) { _, _, _, err = Create(code, &Config{ State: statedb, EVMConfig: vm.Config{ - Tracer: tracer, + Tracer: tracer.LiveLogger, }}) if err != nil { t.Fatal(err) diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index b93afbab06f2..f14b96b43655 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/trie" "golang.org/x/exp/slices" ) @@ -72,7 +73,7 @@ func TestAccountRange(t *testing.T) { hash := common.HexToHash(fmt.Sprintf("%x", i)) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) addrs[i] = addr - sdb.SetBalance(addrs[i], big.NewInt(1), state.BalanceChangeUnspecified) + sdb.SetBalance(addrs[i], big.NewInt(1), live.BalanceChangeUnspecified) if _, ok := m[addr]; ok { t.Fatalf("bad") } else { diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 3e8a68e6e5a8..d1db00f992d9 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -335,7 +335,7 @@ type mdLogger struct { directory.NoopTracer out io.Writer cfg *Config - env *vm.EVM + env *live.VMContext } // NewMarkdownLogger creates a logger which outputs information in a format adapted @@ -348,7 +348,17 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { return l } -func (t *mdLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { +func (t *mdLogger) Logger() *live.LiveLogger { + return &live.LiveLogger{ + CaptureTxStart: t.CaptureTxStart, + CaptureStart: t.CaptureStart, + CaptureState: t.CaptureState, + CaptureFault: t.CaptureFault, + CaptureEnd: t.CaptureEnd, + } +} + +func (t *mdLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { t.env = env } @@ -370,14 +380,14 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b } // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { - stack := scope.Stack +func (t *mdLogger) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { + stack := scope.GetStackData() fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) if !t.cfg.DisableStack { // format stack var a []string - for _, elem := range stack.Data() { + for _, elem := range stack { a = append(a, elem.Hex()) } b := fmt.Sprintf("[%v]", strings.Join(a, ",")) @@ -390,7 +400,7 @@ func (t *mdLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope } } -func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (t *mdLogger) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } diff --git a/tests/state_test.go b/tests/state_test.go index 094dafcafd7a..05e7543bffec 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -152,7 +152,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { } buf := new(bytes.Buffer) w := bufio.NewWriter(buf) - config.Tracer = logger.NewJSONLogger(&logger.Config{}, w) + config.Tracer = logger.NewJSONLogger(&logger.Config{}, w).GetLogger() err2 := test(config) if !reflect.DeepEqual(err, err2) { t.Errorf("different error for second run: %v", err2) From b202e8ab746d51f1248ebf7ed6df08cf7083a10f Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 28 Feb 2024 19:27:39 +0100 Subject: [PATCH 24/50] renaming GetLogger # Conflicts: # cmd/evm/internal/t8ntool/transition.go # eth/tracers/logger/logger_test.go # eth/tracers/tracers_test.go --- cmd/evm/blockrunner.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 3 +-- cmd/evm/runner.go | 4 ++-- cmd/evm/staterunner.go | 4 ++-- core/blockchain_test.go | 4 ++-- eth/tracers/api.go | 4 ++-- eth/tracers/logger/logger.go | 22 +++++++++++++--------- eth/tracers/logger/logger_json.go | 2 +- eth/tracers/logger/logger_test.go | 2 +- eth/tracers/tracers_test.go | 2 +- tests/state_test.go | 2 +- 11 files changed, 27 insertions(+), 24 deletions(-) diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 9c2f4578c816..8e34deb7f298 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -58,7 +58,7 @@ func blockTestCmd(ctx *cli.Context) error { DisableStack: ctx.Bool(DisableStackFlag.Name), DisableStorage: ctx.Bool(DisableStorageFlag.Name), EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - }, os.Stderr).GetLogger() + }, os.Stderr).Logger() } // Load the test content from the input file src, err := os.ReadFile(ctx.Args().First()) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 135b89fc1b25..20b5eee1b190 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -124,8 +124,7 @@ func Transition(ctx *cli.Context) error { if err != nil { return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } - prevFile = traceFile - logger := logger.NewJSONLogger(logConfig, traceFile).GetLogger() + logger := logger.NewJSONLogger(logConfig, traceFile).Logger() tracer := &directory.Tracer{ LiveLogger: logger, // JSONLogger streams out result to file. diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index e247f11f1810..53532aa763f1 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -128,10 +128,10 @@ func runCmd(ctx *cli.Context) error { blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests ) if ctx.Bool(MachineFlag.Name) { - tracer = logger.NewJSONLogger(logconfig, os.Stdout).GetLogger() + tracer = logger.NewJSONLogger(logconfig, os.Stdout).Logger() } else if ctx.Bool(DebugFlag.Name) { debugLogger = logger.NewStructLogger(logconfig) - tracer = debugLogger.GetTracer().LiveLogger + tracer = debugLogger.Logger() } else { debugLogger = logger.NewStructLogger(logconfig) } diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 7d969ebad2fc..5e26ff2ee66c 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -61,10 +61,10 @@ func stateTestCmd(ctx *cli.Context) error { var cfg vm.Config switch { case ctx.Bool(MachineFlag.Name): - cfg.Tracer = logger.NewJSONLogger(config, os.Stderr).GetLogger() + cfg.Tracer = logger.NewJSONLogger(config, os.Stderr).Logger() case ctx.Bool(DebugFlag.Name): - cfg.Tracer = logger.NewStructLogger(config).GetTracer().LiveLogger + cfg.Tracer = logger.NewStructLogger(config).Logger() } // Load the test content from the input file if len(ctx.Args().First()) != 0 { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index aa41ddab8c12..c3f77d70daa7 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3248,7 +3248,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout).GetLogger(), + Tracer: logger.NewJSONLogger(nil, os.Stdout).Logger(), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -3330,7 +3330,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout).GetLogger(), + Tracer: logger.NewJSONLogger(nil, os.Stdout).Logger(), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 422b672086d8..0fb13696c3a1 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -777,7 +777,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Swap out the noop logger to the standard tracer writer = bufio.NewWriter(dump) vmConf = vm.Config{ - Tracer: logger.NewJSONLogger(&logConfig, writer).GetLogger(), + Tracer: logger.NewJSONLogger(&logConfig, writer).Logger(), EnablePreimageRecording: true, } } @@ -930,7 +930,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *direc config = &TraceConfig{} } // Default tracer is the struct logger - tracer = logger.NewStructLogger(config.Config).GetTracer() + tracer = logger.NewStructLogger(config.Config).Tracer() if config.Tracer != nil { tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) if err != nil { diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index d1db00f992d9..143db5752dda 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -133,16 +133,20 @@ func NewStructLogger(cfg *Config) *StructLogger { return logger } -func (l *StructLogger) GetTracer() *directory.Tracer { +func (l *StructLogger) Logger() *live.LiveLogger { + return &live.LiveLogger{ + CaptureTxStart: l.CaptureTxStart, + CaptureTxEnd: l.CaptureTxEnd, + CaptureEnd: l.CaptureEnd, + CaptureState: l.CaptureState, + } +} + +func (l *StructLogger) Tracer() *directory.Tracer { return &directory.Tracer{ - LiveLogger: &live.LiveLogger{ - CaptureTxStart: l.CaptureTxStart, - CaptureTxEnd: l.CaptureTxEnd, - CaptureEnd: l.CaptureEnd, - CaptureState: l.CaptureState, - }, - GetResult: l.GetResult, - Stop: l.Stop, + LiveLogger: l.Logger(), + GetResult: l.GetResult, + Stop: l.Stop, } } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index fecfa0cb73dd..43ba082beb2c 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -45,7 +45,7 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { return l } -func (l *JSONLogger) GetLogger() *live.LiveLogger { +func (l *JSONLogger) Logger() *live.LiveLogger { return &live.LiveLogger{ CaptureTxStart: l.CaptureTxStart, CaptureEnd: l.CaptureEnd, diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index cd70f1a97004..ddb558832d8b 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -55,7 +55,7 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} func TestStoreCapture(t *testing.T) { var ( logger = NewStructLogger(nil) - env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.GetTracer().LiveLogger}) + env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Logger()}) contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index e3ce3963744a..7f509b8ae6e8 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -89,7 +89,7 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.GetTracer().LiveLogger}) + evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Logger()}) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/tests/state_test.go b/tests/state_test.go index 05e7543bffec..9759f9cb7aa5 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -152,7 +152,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { } buf := new(bytes.Buffer) w := bufio.NewWriter(buf) - config.Tracer = logger.NewJSONLogger(&logger.Config{}, w).GetLogger() + config.Tracer = logger.NewJSONLogger(&logger.Config{}, w).Logger() err2 := test(config) if !reflect.DeepEqual(err, err2) { t.Errorf("different error for second run: %v", err2) From 39fc9dcafb4c4c01132471c4871ccfa983de3c51 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 28 Feb 2024 19:34:49 +0100 Subject: [PATCH 25/50] move logger interface to core/tracing # Conflicts: # cmd/evm/internal/t8ntool/execution.go # consensus/beacon/consensus.go # consensus/ethash/consensus.go # consensus/misc/dao.go # core/evm.go # core/genesis.go # core/state/state_object.go # core/state/state_test.go # core/state/statedb.go # core/state/statedb_fuzz_test.go # core/state/statedb_test.go # core/state/sync_test.go # core/state/trie_prefetcher_test.go # core/state_transition.go # core/txpool/blobpool/blobpool_test.go # core/txpool/legacypool/legacypool2_test.go # core/txpool/legacypool/legacypool_test.go # core/vm/evm.go # core/vm/instructions.go # core/vm/interface.go # eth/api_debug_test.go # internal/ethapi/api.go # tests/block_test_util.go # tests/state_test_util.go --- cmd/evm/blockrunner.go | 4 +- cmd/evm/internal/t8ntool/execution.go | 10 +- cmd/evm/runner.go | 4 +- consensus/beacon/consensus.go | 4 +- consensus/ethash/consensus.go | 6 +- consensus/misc/dao.go | 6 +- core/blockchain.go | 8 +- core/evm.go | 6 +- core/genesis.go | 6 +- core/state/state_object.go | 8 +- core/state/state_test.go | 16 +- core/state/statedb.go | 16 +- core/state/statedb_fuzz_test.go | 4 +- core/state/statedb_test.go | 56 ++--- core/state/sync_test.go | 4 +- core/state/trie_prefetcher_test.go | 8 +- core/state_processor.go | 4 +- core/state_transition.go | 16 +- core/tracing/hooks.go | 238 +++++++++++++++++++++ core/txpool/blobpool/blobpool_test.go | 36 ++-- core/txpool/legacypool/legacypool2_test.go | 34 +-- core/txpool/legacypool/legacypool_test.go | 14 +- core/vm/contract.go | 6 +- core/vm/contracts.go | 6 +- core/vm/evm.go | 28 +-- core/vm/instructions.go | 28 +-- core/vm/interface.go | 6 +- core/vm/interpreter.go | 20 +- core/vm/operations_acl.go | 4 +- eth/api_debug_test.go | 4 +- eth/ethconfig/config.go | 4 +- eth/tracers/directory/live/dir.go | 113 +--------- eth/tracers/directory/noop.go | 17 +- eth/tracers/directory/tracers.go | 4 +- eth/tracers/js/goja.go | 18 +- eth/tracers/js/tracer_test.go | 4 +- eth/tracers/live/noop.go | 21 +- eth/tracers/logger/access_list_tracer.go | 8 +- eth/tracers/logger/logger.go | 24 +-- eth/tracers/logger/logger_json.go | 14 +- eth/tracers/native/4byte.go | 8 +- eth/tracers/native/call.go | 10 +- eth/tracers/native/call_flat.go | 12 +- eth/tracers/native/mux.go | 16 +- eth/tracers/native/prestate.go | 10 +- internal/ethapi/api.go | 4 +- tests/block_test_util.go | 4 +- tests/state_test_util.go | 6 +- 48 files changed, 510 insertions(+), 397 deletions(-) create mode 100644 core/tracing/hooks.go diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 8e34deb7f298..ed439b6fb272 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -25,7 +25,7 @@ import ( "sort" "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/tests" "github.com/urfave/cli/v2" @@ -50,7 +50,7 @@ func blockTestCmd(ctx *cli.Context) error { return errors.New("path-to-test argument required") } - var tracer *live.LiveLogger + var tracer *tracing.LiveLogger // Configure the EVM logger if ctx.Bool(MachineFlag.Name) { tracer = logger.NewJSONLogger(&logger.Config{ diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index bfcd9fde7d06..7129ba88deed 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -30,11 +30,11 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "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/crypto" "github.com/ethereum/go-ethereum/eth/tracers/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -326,15 +326,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) reward.Mul(reward, blockReward) reward.Div(reward, big.NewInt(8)) - statedb.AddBalance(ommer.Address, reward, live.BalanceIncreaseRewardMineUncle) + statedb.AddBalance(ommer.Address, reward, tracing.BalanceIncreaseRewardMineUncle) } - statedb.AddBalance(pre.Env.Coinbase, minerReward, live.BalanceIncreaseRewardMineBlock) + statedb.AddBalance(pre.Env.Coinbase, minerReward, tracing.BalanceIncreaseRewardMineBlock) } // Apply withdrawals for _, w := range pre.Env.Withdrawals { // Amount is in gwei, turn into wei amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) - statedb.AddBalance(w.Address, amount, live.BalanceIncreaseWithdrawal) + statedb.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal) } // Commit block root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber)) @@ -377,7 +377,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance, live.BalanceIncreaseGenesisBalance) + statedb.SetBalance(addr, a.Balance, tracing.BalanceIncreaseGenesisBalance) for k, v := range a.Storage { statedb.SetState(addr, k, v) } diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 53532aa763f1..259ea5220e4d 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -33,9 +33,9 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm/runtime" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/params" @@ -117,7 +117,7 @@ func runCmd(ctx *cli.Context) error { } var ( - tracer *live.LiveLogger + tracer *tracing.LiveLogger debugLogger *logger.StructLogger statedb *state.StateDB chainConfig *params.ChainConfig diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 42bf2bd7f99f..9f677cf86144 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -26,8 +26,8 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/trie" @@ -358,7 +358,7 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. // Convert amount from gwei to wei. amount := new(big.Int).SetUint64(w.Amount) amount = amount.Mul(amount, big.NewInt(params.GWei)) - stateDB.AddBalance(w.Address, amount, live.BalanceIncreaseWithdrawal) + stateDB.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal) } // No block reward which is issued by consensus layer instead. } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index e09bfa10a007..a31d9c0f1ca5 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -29,8 +29,8 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -565,10 +565,10 @@ func accumulateRewards(config *params.ChainConfig, stateDB *state.StateDB, heade r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, big8) - stateDB.AddBalance(uncle.Coinbase, r, live.BalanceIncreaseRewardMineUncle) + stateDB.AddBalance(uncle.Coinbase, r, tracing.BalanceIncreaseRewardMineUncle) r.Div(blockReward, big32) reward.Add(reward, r) } - stateDB.AddBalance(header.Coinbase, reward, live.BalanceIncreaseRewardMineBlock) + stateDB.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock) } diff --git a/consensus/misc/dao.go b/consensus/misc/dao.go index 1f97527588a5..17e1b799d162 100644 --- a/consensus/misc/dao.go +++ b/consensus/misc/dao.go @@ -22,8 +22,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -81,7 +81,7 @@ func ApplyDAOHardFork(statedb *state.StateDB) { // Move every DAO account and extra-balance account funds into the refund contract for _, addr := range params.DAODrainList() { - statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), live.BalanceIncreaseDaoContract) - statedb.SetBalance(addr, new(big.Int), live.BalanceDecreaseDaoAccount) + statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract) + statedb.SetBalance(addr, new(big.Int), tracing.BalanceDecreaseDaoAccount) } } diff --git a/core/blockchain.go b/core/blockchain.go index babfcaf8f2c8..b3f72bd21359 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -37,9 +37,9 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" + "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/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/syncx" @@ -266,7 +266,7 @@ type BlockChain struct { processor Processor // Block transaction processor interface forker *ForkChoice vmConfig vm.Config - logger *live.LiveLogger + logger *tracing.LiveLogger } // NewBlockChain returns a fully initialised block chain using information @@ -1794,7 +1794,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) } stats.processed++ if bc.logger != nil && bc.logger.OnSkippedBlock != nil { - bc.logger.OnSkippedBlock(live.BlockEvent{ + bc.logger.OnSkippedBlock(tracing.BlockEvent{ Block: block, TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1), Finalized: bc.CurrentFinalBlock(), @@ -1927,7 +1927,7 @@ type blockProcessingResult struct { func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) { if bc.logger != nil && bc.logger.OnBlockStart != nil { td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) - bc.logger.OnBlockStart(live.BlockEvent{ + bc.logger.OnBlockStart(tracing.BlockEvent{ Block: block, TD: td, Finalized: bc.CurrentFinalBlock(), diff --git a/core/evm.go b/core/evm.go index 515f8d78a9da..52f8b0febfd9 100644 --- a/core/evm.go +++ b/core/evm.go @@ -22,9 +22,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc/eip4844" + "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/directory/live" ) // ChainContext supports retrieving headers and consensus parameters from the @@ -136,6 +136,6 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { // Transfer subtracts amount from sender and adds amount to recipient using the given Db func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { - db.SubBalance(sender, amount, live.BalanceChangeTransfer) - db.AddBalance(recipient, amount, live.BalanceChangeTransfer) + db.SubBalance(sender, amount, tracing.BalanceChangeTransfer) + db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer) } diff --git a/core/genesis.go b/core/genesis.go index 8e58bb134e6d..16f8ec83a1f4 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -30,9 +30,9 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -132,7 +132,7 @@ func (ga *GenesisAlloc) hash() (common.Hash, error) { } for addr, account := range *ga { if account.Balance != nil { - statedb.AddBalance(addr, account.Balance, live.BalanceIncreaseGenesisBalance) + statedb.AddBalance(addr, account.Balance, tracing.BalanceIncreaseGenesisBalance) } statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) @@ -155,7 +155,7 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas if account.Balance != nil { // This is not actually logged via tracer because OnGenesisBlock // already captures the allocations. - statedb.AddBalance(addr, account.Balance, live.BalanceIncreaseGenesisBalance) + statedb.AddBalance(addr, account.Balance, tracing.BalanceIncreaseGenesisBalance) } statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) diff --git a/core/state/state_object.go b/core/state/state_object.go index 2c7ffbf8961d..cd718ae0580a 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -24,9 +24,9 @@ import ( "time" "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/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie/trienode" @@ -405,7 +405,7 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) { // AddBalance adds amount to s's balance. // It is used to add funds to the destination account of a transfer. -func (s *stateObject) AddBalance(amount *big.Int, reason live.BalanceChangeReason) { +func (s *stateObject) AddBalance(amount *big.Int, reason tracing.BalanceChangeReason) { // EIP161: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. if amount.Sign() == 0 { @@ -419,14 +419,14 @@ func (s *stateObject) AddBalance(amount *big.Int, reason live.BalanceChangeReaso // SubBalance removes amount from s's balance. // It is used to remove funds from the origin account of a transfer. -func (s *stateObject) SubBalance(amount *big.Int, reason live.BalanceChangeReason) { +func (s *stateObject) SubBalance(amount *big.Int, reason tracing.BalanceChangeReason) { if amount.IsZero() { return } s.SetBalance(new(big.Int).Sub(s.Balance(), amount), reason) } -func (s *stateObject) SetBalance(amount *big.Int, reason live.BalanceChangeReason) { +func (s *stateObject) SetBalance(amount *big.Int, reason tracing.BalanceChangeReason) { s.db.journal.append(balanceChange{ account: &s.address, prev: new(big.Int).Set(s.data.Balance), diff --git a/core/state/state_test.go b/core/state/state_test.go index fe65b28bb4aa..d749e3219dae 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -24,9 +24,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" ) @@ -50,11 +50,11 @@ func TestDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22), live.BalanceChangeUnspecified) + obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44), live.BalanceChangeUnspecified) + obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified) // write some of them to the trie s.state.updateStateObject(obj1) @@ -104,13 +104,13 @@ func TestIterativeDump(t *testing.T) { // generate a few entries obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) - obj1.AddBalance(big.NewInt(22), live.BalanceChangeUnspecified) + obj1.AddBalance(big.NewInt(22), tracing.BalanceChangeUnspecified) obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) - obj3.SetBalance(big.NewInt(44), live.BalanceChangeUnspecified) + obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified) obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00})) - obj4.AddBalance(big.NewInt(1337), live.BalanceChangeUnspecified) + obj4.AddBalance(big.NewInt(1337), tracing.BalanceChangeUnspecified) // write some of them to the trie s.state.updateStateObject(obj1) @@ -206,7 +206,7 @@ func TestSnapshot2(t *testing.T) { // db, trie are already non-empty values so0 := state.getStateObject(stateobjaddr0) - so0.SetBalance(big.NewInt(42), live.BalanceChangeUnspecified) + so0.SetBalance(big.NewInt(42), tracing.BalanceChangeUnspecified) so0.SetNonce(43) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.selfDestructed = false @@ -218,7 +218,7 @@ func TestSnapshot2(t *testing.T) { // and one with deleted == true so1 := state.getStateObject(stateobjaddr1) - so1.SetBalance(big.NewInt(52), live.BalanceChangeUnspecified) + so1.SetBalance(big.NewInt(52), tracing.BalanceChangeUnspecified) so1.SetNonce(53) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.selfDestructed = true diff --git a/core/state/statedb.go b/core/state/statedb.go index 27bca3d2e8c4..0047a9c8e1bf 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -26,9 +26,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" @@ -64,7 +64,7 @@ type StateDB struct { prefetcher *triePrefetcher trie Trie hasher crypto.KeccakState - logger *live.LiveLogger + logger *tracing.LiveLogger snaps *snapshot.Tree // Nil if snapshot is not available snap snapshot.Snapshot // Nil if snapshot is not available @@ -175,7 +175,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) } // SetLogger sets the logger for account update hooks. -func (s *StateDB) SetLogger(l *live.LiveLogger) { +func (s *StateDB) SetLogger(l *tracing.LiveLogger) { s.logger = l } @@ -383,7 +383,7 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { */ // AddBalance adds amount to the account associated with addr. -func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason live.BalanceChangeReason) { +func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.AddBalance(amount, reason) @@ -391,14 +391,14 @@ func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason live.B } // SubBalance subtracts amount from the account associated with addr. -func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason live.BalanceChangeReason) { +func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SubBalance(amount, reason) } } -func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason live.BalanceChangeReason) { +func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { stateObject := s.getOrNewStateObject(addr) if stateObject != nil { stateObject.SetBalance(amount, reason) @@ -467,7 +467,7 @@ func (s *StateDB) SelfDestruct(addr common.Address) { prevbalance: new(big.Int).Set(stateObject.Balance()), }) if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 { - s.logger.OnBalanceChange(addr, prev, n, live.BalanceDecreaseSelfdestruct) + s.logger.OnBalanceChange(addr, prev, n, tracing.BalanceDecreaseSelfdestruct) } stateObject.markSelfdestructed() stateObject.data.Balance = new(big.Int) @@ -856,7 +856,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // If ether was sent to account post-selfdestruct it is burnt. if bal := obj.Balance(); s.logger != nil && s.logger.OnBalanceChange != nil && obj.selfDestructed && bal.Sign() != 0 { - s.logger.OnBalanceChange(obj.address, bal, new(big.Int), live.BalanceDecreaseSelfdestructBurn) + s.logger.OnBalanceChange(obj.address, bal, new(big.Int), tracing.BalanceDecreaseSelfdestructBurn) } // We need to maintain account deletions explicitly (will remain // set indefinitely). Note only the first occurred self-destruct diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go index 5a6009103f3c..b4b09a5cf753 100644 --- a/core/state/statedb_fuzz_test.go +++ b/core/state/statedb_fuzz_test.go @@ -32,9 +32,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/triedb/pathdb" @@ -61,7 +61,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0]), live.BalanceChangeUnspecified) + s.SetBalance(addr, big.NewInt(a.args[0]), tracing.BalanceChangeUnspecified) }, args: make([]int64, 1), }, diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 0824c2b07e3f..28e850ba86a6 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -33,9 +33,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/snapshot" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/triedb/hashdb" @@ -57,7 +57,7 @@ func TestUpdateLeaks(t *testing.T) { // Update it with some accounts for i := byte(0); i < 255; i++ { addr := common.BytesToAddress([]byte{i}) - state.AddBalance(addr, big.NewInt(int64(11*i)), live.BalanceChangeUnspecified) + state.AddBalance(addr, big.NewInt(int64(11*i)), tracing.BalanceChangeUnspecified) state.SetNonce(addr, uint64(42*i)) if i%2 == 0 { state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) @@ -92,7 +92,7 @@ func TestIntermediateLeaks(t *testing.T) { finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) modify := func(state *StateDB, addr common.Address, i, tweak byte) { - state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)), tracing.BalanceChangeUnspecified) state.SetNonce(addr, uint64(42*i+tweak)) if i%2 == 0 { state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) @@ -168,7 +168,7 @@ func TestCopy(t *testing.T) { for i := byte(0); i < 255; i++ { obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - obj.AddBalance(big.NewInt(int64(i)), live.BalanceChangeUnspecified) + obj.AddBalance(big.NewInt(int64(i)), tracing.BalanceChangeUnspecified) orig.updateStateObject(obj) } orig.Finalise(false) @@ -185,9 +185,9 @@ func TestCopy(t *testing.T) { copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - origObj.AddBalance(big.NewInt(2*int64(i)), live.BalanceChangeUnspecified) - copyObj.AddBalance(big.NewInt(3*int64(i)), live.BalanceChangeUnspecified) - ccopyObj.AddBalance(big.NewInt(4*int64(i)), live.BalanceChangeUnspecified) + origObj.AddBalance(big.NewInt(2*int64(i)), tracing.BalanceChangeUnspecified) + copyObj.AddBalance(big.NewInt(3*int64(i)), tracing.BalanceChangeUnspecified) + ccopyObj.AddBalance(big.NewInt(4*int64(i)), tracing.BalanceChangeUnspecified) orig.updateStateObject(origObj) copy.updateStateObject(copyObj) @@ -267,14 +267,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction { { name: "SetBalance", fn: func(a testAction, s *StateDB) { - s.SetBalance(addr, big.NewInt(a.args[0]), live.BalanceChangeUnspecified) + s.SetBalance(addr, big.NewInt(a.args[0]), tracing.BalanceChangeUnspecified) }, args: make([]int64, 1), }, { name: "AddBalance", fn: func(a testAction, s *StateDB) { - s.AddBalance(addr, big.NewInt(a.args[0]), live.BalanceChangeUnspecified) + s.AddBalance(addr, big.NewInt(a.args[0]), tracing.BalanceChangeUnspecified) }, args: make([]int64, 1), }, @@ -539,7 +539,7 @@ func TestTouchDelete(t *testing.T) { s.state, _ = New(root, s.state.db, s.state.snaps) snapshot := s.state.Snapshot() - s.state.AddBalance(common.Address{}, new(big.Int), live.BalanceChangeUnspecified) + s.state.AddBalance(common.Address{}, new(big.Int), tracing.BalanceChangeUnspecified) if len(s.state.journal.dirties) != 1 { t.Fatal("expected one dirty state object") @@ -555,7 +555,7 @@ func TestTouchDelete(t *testing.T) { func TestCopyOfCopy(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.HexToAddress("aaaa") - state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified) if got := state.Copy().GetBalance(addr).Uint64(); got != 42 { t.Fatalf("1st copy fail, expected 42, got %v", got) @@ -578,9 +578,9 @@ func TestCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -651,9 +651,9 @@ func TestCopyCopyCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -720,9 +720,9 @@ func TestCommitCopy(t *testing.T) { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) @@ -769,7 +769,7 @@ func TestDeleteCreateRevert(t *testing.T) { state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) addr := common.BytesToAddress([]byte("so")) - state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), tracing.BalanceChangeUnspecified) root, _ := state.Commit(0, false) state, _ = New(root, state.db, state.snaps) @@ -779,7 +779,7 @@ func TestDeleteCreateRevert(t *testing.T) { state.Finalise(true) id := state.Snapshot() - state.SetBalance(addr, big.NewInt(2), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(2), tracing.BalanceChangeUnspecified) state.RevertToSnapshot(id) // Commit the entire state and make sure we don't crash and have the correct state @@ -821,10 +821,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) { state, _ := New(types.EmptyRootHash, db, nil) addr := common.BytesToAddress([]byte("so")) { - state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), tracing.BalanceChangeUnspecified) state.SetCode(addr, []byte{1, 2, 3}) a2 := common.BytesToAddress([]byte("another")) - state.SetBalance(a2, big.NewInt(100), live.BalanceChangeUnspecified) + state.SetBalance(a2, big.NewInt(100), tracing.BalanceChangeUnspecified) state.SetCode(a2, []byte{1, 2, 4}) root, _ = state.Commit(0, false) t.Logf("root: %x", root) @@ -849,7 +849,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) { t.Errorf("expected %d, got %d", exp, got) } // Modify the state - state.SetBalance(addr, big.NewInt(2), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(2), tracing.BalanceChangeUnspecified) root, err := state.Commit(0, false) if err == nil { t.Fatalf("expected error, got root :%x", root) @@ -1117,13 +1117,13 @@ func TestResetObject(t *testing.T) { slotB = common.HexToHash("0x2") ) // Initialize account with balance and storage in first transaction. - state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), tracing.BalanceChangeUnspecified) state.SetState(addr, slotA, common.BytesToHash([]byte{0x1})) state.IntermediateRoot(true) // Reset account and mutate balance and storages state.CreateAccount(addr) - state.SetBalance(addr, big.NewInt(2), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(2), tracing.BalanceChangeUnspecified) state.SetState(addr, slotB, common.BytesToHash([]byte{0x2})) root, _ := state.Commit(0, true) @@ -1149,7 +1149,7 @@ func TestDeleteStorage(t *testing.T) { addr = common.HexToAddress("0x1") ) // Initialize account and populate storage - state.SetBalance(addr, big.NewInt(1), live.BalanceChangeUnspecified) + state.SetBalance(addr, big.NewInt(1), tracing.BalanceChangeUnspecified) state.CreateAccount(addr) for i := 0; i < 1000; i++ { slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32()) diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 86c790a16a04..9ee0010730a0 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -23,9 +23,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -61,7 +61,7 @@ func makeTestState(scheme string) (ethdb.Database, Database, *trie.Database, com obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) acc := &testAccount{address: common.BytesToAddress([]byte{i})} - obj.AddBalance(big.NewInt(int64(11*i)), live.BalanceChangeUnspecified) + obj.AddBalance(big.NewInt(int64(11*i)), tracing.BalanceChangeUnspecified) acc.balance = big.NewInt(int64(11 * i)) obj.SetNonce(uint64(42 * i)) diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index 2a1956392d57..a4abe18cc48c 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -23,8 +23,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) func filledStateDB() *StateDB { @@ -35,9 +35,9 @@ func filledStateDB() *StateDB { skey := common.HexToHash("aaa") sval := common.HexToHash("bbb") - state.SetBalance(addr, big.NewInt(42), live.BalanceChangeUnspecified) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie + state.SetBalance(addr, big.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie + state.SetCode(addr, []byte("hello")) // Change an external metadata + state.SetState(addr, skey, sval) // Change the storage trie for i := 0; i < 100; i++ { sk := common.BigToHash(big.NewInt(int64(i))) state.SetState(addr, sk, sk) // Change the storage trie diff --git a/core/state_processor.go b/core/state_processor.go index 776a10422391..d3ca3dde34da 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -25,10 +25,10 @@ import ( "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/core/state" + "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/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -112,7 +112,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // this method takes an already created EVM instance as input. func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { if evm.Config.Tracer != nil && evm.Config.Tracer.CaptureTxStart != nil { - evm.Config.Tracer.CaptureTxStart(&live.VMContext{ + evm.Config.Tracer.CaptureTxStart(&tracing.VMContext{ ChainConfig: evm.ChainConfig(), StateDB: statedb, BlockNumber: evm.Context.BlockNumber, diff --git a/core/state_transition.go b/core/state_transition.go index f149d2a3a141..b908adeb4cc5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -24,9 +24,9 @@ import ( "github.com/ethereum/go-ethereum/common" cmath "github.com/ethereum/go-ethereum/common/math" + "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/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -263,13 +263,13 @@ func (st *StateTransition) buyGas() error { } if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil { - st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, live.GasChangeTxInitialBalance) + st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance) } st.gasRemaining += st.msg.GasLimit st.initialGas = st.msg.GasLimit - st.state.SubBalance(st.msg.From, mgval, live.BalanceDecreaseGasBuy) + st.state.SubBalance(st.msg.From, mgval, tracing.BalanceDecreaseGasBuy) return nil } @@ -393,7 +393,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil { - t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, live.GasChangeTxIntrinsicGas) + t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas) } st.gasRemaining -= gas @@ -443,7 +443,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } else { fee := new(big.Int).SetUint64(st.gasUsed()) fee.Mul(fee, effectiveTip) - st.state.AddBalance(st.evm.Context.Coinbase, fee, live.BalanceIncreaseRewardTransactionFee) + st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee) } return &ExecutionResult{ @@ -461,17 +461,17 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { } if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 { - st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, live.GasChangeTxRefunds) + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, tracing.GasChangeTxRefunds) } st.gasRemaining += refund // Return ETH for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) - st.state.AddBalance(st.msg.From, remaining, live.BalanceIncreaseGasReturn) + st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn) if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 { - st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, live.GasChangeTxLeftOverReturned) + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned) } // Also return remaining gas to the block gas counter so it is diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go new file mode 100644 index 000000000000..6f9795af3b5e --- /dev/null +++ b/core/tracing/hooks.go @@ -0,0 +1,238 @@ +// 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 tracing + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +type ScopeContext interface { + GetMemoryData() []byte + GetStackData() []uint256.Int + GetCaller() common.Address + GetAddress() common.Address + GetCallValue() *uint256.Int + GetCallInput() []byte +} + +type StateDB interface { + GetBalance(common.Address) *uint256.Int + GetNonce(common.Address) uint64 + GetCode(common.Address) []byte + GetState(common.Address, common.Hash) common.Hash + Exist(common.Address) bool + GetRefund() uint64 +} + +// Canceler is an interface that wraps the Cancel method. +// It allows loggers to cancel EVM processing. +type Canceler interface { + Cancel() +} + +type VMContext struct { + Coinbase common.Address + BlockNumber *big.Int + Time uint64 + Random *common.Hash + // Effective tx gas price + GasPrice *big.Int + ChainConfig *params.ChainConfig + StateDB StateDB + VM Canceler +} + +// BlockEvent is emitted upon tracing an incoming block. +// It contains the block as well as consensus related information. +type BlockEvent struct { + Block *types.Block + TD *big.Int + Finalized *types.Header + Safe *types.Header +} + +// OpCode is an EVM opcode +// TODO: provide utils for consumers +type OpCode byte + +type LiveLogger struct { + /* + - VM events - + */ + // Transaction level + // Call simulations don't come with a valid signature. `from` field + // to be used for address of the caller. + CaptureTxStart func(vm *VMContext, tx *types.Transaction, from common.Address) + CaptureTxEnd func(receipt *types.Receipt, err error) + // Top call frame + CaptureStart func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) + // CaptureEnd is invoked when the processing of the top call ends. + // See docs for `CaptureExit` for info on the `reverted` parameter. + CaptureEnd func(output []byte, gasUsed uint64, err error, reverted bool) + // Rest of call frames + CaptureEnter func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) + // CaptureExit is invoked when the processing of a message ends. + // `revert` is true when there was an error during the execution. + // Exceptionally, before the homestead hardfork a contract creation that + // ran out of gas when attempting to persist the code to database did not + // count as a call failure and did not cause a revert of the call. This will + // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. + CaptureExit func(output []byte, gasUsed uint64, err error, reverted bool) + // Opcode level + CaptureState func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, rData []byte, depth int, err error) + CaptureFault func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, depth int, err error) + CaptureKeccakPreimage func(hash common.Hash, data []byte) + // Misc + OnGasChange func(old, new uint64, reason GasChangeReason) + + /* + - Chain events - + */ + OnBlockchainInit func(chainConfig *params.ChainConfig) + // OnBlockStart is called before executing `block`. + // `td` is the total difficulty prior to `block`. + OnBlockStart func(event BlockEvent) + OnBlockEnd func(err error) + // OnSkippedBlock indicates a block was skipped during processing + // due to it being known previously. This can happen e.g. when recovering + // from a crash. + OnSkippedBlock func(event BlockEvent) + OnGenesisBlock func(genesis *types.Block, alloc types.GenesisAlloc) + + /* + - State events - + */ + OnBalanceChange func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) + OnNonceChange func(addr common.Address, prev, new uint64) + OnCodeChange func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) + OnStorageChange func(addr common.Address, slot common.Hash, prev, new common.Hash) + OnLog func(log *types.Log) +} + +// BalanceChangeReason is used to indicate the reason for a balance change, useful +// for tracing and reporting. +type BalanceChangeReason byte + +const ( + BalanceChangeUnspecified BalanceChangeReason = 0 + + // Issuance + // BalanceIncreaseRewardMineUncle is a reward for mining an uncle block. + BalanceIncreaseRewardMineUncle BalanceChangeReason = 1 + // BalanceIncreaseRewardMineBlock is a reward for mining a block. + BalanceIncreaseRewardMineBlock BalanceChangeReason = 2 + // BalanceIncreaseWithdrawal is ether withdrawn from the beacon chain. + BalanceIncreaseWithdrawal BalanceChangeReason = 3 + // BalanceIncreaseGenesisBalance is ether allocated at the genesis block. + BalanceIncreaseGenesisBalance BalanceChangeReason = 4 + + // Transaction fees + // BalanceIncreaseRewardTransactionFee is the transaction tip increasing block builder's balance. + BalanceIncreaseRewardTransactionFee BalanceChangeReason = 5 + // BalanceDecreaseGasBuy is spent to purchase gas for execution a transaction. + // Part of this gas will be burnt as per EIP-1559 rules. + BalanceDecreaseGasBuy BalanceChangeReason = 6 + // BalanceIncreaseGasReturn is ether returned for unused gas at the end of execution. + BalanceIncreaseGasReturn BalanceChangeReason = 7 + + // DAO fork + // BalanceIncreaseDaoContract is ether sent to the DAO refund contract. + BalanceIncreaseDaoContract BalanceChangeReason = 8 + // BalanceDecreaseDaoAccount is ether taken from a DAO account to be moved to the refund contract. + BalanceDecreaseDaoAccount BalanceChangeReason = 9 + + // BalanceChangeTransfer is ether transferred via a call. + // it is a decrease for the sender and an increase for the recipient. + BalanceChangeTransfer BalanceChangeReason = 10 + // BalanceChangeTouchAccount is a transfer of zero value. It is only there to + // touch-create an account. + BalanceChangeTouchAccount BalanceChangeReason = 11 + + // BalanceIncreaseSelfdestruct is added to the recipient as indicated by a selfdestructing account. + BalanceIncreaseSelfdestruct BalanceChangeReason = 12 + // BalanceDecreaseSelfdestruct is deducted from a contract due to self-destruct. + BalanceDecreaseSelfdestruct BalanceChangeReason = 13 + // BalanceDecreaseSelfdestructBurn is ether that is sent to an already self-destructed + // account within the same tx (captured at end of tx). + // Note it doesn't account for a self-destruct which appoints itself as recipient. + BalanceDecreaseSelfdestructBurn BalanceChangeReason = 14 +) + +// GasChangeReason is used to indicate the reason for a gas change, useful +// for tracing and reporting. +// +// There is essentially two types of gas changes, those that can be emitted once per transaction +// and those that can be emitted on a call basis, so possibly multiple times per transaction. +// +// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted +// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis. +type GasChangeReason byte + +const ( + GasChangeUnspecified GasChangeReason = iota + + // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per transaction. + GasChangeTxInitialBalance + // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is + // always exactly one of those per transaction. + GasChangeTxIntrinsicGas + // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) + // this generates an increase in gas. There is at most one of such gas change per transaction. + GasChangeTxRefunds + // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned + // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas + // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. + // There is at most one of such gas change per transaction. + GasChangeTxLeftOverReturned + + // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per call. + GasChangeCallInitialBalance + // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always + // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even + // will be emitted. + GasChangeCallLeftOverReturned + // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it + // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child. + // If there was no gas left to be refunded, no such even will be emitted. + GasChangeCallLeftOverRefunded + // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE. + GasChangeCallContractCreation + // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2. + GasChangeCallContractCreation2 + // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. + GasChangeCallCodeStorage + // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was + // performed can be check by `CaptureState` handling. + GasChangeCallOpCode + // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. + GasChangeCallPrecompiledContract + // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules. + GasChangeCallStorageColdAccess + // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. + GasChangeCallFailedExecution + + // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as + // it will be "manually" tracked by a direct emit of the gas change event. + GasChangeIgnored GasChangeReason = 0xFF +) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index a5848fdf0b51..e18b1c4caabd 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -35,11 +35,11 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/kzg4844" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -513,17 +513,17 @@ func TestOpenDrops(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3) - statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2) - statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000), live.BalanceChangeUnspecified) - statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000), live.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000), tracing.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -638,7 +638,7 @@ func TestOpenIndex(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) + statedb.AddBalance(addr, big.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -738,9 +738,9 @@ func TestOpenHeap(t *testing.T) { // Create a blob pool out of the pre-seeded data statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) + statedb.AddBalance(addr1, big.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr2, big.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr3, big.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -818,9 +818,9 @@ func TestOpenCap(t *testing.T) { for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} { // Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) - statedb.AddBalance(addr1, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) - statedb.AddBalance(addr2, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) - statedb.AddBalance(addr3, big.NewInt(1_000_000_000), live.BalanceChangeUnspecified) + statedb.AddBalance(addr1, big.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr2, big.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.AddBalance(addr3, big.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) statedb.Commit(0, true) chain := &testBlockChain{ @@ -1211,7 +1211,7 @@ func TestAdd(t *testing.T) { addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey) // Seed the state database with this acocunt - statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance), live.BalanceChangeUnspecified) + statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance), tracing.BalanceChangeUnspecified) statedb.SetNonce(addrs[acc], seed.nonce) // Sign the seed transactions and store them in the data store diff --git a/core/txpool/legacypool/legacypool2_test.go b/core/txpool/legacypool/legacypool2_test.go index 621987b38476..dc62822c9f3c 100644 --- a/core/txpool/legacypool/legacypool2_test.go +++ b/core/txpool/legacypool/legacypool2_test.go @@ -23,9 +23,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/event" ) @@ -50,11 +50,7 @@ func fillPool(t testing.TB, pool *LegacyPool) { nonExecutableTxs := types.Transactions{} for i := 0; i < 384; i++ { key, _ := crypto.GenerateKey() -<<<<<<< HEAD - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000), state.BalanceChangeUnspecified) -======= - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(10000000000), live.BalanceChangeUnspecified) ->>>>>>> 8cc747f439 (moaar fixes) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000), tracing.BalanceChangeUnspecified) // Add executable ones for j := 0; j < int(pool.config.AccountSlots); j++ { executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key)) @@ -96,11 +92,7 @@ func TestTransactionFutureAttack(t *testing.T) { // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops { key, _ := crypto.GenerateKey() -<<<<<<< HEAD - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) -======= - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) ->>>>>>> 8cc747f439 (moaar fixes) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), tracing.BalanceChangeUnspecified) futureTxs := types.Transactions{} for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key)) @@ -137,11 +129,7 @@ func TestTransactionFuture1559(t *testing.T) { // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops { key, _ := crypto.GenerateKey() -<<<<<<< HEAD - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) -======= - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) ->>>>>>> 8cc747f439 (moaar fixes) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), tracing.BalanceChangeUnspecified) futureTxs := types.Transactions{} for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key)) @@ -195,11 +183,7 @@ func TestTransactionZAttack(t *testing.T) { for j := 0; j < int(pool.config.GlobalQueue); j++ { futureTxs := types.Transactions{} key, _ := crypto.GenerateKey() -<<<<<<< HEAD - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) -======= - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) ->>>>>>> 8cc747f439 (moaar fixes) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), tracing.BalanceChangeUnspecified) futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key)) pool.addRemotesSync(futureTxs) } @@ -207,11 +191,7 @@ func TestTransactionZAttack(t *testing.T) { overDraftTxs := types.Transactions{} { key, _ := crypto.GenerateKey() -<<<<<<< HEAD - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified) -======= - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000), live.BalanceChangeUnspecified) ->>>>>>> 8cc747f439 (moaar fixes) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), tracing.BalanceChangeUnspecified) for j := 0; j < int(pool.config.GlobalSlots); j++ { overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key)) } @@ -248,7 +228,7 @@ func BenchmarkFutureAttack(b *testing.B) { fillPool(b, pool) key, _ := crypto.GenerateKey() - pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), core/state/state_test.go.BalanceChangeUnspecified) + pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), tracing.BalanceChangeUnspecified) futureTxs := types.Transactions{} for n := 0; n < b.N; n++ { diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 719b71bb34e5..12b759490e46 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -33,10 +33,10 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -256,7 +256,7 @@ func (c *testChain) State() (*state.StateDB, error) { c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) // simulate that the new head block included tx0 and tx1 c.statedb.SetNonce(c.address, 2) - c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether), core/state/state_test.go.BalanceChangeUnspecified) + c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether), tracing.BalanceChangeUnspecified) *c.trigger = false } return stdb, nil @@ -276,7 +276,7 @@ func TestStateChangeDuringReset(t *testing.T) { ) // setup pool with 2 transaction in it - statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether), core/state/state_test.go.BalanceChangeUnspecified) + statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether), tracing.BalanceChangeUnspecified) blockchain := &testChain{newTestBlockChain(params.TestChainConfig, 1000000000, statedb, new(event.Feed)), address, &trigger} tx0 := transaction(0, 100000, key) @@ -310,7 +310,7 @@ func TestStateChangeDuringReset(t *testing.T) { func testAddBalance(pool *LegacyPool, addr common.Address, amount *big.Int) { pool.mu.Lock() - pool.currentState.AddBalance(addr, amount, core/state/state_test.go.BalanceChangeUnspecified) + pool.currentState.AddBalance(addr, amount, tracing.BalanceChangeUnspecified) pool.mu.Unlock() } @@ -471,7 +471,7 @@ func TestChainFork(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000), core/state/state_test.go.BalanceChangeUnspecified) + statedb.AddBalance(addr, big.NewInt(100000000000000), tracing.BalanceChangeUnspecified) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) <-pool.requestReset(nil, nil) @@ -500,7 +500,7 @@ func TestDoubleNonce(t *testing.T) { addr := crypto.PubkeyToAddress(key.PublicKey) resetState := func() { statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.AddBalance(addr, big.NewInt(100000000000000), core/state/state_test.go.BalanceChangeUnspecified) + statedb.AddBalance(addr, big.NewInt(100000000000000), tracing.BalanceChangeUnspecified) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) <-pool.requestReset(nil, nil) @@ -2619,7 +2619,7 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) { for i := 0; i < b.N; i++ { key, _ := crypto.GenerateKey() account := crypto.PubkeyToAddress(key.PublicKey) - pool.currentState.AddBalance(account, big.NewInt(1000000), live.BalanceChangeUnspecified) + pool.currentState.AddBalance(account, big.NewInt(1000000), tracing.BalanceChangeUnspecified) tx := transaction(uint64(0), 100000, key) batches[i] = tx } diff --git a/core/vm/contract.go b/core/vm/contract.go index fbfc3b125d82..291a426b3b23 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -20,7 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/holiman/uint256" ) @@ -160,11 +160,11 @@ func (c *Contract) Caller() common.Address { } // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas uint64, logger *live.LiveLogger, reason live.GasChangeReason) (ok bool) { +func (c *Contract) UseGas(gas uint64, logger *tracing.LiveLogger, reason tracing.GasChangeReason) (ok bool) { if c.Gas < gas { return false } - if logger != nil && logger.OnGasChange != nil && reason != live.GasChangeIgnored { + if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored { logger.OnGasChange(c.Gas, c.Gas-gas, reason) } c.Gas -= gas diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 799665f0cb9e..6b2512570a1b 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -25,12 +25,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/blake2b" "github.com/ethereum/go-ethereum/crypto/bls12381" "github.com/ethereum/go-ethereum/crypto/bn256" "github.com/ethereum/go-ethereum/crypto/kzg4844" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "golang.org/x/crypto/ripemd160" ) @@ -169,13 +169,13 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *live.LiveLogger) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.LiveLogger) (ret []byte, remainingGas uint64, err error) { gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas } if logger != nil && logger.OnGasChange != nil { - logger.OnGasChange(suppliedGas, suppliedGas-gasCost, live.GasChangeCallPrecompiledContract) + logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract) } suppliedGas -= gasCost output, err := p.Run(input) diff --git a/core/vm/evm.go b/core/vm/evm.go index a3fd2da531db..8064de4a86ea 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -22,9 +22,9 @@ import ( "sync/atomic" "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/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -231,7 +231,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { - evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } gas = 0 @@ -287,7 +287,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { - evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } gas = 0 @@ -334,7 +334,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { - evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } gas = 0 } @@ -369,7 +369,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // but is the correct thing to do and matters on other networks, in tests, and potential // future scenarios - evm.StateDB.AddBalance(addr, new(big.Int), live.BalanceChangeTouchAccount) + evm.StateDB.AddBalance(addr, new(big.Int), tracing.BalanceChangeTouchAccount) if p, isPrecompile := evm.precompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) @@ -392,7 +392,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { - evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } gas = 0 @@ -443,7 +443,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil { - evm.Config.Tracer.OnGasChange(gas, 0, live.GasChangeCallFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution) } return nil, common.Address{}, 0, ErrContractAddressCollision @@ -479,7 +479,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // by the error checking condition below. if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas, evm.Config.Tracer, live.GasChangeCallCodeStorage) { + if contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -492,7 +492,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas, evm.Config.Tracer, live.GasChangeCallFailedExecution) + contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution) } } @@ -526,11 +526,11 @@ func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to co tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) } } else if tracer.CaptureEnter != nil { - tracer.CaptureEnter(live.OpCode(typ), from, to, input, startGas, value) + tracer.CaptureEnter(tracing.OpCode(typ), from, to, input, startGas, value) } if tracer.OnGasChange != nil { - tracer.OnGasChange(0, startGas, live.GasChangeCallInitialBalance) + tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance) } } @@ -538,7 +538,7 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas tracer := evm.Config.Tracer if leftOverGas != 0 && tracer.OnGasChange != nil { - tracer.OnGasChange(leftOverGas, 0, live.GasChangeCallLeftOverReturned) + tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned) } var reverted bool if err != nil { @@ -556,8 +556,8 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas } } -func (evm *EVM) GetVMContext() *live.VMContext { - return &live.VMContext{ +func (evm *EVM) GetVMContext() *tracing.VMContext { + return &tracing.VMContext{ Coinbase: evm.Context.Coinbase, BlockNumber: evm.Context.BlockNumber, Time: evm.Context.Time, diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 3a681775bbcf..d06b59b1211c 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -18,9 +18,9 @@ package vm import ( "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/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -594,7 +594,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // reuse size int for stackvalue stackvalue := size - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, live.GasChangeCallContractCreation) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 if !value.IsZero() { @@ -616,7 +616,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -642,7 +642,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ) // Apply EIP150 gas -= gas / 64 - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, live.GasChangeCallContractCreation2) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2) // reuse size int for stackvalue stackvalue := size //TODO: use uint256.Int instead of converting with toBig() @@ -661,7 +661,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] scope.Stack.push(&stackvalue) if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -711,7 +711,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -751,7 +751,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -784,7 +784,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -817,7 +817,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, live.GasChangeCallLeftOverRefunded) + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) } scope.Contract.Gas += returnGas @@ -855,11 +855,11 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.CaptureEnter != nil { - tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.CaptureEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } if tracer.CaptureExit != nil { tracer.CaptureExit([]byte{}, 0, nil, false) @@ -874,12 +874,12 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon } beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) - interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, live.BalanceDecreaseSelfdestruct) - interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, live.BalanceIncreaseSelfdestruct) + interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) + interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.CaptureEnter != nil { - tracer.CaptureEnter(live.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.CaptureEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } if tracer.CaptureExit != nil { tracer.CaptureExit([]byte{}, 0, nil, false) diff --git a/core/vm/interface.go b/core/vm/interface.go index 94aa015ed918..f6d9cb30ad72 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -20,8 +20,8 @@ import ( "math/big" "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/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -29,8 +29,8 @@ import ( type StateDB interface { CreateAccount(common.Address) - SubBalance(common.Address, *big.Int, live.BalanceChangeReason) - AddBalance(common.Address, *big.Int, live.BalanceChangeReason) + SubBalance(common.Address, *big.Int, tracing.BalanceChangeReason) + AddBalance(common.Address, *big.Int, tracing.BalanceChangeReason) GetBalance(common.Address) *big.Int GetNonce(common.Address) uint64 diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 6da530cd0602..99d95f0160f4 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -19,15 +19,15 @@ package vm import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/log" "github.com/holiman/uint256" ) // Config are the configuration options for the Interpreter type Config struct { - Tracer *live.LiveLogger + Tracer *tracing.LiveLogger NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages ExtraEips []int // Additional EIPS that are to be enabled @@ -191,10 +191,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if err != nil { if !logged { if in.evm.Config.Tracer.CaptureState != nil { - in.evm.Config.Tracer.CaptureState(pcCopy, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.CaptureState(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) } } else if in.evm.Config.Tracer.CaptureFault != nil { - in.evm.Config.Tracer.CaptureFault(pcCopy, live.OpCode(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.CaptureFault(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) } } }() @@ -219,7 +219,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - if !contract.UseGas(cost, in.evm.Config.Tracer, live.GasChangeIgnored) { + if !contract.UseGas(cost, in.evm.Config.Tracer, tracing.GasChangeIgnored) { return nil, ErrOutOfGas } @@ -246,17 +246,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( var dynamicCost uint64 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // for tracing - if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, live.GasChangeIgnored) { + if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, tracing.GasChangeIgnored) { return nil, ErrOutOfGas } // Do tracing before memory expansion if debug { if in.evm.Config.Tracer.OnGasChange != nil { - in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, live.GasChangeCallOpCode) + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) } if in.evm.Config.Tracer.CaptureState != nil { - in.evm.Config.Tracer.CaptureState(pc, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.CaptureState(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } } @@ -265,10 +265,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } } else if debug { if in.evm.Config.Tracer.OnGasChange != nil { - in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, live.GasChangeCallOpCode) + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) } if in.evm.Config.Tracer.CaptureState != nil { - in.evm.Config.Tracer.CaptureState(pc, live.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.CaptureState(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } } diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index e9f26a88c140..5986de2e7e3c 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -21,7 +21,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/params" ) @@ -170,7 +170,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call - if !contract.UseGas(coldCost, evm.Config.Tracer, live.GasChangeCallStorageColdAccess) { + if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) { return 0, ErrOutOfGas } } diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index f14b96b43655..4a5ecf3b7b61 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -27,9 +27,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/trie" "golang.org/x/exp/slices" ) @@ -73,7 +73,7 @@ func TestAccountRange(t *testing.T) { hash := common.HexToHash(fmt.Sprintf("%x", i)) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) addrs[i] = addr - sdb.SetBalance(addrs[i], big.NewInt(1), live.BalanceChangeUnspecified) + sdb.SetBalance(addrs[i], big.NewInt(1), tracing.BalanceChangeUnspecified) if _, ok := m[addr]; ok { t.Fatalf("bad") } else { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 11e43e41b1b9..7abac3bec57f 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -27,11 +27,11 @@ import ( "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" @@ -153,7 +153,7 @@ type Config struct { EnablePreimageRecording bool // Enables VM tracing - VMTracer *live.LiveLogger + VMTracer *tracing.LiveLogger // Miscellaneous options DocRoot string `toml:"-"` diff --git a/eth/tracers/directory/live/dir.go b/eth/tracers/directory/live/dir.go index fca365414185..a097754310f6 100644 --- a/eth/tracers/directory/live/dir.go +++ b/eth/tracers/directory/live/dir.go @@ -3,118 +3,11 @@ package live import ( "encoding/json" "errors" - "math/big" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" + "github.com/ethereum/go-ethereum/core/tracing" ) -type ScopeContext interface { - GetMemoryData() []byte - GetStackData() []uint256.Int - GetCaller() common.Address - GetAddress() common.Address - GetCallValue() *uint256.Int - GetCallInput() []byte -} - -type StateDB interface { - GetBalance(common.Address) *uint256.Int - GetNonce(common.Address) uint64 - GetCode(common.Address) []byte - GetState(common.Address, common.Hash) common.Hash - Exist(common.Address) bool - GetRefund() uint64 -} - -// Canceler is an interface that wraps the Cancel method. -// It allows loggers to cancel EVM processing. -type Canceler interface { - Cancel() -} - -type VMContext struct { - Coinbase common.Address - BlockNumber *big.Int - Time uint64 - Random *common.Hash - // Effective tx gas price - GasPrice *big.Int - ChainConfig *params.ChainConfig - StateDB StateDB - VM Canceler -} - -// BlockEvent is emitted upon tracing an incoming block. -// It contains the block as well as consensus related information. -type BlockEvent struct { - Block *types.Block - TD *big.Int - Finalized *types.Header - Safe *types.Header -} - -// OpCode is an EVM opcode -// TODO: provide utils for consumers -type OpCode byte - -type LiveLogger struct { - /* - - VM events - - */ - // Transaction level - // Call simulations don't come with a valid signature. `from` field - // to be used for address of the caller. - CaptureTxStart func(vm *VMContext, tx *types.Transaction, from common.Address) - CaptureTxEnd func(receipt *types.Receipt, err error) - // Top call frame - CaptureStart func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) - // CaptureEnd is invoked when the processing of the top call ends. - // See docs for `CaptureExit` for info on the `reverted` parameter. - CaptureEnd func(output []byte, gasUsed uint64, err error, reverted bool) - // Rest of call frames - CaptureEnter func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) - // CaptureExit is invoked when the processing of a message ends. - // `revert` is true when there was an error during the execution. - // Exceptionally, before the homestead hardfork a contract creation that - // ran out of gas when attempting to persist the code to database did not - // count as a call failure and did not cause a revert of the call. This will - // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. - CaptureExit func(output []byte, gasUsed uint64, err error, reverted bool) - // Opcode level - CaptureState func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, rData []byte, depth int, err error) - CaptureFault func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, depth int, err error) - CaptureKeccakPreimage func(hash common.Hash, data []byte) - // Misc - OnGasChange func(old, new uint64, reason GasChangeReason) - - /* - - Chain events - - */ - OnBlockchainInit func(chainConfig *params.ChainConfig) - // OnBlockStart is called before executing `block`. - // `td` is the total difficulty prior to `block`. - OnBlockStart func(event BlockEvent) - OnBlockEnd func(err error) - // OnSkippedBlock indicates a block was skipped during processing - // due to it being known previously. This can happen e.g. when recovering - // from a crash. - OnSkippedBlock func(event BlockEvent) - OnGenesisBlock func(genesis *types.Block, alloc types.GenesisAlloc) - - /* - - State events - - */ - OnBalanceChange func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) - OnNonceChange func(addr common.Address, prev, new uint64) - OnCodeChange func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) - OnStorageChange func(addr common.Address, slot common.Hash, prev, new common.Hash) - OnLog func(log *types.Log) -} - -type ctorFunc func(config json.RawMessage) (*LiveLogger, error) +type ctorFunc func(config json.RawMessage) (*tracing.LiveLogger, error) // Directory is the collection of tracers which can be used // during normal block import operations. @@ -130,7 +23,7 @@ func (d *directory) Register(name string, f ctorFunc) { } // New instantiates a tracer by name. -func (d *directory) New(name string, config json.RawMessage) (*LiveLogger, error) { +func (d *directory) New(name string, config json.RawMessage) (*tracing.LiveLogger, error) { if f, ok := d.elems[name]; ok { return f(config) } diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index 9c28770494b2..a6bb457d2f1b 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -21,8 +21,8 @@ import ( "math/big" "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/eth/tracers/directory/live" ) func init() { @@ -37,7 +37,7 @@ type NoopTracer struct{} func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) { t := &NoopTracer{} return &Tracer{ - LiveLogger: &live.LiveLogger{ + LiveLogger: &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureTxEnd: t.CaptureTxEnd, CaptureStart: t.CaptureStart, @@ -68,21 +68,21 @@ func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *NoopTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *NoopTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *NoopTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, _ live.ScopeContext, depth int, err error) { +func (t *NoopTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.ScopeContext, depth int, err error) { } // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} // OnGasChange is called when gas is either consumed or refunded. -func (t *NoopTracer) OnGasChange(old, new uint64, reason live.GasChangeReason) {} +func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *NoopTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *NoopTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } // CaptureExit is called when EVM exits a scope, even if the scope didn't @@ -90,11 +90,12 @@ func (t *NoopTracer) CaptureEnter(typ live.OpCode, from common.Address, to commo func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { } -func (*NoopTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) {} +func (*NoopTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +} func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} -func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) { +func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { } func (*NoopTracer) OnNonceChange(a common.Address, prev, new uint64) {} diff --git a/eth/tracers/directory/tracers.go b/eth/tracers/directory/tracers.go index 4953c46c5683..78dc9ae6e7b1 100644 --- a/eth/tracers/directory/tracers.go +++ b/eth/tracers/directory/tracers.go @@ -24,7 +24,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" + "github.com/ethereum/go-ethereum/core/tracing" ) // Context contains some contextual infos for a transaction execution that is not @@ -41,7 +41,7 @@ type Context struct { // This involves a method to retrieve results and one to // stop tracing. type Tracer struct { - *live.LiveLogger + *tracing.LiveLogger GetResult func() (json.RawMessage, error) // Stop terminates execution of the tracer at the first opportune moment. Stop func(err error) diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 62115f40a2b2..578b1a10af67 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -23,9 +23,9 @@ import ( "math/big" "github.com/dop251/goja" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/holiman/uint256" "github.com/ethereum/go-ethereum/common" @@ -101,7 +101,7 @@ type jsTracer struct { directory.NoopTracer vm *goja.Runtime - env *live.VMContext + env *tracing.VMContext toBig toBigFn // Converts a hex string into a JS bigint toBuf toBufFn // Converts a []byte into a JS buffer fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte @@ -213,7 +213,7 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir t.logValue = t.log.setupObject() return &directory.Tracer{ - LiveLogger: &live.LiveLogger{ + LiveLogger: &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureTxEnd: t.CaptureTxEnd, CaptureStart: t.CaptureStart, @@ -230,7 +230,7 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir // CaptureTxStart implements the Tracer interface and is invoked at the beginning of // transaction processing. -func (t *jsTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *jsTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.env = env // Need statedb access for db object db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} @@ -300,7 +300,7 @@ func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create b } // CaptureState implements the Tracer interface to trace a single step of VM execution. -func (t *jsTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *jsTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { if !t.traceStep { return } @@ -325,7 +325,7 @@ func (t *jsTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, sco } // CaptureFault implements the Tracer interface to trace an execution fault -func (t *jsTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { +func (t *jsTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { if t.err != nil { return } @@ -344,7 +344,7 @@ func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *jsTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *jsTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { if !t.traceFrame { return } @@ -697,7 +697,7 @@ func (s *stackObj) setupObject() *goja.Object { } type dbObj struct { - db live.StateDB + db tracing.StateDB vm *goja.Runtime toBig toBigFn toBuf toBufFn @@ -789,7 +789,7 @@ func (do *dbObj) setupObject() *goja.Object { } type contractObj struct { - scope live.ScopeContext + scope tracing.ScopeContext vm *goja.Runtime toBig toBigFn toBuf toBufFn diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index c5a3a7e2f85b..ac8fbe23fcfb 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -26,10 +26,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "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/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" ) @@ -279,7 +279,7 @@ func TestEnterExit(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - tracer.CaptureEnter(live.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) + tracer.CaptureEnter(tracing.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.CaptureExit([]byte{}, 400, nil, false) have, err := tracer.GetResult() diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index e099ef9ac514..ea843a38e7f4 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -5,6 +5,7 @@ import ( "math/big" "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/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" @@ -20,9 +21,9 @@ func init() { // as soon as we have a real live tracer. type noop struct{} -func newNoopTracer(_ json.RawMessage) (*live.LiveLogger, error) { +func newNoopTracer(_ json.RawMessage) (*tracing.LiveLogger, error) { t := &noop{} - return &live.LiveLogger{ + return &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureTxEnd: t.CaptureTxEnd, CaptureStart: t.CaptureStart, @@ -55,18 +56,18 @@ func (t *noop) CaptureEnd(output []byte, gasUsed uint64, err error, reverted boo } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *noop) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *noop) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *noop) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, _ live.ScopeContext, depth int, err error) { +func (t *noop) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.ScopeContext, depth int, err error) { } // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *noop) CaptureKeccakPreimage(hash common.Hash, data []byte) {} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *noop) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *noop) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } // CaptureExit is called when EVM exits a scope, even if the scope didn't @@ -74,19 +75,19 @@ func (t *noop) CaptureEnter(typ live.OpCode, from common.Address, to common.Addr func (t *noop) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { } -func (t *noop) CaptureTxStart(vm *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *noop) CaptureTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { } func (t *noop) CaptureTxEnd(receipt *types.Receipt, err error) { } -func (t *noop) OnBlockStart(ev live.BlockEvent) { +func (t *noop) OnBlockStart(ev tracing.BlockEvent) { } func (t *noop) OnBlockEnd(err error) { } -func (t *noop) OnSkippedBlock(ev live.BlockEvent) {} +func (t *noop) OnSkippedBlock(ev tracing.BlockEvent) {} func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) { } @@ -94,7 +95,7 @@ func (t *noop) OnBlockchainInit(chainConfig *params.ChainConfig) { func (t *noop) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { } -func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) { +func (t *noop) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { } func (t *noop) OnNonceChange(a common.Address, prev, new uint64) { @@ -110,5 +111,5 @@ func (t *noop) OnLog(l *types.Log) { } -func (t *noop) OnGasChange(old, new uint64, reason live.GasChangeReason) { +func (t *noop) OnGasChange(old, new uint64, reason tracing.GasChangeReason) { } diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index fdc990dfee25..51e03ba25de9 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -18,10 +18,10 @@ package logger import ( "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/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) // accessList is an accumulator for the set of accounts and storage slots an EVM @@ -133,14 +133,14 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi } } -func (a *AccessListTracer) GetLogger() *live.LiveLogger { - return &live.LiveLogger{ +func (a *AccessListTracer) GetLogger() *tracing.LiveLogger { + return &tracing.LiveLogger{ CaptureState: a.CaptureState, } } // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. -func (a *AccessListTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (a *AccessListTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { stackData := scope.GetStackData() stackLen := len(stackData) op := vm.OpCode(opcode) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 143db5752dda..8165ce50870a 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -28,10 +28,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "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/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -110,7 +110,7 @@ func (s *StructLog) ErrorString() string { type StructLogger struct { directory.NoopTracer cfg Config - env *live.VMContext + env *tracing.VMContext storage map[common.Address]Storage logs []StructLog @@ -133,8 +133,8 @@ func NewStructLogger(cfg *Config) *StructLogger { return logger } -func (l *StructLogger) Logger() *live.LiveLogger { - return &live.LiveLogger{ +func (l *StructLogger) Logger() *tracing.LiveLogger { + return &tracing.LiveLogger{ CaptureTxStart: l.CaptureTxStart, CaptureTxEnd: l.CaptureTxEnd, CaptureEnd: l.CaptureEnd, @@ -161,7 +161,7 @@ func (l *StructLogger) Reset() { // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { // If tracing was interrupted, set the error and stop if l.interrupt.Load() { return @@ -264,7 +264,7 @@ func (l *StructLogger) Stop(err error) { l.interrupt.Store(true) } -func (l *StructLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (l *StructLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { l.env = env } @@ -339,7 +339,7 @@ type mdLogger struct { directory.NoopTracer out io.Writer cfg *Config - env *live.VMContext + env *tracing.VMContext } // NewMarkdownLogger creates a logger which outputs information in a format adapted @@ -352,8 +352,8 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { return l } -func (t *mdLogger) Logger() *live.LiveLogger { - return &live.LiveLogger{ +func (t *mdLogger) Logger() *tracing.LiveLogger { + return &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureStart: t.CaptureStart, CaptureState: t.CaptureState, @@ -362,7 +362,7 @@ func (t *mdLogger) Logger() *live.LiveLogger { } } -func (t *mdLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *mdLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.env = env } @@ -384,7 +384,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b } // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (t *mdLogger) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { stack := scope.GetStackData() fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) @@ -404,7 +404,7 @@ func (t *mdLogger) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, sco } } -func (t *mdLogger) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { +func (t *mdLogger) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 43ba082beb2c..76278c2bcaef 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -22,17 +22,17 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "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/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) type JSONLogger struct { directory.NoopTracer encoder *json.Encoder cfg *Config - env *live.VMContext + env *tracing.VMContext } // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects @@ -45,8 +45,8 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { return l } -func (l *JSONLogger) Logger() *live.LiveLogger { - return &live.LiveLogger{ +func (l *JSONLogger) Logger() *tracing.LiveLogger { + return &tracing.LiveLogger{ CaptureTxStart: l.CaptureTxStart, CaptureEnd: l.CaptureEnd, CaptureState: l.CaptureState, @@ -54,13 +54,13 @@ func (l *JSONLogger) Logger() *live.LiveLogger { } } -func (l *JSONLogger) CaptureFault(pc uint64, op live.OpCode, gas uint64, cost uint64, scope live.ScopeContext, depth int, err error) { +func (l *JSONLogger) CaptureFault(pc uint64, op tracing.OpCode, gas uint64, cost uint64, scope tracing.ScopeContext, depth int, err error) { // TODO: Add rData to this interface as well l.CaptureState(pc, op, gas, cost, scope, nil, depth, err) } // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (l *JSONLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { memory := scope.GetMemoryData() stack := scope.GetStackData() @@ -100,6 +100,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, revert l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg}) } -func (l *JSONLogger) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (l *JSONLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { l.env = env } diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index aa2dbd906a13..8f83f068b2e8 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -23,10 +23,10 @@ import ( "sync/atomic" "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/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) func init() { @@ -62,7 +62,7 @@ func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (*directory.Tr ids: make(map[string]int), } return &directory.Tracer{ - LiveLogger: &live.LiveLogger{ + LiveLogger: &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureStart: t.CaptureStart, CaptureEnter: t.CaptureEnter, @@ -88,7 +88,7 @@ func (t *fourByteTracer) store(id []byte, size int) { t.ids[key] += 1 } -func (t *fourByteTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *fourByteTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { // Update list of precompiles based on current block rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) @@ -103,7 +103,7 @@ func (t *fourByteTracer) CaptureStart(from common.Address, to common.Address, cr } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *fourByteTracer) CaptureEnter(opcode live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *fourByteTracer) CaptureEnter(opcode tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { // Skip if tracing was interrupted if t.interrupt.Load() { return diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 74a13038d9f3..831b39cf7de5 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -25,10 +25,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "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/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) //go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go @@ -127,7 +127,7 @@ func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trac return nil, err } return &directory.Tracer{ - LiveLogger: &live.LiveLogger{ + LiveLogger: &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureTxEnd: t.CaptureTxEnd, CaptureStart: t.CaptureStart, @@ -176,11 +176,11 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *callTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *callTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *callTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *callTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { t.depth++ if t.config.OnlyTopCall { return @@ -223,7 +223,7 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, rever t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) } -func (t *callTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *callTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.gasLimit = tx.Gas() } diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 4cd5308e4054..d0914e04a8b3 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -25,10 +25,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "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/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) //go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go @@ -141,7 +141,7 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. ft := &flatCallTracer{tracer: t, ctx: ctx, config: config} return &directory.Tracer{ - LiveLogger: &live.LiveLogger{ + LiveLogger: &tracing.LiveLogger{ CaptureTxStart: ft.CaptureTxStart, CaptureTxEnd: ft.CaptureTxEnd, CaptureStart: ft.CaptureStart, @@ -167,17 +167,17 @@ func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, re } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *flatCallTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *flatCallTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err) } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *flatCallTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { +func (t *flatCallTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err) } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *flatCallTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *flatCallTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { t.tracer.CaptureEnter(typ, from, to, input, gas, value) // Child calls must have a value, even if it's zero. @@ -211,7 +211,7 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, r } } -func (t *flatCallTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *flatCallTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.tracer.CaptureTxStart(env, tx, from) // Update list of precompiles based on current block rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 501f59838557..92099133bfec 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -21,9 +21,9 @@ import ( "math/big" "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/eth/tracers/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" ) func init() { @@ -58,7 +58,7 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace t := &muxTracer{names: names, tracers: objects} return &directory.Tracer{ - LiveLogger: &live.LiveLogger{ + LiveLogger: &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureTxEnd: t.CaptureTxEnd, CaptureStart: t.CaptureStart, @@ -99,7 +99,7 @@ func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverte } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *muxTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { for _, t := range t.tracers { if t.CaptureState != nil { t.CaptureState(pc, op, gas, cost, scope, rData, depth, err) @@ -108,7 +108,7 @@ func (t *muxTracer) CaptureState(pc uint64, op live.OpCode, gas, cost uint64, sc } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *muxTracer) CaptureFault(pc uint64, op live.OpCode, gas, cost uint64, scope live.ScopeContext, depth int, err error) { +func (t *muxTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { for _, t := range t.tracers { if t.CaptureFault != nil { t.CaptureFault(pc, op, gas, cost, scope, depth, err) @@ -126,7 +126,7 @@ func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { } // CaptureGasConsumed is called when gas is consumed. -func (t *muxTracer) OnGasChange(old, new uint64, reason live.GasChangeReason) { +func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) { for _, t := range t.tracers { if t.OnGasChange != nil { t.OnGasChange(old, new, reason) @@ -135,7 +135,7 @@ func (t *muxTracer) OnGasChange(old, new uint64, reason live.GasChangeReason) { } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *muxTracer) CaptureEnter(typ live.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *muxTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { if t.CaptureEnter != nil { t.CaptureEnter(typ, from, to, input, gas, value) @@ -153,7 +153,7 @@ func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, revert } } -func (t *muxTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *muxTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { for _, t := range t.tracers { if t.CaptureTxStart != nil { t.CaptureTxStart(env, tx, from) @@ -169,7 +169,7 @@ func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) { } } -func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason live.BalanceChangeReason) { +func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { for _, t := range t.tracers { if t.OnBalanceChange != nil { t.OnBalanceChange(a, prev, new, reason) diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 7ac77ffaa976..f6483eb2cb51 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -24,11 +24,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "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/crypto" "github.com/ethereum/go-ethereum/eth/tracers/directory" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/log" ) @@ -59,7 +59,7 @@ type accountMarshaling struct { type prestateTracer struct { directory.NoopTracer - env *live.VMContext + env *tracing.VMContext pre stateMap post stateMap to common.Address @@ -89,7 +89,7 @@ func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. deleted: make(map[common.Address]bool), } return &directory.Tracer{ - LiveLogger: &live.LiveLogger{ + LiveLogger: &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureTxEnd: t.CaptureTxEnd, CaptureState: t.CaptureState, @@ -100,7 +100,7 @@ func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *prestateTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost uint64, scope live.ScopeContext, rData []byte, depth int, err error) { +func (t *prestateTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { if err != nil { return } @@ -146,7 +146,7 @@ func (t *prestateTracer) CaptureState(pc uint64, opcode live.OpCode, gas, cost u } } -func (t *prestateTracer) CaptureTxStart(env *live.VMContext, tx *types.Transaction, from common.Address) { +func (t *prestateTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.env = env if tx.To() == nil { t.to = crypto.CreateAddress(from, env.StateDB.GetNonce(from)) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 39d79413bd7b..305975775fed 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -37,10 +37,10 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip1559" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" + "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/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" @@ -961,7 +961,7 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error { } // Override account balance. if account.Balance != nil { - statedb.SetBalance(addr, (*big.Int)(*account.Balance), live.BalanceChangeUnspecified) + statedb.SetBalance(addr, (*big.Int)(*account.Balance), tracing.BalanceChangeUnspecified) } if account.State != nil && account.StateDiff != nil { return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex()) diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 5a021b245a55..37b89afc5131 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -34,9 +34,9 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "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/directory/live" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" @@ -109,7 +109,7 @@ type btHeaderMarshaling struct { ExcessBlobGas *math.HexOrDecimal64 } -func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *live.LiveLogger) error { +func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *tracing.LiveLogger) error { config, ok := Forks[t.json.Network] if !ok { return UnsupportedForkError{t.json.Network} diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 25b50a79e3cd..a12714181bc7 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -32,10 +32,10 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state/snapshot" + "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/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" @@ -299,7 +299,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // - the coinbase self-destructed, or // - there are only 'bad' transactions, which aren't executed. In those cases, // the coinbase gets no txfee, so isn't created, and thus needs to be touched - statedb.AddBalance(block.Coinbase(), new(big.Int), live.BalanceChangeUnspecified) + statedb.AddBalance(block.Coinbase(), new(big.Int), tracing.BalanceChangeUnspecified) // Commit state mutations into database. root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number())) @@ -323,7 +323,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo for addr, a := range accounts { statedb.SetCode(addr, a.Code) statedb.SetNonce(addr, a.Nonce) - statedb.SetBalance(addr, a.Balance, live.BalanceChangeUnspecified) + statedb.SetBalance(addr, a.Balance, tracing.BalanceChangeUnspecified) for k, v := range a.Storage { statedb.SetState(addr, k, v) } From d4ec634c05a2308389962e00dc849ecc36e84705 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 28 Feb 2024 19:41:58 +0100 Subject: [PATCH 26/50] rename GetLogger to Hooks # Conflicts: # eth/tracers/logger/access_list_tracer.go # eth/tracers/logger/logger.go # eth/tracers/logger/logger_json.go # eth/tracers/logger/logger_test.go # eth/tracers/tracers_test.go # internal/ethapi/api.go --- cmd/evm/blockrunner.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 2 +- cmd/evm/runner.go | 4 ++-- cmd/evm/staterunner.go | 4 ++-- core/blockchain_test.go | 6 +++--- core/tracing/hooks.go | 3 ++- eth/tracers/api.go | 2 +- eth/tracers/logger/access_list_tracer.go | 4 ++-- eth/tracers/logger/logger.go | 14 +++++++------- eth/tracers/logger/logger_json.go | 4 ++-- eth/tracers/logger/logger_test.go | 2 +- eth/tracers/tracers_test.go | 2 +- internal/ethapi/api.go | 2 +- tests/state_test.go | 2 +- 14 files changed, 27 insertions(+), 26 deletions(-) diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index ed439b6fb272..1f7d07f31a83 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -58,7 +58,7 @@ func blockTestCmd(ctx *cli.Context) error { DisableStack: ctx.Bool(DisableStackFlag.Name), DisableStorage: ctx.Bool(DisableStorageFlag.Name), EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - }, os.Stderr).Logger() + }, os.Stderr).Hooks() } // Load the test content from the input file src, err := os.ReadFile(ctx.Args().First()) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 20b5eee1b190..97a11015442d 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -124,7 +124,7 @@ func Transition(ctx *cli.Context) error { if err != nil { return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } - logger := logger.NewJSONLogger(logConfig, traceFile).Logger() + logger := logger.NewJSONLogger(logConfig, traceFile).Hooks() tracer := &directory.Tracer{ LiveLogger: logger, // JSONLogger streams out result to file. diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 259ea5220e4d..47ae7070639a 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -128,10 +128,10 @@ func runCmd(ctx *cli.Context) error { blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests ) if ctx.Bool(MachineFlag.Name) { - tracer = logger.NewJSONLogger(logconfig, os.Stdout).Logger() + tracer = logger.NewJSONLogger(logconfig, os.Stdout).Hooks() } else if ctx.Bool(DebugFlag.Name) { debugLogger = logger.NewStructLogger(logconfig) - tracer = debugLogger.Logger() + tracer = debugLogger.Hooks() } else { debugLogger = logger.NewStructLogger(logconfig) } diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 5e26ff2ee66c..0bb8f73439ac 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -61,10 +61,10 @@ func stateTestCmd(ctx *cli.Context) error { var cfg vm.Config switch { case ctx.Bool(MachineFlag.Name): - cfg.Tracer = logger.NewJSONLogger(config, os.Stderr).Logger() + cfg.Tracer = logger.NewJSONLogger(config, os.Stderr).Hooks() case ctx.Bool(DebugFlag.Name): - cfg.Tracer = logger.NewStructLogger(config).Logger() + cfg.Tracer = logger.NewStructLogger(config).Hooks() } // Load the test content from the input file if len(ctx.Args().First()) != 0 { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index c3f77d70daa7..b5fa0cffa26a 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3248,7 +3248,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout).Logger(), + Tracer: logger.NewJSONLogger(nil, os.Stdout).Hooks(), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -3330,7 +3330,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout).Logger(), + Tracer: logger.NewJSONLogger(nil, os.Stdout).Hooks(), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -4682,7 +4682,7 @@ func TestEIP3651(t *testing.T) { b.AddTx(tx) }) - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Logger()}, nil, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 6f9795af3b5e..6a8900367707 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -116,7 +117,7 @@ type LiveLogger struct { // due to it being known previously. This can happen e.g. when recovering // from a crash. OnSkippedBlock func(event BlockEvent) - OnGenesisBlock func(genesis *types.Block, alloc types.GenesisAlloc) + OnGenesisBlock func(genesis *types.Block, alloc core.GenesisAlloc) /* - State events - diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 0fb13696c3a1..d3f565edbe49 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -777,7 +777,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Swap out the noop logger to the standard tracer writer = bufio.NewWriter(dump) vmConf = vm.Config{ - Tracer: logger.NewJSONLogger(&logConfig, writer).Logger(), + Tracer: logger.NewJSONLogger(&logConfig, writer).Hooks(), EnablePreimageRecording: true, } } diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 51e03ba25de9..a69c8b30480b 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -133,8 +133,8 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi } } -func (a *AccessListTracer) GetLogger() *tracing.LiveLogger { - return &tracing.LiveLogger{ +func (a *AccessListTracer) Hooks() *tracing.Hooks { + return &tracing.Hooks{ CaptureState: a.CaptureState, } } diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 8165ce50870a..2398a803d8ee 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -133,8 +133,8 @@ func NewStructLogger(cfg *Config) *StructLogger { return logger } -func (l *StructLogger) Logger() *tracing.LiveLogger { - return &tracing.LiveLogger{ +func (l *StructLogger) Hooks() *tracing.Hooks { + return &tracing.Hooks{ CaptureTxStart: l.CaptureTxStart, CaptureTxEnd: l.CaptureTxEnd, CaptureEnd: l.CaptureEnd, @@ -144,9 +144,9 @@ func (l *StructLogger) Logger() *tracing.LiveLogger { func (l *StructLogger) Tracer() *directory.Tracer { return &directory.Tracer{ - LiveLogger: l.Logger(), - GetResult: l.GetResult, - Stop: l.Stop, + Hooks: l.Hooks(), + GetResult: l.GetResult, + Stop: l.Stop, } } @@ -352,8 +352,8 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { return l } -func (t *mdLogger) Logger() *tracing.LiveLogger { - return &tracing.LiveLogger{ +func (t *mdLogger) Hooks() *tracing.Hooks { + return &tracing.Hooks{ CaptureTxStart: t.CaptureTxStart, CaptureStart: t.CaptureStart, CaptureState: t.CaptureState, diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 76278c2bcaef..246b91bdf338 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -45,8 +45,8 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { return l } -func (l *JSONLogger) Logger() *tracing.LiveLogger { - return &tracing.LiveLogger{ +func (l *JSONLogger) Hooks() *tracing.Hooks { + return &tracing.Hooks{ CaptureTxStart: l.CaptureTxStart, CaptureEnd: l.CaptureEnd, CaptureState: l.CaptureState, diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index ddb558832d8b..c2982ab0361f 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -55,7 +55,7 @@ func (*dummyStatedb) SetState(_ common.Address, _ common.Hash, _ common.Hash) {} func TestStoreCapture(t *testing.T) { var ( logger = NewStructLogger(nil) - env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Logger()}) + env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 7f509b8ae6e8..505db4fa4e47 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -89,7 +89,7 @@ func BenchmarkTransactionTrace(b *testing.B) { //EnableMemory: false, //EnableReturnData: false, }) - evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Logger()}) + evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Hooks()}) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 305975775fed..d1ac21b9d8d6 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1640,7 +1640,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) - config := vm.Config{Tracer: tracer.GetLogger(), NoBaseFee: true} + config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true} vmenv, _ := b.GetEVM(ctx, msg, statedb, header, &config, nil) res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) if err != nil { diff --git a/tests/state_test.go b/tests/state_test.go index 9759f9cb7aa5..bf098055d4ec 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -152,7 +152,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { } buf := new(bytes.Buffer) w := bufio.NewWriter(buf) - config.Tracer = logger.NewJSONLogger(&logger.Config{}, w).Logger() + config.Tracer = logger.NewJSONLogger(&logger.Config{}, w).Hooks() err2 := test(config) if !reflect.DeepEqual(err, err2) { t.Errorf("different error for second run: %v", err2) From 688f0b965460eb9a19e38adce8ac77e0b67517c9 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 29 Feb 2024 15:13:03 +0100 Subject: [PATCH 27/50] rename scopeContext to opContext --- core/tracing/hooks.go | 20 +++++++++++--------- core/vm/interpreter.go | 12 ++++++------ eth/tracers/directory/noop.go | 4 ++-- eth/tracers/js/goja.go | 18 +++++++++--------- eth/tracers/live/noop.go | 4 ++-- eth/tracers/logger/access_list_tracer.go | 6 +++--- eth/tracers/logger/logger.go | 14 +++++++------- eth/tracers/logger/logger_json.go | 8 ++++---- eth/tracers/native/call.go | 2 +- eth/tracers/native/call_flat.go | 4 ++-- eth/tracers/native/mux.go | 4 ++-- eth/tracers/native/prestate.go | 8 ++++---- 12 files changed, 53 insertions(+), 51 deletions(-) diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 6a8900367707..3d78009f958e 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -26,13 +26,15 @@ import ( "github.com/holiman/uint256" ) -type ScopeContext interface { - GetMemoryData() []byte - GetStackData() []uint256.Int - GetCaller() common.Address - GetAddress() common.Address - GetCallValue() *uint256.Int - GetCallInput() []byte +// OpContext provides the context at which the opcode is being +// executed in, including the memory, stack and various contract-level information. +type OpContext interface { + MemoryData() []byte + StackData() []uint256.Int + Caller() common.Address + Address() common.Address + CallValue() *uint256.Int + CallInput() []byte } type StateDB interface { @@ -99,8 +101,8 @@ type LiveLogger struct { // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. CaptureExit func(output []byte, gasUsed uint64, err error, reverted bool) // Opcode level - CaptureState func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, rData []byte, depth int, err error) - CaptureFault func(pc uint64, op OpCode, gas, cost uint64, scope ScopeContext, depth int, err error) + CaptureState func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) + CaptureFault func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, depth int, err error) CaptureKeccakPreimage func(hash common.Hash, data []byte) // Misc OnGasChange func(old, new uint64, reason GasChangeReason) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 99d95f0160f4..b19e79531fd9 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -41,33 +41,33 @@ type ScopeContext struct { Contract *Contract } -func (ctx *ScopeContext) GetMemoryData() []byte { +func (ctx *ScopeContext) MemoryData() []byte { if ctx.Memory == nil { return nil } return ctx.Memory.Data() } -func (ctx *ScopeContext) GetStackData() []uint256.Int { +func (ctx *ScopeContext) StackData() []uint256.Int { if ctx.Stack == nil { return nil } return ctx.Stack.Data() } -func (ctx *ScopeContext) GetCaller() common.Address { +func (ctx *ScopeContext) Caller() common.Address { return ctx.Contract.Caller() } -func (ctx *ScopeContext) GetAddress() common.Address { +func (ctx *ScopeContext) Address() common.Address { return ctx.Contract.Address() } -func (ctx *ScopeContext) GetCallValue() *uint256.Int { +func (ctx *ScopeContext) CallValue() *uint256.Int { return ctx.Contract.Value() } -func (ctx *ScopeContext) GetCallInput() []byte { +func (ctx *ScopeContext) CallInput() []byte { return ctx.Contract.Input } diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index a6bb457d2f1b..2c250a066a86 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -68,11 +68,11 @@ func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *NoopTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (t *NoopTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *NoopTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.ScopeContext, depth int, err error) { +func (t *NoopTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } // CaptureKeccakPreimage is called during the KECCAK256 opcode. diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 578b1a10af67..c9bf5ab5b9e0 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -300,7 +300,7 @@ func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create b } // CaptureState implements the Tracer interface to trace a single step of VM execution. -func (t *jsTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (t *jsTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { if !t.traceStep { return } @@ -310,8 +310,8 @@ func (t *jsTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, log := t.log log.op.op = vm.OpCode(op) - log.memory.memory = scope.GetMemoryData() - log.stack.stack = scope.GetStackData() + log.memory.memory = scope.MemoryData() + log.stack.stack = scope.StackData() log.contract.scope = scope log.pc = pc log.gas = gas @@ -325,7 +325,7 @@ func (t *jsTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, } // CaptureFault implements the Tracer interface to trace an execution fault -func (t *jsTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { +func (t *jsTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { if t.err != nil { return } @@ -789,14 +789,14 @@ func (do *dbObj) setupObject() *goja.Object { } type contractObj struct { - scope tracing.ScopeContext + scope tracing.OpContext vm *goja.Runtime toBig toBigFn toBuf toBufFn } func (co *contractObj) GetCaller() goja.Value { - caller := co.scope.GetCaller().Bytes() + caller := co.scope.Caller().Bytes() res, err := co.toBuf(co.vm, caller) if err != nil { co.vm.Interrupt(err) @@ -806,7 +806,7 @@ func (co *contractObj) GetCaller() goja.Value { } func (co *contractObj) GetAddress() goja.Value { - addr := co.scope.GetAddress().Bytes() + addr := co.scope.Address().Bytes() res, err := co.toBuf(co.vm, addr) if err != nil { co.vm.Interrupt(err) @@ -816,7 +816,7 @@ func (co *contractObj) GetAddress() goja.Value { } func (co *contractObj) GetValue() goja.Value { - value := co.scope.GetCallValue() + value := co.scope.CallValue() res, err := co.toBig(co.vm, value.String()) if err != nil { co.vm.Interrupt(err) @@ -826,7 +826,7 @@ func (co *contractObj) GetValue() goja.Value { } func (co *contractObj) GetInput() goja.Value { - input := common.CopyBytes(co.scope.GetCallInput()) + input := common.CopyBytes(co.scope.CallInput()) res, err := co.toBuf(co.vm, input) if err != nil { co.vm.Interrupt(err) diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index ea843a38e7f4..b5d8c3ae5b14 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -56,11 +56,11 @@ func (t *noop) CaptureEnd(output []byte, gasUsed uint64, err error, reverted boo } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *noop) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (t *noop) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *noop) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.ScopeContext, depth int, err error) { +func (t *noop) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } // CaptureKeccakPreimage is called during the KECCAK256 opcode. diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index a69c8b30480b..7e76080a9487 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -140,13 +140,13 @@ func (a *AccessListTracer) Hooks() *tracing.Hooks { } // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. -func (a *AccessListTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { - stackData := scope.GetStackData() +func (a *AccessListTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + stackData := scope.StackData() stackLen := len(stackData) op := vm.OpCode(opcode) if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 { slot := common.Hash(stackData[stackLen-1].Bytes32()) - a.list.addSlot(scope.GetAddress(), slot) + a.list.addSlot(scope.Address(), slot) } if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 { addr := common.Address(stackData[stackLen-1].Bytes20()) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 2398a803d8ee..3867749d46a1 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -161,7 +161,7 @@ func (l *StructLogger) Reset() { // CaptureState logs a new structured log message and pushes it out to the environment // // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { // If tracing was interrupted, set the error and stop if l.interrupt.Load() { return @@ -172,8 +172,8 @@ func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost } op := vm.OpCode(opcode) - memory := scope.GetMemoryData() - stack := scope.GetStackData() + memory := scope.MemoryData() + stack := scope.StackData() // Copy a snapshot of the current memory state to a new buffer var mem []byte if l.cfg.EnableMemory { @@ -188,7 +188,7 @@ func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost stck[i] = item } } - contractAddr := scope.GetAddress() + contractAddr := scope.Address() stackLen := len(stack) // Copy a snapshot of the current storage to a new container var storage Storage @@ -384,8 +384,8 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b } // CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { - stack := scope.GetStackData() +func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + stack := scope.StackData() fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) if !t.cfg.DisableStack { @@ -404,7 +404,7 @@ func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, } } -func (t *mdLogger) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { +func (t *mdLogger) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 246b91bdf338..72eda302d8e6 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -54,15 +54,15 @@ func (l *JSONLogger) Hooks() *tracing.Hooks { } } -func (l *JSONLogger) CaptureFault(pc uint64, op tracing.OpCode, gas uint64, cost uint64, scope tracing.ScopeContext, depth int, err error) { +func (l *JSONLogger) CaptureFault(pc uint64, op tracing.OpCode, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { // TODO: Add rData to this interface as well l.CaptureState(pc, op, gas, cost, scope, nil, depth, err) } // CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { - memory := scope.GetMemoryData() - stack := scope.GetStackData() +func (l *JSONLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + memory := scope.MemoryData() + stack := scope.StackData() log := StructLog{ Pc: pc, diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 831b39cf7de5..0b6790ce8993 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -176,7 +176,7 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *callTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (t *callTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index d0914e04a8b3..9eef26ca5e69 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -167,12 +167,12 @@ func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, re } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *flatCallTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (t *flatCallTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err) } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *flatCallTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { +func (t *flatCallTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err) } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 92099133bfec..68b600b923c4 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -99,7 +99,7 @@ func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverte } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { for _, t := range t.tracers { if t.CaptureState != nil { t.CaptureState(pc, op, gas, cost, scope, rData, depth, err) @@ -108,7 +108,7 @@ func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, } // CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *muxTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, depth int, err error) { +func (t *muxTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { for _, t := range t.tracers { if t.CaptureFault != nil { t.CaptureFault(pc, op, gas, cost, scope, depth, err) diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index f6483eb2cb51..765356f10f7b 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -100,7 +100,7 @@ func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. } // CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *prestateTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.ScopeContext, rData []byte, depth int, err error) { +func (t *prestateTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { if err != nil { return } @@ -109,9 +109,9 @@ func (t *prestateTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cos return } op := vm.OpCode(opcode) - stackData := scope.GetStackData() + stackData := scope.StackData() stackLen := len(stackData) - caller := scope.GetAddress() + caller := scope.Address() switch { case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE): slot := common.Hash(stackData[stackLen-1].Bytes32()) @@ -133,7 +133,7 @@ func (t *prestateTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cos case stackLen >= 4 && op == vm.CREATE2: offset := stackData[stackLen-2] size := stackData[stackLen-3] - init, err := directory.GetMemoryCopyPadded(scope.GetMemoryData(), int64(offset.Uint64()), int64(size.Uint64())) + init, err := directory.GetMemoryCopyPadded(scope.MemoryData(), int64(offset.Uint64()), int64(size.Uint64())) if err != nil { log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size) return From 3253b8bab55fa1d467afc03eb12d3bca3b1a3d41 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 29 Feb 2024 15:33:23 +0100 Subject: [PATCH 28/50] assign const values for gas change reasons --- core/tracing/hooks.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 3d78009f958e..f7488e537de7 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -37,6 +37,7 @@ type OpContext interface { CallInput() []byte } +// StateDB gives tracers access to the whole state. type StateDB interface { GetBalance(common.Address) *uint256.Int GetNonce(common.Address) uint64 @@ -52,6 +53,7 @@ type Canceler interface { Cancel() } +// VMContext provides the context for the EVM execution. type VMContext struct { Coinbase common.Address BlockNumber *big.Int @@ -191,49 +193,49 @@ const ( type GasChangeReason byte const ( - GasChangeUnspecified GasChangeReason = iota + GasChangeUnspecified GasChangeReason = 0 // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only // one such gas change per transaction. - GasChangeTxInitialBalance + GasChangeTxInitialBalance GasChangeReason = 1 // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is // always exactly one of those per transaction. - GasChangeTxIntrinsicGas + GasChangeTxIntrinsicGas GasChangeReason = 2 // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) // this generates an increase in gas. There is at most one of such gas change per transaction. - GasChangeTxRefunds + GasChangeTxRefunds GasChangeReason = 3 // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. // There is at most one of such gas change per transaction. - GasChangeTxLeftOverReturned + GasChangeTxLeftOverReturned GasChangeReason = 4 // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only // one such gas change per call. - GasChangeCallInitialBalance + GasChangeCallInitialBalance GasChangeReason = 5 // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even // will be emitted. - GasChangeCallLeftOverReturned + GasChangeCallLeftOverReturned GasChangeReason = 6 // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child. // If there was no gas left to be refunded, no such even will be emitted. - GasChangeCallLeftOverRefunded + GasChangeCallLeftOverRefunded GasChangeReason = 7 // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE. - GasChangeCallContractCreation + GasChangeCallContractCreation GasChangeReason = 8 // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2. - GasChangeCallContractCreation2 + GasChangeCallContractCreation2 GasChangeReason = 9 // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. - GasChangeCallCodeStorage + GasChangeCallCodeStorage GasChangeReason = 10 // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was // performed can be check by `CaptureState` handling. - GasChangeCallOpCode + GasChangeCallOpCode GasChangeReason = 11 // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. - GasChangeCallPrecompiledContract + GasChangeCallPrecompiledContract GasChangeReason = 12 // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules. - GasChangeCallStorageColdAccess + GasChangeCallStorageColdAccess GasChangeReason = 13 // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. - GasChangeCallFailedExecution + GasChangeCallFailedExecution GasChangeReason = 14 // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as // it will be "manually" tracked by a direct emit of the gas change event. From e7fe23698816620dc91c828c51509a1629f6e250 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 29 Feb 2024 16:22:34 +0100 Subject: [PATCH 29/50] a type for each hook # Conflicts: # core/tracing/hooks.go --- core/tracing/hooks.go | 120 +++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 32 deletions(-) diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index f7488e537de7..e8584c2b6408 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -20,7 +20,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -79,58 +78,115 @@ type BlockEvent struct { // TODO: provide utils for consumers type OpCode byte -type LiveLogger struct { +type ( /* - VM events - */ - // Transaction level + + // TxStartHook is called before the execution of a transaction starts. // Call simulations don't come with a valid signature. `from` field // to be used for address of the caller. - CaptureTxStart func(vm *VMContext, tx *types.Transaction, from common.Address) - CaptureTxEnd func(receipt *types.Receipt, err error) - // Top call frame - CaptureStart func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) - // CaptureEnd is invoked when the processing of the top call ends. - // See docs for `CaptureExit` for info on the `reverted` parameter. - CaptureEnd func(output []byte, gasUsed uint64, err error, reverted bool) - // Rest of call frames - CaptureEnter func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) - // CaptureExit is invoked when the processing of a message ends. + TxStartHook = func(vm *VMContext, tx *types.Transaction, from common.Address) + + // TxEndHook is called after the execution of a transaction ends. + TxEndHook = func(receipt *types.Receipt, err error) + + // StartHook is invoked when the processing of the root call starts. + StartHook = func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) + + // EndHook is invoked when the processing of the top call ends. + // See docs for `ExitHook` for info on the `reverted` parameter. + EndHook = func(output []byte, gasUsed uint64, err error, reverted bool) + + // EnterHook is invoked when the processing of a message starts. + EnterHook = func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) + + // ExitHook is invoked when the processing of a message ends. // `revert` is true when there was an error during the execution. // Exceptionally, before the homestead hardfork a contract creation that // ran out of gas when attempting to persist the code to database did not // count as a call failure and did not cause a revert of the call. This will // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. - CaptureExit func(output []byte, gasUsed uint64, err error, reverted bool) - // Opcode level - CaptureState func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) - CaptureFault func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, depth int, err error) - CaptureKeccakPreimage func(hash common.Hash, data []byte) - // Misc - OnGasChange func(old, new uint64, reason GasChangeReason) + ExitHook = func(output []byte, gasUsed uint64, err error, reverted bool) + + // StateHook is invoked just prior to the execution of an opcode. + StateHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) + + // FaultHook is invoked when an error occurs during the execution of an opcode. + FaultHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, depth int, err error) + + // KeccakPreimageHook is invoked on the KECCAK256 opcode to provide the preimage for the hash. + KeccakPreimageHook = func(hash common.Hash, data []byte) + + // GasChangeHook is invoked when the gas changes. + GasChangeHook = func(old, new uint64, reason GasChangeReason) /* - Chain events - */ - OnBlockchainInit func(chainConfig *params.ChainConfig) - // OnBlockStart is called before executing `block`. + + // BlockchainInitHook is called when the blockchain is initialized. + BlockchainInitHook = func(chainConfig *params.ChainConfig) + + // BlockStartHook is called before executing `block`. // `td` is the total difficulty prior to `block`. - OnBlockStart func(event BlockEvent) - OnBlockEnd func(err error) - // OnSkippedBlock indicates a block was skipped during processing + BlockStartHook = func(event BlockEvent) + + // BlockEndHook is called after executing a block. + BlockEndHook = func(err error) + + // SkippedBlockHook indicates a block was skipped during processing // due to it being known previously. This can happen e.g. when recovering // from a crash. - OnSkippedBlock func(event BlockEvent) - OnGenesisBlock func(genesis *types.Block, alloc core.GenesisAlloc) + SkippedBlockHook = func(event BlockEvent) + + // GenesisBlockHook is called when the genesis block is being processed. + GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc) /* - State events - */ - OnBalanceChange func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) - OnNonceChange func(addr common.Address, prev, new uint64) - OnCodeChange func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) - OnStorageChange func(addr common.Address, slot common.Hash, prev, new common.Hash) - OnLog func(log *types.Log) + + // BalanceChangeHook is called when the balance of an account changes. + BalanceChangeHook = func(addr common.Address, prev, new *big.Int, reason BalanceChangeReason) + + // NonceChangeHook is called when the nonce of an account changes. + NonceChangeHook = func(addr common.Address, prev, new uint64) + + // CodeChangeHook is called when the code of an account changes. + CodeChangeHook = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte) + + // StorageChangeHook is called when the storage of an account changes. + StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash) + + // LogHook is called when a log is emitted. + LogHook = func(log *types.Log) +) + +type Hooks struct { + // VM events + CaptureTxStart TxStartHook + CaptureTxEnd TxEndHook + CaptureStart StartHook + CaptureEnd EndHook + CaptureEnter EnterHook + CaptureExit ExitHook + CaptureState StateHook + CaptureFault FaultHook + CaptureKeccakPreimage KeccakPreimageHook + OnGasChange GasChangeHook + // Chain events + OnBlockchainInit BlockchainInitHook + OnBlockStart BlockStartHook + OnBlockEnd BlockEndHook + OnSkippedBlock SkippedBlockHook + OnGenesisBlock GenesisBlockHook + // State events + OnBalanceChange BalanceChangeHook + OnNonceChange NonceChangeHook + OnCodeChange CodeChangeHook + OnStorageChange StorageChangeHook + OnLog LogHook } // BalanceChangeReason is used to indicate the reason for a balance change, useful From a2ad953d213f770ebf872f222322962d0f82dbd7 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 29 Feb 2024 16:38:54 +0100 Subject: [PATCH 30/50] rm old logger file --- core/vm/logger.go | 51 ----------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 core/vm/logger.go diff --git a/core/vm/logger.go b/core/vm/logger.go deleted file mode 100644 index 269aed509cfa..000000000000 --- a/core/vm/logger.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 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 vm - -// EVMLogger is used to collect execution traces from an EVM transaction -// execution. CaptureState is called for each step of the VM with the -// current VM state. -// Note that reference types are actual VM data structures; make copies -// if you need to retain them beyond the current call. -/*type EVMLogger interface { - // Transaction level - // Call simulations don't come with a valid signature. `from` field - // to be used for address of the caller. - CaptureTxStart(evm *EVM, tx *types.Transaction, from common.Address) - CaptureTxEnd(receipt *types.Receipt, err error) - // Top call frame - CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) - // CaptureEnd is invoked when the processing of the top call ends. - // See docs for `CaptureExit` for info on the `reverted` parameter. - CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) - // Rest of call frames - CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) - // CaptureExit is invoked when the processing of a message ends. - // `revert` is true when there was an error during the execution. - // Exceptionally, before the homestead hardfork a contract creation that - // ran out of gas when attempting to persist the code to database did not - // count as a call failure and did not cause a revert of the call. This will - // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. - CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) - // Opcode level - CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) - CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) - CaptureKeccakPreimage(hash common.Hash, data []byte) - // Misc - OnGasChange(old, new uint64, reason GasChangeReason) -} -*/ From 25a3a744c3202c535e5ff57233c7c36125622a72 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 29 Feb 2024 17:18:43 +0100 Subject: [PATCH 31/50] rename Capture hooks to On # Conflicts: # cmd/evm/internal/t8ntool/execution.go # core/vm/instructions.go # eth/tracers/directory/noop.go # eth/tracers/internal/tracetest/calltrace_test.go # eth/tracers/internal/tracetest/flat_calltrace_test.go # eth/tracers/internal/tracetest/prestate_test.go # eth/tracers/js/goja.go # eth/tracers/js/tracer_test.go # eth/tracers/live/noop.go # eth/tracers/native/4byte.go # eth/tracers/native/call.go # eth/tracers/native/call_flat.go # eth/tracers/native/mux.go # eth/tracers/native/prestate.go --- cmd/evm/internal/t8ntool/execution.go | 6 +- core/state_processor.go | 8 +-- core/tracing/hooks.go | 26 +++---- core/vm/evm.go | 16 ++--- core/vm/instructions.go | 20 +++--- core/vm/interpreter.go | 16 ++--- core/vm/runtime/runtime.go | 6 +- core/vm/runtime/runtime_test.go | 10 +-- eth/tracers/api.go | 4 +- eth/tracers/directory/noop.go | 32 ++++----- .../internal/tracetest/calltrace_test.go | 10 +-- .../internal/tracetest/flat_calltrace_test.go | 6 +- .../internal/tracetest/prestate_test.go | 6 +- eth/tracers/js/goja.go | 22 +++--- eth/tracers/js/tracer_test.go | 30 ++++---- eth/tracers/live/noop.go | 42 ++++++------ eth/tracers/logger/access_list_tracer.go | 2 +- eth/tracers/logger/logger.go | 18 ++--- eth/tracers/logger/logger_json.go | 8 +-- eth/tracers/native/4byte.go | 8 +-- eth/tracers/native/call.go | 12 ++++ eth/tracers/native/call_flat.go | 18 ++--- eth/tracers/native/mux.go | 68 +++++++++---------- eth/tracers/native/prestate.go | 8 +-- 24 files changed, 207 insertions(+), 195 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 7129ba88deed..4e72d11b5bbf 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -241,7 +241,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) if tracer != nil { - tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) } // (ret []byte, usedGas uint64, failed bool, err error) msgResult, err := core.ApplyMessage(evm, msg, gaspool) @@ -251,7 +251,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) gaspool.SetGas(prevGas) if tracer != nil { - tracer.CaptureTxEnd(nil, err) + tracer.OnTxEnd(nil, err) if err := writeTraceResult(tracer, traceOutput); err != nil { log.Warn("Error writing tracer output", "err", err) } @@ -298,7 +298,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, receipt.TransactionIndex = uint(txIndex) receipts = append(receipts, receipt) if tracer != nil { - tracer.LiveLogger.CaptureTxEnd(receipt, nil) + tracer.Hooks.OnTxEnd(receipt, nil) writeTraceResult(tracer, traceOutput) } } diff --git a/core/state_processor.go b/core/state_processor.go index d3ca3dde34da..59e3c80c2a8f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -111,8 +111,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { - if evm.Config.Tracer != nil && evm.Config.Tracer.CaptureTxStart != nil { - evm.Config.Tracer.CaptureTxStart(&tracing.VMContext{ + if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil { + evm.Config.Tracer.OnTxStart(&tracing.VMContext{ ChainConfig: evm.ChainConfig(), StateDB: statedb, BlockNumber: evm.Context.BlockNumber, @@ -120,9 +120,9 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo Coinbase: evm.Context.Coinbase, Random: evm.Context.Random, }, tx, msg.From) - if evm.Config.Tracer.CaptureTxEnd != nil { + if evm.Config.Tracer.OnTxEnd != nil { defer func() { - evm.Config.Tracer.CaptureTxEnd(receipt, err) + evm.Config.Tracer.OnTxEnd(receipt, err) }() } } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index e8584c2b6408..f912a6c87db1 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -109,8 +109,8 @@ type ( // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. ExitHook = func(output []byte, gasUsed uint64, err error, reverted bool) - // StateHook is invoked just prior to the execution of an opcode. - StateHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) + // OpcodeHook is invoked just prior to the execution of an opcode. + OpcodeHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) // FaultHook is invoked when an error occurs during the execution of an opcode. FaultHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, depth int, err error) @@ -165,16 +165,16 @@ type ( type Hooks struct { // VM events - CaptureTxStart TxStartHook - CaptureTxEnd TxEndHook - CaptureStart StartHook - CaptureEnd EndHook - CaptureEnter EnterHook - CaptureExit ExitHook - CaptureState StateHook - CaptureFault FaultHook - CaptureKeccakPreimage KeccakPreimageHook - OnGasChange GasChangeHook + OnTxStart TxStartHook + OnTxEnd TxEndHook + OnStart StartHook + OnEnd EndHook + OnEnter EnterHook + OnExit ExitHook + OnOpcode OpcodeHook + OnFault FaultHook + OnKeccakPreimage KeccakPreimageHook + OnGasChange GasChangeHook // Chain events OnBlockchainInit BlockchainInitHook OnBlockStart BlockStartHook @@ -284,7 +284,7 @@ const ( // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. GasChangeCallCodeStorage GasChangeReason = 10 // GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was - // performed can be check by `CaptureState` handling. + // performed can be check by `OnOpcode` handling. GasChangeCallOpCode GasChangeReason = 11 // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. GasChangeCallPrecompiledContract GasChangeReason = 12 diff --git a/core/vm/evm.go b/core/vm/evm.go index 8064de4a86ea..ba314b51da49 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -522,11 +522,11 @@ func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to co tracer := evm.Config.Tracer if isRoot { - if tracer.CaptureStart != nil { - tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) + if tracer.OnStart != nil { + tracer.OnStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) } - } else if tracer.CaptureEnter != nil { - tracer.CaptureEnter(tracing.OpCode(typ), from, to, input, startGas, value) + } else if tracer.OnEnter != nil { + tracer.OnEnter(tracing.OpCode(typ), from, to, input, startGas, value) } if tracer.OnGasChange != nil { @@ -548,11 +548,11 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas reverted = false } if isRoot { - if tracer.CaptureEnd != nil { - tracer.CaptureEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + if tracer.OnEnd != nil { + tracer.OnEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) } - } else if tracer.CaptureExit != nil { - tracer.CaptureExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + } else if tracer.OnExit != nil { + tracer.OnExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index d06b59b1211c..0f85f424e521 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -248,8 +248,8 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if evm.Config.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.CaptureKeccakPreimage != nil { - interpreter.evm.Config.Tracer.CaptureKeccakPreimage(common.BytesToHash(interpreter.hasherBuf[:]), data) + if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnKeccakPreimage != nil { + interpreter.evm.Config.Tracer.OnKeccakPreimage(common.BytesToHash(interpreter.hasherBuf[:]), data) } size.SetBytes(interpreter.hasherBuf[:]) return nil, nil @@ -858,11 +858,11 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - if tracer.CaptureEnter != nil { - tracer.CaptureEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + if tracer.OnEnter != nil { + tracer.OnEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } - if tracer.CaptureExit != nil { - tracer.CaptureExit([]byte{}, 0, nil, false) + if tracer.OnExit != nil { + tracer.OnExit([]byte{}, 0, nil, false) } } return nil, errStopToken @@ -878,11 +878,11 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { - if tracer.CaptureEnter != nil { - tracer.CaptureEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + if tracer.OnEnter != nil { + tracer.OnEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } - if tracer.CaptureExit != nil { - tracer.CaptureExit([]byte{}, 0, nil, false) + if tracer.OnExit != nil { + tracer.OnExit([]byte{}, 0, nil, false) } } return nil, errStopToken diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index b19e79531fd9..4d867fbc05f2 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -190,11 +190,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( defer func() { if err != nil { if !logged { - if in.evm.Config.Tracer.CaptureState != nil { - in.evm.Config.Tracer.CaptureState(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + if in.evm.Config.Tracer.OnOpcode != nil { + in.evm.Config.Tracer.OnOpcode(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) } - } else if in.evm.Config.Tracer.CaptureFault != nil { - in.evm.Config.Tracer.CaptureFault(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) + } else if in.evm.Config.Tracer.OnFault != nil { + in.evm.Config.Tracer.OnFault(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) } } }() @@ -255,8 +255,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if in.evm.Config.Tracer.OnGasChange != nil { in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) } - if in.evm.Config.Tracer.CaptureState != nil { - in.evm.Config.Tracer.CaptureState(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + if in.evm.Config.Tracer.OnOpcode != nil { + in.evm.Config.Tracer.OnOpcode(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } } @@ -267,8 +267,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if in.evm.Config.Tracer.OnGasChange != nil { in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) } - if in.evm.Config.Tracer.CaptureState != nil { - in.evm.Config.Tracer.CaptureState(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + if in.evm.Config.Tracer.OnOpcode != nil { + in.evm.Config.Tracer.OnOpcode(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index aba137c50cff..49415e50fa65 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -123,7 +123,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil { - cfg.EVMConfig.Tracer.CaptureTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) @@ -159,7 +159,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil { - cfg.EVMConfig.Tracer.CaptureTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) @@ -190,7 +190,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) if cfg.EVMConfig.Tracer != nil { - cfg.EVMConfig.Tracer.CaptureTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) + cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin) } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 2997ca5351f4..fdf8abbc1ffe 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -335,7 +335,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode b.Fatal(err) } cfg.EVMConfig = vm.Config{ - Tracer: tracer.LiveLogger, + Tracer: tracer.Hooks, } } var ( @@ -510,7 +510,7 @@ func TestEip2929Cases(t *testing.T) { code, ops) Execute(code, nil, &Config{ EVMConfig: vm.Config{ - Tracer: logger.NewMarkdownLogger(nil, os.Stdout).Logger(), + Tracer: logger.NewMarkdownLogger(nil, os.Stdout).Hooks(), ExtraEips: []int{2929}, }, }) @@ -663,7 +663,7 @@ func TestColdAccountAccessCost(t *testing.T) { tracer := logger.NewStructLogger(nil) Execute(tc.code, nil, &Config{ EVMConfig: vm.Config{ - Tracer: tracer.GetTracer().LiveLogger, + Tracer: tracer.Hooks(), }, }) have := tracer.StructLogs()[tc.step].GasCost @@ -834,7 +834,7 @@ func TestRuntimeJSTracer(t *testing.T) { GasLimit: 1000000, State: statedb, EVMConfig: vm.Config{ - Tracer: tracer.LiveLogger, + Tracer: tracer.Hooks, }}) if err != nil { t.Fatal("didn't expect error", err) @@ -868,7 +868,7 @@ func TestJSTracerCreateTx(t *testing.T) { _, _, _, err = Create(code, &Config{ State: statedb, EVMConfig: vm.Config{ - Tracer: tracer.LiveLogger, + Tracer: tracer.Hooks, }}) if err != nil { t.Fatal(err) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index d3f565edbe49..d0eefa84b134 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -784,9 +784,9 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Execute the transaction and flush any traces to disk vmenv := vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) statedb.SetTxContext(tx.Hash(), i) - vmConf.Tracer.CaptureTxStart(vmenv.GetVMContext(), tx, msg.From) + vmConf.Tracer.OnTxStart(vmenv.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) - vmConf.Tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) + vmConf.Tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) if writer != nil { writer.Flush() } diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index 2c250a066a86..637f0da326d2 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -37,22 +37,22 @@ type NoopTracer struct{} func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) { t := &NoopTracer{} return &Tracer{ - LiveLogger: &tracing.LiveLogger{ - CaptureTxStart: t.CaptureTxStart, - CaptureTxEnd: t.CaptureTxEnd, - CaptureStart: t.CaptureStart, - CaptureEnd: t.CaptureEnd, - CaptureEnter: t.CaptureEnter, - CaptureExit: t.CaptureExit, - CaptureState: t.CaptureState, - CaptureFault: t.CaptureFault, - CaptureKeccakPreimage: t.CaptureKeccakPreimage, - OnGasChange: t.OnGasChange, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, + Hooks: &tracing.Hooks{ + OnTxStart: t.CaptureTxStart, + OnTxEnd: t.CaptureTxEnd, + OnStart: t.CaptureStart, + OnEnd: t.CaptureEnd, + OnEnter: t.CaptureEnter, + OnExit: t.CaptureExit, + OnOpcode: t.CaptureState, + OnFault: t.CaptureFault, + OnKeccakPreimage: t.CaptureKeccakPreimage, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, }, GetResult: t.GetResult, Stop: t.Stop, diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index fcc56180d148..01c5ae1fdba1 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -149,13 +149,13 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) - tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("failed to execute transaction: %v", err) } - tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) + tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected. res, err := tracer.GetResult() if err != nil { @@ -398,12 +398,12 @@ func TestInternals(t *testing.T) { if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) } - tc.tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) + tc.tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err) } - tc.tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) + tc.tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected res, err := tc.tracer.GetResult() if err != nil { diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 7ae99d2b9d93..8cf9eae541d9 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -107,13 +107,13 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) - tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { return fmt.Errorf("failed to execute transaction: %v", err) } - tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) + tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the etalon res, err := tracer.GetResult() diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 08e1eccc3c45..a055ba334d7a 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -117,13 +117,13 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) - tracer.CaptureTxStart(evm.GetVMContext(), tx, msg.From) + evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { t.Fatalf("failed to execute transaction: %v", err) } - tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) + tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected res, err := tracer.GetResult() if err != nil { diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index c9bf5ab5b9e0..3d96253b7132 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -213,15 +213,15 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir t.logValue = t.log.setupObject() return &directory.Tracer{ - LiveLogger: &tracing.LiveLogger{ - CaptureTxStart: t.CaptureTxStart, - CaptureTxEnd: t.CaptureTxEnd, - CaptureStart: t.CaptureStart, - CaptureEnd: t.CaptureEnd, - CaptureEnter: t.CaptureEnter, - CaptureExit: t.CaptureExit, - CaptureState: t.CaptureState, - CaptureFault: t.CaptureFault, + Hooks: &tracing.Hooks{ + OnTxStart: t.CaptureTxStart, + OnTxEnd: t.CaptureTxEnd, + OnStart: t.CaptureStart, + OnEnd: t.CaptureEnd, + OnEnter: t.CaptureEnter, + OnExit: t.CaptureExit, + OnOpcode: t.CaptureState, + OnFault: t.CaptureFault, }, GetResult: t.GetResult, Stop: t.Stop, @@ -329,7 +329,7 @@ func (t *jsTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, if t.err != nil { return } - // Other log fields have been already set as part of the last CaptureState. + // Other log fields have been already set as part of the last OnOpcode. t.log.err = err if _, err := t.fault(t.obj, t.logValue, t.dbValue); err != nil { t.onError("fault", err) @@ -407,7 +407,7 @@ func (t *jsTracer) Stop(err error) { // execution. func (t *jsTracer) onError(context string, err error) { t.err = wrapError(context, err) - // `env` is set on CaptureStart which comes before any JS execution. + // `env` is set on OnStart which comes before any JS execution. // So it should be non-nil. t.env.VM.Cancel() } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index ac8fbe23fcfb..c1546ca05511 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -75,12 +75,12 @@ func runTrace(tracer *directory.Tracer, vmctx *vmContext, chaincfg *params.Chain contract.Code = contractCode } - tracer.CaptureTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) - tracer.CaptureStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value) + tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) + tracer.OnStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value) ret, err := env.Interpreter().Run(contract, []byte{}, false) - tracer.CaptureEnd(ret, startGas-contract.Gas, err, true) + tracer.OnEnd(ret, startGas-contract.Gas, err, true) // Rest gas assumes no refund - tracer.CaptureTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) + tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) if err != nil { return nil, err } @@ -185,13 +185,13 @@ func TestHaltBetweenSteps(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.LiveLogger}) - tracer.CaptureTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) - tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) - tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks}) + tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) + tracer.OnStart(common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) + tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) - tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) + tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil) if _, err := tracer.GetResult(); !strings.Contains(err.Error(), timeout.Error()) { t.Errorf("Expected timeout error, got %v", err) @@ -207,10 +207,10 @@ func TestNoStepExec(t *testing.T) { if err != nil { t.Fatal(err) } - env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.LiveLogger}) - tracer.CaptureTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) - tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) - tracer.CaptureEnd(nil, 0, nil, false) + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks}) + tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) + tracer.OnStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) + tracer.OnEnd(nil, 0, nil, false) ret, err := tracer.GetResult() if err != nil { t.Fatal(err) @@ -279,8 +279,8 @@ func TestEnterExit(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - tracer.CaptureEnter(tracing.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) - tracer.CaptureExit([]byte{}, 400, nil, false) + tracer.OnEnter(tracing.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) + tracer.OnExit([]byte{}, 400, nil, false) have, err := tracer.GetResult() if err != nil { diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index b5d8c3ae5b14..05560cfb24f0 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -23,27 +23,27 @@ type noop struct{} func newNoopTracer(_ json.RawMessage) (*tracing.LiveLogger, error) { t := &noop{} - return &tracing.LiveLogger{ - CaptureTxStart: t.CaptureTxStart, - CaptureTxEnd: t.CaptureTxEnd, - CaptureStart: t.CaptureStart, - CaptureEnd: t.CaptureEnd, - CaptureEnter: t.CaptureEnter, - CaptureExit: t.CaptureExit, - CaptureState: t.CaptureState, - CaptureFault: t.CaptureFault, - CaptureKeccakPreimage: t.CaptureKeccakPreimage, - OnGasChange: t.OnGasChange, - OnBlockchainInit: t.OnBlockchainInit, - OnBlockStart: t.OnBlockStart, - OnBlockEnd: t.OnBlockEnd, - OnSkippedBlock: t.OnSkippedBlock, - OnGenesisBlock: t.OnGenesisBlock, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, + return &tracing.Hooks{ + OnTxStart: t.CaptureTxStart, + OnTxEnd: t.CaptureTxEnd, + OnStart: t.CaptureStart, + OnEnd: t.CaptureEnd, + OnEnter: t.CaptureEnter, + OnExit: t.CaptureExit, + OnOpcode: t.CaptureState, + OnFault: t.CaptureFault, + OnKeccakPreimage: t.CaptureKeccakPreimage, + OnGasChange: t.OnGasChange, + OnBlockchainInit: t.OnBlockchainInit, + OnBlockStart: t.OnBlockStart, + OnBlockEnd: t.OnBlockEnd, + OnSkippedBlock: t.OnSkippedBlock, + OnGenesisBlock: t.OnGenesisBlock, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, }, nil } diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 7e76080a9487..92df195fe01a 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -135,7 +135,7 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi func (a *AccessListTracer) Hooks() *tracing.Hooks { return &tracing.Hooks{ - CaptureState: a.CaptureState, + OnOpcode: a.CaptureState, } } diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 3867749d46a1..6e694368b143 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -135,10 +135,10 @@ func NewStructLogger(cfg *Config) *StructLogger { func (l *StructLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ - CaptureTxStart: l.CaptureTxStart, - CaptureTxEnd: l.CaptureTxEnd, - CaptureEnd: l.CaptureEnd, - CaptureState: l.CaptureState, + OnTxStart: l.CaptureTxStart, + OnTxEnd: l.CaptureTxEnd, + OnEnd: l.CaptureEnd, + OnOpcode: l.CaptureState, } } @@ -354,11 +354,11 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { func (t *mdLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ - CaptureTxStart: t.CaptureTxStart, - CaptureStart: t.CaptureStart, - CaptureState: t.CaptureState, - CaptureFault: t.CaptureFault, - CaptureEnd: t.CaptureEnd, + OnTxStart: t.CaptureTxStart, + OnStart: t.CaptureStart, + OnOpcode: t.CaptureState, + OnFault: t.CaptureFault, + OnEnd: t.CaptureEnd, } } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 72eda302d8e6..5067fb8a584d 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -47,10 +47,10 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { func (l *JSONLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ - CaptureTxStart: l.CaptureTxStart, - CaptureEnd: l.CaptureEnd, - CaptureState: l.CaptureState, - CaptureFault: l.CaptureFault, + OnTxStart: l.CaptureTxStart, + OnEnd: l.CaptureEnd, + OnOpcode: l.CaptureState, + OnFault: l.CaptureFault, } } diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 8f83f068b2e8..a0a4a6ff4f71 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -62,10 +62,10 @@ func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (*directory.Tr ids: make(map[string]int), } return &directory.Tracer{ - LiveLogger: &tracing.LiveLogger{ - CaptureTxStart: t.CaptureTxStart, - CaptureStart: t.CaptureStart, - CaptureEnter: t.CaptureEnter, + Hooks: &tracing.Hooks{ + OnTxStart: t.CaptureTxStart, + OnStart: t.CaptureStart, + OnEnter: t.CaptureEnter, }, GetResult: t.GetResult, Stop: t.Stop, diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 0b6790ce8993..d42f36666be3 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -127,6 +127,7 @@ func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trac return nil, err } return &directory.Tracer{ +<<<<<<< HEAD LiveLogger: &tracing.LiveLogger{ CaptureTxStart: t.CaptureTxStart, CaptureTxEnd: t.CaptureTxEnd, @@ -136,6 +137,17 @@ func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trac CaptureExit: t.CaptureExit, CaptureState: t.CaptureState, OnLog: t.OnLog, +======= + Hooks: &tracing.Hooks{ + OnTxStart: t.CaptureTxStart, + OnTxEnd: t.CaptureTxEnd, + OnStart: t.CaptureStart, + OnEnd: t.CaptureEnd, + OnEnter: t.CaptureEnter, + OnExit: t.CaptureExit, + OnOpcode: t.CaptureState, + OnLog: t.OnLog, +>>>>>>> 923c180058 (rename Capture hooks to On) }, GetResult: t.GetResult, Stop: t.Stop, diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 9eef26ca5e69..c0ec5c312e0b 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -141,15 +141,15 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. ft := &flatCallTracer{tracer: t, ctx: ctx, config: config} return &directory.Tracer{ - LiveLogger: &tracing.LiveLogger{ - CaptureTxStart: ft.CaptureTxStart, - CaptureTxEnd: ft.CaptureTxEnd, - CaptureStart: ft.CaptureStart, - CaptureEnd: ft.CaptureEnd, - CaptureEnter: ft.CaptureEnter, - CaptureExit: ft.CaptureExit, - CaptureState: ft.CaptureState, - CaptureFault: ft.CaptureFault, + Hooks: &tracing.Hooks{ + OnTxStart: ft.CaptureTxStart, + OnTxEnd: ft.CaptureTxEnd, + OnStart: ft.CaptureStart, + OnEnd: ft.CaptureEnd, + OnEnter: ft.CaptureEnter, + OnExit: ft.CaptureExit, + OnOpcode: ft.CaptureState, + OnFault: ft.CaptureFault, }, Stop: ft.Stop, GetResult: ft.GetResult, diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 68b600b923c4..4516ab1dffef 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -58,22 +58,22 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace t := &muxTracer{names: names, tracers: objects} return &directory.Tracer{ - LiveLogger: &tracing.LiveLogger{ - CaptureTxStart: t.CaptureTxStart, - CaptureTxEnd: t.CaptureTxEnd, - CaptureStart: t.CaptureStart, - CaptureEnd: t.CaptureEnd, - CaptureEnter: t.CaptureEnter, - CaptureExit: t.CaptureExit, - CaptureState: t.CaptureState, - CaptureFault: t.CaptureFault, - CaptureKeccakPreimage: t.CaptureKeccakPreimage, - OnGasChange: t.OnGasChange, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, + Hooks: &tracing.Hooks{ + OnTxStart: t.CaptureTxStart, + OnTxEnd: t.CaptureTxEnd, + OnStart: t.CaptureStart, + OnEnd: t.CaptureEnd, + OnEnter: t.CaptureEnter, + OnExit: t.CaptureExit, + OnOpcode: t.CaptureState, + OnFault: t.CaptureFault, + OnKeccakPreimage: t.CaptureKeccakPreimage, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, }, GetResult: t.GetResult, Stop: t.Stop, @@ -83,8 +83,8 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { - if t.CaptureStart != nil { - t.CaptureStart(from, to, create, input, gas, value) + if t.OnStart != nil { + t.OnStart(from, to, create, input, gas, value) } } } @@ -92,8 +92,8 @@ func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create // CaptureEnd is called after the call finishes to finalize the tracing. func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { for _, t := range t.tracers { - if t.CaptureEnd != nil { - t.CaptureEnd(output, gasUsed, err, reverted) + if t.OnEnd != nil { + t.OnEnd(output, gasUsed, err, reverted) } } } @@ -101,8 +101,8 @@ func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverte // CaptureState implements the EVMLogger interface to trace a single step of VM execution. func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { for _, t := range t.tracers { - if t.CaptureState != nil { - t.CaptureState(pc, op, gas, cost, scope, rData, depth, err) + if t.OnOpcode != nil { + t.OnOpcode(pc, op, gas, cost, scope, rData, depth, err) } } } @@ -110,8 +110,8 @@ func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, // CaptureFault implements the EVMLogger interface to trace an execution fault. func (t *muxTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { for _, t := range t.tracers { - if t.CaptureFault != nil { - t.CaptureFault(pc, op, gas, cost, scope, depth, err) + if t.OnFault != nil { + t.OnFault(pc, op, gas, cost, scope, depth, err) } } } @@ -119,8 +119,8 @@ func (t *muxTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { for _, t := range t.tracers { - if t.CaptureKeccakPreimage != nil { - t.CaptureKeccakPreimage(hash, data) + if t.OnKeccakPreimage != nil { + t.OnKeccakPreimage(hash, data) } } } @@ -137,8 +137,8 @@ func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *muxTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { - if t.CaptureEnter != nil { - t.CaptureEnter(typ, from, to, input, gas, value) + if t.OnEnter != nil { + t.OnEnter(typ, from, to, input, gas, value) } } } @@ -147,24 +147,24 @@ func (t *muxTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to com // execute any code. func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { for _, t := range t.tracers { - if t.CaptureExit != nil { - t.CaptureExit(output, gasUsed, err, reverted) + if t.OnExit != nil { + t.OnExit(output, gasUsed, err, reverted) } } } func (t *muxTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { for _, t := range t.tracers { - if t.CaptureTxStart != nil { - t.CaptureTxStart(env, tx, from) + if t.OnTxStart != nil { + t.OnTxStart(env, tx, from) } } } func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) { for _, t := range t.tracers { - if t.CaptureTxEnd != nil { - t.CaptureTxEnd(receipt, err) + if t.OnTxEnd != nil { + t.OnTxEnd(receipt, err) } } } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 765356f10f7b..5e053236a975 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -89,10 +89,10 @@ func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. deleted: make(map[common.Address]bool), } return &directory.Tracer{ - LiveLogger: &tracing.LiveLogger{ - CaptureTxStart: t.CaptureTxStart, - CaptureTxEnd: t.CaptureTxEnd, - CaptureState: t.CaptureState, + Hooks: &tracing.Hooks{ + OnTxStart: t.CaptureTxStart, + OnTxEnd: t.CaptureTxEnd, + OnOpcode: t.CaptureState, }, GetResult: t.GetResult, Stop: t.Stop, From 03e48ee4512a0c8a8551b2ac36f95d5e2d7a4944 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 6 Mar 2024 16:34:18 +0100 Subject: [PATCH 32/50] rename tracer methods to On pattern # Conflicts # core/vm/interpreter.go # eth/tracers/directory/noop.go # eth/tracers/js/goja.go # eth/tracers/live/noop.go # eth/tracers/logger/logger.go # eth/tracers/logger/logger_json.go # eth/tracers/native/4byte.go # eth/tracers/native/call.go # eth/tracers/native/call_flat.go # eth/tracers/native/mux.go --- cmd/evm/blockrunner.go | 2 +- cmd/evm/internal/t8ntool/execution.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 2 +- cmd/evm/runner.go | 2 +- core/blockchain.go | 2 +- core/state/statedb.go | 4 +- core/tracing/hooks.go | 3 +- core/vm/contract.go | 2 +- core/vm/contracts.go | 2 +- core/vm/interpreter.go | 6 +- eth/ethconfig/config.go | 2 +- eth/tracers/api.go | 4 +- eth/tracers/directory/live/dir.go | 4 +- eth/tracers/directory/noop.go | 45 +++++---------- eth/tracers/directory/tracers.go | 2 +- .../internal/tracetest/calltrace_test.go | 8 +-- .../internal/tracetest/flat_calltrace_test.go | 2 +- .../internal/tracetest/prestate_test.go | 2 +- eth/tracers/js/goja.go | 54 ++++++++++-------- eth/tracers/js/tracer_test.go | 2 +- eth/tracers/live/noop.go | 46 +++++---------- eth/tracers/logger/access_list_tracer.go | 6 +- eth/tracers/logger/logger.go | 56 ++++++++++++------- eth/tracers/logger/logger_json.go | 23 ++++---- eth/tracers/logger/logger_test.go | 3 +- eth/tracers/native/4byte.go | 21 ++----- eth/tracers/native/call.go | 53 +++++++++--------- eth/tracers/native/call_flat.go | 54 +++++------------- eth/tracers/native/mux.go | 55 +++++------------- eth/tracers/native/prestate.go | 14 ++--- tests/block_test_util.go | 2 +- 31 files changed, 205 insertions(+), 280 deletions(-) diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 1f7d07f31a83..07af0bc9a24a 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -50,7 +50,7 @@ func blockTestCmd(ctx *cli.Context) error { return errors.New("path-to-test argument required") } - var tracer *tracing.LiveLogger + var tracer *tracing.Hooks // Configure the EVM logger if ctx.Bool(MachineFlag.Name) { tracer = logger.NewJSONLogger(&logger.Config{ diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 4e72d11b5bbf..52e5bbe9de92 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -229,7 +229,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, err } if vmConfig.Tracer != nil { - vmConfig.Tracer = tracer.LiveLogger + vmConfig.Tracer = tracer.Hooks } statedb.SetTxContext(tx.Hash(), txIndex) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 97a11015442d..bb28620b0160 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -126,7 +126,7 @@ func Transition(ctx *cli.Context) error { } logger := logger.NewJSONLogger(logConfig, traceFile).Hooks() tracer := &directory.Tracer{ - LiveLogger: logger, + Hooks: logger, // JSONLogger streams out result to file. GetResult: func() (json.RawMessage, error) { return nil, nil }, Stop: func(err error) {}, diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 47ae7070639a..3d481d5fe70c 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -117,7 +117,7 @@ func runCmd(ctx *cli.Context) error { } var ( - tracer *tracing.LiveLogger + tracer *tracing.Hooks debugLogger *logger.StructLogger statedb *state.StateDB chainConfig *params.ChainConfig diff --git a/core/blockchain.go b/core/blockchain.go index b3f72bd21359..cc88434e8f69 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -266,7 +266,7 @@ type BlockChain struct { processor Processor // Block transaction processor interface forker *ForkChoice vmConfig vm.Config - logger *tracing.LiveLogger + logger *tracing.Hooks } // NewBlockChain returns a fully initialised block chain using information diff --git a/core/state/statedb.go b/core/state/statedb.go index 0047a9c8e1bf..054f02270868 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -64,7 +64,7 @@ type StateDB struct { prefetcher *triePrefetcher trie Trie hasher crypto.KeccakState - logger *tracing.LiveLogger + logger *tracing.Hooks snaps *snapshot.Tree // Nil if snapshot is not available snap snapshot.Snapshot // Nil if snapshot is not available @@ -175,7 +175,7 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) } // SetLogger sets the logger for account update hooks. -func (s *StateDB) SetLogger(l *tracing.LiveLogger) { +func (s *StateDB) SetLogger(l *tracing.Hooks) { s.logger = l } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index f912a6c87db1..872d4af2dd27 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" @@ -141,7 +142,7 @@ type ( SkippedBlockHook = func(event BlockEvent) // GenesisBlockHook is called when the genesis block is being processed. - GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc) + GenesisBlockHook = func(genesis *types.Block, alloc core.GenesisAlloc) /* - State events - diff --git a/core/vm/contract.go b/core/vm/contract.go index 291a426b3b23..860d0f246754 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -160,7 +160,7 @@ func (c *Contract) Caller() common.Address { } // UseGas attempts the use gas and subtracts it and returns true on success -func (c *Contract) UseGas(gas uint64, logger *tracing.LiveLogger, reason tracing.GasChangeReason) (ok bool) { +func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) { if c.Gas < gas { return false } diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 6b2512570a1b..821f33327a78 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -169,7 +169,7 @@ func ActivePrecompiles(rules params.Rules) []common.Address { // - the returned bytes, // - the _remaining_ gas, // - any error that occurred -func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.LiveLogger) (ret []byte, remainingGas uint64, err error) { +func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { gasCost := p.RequiredGas(input) if suppliedGas < gasCost { return nil, 0, ErrOutOfGas diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 4d867fbc05f2..bee43ac1cdf5 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -27,7 +27,7 @@ import ( // Config are the configuration options for the Interpreter type Config struct { - Tracer *tracing.LiveLogger + Tracer *tracing.Hooks NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages ExtraEips []int // Additional EIPS that are to be enabled @@ -178,8 +178,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( res []byte // result of the opcode execution function debug = in.evm.Config.Tracer != nil ) - // Don't move this deferred function, it's placed before the capturestate-deferred method, - // so that it get's executed _after_: the capturestate needs the stacks before + // Don't move this deferred function, it's placed before the OnOpcode-deferred method, + // so that it gets executed _after_: the OnOpcode needs the stacks before // they are returned to the pools defer func() { returnStack(stack) diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 7abac3bec57f..33ee536f8841 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -153,7 +153,7 @@ type Config struct { EnablePreimageRecording bool // Enables VM tracing - VMTracer *tracing.LiveLogger + VMTracer *tracing.Hooks // Miscellaneous options DocRoot string `toml:"-"` diff --git a/eth/tracers/api.go b/eth/tracers/api.go index d0eefa84b134..447ba3119c3e 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -937,8 +937,8 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *direc return nil, err } } - vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.LiveLogger, NoBaseFee: true}) - statedb.SetLogger(tracer.LiveLogger) + vmenv := vm.NewEVM(vmctx, vm.TxContext{GasPrice: big.NewInt(0)}, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) + statedb.SetLogger(tracer.Hooks) // Define a meaningful timeout of a single transaction trace if config.Timeout != nil { diff --git a/eth/tracers/directory/live/dir.go b/eth/tracers/directory/live/dir.go index a097754310f6..cedf81c35ee2 100644 --- a/eth/tracers/directory/live/dir.go +++ b/eth/tracers/directory/live/dir.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" ) -type ctorFunc func(config json.RawMessage) (*tracing.LiveLogger, error) +type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error) // Directory is the collection of tracers which can be used // during normal block import operations. @@ -23,7 +23,7 @@ func (d *directory) Register(name string, f ctorFunc) { } // New instantiates a tracer by name. -func (d *directory) New(name string, config json.RawMessage) (*tracing.LiveLogger, error) { +func (d *directory) New(name string, config json.RawMessage) (*tracing.Hooks, error) { if f, ok := d.elems[name]; ok { return f(config) } diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index 637f0da326d2..551c6635e1ed 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -38,15 +38,13 @@ func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) { t := &NoopTracer{} return &Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnTxEnd: t.CaptureTxEnd, - OnStart: t.CaptureStart, - OnEnd: t.CaptureEnd, - OnEnter: t.CaptureEnter, - OnExit: t.CaptureExit, - OnOpcode: t.CaptureState, - OnFault: t.CaptureFault, - OnKeccakPreimage: t.CaptureKeccakPreimage, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnKeccakPreimage: t.OnKeccakPreimage, OnGasChange: t.OnGasChange, OnBalanceChange: t.OnBalanceChange, OnNonceChange: t.OnNonceChange, @@ -59,41 +57,26 @@ func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) { }, nil } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *NoopTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *NoopTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } -// CaptureEnd is called after the call finishes to finalize the tracing. -func (t *NoopTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { +func (t *NoopTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } -// CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *NoopTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { -} - -// CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *NoopTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { -} - -// CaptureKeccakPreimage is called during the KECCAK256 opcode. -func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} +func (t *NoopTracer) OnKeccakPreimage(hash common.Hash, data []byte) {} -// OnGasChange is called when gas is either consumed or refunded. func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {} -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *NoopTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *NoopTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } -// CaptureExit is called when EVM exits a scope, even if the scope didn't -// execute any code. -func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { +func (t *NoopTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { } -func (*NoopTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (*NoopTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { } -func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} +func (*NoopTracer) OnTxEnd(receipt *types.Receipt, err error) {} func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { } diff --git a/eth/tracers/directory/tracers.go b/eth/tracers/directory/tracers.go index 78dc9ae6e7b1..766278974cfc 100644 --- a/eth/tracers/directory/tracers.go +++ b/eth/tracers/directory/tracers.go @@ -41,7 +41,7 @@ type Context struct { // This involves a method to retrieve results and one to // stop tracing. type Tracer struct { - *tracing.LiveLogger + *tracing.Hooks GetResult func() (json.RawMessage, error) // Stop terminates execution of the tracer at the first opportune moment. Stop func(err error) diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 01c5ae1fdba1..8ca877aea53a 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -144,7 +144,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } - statedb.SetLogger(tracer.LiveLogger) + statedb.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -251,7 +251,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if err != nil { b.Fatalf("failed to create call tracer: %v", err) } - evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer.LiveLogger}) + evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, err = st.TransitionDb(); err != nil { @@ -379,7 +379,7 @@ func TestInternals(t *testing.T) { }, }, false, rawdb.HashScheme) defer triedb.Close() - statedb.SetLogger(tc.tracer.LiveLogger) + statedb.SetLogger(tc.tracer.Hooks) tx, err := types.SignNewTx(key, signer, &types.LegacyTx{ To: &to, Value: big.NewInt(0), @@ -393,7 +393,7 @@ func TestInternals(t *testing.T) { Origin: origin, GasPrice: tx.GasPrice(), } - evm := vm.NewEVM(context, txContext, statedb, config, vm.Config{Tracer: tc.tracer.LiveLogger}) + evm := vm.NewEVM(context, txContext, statedb, config, vm.Config{Tracer: tc.tracer.Hooks}) msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0)) if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 8cf9eae541d9..afffb80bf8c1 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -102,7 +102,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to create call tracer: %v", err) } - statedb.SetLogger(tracer.LiveLogger) + statedb.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index a055ba334d7a..48a95f43c138 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -112,7 +112,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } - statedb.SetLogger(tracer.LiveLogger) + statedb.SetLogger(tracer.Hooks) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 3d96253b7132..4bac8edfebd4 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -214,23 +214,21 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir return &directory.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnTxEnd: t.CaptureTxEnd, - OnStart: t.CaptureStart, - OnEnd: t.CaptureEnd, - OnEnter: t.CaptureEnter, - OnExit: t.CaptureExit, - OnOpcode: t.CaptureState, - OnFault: t.CaptureFault, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, }, GetResult: t.GetResult, Stop: t.Stop, }, nil } -// CaptureTxStart implements the Tracer interface and is invoked at the beginning of +// OnTxStart implements the Tracer interface and is invoked at the beginning of // transaction processing. -func (t *jsTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.env = env // Need statedb access for db object db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} @@ -249,9 +247,9 @@ func (t *jsTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, t.ctx["gasPrice"] = gasPriceBig } -// CaptureTxEnd implements the Tracer interface and is invoked at the end of +// OnTxEnd implements the Tracer interface and is invoked at the end of // transaction processing. -func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) { +func (t *jsTracer) OnTxEnd(receipt *types.Receipt, err error) { if err != nil { // Don't override vm error if _, ok := t.ctx["error"]; !ok { @@ -262,8 +260,8 @@ func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) { t.ctx["gasUsed"] = t.vm.ToValue(receipt.GasUsed) } -// CaptureStart implements the Tracer interface to initialize the tracing operation. -func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +// onStart implements the Tracer interface to initialize the tracing operation. +func (t *jsTracer) onStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { cancel := func(err error) { t.err = err t.env.VM.Cancel() @@ -299,8 +297,8 @@ func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create b t.ctx["value"] = valueBig } -// CaptureState implements the Tracer interface to trace a single step of VM execution. -func (t *jsTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +// OnOpcode implements the Tracer interface to trace a single step of VM execution. +func (t *jsTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { if !t.traceStep { return } @@ -324,8 +322,8 @@ func (t *jsTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, } } -// CaptureFault implements the Tracer interface to trace an execution fault -func (t *jsTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { +// OnFault implements the Tracer interface to trace an execution fault +func (t *jsTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { if t.err != nil { return } @@ -336,15 +334,19 @@ func (t *jsTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, } } -// CaptureEnd is called after the call finishes to finalize the tracing. -func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { +// onEnd is called after the call finishes to finalize the tracing. +func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool) { if err != nil { t.ctx["error"] = t.vm.ToValue(err.Error()) } } -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *jsTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *jsTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if depth == 0 { + t.onStart(from, to, vm.OpCode(typ) == vm.CREATE, input, gas, value) + return + } if !t.traceFrame { return } @@ -367,9 +369,13 @@ func (t *jsTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to comm } } -// CaptureExit is called when EVM exits a scope, even if the scope didn't +// OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { +func (t *jsTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if depth == 0 { + t.onEnd(output, gasUsed, err, reverted) + return + } if !t.traceFrame { return } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index c1546ca05511..5893c6844f33 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -64,7 +64,7 @@ func testCtx() *vmContext { func runTrace(tracer *directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { var ( - env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.LiveLogger}) + env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer.Hooks}) gasLimit uint64 = 31000 startGas uint64 = 10000 value = big.NewInt(0) diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index 05560cfb24f0..afb52a9448e9 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -21,18 +21,16 @@ func init() { // as soon as we have a real live tracer. type noop struct{} -func newNoopTracer(_ json.RawMessage) (*tracing.LiveLogger, error) { +func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) { t := &noop{} return &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnTxEnd: t.CaptureTxEnd, - OnStart: t.CaptureStart, - OnEnd: t.CaptureEnd, - OnEnter: t.CaptureEnter, - OnExit: t.CaptureExit, - OnOpcode: t.CaptureState, - OnFault: t.CaptureFault, - OnKeccakPreimage: t.CaptureKeccakPreimage, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnKeccakPreimage: t.OnKeccakPreimage, OnGasChange: t.OnGasChange, OnBlockchainInit: t.OnBlockchainInit, OnBlockStart: t.OnBlockStart, @@ -47,38 +45,24 @@ func newNoopTracer(_ json.RawMessage) (*tracing.LiveLogger, error) { }, nil } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *noop) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *noop) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } -// CaptureEnd is called after the call finishes to finalize the tracing. -func (t *noop) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { +func (t *noop) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } -// CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *noop) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { -} - -// CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *noop) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { -} - -// CaptureKeccakPreimage is called during the KECCAK256 opcode. -func (t *noop) CaptureKeccakPreimage(hash common.Hash, data []byte) {} +func (t *noop) OnKeccakPreimage(hash common.Hash, data []byte) {} -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *noop) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *noop) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } -// CaptureExit is called when EVM exits a scope, even if the scope didn't -// execute any code. -func (t *noop) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { +func (t *noop) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { } -func (t *noop) CaptureTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (t *noop) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { } -func (t *noop) CaptureTxEnd(receipt *types.Receipt, err error) { +func (t *noop) OnTxEnd(receipt *types.Receipt, err error) { } func (t *noop) OnBlockStart(ev tracing.BlockEvent) { diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 92df195fe01a..4ac49fd91fef 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -135,12 +135,12 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi func (a *AccessListTracer) Hooks() *tracing.Hooks { return &tracing.Hooks{ - OnOpcode: a.CaptureState, + OnOpcode: a.OnOpcode, } } -// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. -func (a *AccessListTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +// OnOpcode captures all opcodes that touch storage or addresses and adds them to the accesslist. +func (a *AccessListTracer) OnOpcode(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { stackData := scope.StackData() stackLen := len(stackData) op := vm.OpCode(opcode) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 6e694368b143..c0b66d8c2704 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -135,10 +135,10 @@ func NewStructLogger(cfg *Config) *StructLogger { func (l *StructLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ - OnTxStart: l.CaptureTxStart, - OnTxEnd: l.CaptureTxEnd, - OnEnd: l.CaptureEnd, - OnOpcode: l.CaptureState, + OnTxStart: l.OnTxStart, + OnTxEnd: l.OnTxEnd, + OnExit: l.OnExit, + OnOpcode: l.OnOpcode, } } @@ -158,10 +158,10 @@ func (l *StructLogger) Reset() { l.err = nil } -// CaptureState logs a new structured log message and pushes it out to the environment +// OnOpcode logs a new structured log message and pushes it out to the environment // -// CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +// OnOpcode also tracks SLOAD/SSTORE ops to track storage change. +func (l *StructLogger) OnOpcode(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { // If tracing was interrupted, set the error and stop if l.interrupt.Load() { return @@ -226,8 +226,11 @@ func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost l.logs = append(l.logs, log) } -// CaptureEnd is called after the call finishes to finalize the tracing. -func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { +// OnExit is called a call frame finishes processing. +func (l *StructLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if depth != 0 { + return + } l.output = output l.err = err if l.cfg.Debug { @@ -264,11 +267,11 @@ func (l *StructLogger) Stop(err error) { l.interrupt.Store(true) } -func (l *StructLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (l *StructLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { l.env = env } -func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) { +func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) { if err != nil { // Don't override vm error if l.err == nil { @@ -354,19 +357,23 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger { func (t *mdLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnStart: t.CaptureStart, - OnOpcode: t.CaptureState, - OnFault: t.CaptureFault, - OnEnd: t.CaptureEnd, + OnTxStart: t.OnTxStart, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, } } -func (t *mdLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (t *mdLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.env = env } -func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *mdLogger) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if depth != 0 { + return + } + create := vm.OpCode(typ) == vm.CREATE if !create { fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", from.String(), to.String(), @@ -383,8 +390,15 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b `) } -// CaptureState also tracks SLOAD/SSTORE ops to track storage change. -func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *mdLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if depth == 0 { + fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", + output, gasUsed, err) + } +} + +// OnOpcode also tracks SLOAD/SSTORE ops to track storage change. +func (t *mdLogger) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { stack := scope.StackData() fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) @@ -404,7 +418,7 @@ func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, } } -func (t *mdLogger) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { +func (t *mdLogger) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 5067fb8a584d..6744ac729c6f 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -47,20 +47,19 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { func (l *JSONLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ - OnTxStart: l.CaptureTxStart, - OnEnd: l.CaptureEnd, - OnOpcode: l.CaptureState, - OnFault: l.CaptureFault, + OnTxStart: l.OnTxStart, + OnExit: l.OnExit, + OnOpcode: l.OnOpcode, + OnFault: l.OnFault, } } -func (l *JSONLogger) CaptureFault(pc uint64, op tracing.OpCode, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { +func (l *JSONLogger) OnFault(pc uint64, op tracing.OpCode, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { // TODO: Add rData to this interface as well - l.CaptureState(pc, op, gas, cost, scope, nil, depth, err) + l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err) } -// CaptureState outputs state information on the logger. -func (l *JSONLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (l *JSONLogger) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { memory := scope.MemoryData() stack := scope.StackData() @@ -86,8 +85,10 @@ func (l *JSONLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64 l.encoder.Encode(log) } -// CaptureEnd is triggered at end of execution. -func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { +func (l *JSONLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if depth > 0 { + return + } type endLog struct { Output string `json:"output"` GasUsed math.HexOrDecimal64 `json:"gasUsed"` @@ -100,6 +101,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, revert l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg}) } -func (l *JSONLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (l *JSONLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { l.env = env } diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index c2982ab0361f..d3bfc13a03d0 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -60,8 +60,7 @@ func TestStoreCapture(t *testing.T) { ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash - logger.CaptureTxStart(env.GetVMContext(), nil, common.Address{}) - logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil) + logger.OnTxStart(env.GetVMContext(), nil, common.Address{}) _, err := env.Interpreter().Run(contract, []byte{}, false) if err != nil { t.Fatal(err) diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index a0a4a6ff4f71..7add963e16f4 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -52,7 +52,7 @@ type fourByteTracer struct { ids map[string]int // ids aggregates the 4byte ids found interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption - activePrecompiles []common.Address // Updated on CaptureStart based on given rules + activePrecompiles []common.Address // Updated on tx start based on given rules } // newFourByteTracer returns a native go tracer which collects @@ -63,9 +63,8 @@ func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (*directory.Tr } return &directory.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnStart: t.CaptureStart, - OnEnter: t.CaptureEnter, + OnTxStart: t.OnTxStart, + OnEnter: t.OnEnter, }, GetResult: t.GetResult, Stop: t.Stop, @@ -88,22 +87,14 @@ func (t *fourByteTracer) store(id []byte, size int) { t.ids[key] += 1 } -func (t *fourByteTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (t *fourByteTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { // Update list of precompiles based on current block rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time) t.activePrecompiles = vm.ActivePrecompiles(rules) } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *fourByteTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - // Save the outer calldata - if len(input) >= 4 { - t.store(input[0:4], len(input)-4) - } -} - -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *fourByteTracer) CaptureEnter(opcode tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *fourByteTracer) OnEnter(depth int, opcode tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { // Skip if tracing was interrupted if t.interrupt.Load() { return diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index d42f36666be3..07e86ab1f0c8 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -127,27 +127,12 @@ func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trac return nil, err } return &directory.Tracer{ -<<<<<<< HEAD - LiveLogger: &tracing.LiveLogger{ - CaptureTxStart: t.CaptureTxStart, - CaptureTxEnd: t.CaptureTxEnd, - CaptureStart: t.CaptureStart, - CaptureEnd: t.CaptureEnd, - CaptureEnter: t.CaptureEnter, - CaptureExit: t.CaptureExit, - CaptureState: t.CaptureState, - OnLog: t.OnLog, -======= Hooks: &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnTxEnd: t.CaptureTxEnd, - OnStart: t.CaptureStart, - OnEnd: t.CaptureEnd, - OnEnter: t.CaptureEnter, - OnExit: t.CaptureExit, - OnOpcode: t.CaptureState, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, OnLog: t.OnLog, ->>>>>>> 923c180058 (rename Capture hooks to On) }, GetResult: t.GetResult, Stop: t.Stop, @@ -191,10 +176,10 @@ func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, revert func (t *callTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *callTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { - t.depth++ - if t.config.OnlyTopCall { +// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *callTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + t.depth = depth + if t.config.OnlyTopCall && depth > 0 { return } // Skip if tracing was interrupted @@ -214,10 +199,15 @@ func (t *callTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to co t.callstack = append(t.callstack, call) } -// CaptureExit is called when EVM exits a scope, even if the scope didn't +// OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { - t.depth-- +func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if depth == 0 { + t.captureEnd(output, gasUsed, err, reverted) + return + } + + t.depth = depth - 1 if t.config.OnlyTopCall { return } @@ -235,11 +225,18 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, rever t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) } -func (t *callTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (t *callTracer) captureEnd(output []byte, gasUsed uint64, err error, reverted bool) { + if len(t.callstack) != 1 { + return + } + t.callstack[0].processOutput(output, err, reverted) +} + +func (t *callTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.gasLimit = tx.Gas() } -func (t *callTracer) CaptureTxEnd(receipt *types.Receipt, err error) { +func (t *callTracer) OnTxEnd(receipt *types.Receipt, err error) { // Error happened during tx validation. if err != nil { return diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index c0ec5c312e0b..d3212787325c 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -115,7 +115,7 @@ type flatCallTracer struct { config flatCallTracerConfig ctx *directory.Context // Holds tracer context data reason error // Textual reason for the interruption - activePrecompiles []common.Address // Updated on CaptureStart based on given rules + activePrecompiles []common.Address // Updated on tx start based on given rules } type flatCallTracerConfig struct { @@ -142,43 +142,19 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. ft := &flatCallTracer{tracer: t, ctx: ctx, config: config} return &directory.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: ft.CaptureTxStart, - OnTxEnd: ft.CaptureTxEnd, - OnStart: ft.CaptureStart, - OnEnd: ft.CaptureEnd, - OnEnter: ft.CaptureEnter, - OnExit: ft.CaptureExit, - OnOpcode: ft.CaptureState, - OnFault: ft.CaptureFault, + OnTxStart: ft.OnTxStart, + OnTxEnd: ft.OnTxEnd, + OnEnter: ft.OnEnter, + OnExit: ft.OnExit, }, Stop: ft.Stop, GetResult: ft.GetResult, }, nil } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *flatCallTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - t.tracer.CaptureStart(from, to, create, input, gas, value) -} - -// CaptureEnd is called after the call finishes to finalize the tracing. -func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { - t.tracer.CaptureEnd(output, gasUsed, err, reverted) -} - -// CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *flatCallTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { - t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err) -} - -// CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *flatCallTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { - t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err) -} - -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *flatCallTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { - t.tracer.CaptureEnter(typ, from, to, input, gas, value) +// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). +func (t *flatCallTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + t.tracer.OnEnter(depth, typ, from, to, input, gas, value) // Child calls must have a value, even if it's zero. // Practically speaking, only STATICCALL has nil value. Set it to zero. @@ -187,10 +163,10 @@ func (t *flatCallTracer) CaptureEnter(typ tracing.OpCode, from common.Address, t } } -// CaptureExit is called when EVM exits a scope, even if the scope didn't +// OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { - t.tracer.CaptureExit(output, gasUsed, err, reverted) +func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + t.tracer.OnExit(depth, output, gasUsed, err, reverted) // Parity traces don't include CALL/STATICCALLs to precompiles. // By default we remove them from the callstack. @@ -211,15 +187,15 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, r } } -func (t *flatCallTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { - t.tracer.CaptureTxStart(env, tx, from) +func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { + 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) t.activePrecompiles = vm.ActivePrecompiles(rules) } -func (t *flatCallTracer) CaptureTxEnd(receipt *types.Receipt, err error) { - t.tracer.CaptureTxEnd(receipt, err) +func (t *flatCallTracer) OnTxEnd(receipt *types.Receipt, err error) { + t.tracer.OnTxEnd(receipt, err) } // GetResult returns an empty json object. diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 4516ab1dffef..a54b5611affa 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -59,15 +59,13 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace t := &muxTracer{names: names, tracers: objects} return &directory.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnTxEnd: t.CaptureTxEnd, - OnStart: t.CaptureStart, - OnEnd: t.CaptureEnd, - OnEnter: t.CaptureEnter, - OnExit: t.CaptureExit, - OnOpcode: t.CaptureState, - OnFault: t.CaptureFault, - OnKeccakPreimage: t.CaptureKeccakPreimage, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnKeccakPreimage: t.OnKeccakPreimage, OnGasChange: t.OnGasChange, OnBalanceChange: t.OnBalanceChange, OnNonceChange: t.OnNonceChange, @@ -80,26 +78,7 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace }, nil } -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - for _, t := range t.tracers { - if t.OnStart != nil { - t.OnStart(from, to, create, input, gas, value) - } - } -} - -// CaptureEnd is called after the call finishes to finalize the tracing. -func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { - for _, t := range t.tracers { - if t.OnEnd != nil { - t.OnEnd(output, gasUsed, err, reverted) - } - } -} - -// CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *muxTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { for _, t := range t.tracers { if t.OnOpcode != nil { t.OnOpcode(pc, op, gas, cost, scope, rData, depth, err) @@ -107,8 +86,7 @@ func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, } } -// CaptureFault implements the EVMLogger interface to trace an execution fault. -func (t *muxTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { +func (t *muxTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { for _, t := range t.tracers { if t.OnFault != nil { t.OnFault(pc, op, gas, cost, scope, depth, err) @@ -116,8 +94,7 @@ func (t *muxTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, } } -// CaptureKeccakPreimage is called during the KECCAK256 opcode. -func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { +func (t *muxTracer) OnKeccakPreimage(hash common.Hash, data []byte) { for _, t := range t.tracers { if t.OnKeccakPreimage != nil { t.OnKeccakPreimage(hash, data) @@ -125,7 +102,6 @@ func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { } } -// CaptureGasConsumed is called when gas is consumed. func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) { for _, t := range t.tracers { if t.OnGasChange != nil { @@ -134,8 +110,7 @@ func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) } } -// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *muxTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *muxTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { if t.OnEnter != nil { t.OnEnter(typ, from, to, input, gas, value) @@ -143,9 +118,7 @@ func (t *muxTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to com } } -// CaptureExit is called when EVM exits a scope, even if the scope didn't -// execute any code. -func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { +func (t *muxTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { for _, t := range t.tracers { if t.OnExit != nil { t.OnExit(output, gasUsed, err, reverted) @@ -153,7 +126,7 @@ func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, revert } } -func (t *muxTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (t *muxTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { for _, t := range t.tracers { if t.OnTxStart != nil { t.OnTxStart(env, tx, from) @@ -161,7 +134,7 @@ func (t *muxTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction } } -func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) { +func (t *muxTracer) OnTxEnd(receipt *types.Receipt, err error) { for _, t := range t.tracers { if t.OnTxEnd != nil { t.OnTxEnd(receipt, err) diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 5e053236a975..d51780da2127 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -90,17 +90,17 @@ func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. } return &directory.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.CaptureTxStart, - OnTxEnd: t.CaptureTxEnd, - OnOpcode: t.CaptureState, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnOpcode: t.OnOpcode, }, GetResult: t.GetResult, Stop: t.Stop, }, nil } -// CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *prestateTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +// OnOpcode implements the EVMLogger interface to trace a single step of VM execution. +func (t *prestateTracer) OnOpcode(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { if err != nil { return } @@ -146,7 +146,7 @@ func (t *prestateTracer) CaptureState(pc uint64, opcode tracing.OpCode, gas, cos } } -func (t *prestateTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { t.env = env if tx.To() == nil { t.to = crypto.CreateAddress(from, env.StateDB.GetNonce(from)) @@ -160,7 +160,7 @@ func (t *prestateTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transa t.lookupAccount(env.Coinbase) } -func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) { +func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) { if err != nil { return } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 37b89afc5131..e39cbf5aae12 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -109,7 +109,7 @@ type btHeaderMarshaling struct { ExcessBlobGas *math.HexOrDecimal64 } -func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *tracing.LiveLogger) error { +func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *tracing.Hooks) error { config, ok := Forks[t.json.Network] if !ok { return UnsupportedForkError{t.json.Network} From 7190602bd20830f6a07491700a0e3fb9d2c0dc45 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 6 Mar 2024 16:44:19 +0100 Subject: [PATCH 33/50] rm embedding of noop tracer --- eth/tracers/js/goja.go | 2 -- eth/tracers/logger/access_list_tracer.go | 2 -- eth/tracers/logger/logger.go | 2 -- eth/tracers/logger/logger_json.go | 2 -- eth/tracers/native/4byte.go | 1 - eth/tracers/native/call.go | 1 - eth/tracers/native/call_flat.go | 1 - eth/tracers/native/prestate.go | 1 - 8 files changed, 12 deletions(-) diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 4bac8edfebd4..9e7c699f403b 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -98,8 +98,6 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b // jsTracer is an implementation of the Tracer interface which evaluates // JS functions on the relevant EVM hooks. It uses Goja as its JS engine. type jsTracer struct { - directory.NoopTracer - vm *goja.Runtime env *tracing.VMContext toBig toBigFn // Converts a hex string into a JS bigint diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index 4ac49fd91fef..f4206d524cbc 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -21,7 +21,6 @@ import ( "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/directory" ) // accessList is an accumulator for the set of accounts and storage slots an EVM @@ -103,7 +102,6 @@ func (al accessList) accessList() types.AccessList { // AccessListTracer is a tracer that accumulates touched accounts and storage // slots into an internal set. type AccessListTracer struct { - directory.NoopTracer excl map[common.Address]struct{} // Set of account to exclude from the list list accessList // Set of accounts and storage slots touched } diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index c0b66d8c2704..fb4a008ca2e5 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -108,7 +108,6 @@ func (s *StructLog) ErrorString() string { // a track record of modified storage which is used in reporting snapshots of the // contract their storage. type StructLogger struct { - directory.NoopTracer cfg Config env *tracing.VMContext @@ -339,7 +338,6 @@ func WriteLogs(writer io.Writer, logs []*types.Log) { } type mdLogger struct { - directory.NoopTracer out io.Writer cfg *Config env *tracing.VMContext diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 6744ac729c6f..a1c81c885656 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -25,11 +25,9 @@ import ( "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/directory" ) type JSONLogger struct { - directory.NoopTracer encoder *json.Encoder cfg *Config env *tracing.VMContext diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 7add963e16f4..e6e9ad0e51b5 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -48,7 +48,6 @@ func init() { // 0xc281d19e-0: 1 // } type fourByteTracer struct { - directory.NoopTracer ids map[string]int // ids aggregates the 4byte ids found interrupt atomic.Bool // Atomic flag to signal execution interruption reason error // Textual reason for the interruption diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 07e86ab1f0c8..72a07fc854e3 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -105,7 +105,6 @@ type callFrameMarshaling struct { } type callTracer struct { - directory.NoopTracer callstack []callFrame config callTracerConfig gasLimit uint64 diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index d3212787325c..e5e83bc2e875 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -110,7 +110,6 @@ type flatCallResultMarshaling struct { // flatCallTracer reports call frame information of a tx in a flat format, i.e. // as opposed to the nested format of `callTracer`. type flatCallTracer struct { - directory.NoopTracer tracer *callTracer config flatCallTracerConfig ctx *directory.Context // Holds tracer context data diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index d51780da2127..97b29493a7b7 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -58,7 +58,6 @@ type accountMarshaling struct { } type prestateTracer struct { - directory.NoopTracer env *tracing.VMContext pre stateMap post stateMap From 5c6fb316b76b1e48c64fba5f16a0bc8a9e584ab9 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 6 Mar 2024 17:08:00 +0100 Subject: [PATCH 34/50] rm OnKeccakPreimage # Conflicts: # core/tracing/hooks.go --- core/tracing/hooks.go | 20 +++++++------------- core/vm/instructions.go | 3 --- eth/tracers/directory/noop.go | 27 ++++++++++++--------------- eth/tracers/live/noop.go | 3 --- eth/tracers/native/mux.go | 33 ++++++++++++--------------------- 5 files changed, 31 insertions(+), 55 deletions(-) diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 872d4af2dd27..ca3d17c1a9ff 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -116,9 +116,6 @@ type ( // FaultHook is invoked when an error occurs during the execution of an opcode. FaultHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, depth int, err error) - // KeccakPreimageHook is invoked on the KECCAK256 opcode to provide the preimage for the hash. - KeccakPreimageHook = func(hash common.Hash, data []byte) - // GasChangeHook is invoked when the gas changes. GasChangeHook = func(old, new uint64, reason GasChangeReason) @@ -166,16 +163,13 @@ type ( type Hooks struct { // VM events - OnTxStart TxStartHook - OnTxEnd TxEndHook - OnStart StartHook - OnEnd EndHook - OnEnter EnterHook - OnExit ExitHook - OnOpcode OpcodeHook - OnFault FaultHook - OnKeccakPreimage KeccakPreimageHook - OnGasChange GasChangeHook + OnTxStart TxStartHook + OnTxEnd TxEndHook + OnEnter EnterHook + OnExit ExitHook + OnOpcode OpcodeHook + OnFault FaultHook + OnGasChange GasChangeHook // Chain events OnBlockchainInit BlockchainInitHook OnBlockStart BlockStartHook diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0f85f424e521..1a5a29dd43da 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -248,9 +248,6 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if evm.Config.EnablePreimageRecording { evm.StateDB.AddPreimage(interpreter.hasherBuf, data) } - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnKeccakPreimage != nil { - interpreter.evm.Config.Tracer.OnKeccakPreimage(common.BytesToHash(interpreter.hasherBuf[:]), data) - } size.SetBytes(interpreter.hasherBuf[:]) return nil, nil } diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index 551c6635e1ed..39049dace403 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -38,19 +38,18 @@ func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) { t := &NoopTracer{} return &Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.OnTxStart, - OnTxEnd: t.OnTxEnd, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnOpcode: t.OnOpcode, - OnFault: t.OnFault, - OnKeccakPreimage: t.OnKeccakPreimage, - OnGasChange: t.OnGasChange, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, }, GetResult: t.GetResult, Stop: t.Stop, @@ -63,8 +62,6 @@ func (t *NoopTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, sc func (t *NoopTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } -func (t *NoopTracer) OnKeccakPreimage(hash common.Hash, data []byte) {} - func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {} func (t *NoopTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index afb52a9448e9..31e9ce7e3e05 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -30,7 +30,6 @@ func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) { OnExit: t.OnExit, OnOpcode: t.OnOpcode, OnFault: t.OnFault, - OnKeccakPreimage: t.OnKeccakPreimage, OnGasChange: t.OnGasChange, OnBlockchainInit: t.OnBlockchainInit, OnBlockStart: t.OnBlockStart, @@ -51,8 +50,6 @@ func (t *noop) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tr func (t *noop) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } -func (t *noop) OnKeccakPreimage(hash common.Hash, data []byte) {} - func (t *noop) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index a54b5611affa..885cc3a83077 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -59,19 +59,18 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace t := &muxTracer{names: names, tracers: objects} return &directory.Tracer{ Hooks: &tracing.Hooks{ - OnTxStart: t.OnTxStart, - OnTxEnd: t.OnTxEnd, - OnEnter: t.OnEnter, - OnExit: t.OnExit, - OnOpcode: t.OnOpcode, - OnFault: t.OnFault, - OnKeccakPreimage: t.OnKeccakPreimage, - OnGasChange: t.OnGasChange, - OnBalanceChange: t.OnBalanceChange, - OnNonceChange: t.OnNonceChange, - OnCodeChange: t.OnCodeChange, - OnStorageChange: t.OnStorageChange, - OnLog: t.OnLog, + OnTxStart: t.OnTxStart, + OnTxEnd: t.OnTxEnd, + OnEnter: t.OnEnter, + OnExit: t.OnExit, + OnOpcode: t.OnOpcode, + OnFault: t.OnFault, + OnGasChange: t.OnGasChange, + OnBalanceChange: t.OnBalanceChange, + OnNonceChange: t.OnNonceChange, + OnCodeChange: t.OnCodeChange, + OnStorageChange: t.OnStorageChange, + OnLog: t.OnLog, }, GetResult: t.GetResult, Stop: t.Stop, @@ -94,14 +93,6 @@ func (t *muxTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, scop } } -func (t *muxTracer) OnKeccakPreimage(hash common.Hash, data []byte) { - for _, t := range t.tracers { - if t.OnKeccakPreimage != nil { - t.OnKeccakPreimage(hash, data) - } - } -} - func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) { for _, t := range t.tracers { if t.OnGasChange != nil { From ea02c9ebc2e211027105a6847ed4508df716096c Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 6 Mar 2024 18:42:18 +0100 Subject: [PATCH 35/50] fix eth config --- cmd/geth/config.go | 17 ----------------- cmd/utils/flags.go | 12 ++++++++++++ eth/backend.go | 10 +++++++++- eth/ethconfig/config.go | 4 ++-- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index 95d59a04fa0d..2f49c486f96f 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -18,7 +18,6 @@ package main import ( "bufio" - "encoding/json" "errors" "fmt" "os" @@ -38,7 +37,6 @@ import ( "github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/version" @@ -181,21 +179,6 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) { cfg.Eth.OverrideVerkle = &v } - if ctx.IsSet(utils.VMTraceFlag.Name) { - if name := ctx.String(utils.VMTraceFlag.Name); name != "" { - var config string - if ctx.IsSet(utils.VMTraceConfigFlag.Name) { - config = ctx.String(utils.VMTraceConfigFlag.Name) - } - t, err := live.Directory.New(name, json.RawMessage(config)) - if err != nil { - utils.Fatalf("Failed to create tracer %q: %v", name, err) - } - - cfg.Eth.VMTracer = t - } - } - backend, eth := utils.RegisterEthService(stack, &cfg.Eth) // Create gauge with geth system and build information diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2490e79fe5c8..41f782ed4f35 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1900,6 +1900,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil { Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err) } + // VM tracing config. + if ctx.IsSet(VMTraceFlag.Name) { + if name := ctx.String(VMTraceFlag.Name); name != "" { + var config string + if ctx.IsSet(VMTraceConfigFlag.Name) { + config = ctx.String(VMTraceConfigFlag.Name) + } + + cfg.VMTrace = name + cfg.VMTraceConfig = config + } + } } // SetDNSDiscoveryDefaults configures DNS discovery with the given URL if diff --git a/eth/backend.go b/eth/backend.go index 1ba7552d76eb..29af07cc5649 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -18,6 +18,7 @@ package eth import ( + "encoding/json" "errors" "fmt" "math/big" @@ -44,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -192,7 +194,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { var ( vmConfig = vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, - Tracer: config.VMTracer, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, @@ -206,6 +207,13 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { StateScheme: scheme, } ) + if config.VMTrace != "" { + t, err := live.Directory.New(config.VMTrace, json.RawMessage(config.VMTraceConfig)) + if err != nil { + return nil, fmt.Errorf("Failed to create tracer %s: %v", config.VMTrace, err) + } + vmConfig.Tracer = t + } // Override the chain config with provided settings. var overrides core.ChainOverrides if config.OverrideCancun != nil { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 33ee536f8841..27754f7c1545 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/eth/downloader" @@ -153,7 +152,8 @@ type Config struct { EnablePreimageRecording bool // Enables VM tracing - VMTracer *tracing.Hooks + VMTrace string + VMTraceConfig string // Miscellaneous options DocRoot string `toml:"-"` From 94fc0b4a9edfe62a735c988cb1bd41afe997bbab Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 7 Mar 2024 10:30:13 +0100 Subject: [PATCH 36/50] replace opcode with byte # Conflicts: # core/tracing/hooks.go # core/vm/evm.go # core/vm/instructions.go # eth/tracers/js/tracer_test.go --- core/tracing/hooks.go | 29 +++++++----------------- core/vm/evm.go | 17 +++----------- core/vm/instructions.go | 4 ++-- core/vm/interpreter.go | 8 +++---- eth/tracers/directory/noop.go | 6 ++--- eth/tracers/js/goja.go | 6 ++--- eth/tracers/js/tracer_test.go | 13 +++++------ eth/tracers/live/noop.go | 6 ++--- eth/tracers/logger/access_list_tracer.go | 2 +- eth/tracers/logger/logger.go | 8 +++---- eth/tracers/logger/logger_json.go | 4 ++-- eth/tracers/native/4byte.go | 2 +- eth/tracers/native/call.go | 2 +- eth/tracers/native/call_flat.go | 2 +- eth/tracers/native/mux.go | 6 ++--- eth/tracers/native/prestate.go | 2 +- 16 files changed, 46 insertions(+), 71 deletions(-) diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index ca3d17c1a9ff..b262b0aed7f7 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -20,26 +20,24 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" ) // OpContext provides the context at which the opcode is being // executed in, including the memory, stack and various contract-level information. type OpContext interface { MemoryData() []byte - StackData() []uint256.Int + StackData() []big.Int Caller() common.Address Address() common.Address - CallValue() *uint256.Int + CallValue() *big.Int CallInput() []byte } // StateDB gives tracers access to the whole state. type StateDB interface { - GetBalance(common.Address) *uint256.Int + GetBalance(common.Address) *big.Int GetNonce(common.Address) uint64 GetCode(common.Address) []byte GetState(common.Address, common.Hash) common.Hash @@ -75,10 +73,6 @@ type BlockEvent struct { Safe *types.Header } -// OpCode is an EVM opcode -// TODO: provide utils for consumers -type OpCode byte - type ( /* - VM events - @@ -92,15 +86,8 @@ type ( // TxEndHook is called after the execution of a transaction ends. TxEndHook = func(receipt *types.Receipt, err error) - // StartHook is invoked when the processing of the root call starts. - StartHook = func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) - - // EndHook is invoked when the processing of the top call ends. - // See docs for `ExitHook` for info on the `reverted` parameter. - EndHook = func(output []byte, gasUsed uint64, err error, reverted bool) - // EnterHook is invoked when the processing of a message starts. - EnterHook = func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) + EnterHook = func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) // ExitHook is invoked when the processing of a message ends. // `revert` is true when there was an error during the execution. @@ -108,13 +95,13 @@ type ( // ran out of gas when attempting to persist the code to database did not // count as a call failure and did not cause a revert of the call. This will // be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. - ExitHook = func(output []byte, gasUsed uint64, err error, reverted bool) + ExitHook = func(depth int, output []byte, gasUsed uint64, err error, reverted bool) // OpcodeHook is invoked just prior to the execution of an opcode. - OpcodeHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) + OpcodeHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, rData []byte, depth int, err error) // FaultHook is invoked when an error occurs during the execution of an opcode. - FaultHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, depth int, err error) + FaultHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, depth int, err error) // GasChangeHook is invoked when the gas changes. GasChangeHook = func(old, new uint64, reason GasChangeReason) @@ -139,7 +126,7 @@ type ( SkippedBlockHook = func(event BlockEvent) // GenesisBlockHook is called when the genesis block is being processed. - GenesisBlockHook = func(genesis *types.Block, alloc core.GenesisAlloc) + GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc) /* - State events - diff --git a/core/vm/evm.go b/core/vm/evm.go index ba314b51da49..8baa69adfa7f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -520,13 +520,8 @@ func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) { tracer := evm.Config.Tracer - - if isRoot { - if tracer.OnStart != nil { - tracer.OnStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) - } - } else if tracer.OnEnter != nil { - tracer.OnEnter(tracing.OpCode(typ), from, to, input, startGas, value) + if tracer.OnEnter != nil { + tracer.OnEnter(0, byte(typ), from, to, input, startGas, value) } if tracer.OnGasChange != nil { @@ -547,13 +542,7 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) { reverted = false } - if isRoot { - if tracer.OnEnd != nil { - tracer.OnEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) - } - } else if tracer.OnExit != nil { - tracer.OnExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) - } + tracer.OnExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) } func (evm *EVM) GetVMContext() *tracing.VMContext { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1a5a29dd43da..ddf5e7374ba0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -856,7 +856,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { - tracer.OnEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } if tracer.OnExit != nil { tracer.OnExit([]byte{}, 0, nil, false) @@ -876,7 +876,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { - tracer.OnEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) + tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } if tracer.OnExit != nil { tracer.OnExit([]byte{}, 0, nil, false) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index bee43ac1cdf5..47d75a89c972 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -191,10 +191,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if err != nil { if !logged { if in.evm.Config.Tracer.OnOpcode != nil { - in.evm.Config.Tracer.OnOpcode(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) } } else if in.evm.Config.Tracer.OnFault != nil { - in.evm.Config.Tracer.OnFault(pcCopy, tracing.OpCode(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) } } }() @@ -256,7 +256,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) } if in.evm.Config.Tracer.OnOpcode != nil { - in.evm.Config.Tracer.OnOpcode(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } } @@ -268,7 +268,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode) } if in.evm.Config.Tracer.OnOpcode != nil { - in.evm.Config.Tracer.OnOpcode(pc, tracing.OpCode(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + in.evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) logged = true } } diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index 39049dace403..d0edf12e214a 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -56,15 +56,15 @@ func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) { }, nil } -func (t *NoopTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *NoopTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } -func (t *NoopTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { +func (t *NoopTracer) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {} -func (t *NoopTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *NoopTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } func (t *NoopTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 9e7c699f403b..458b99f525af 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -296,7 +296,7 @@ func (t *jsTracer) onStart(from common.Address, to common.Address, create bool, } // OnOpcode implements the Tracer interface to trace a single step of VM execution. -func (t *jsTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *jsTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { if !t.traceStep { return } @@ -321,7 +321,7 @@ func (t *jsTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scop } // OnFault implements the Tracer interface to trace an execution fault -func (t *jsTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { +func (t *jsTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { if t.err != nil { return } @@ -340,7 +340,7 @@ func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool } // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *jsTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *jsTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { if depth == 0 { t.onStart(from, to, vm.OpCode(typ) == vm.CREATE, input, gas, value) return diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 5893c6844f33..9ca636325056 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -26,7 +26,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" - "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/directory" @@ -76,7 +75,7 @@ func runTrace(tracer *directory.Tracer, vmctx *vmContext, chaincfg *params.Chain } tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) - tracer.OnStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value) + tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value) ret, err := env.Interpreter().Run(contract, []byte{}, false) tracer.OnEnd(ret, startGas-contract.Gas, err, true) // Rest gas assumes no refund @@ -187,7 +186,7 @@ func TestHaltBetweenSteps(t *testing.T) { } env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) - tracer.OnStart(common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) + tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0)) tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") tracer.Stop(timeout) @@ -209,8 +208,8 @@ func TestNoStepExec(t *testing.T) { } env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) - tracer.OnStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) - tracer.OnEnd(nil, 0, nil, false) + tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0)) + tracer.OnExit(0, nil, 0, nil, false) ret, err := tracer.GetResult() if err != nil { t.Fatal(err) @@ -279,8 +278,8 @@ func TestEnterExit(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } - tracer.OnEnter(tracing.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) - tracer.OnExit([]byte{}, 400, nil, false) + tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) + tracer.OnExit(1, []byte{}, 400, nil, false) have, err := tracer.GetResult() if err != nil { diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index 31e9ce7e3e05..223667420111 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -44,13 +44,13 @@ func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) { }, nil } -func (t *noop) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *noop) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { } -func (t *noop) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tracing.OpContext, depth int, err error) { +func (t *noop) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpContext, depth int, err error) { } -func (t *noop) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *noop) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { } func (t *noop) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index f4206d524cbc..fc7713b24527 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -138,7 +138,7 @@ func (a *AccessListTracer) Hooks() *tracing.Hooks { } // OnOpcode captures all opcodes that touch storage or addresses and adds them to the accesslist. -func (a *AccessListTracer) OnOpcode(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (a *AccessListTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { stackData := scope.StackData() stackLen := len(stackData) op := vm.OpCode(opcode) diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index fb4a008ca2e5..791a3db2eb57 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -160,7 +160,7 @@ func (l *StructLogger) Reset() { // OnOpcode logs a new structured log message and pushes it out to the environment // // OnOpcode also tracks SLOAD/SSTORE ops to track storage change. -func (l *StructLogger) OnOpcode(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { // If tracing was interrupted, set the error and stop if l.interrupt.Load() { return @@ -367,7 +367,7 @@ func (t *mdLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from t.env = env } -func (t *mdLogger) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *mdLogger) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { if depth != 0 { return } @@ -396,7 +396,7 @@ func (t *mdLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, r } // OnOpcode also tracks SLOAD/SSTORE ops to track storage change. -func (t *mdLogger) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *mdLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { stack := scope.StackData() fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost) @@ -416,7 +416,7 @@ func (t *mdLogger) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scop } } -func (t *mdLogger) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { +func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index a1c81c885656..e9a450896950 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -52,12 +52,12 @@ func (l *JSONLogger) Hooks() *tracing.Hooks { } } -func (l *JSONLogger) OnFault(pc uint64, op tracing.OpCode, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { +func (l *JSONLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { // TODO: Add rData to this interface as well l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err) } -func (l *JSONLogger) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (l *JSONLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { memory := scope.MemoryData() stack := scope.StackData() diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index e6e9ad0e51b5..550b0115a310 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -93,7 +93,7 @@ func (t *fourByteTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction } // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *fourByteTracer) OnEnter(depth int, opcode tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *fourByteTracer) OnEnter(depth int, opcode byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { // Skip if tracing was interrupted if t.interrupt.Load() { return diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 72a07fc854e3..d671d3d364d5 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -176,7 +176,7 @@ func (t *callTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64 } // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *callTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *callTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { t.depth = depth if t.config.OnlyTopCall && depth > 0 { return diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index e5e83bc2e875..47d388d312f3 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -152,7 +152,7 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. } // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (t *flatCallTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { t.tracer.OnEnter(depth, typ, from, to, input, gas, value) // Child calls must have a value, even if it's zero. diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 885cc3a83077..527411351845 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -77,7 +77,7 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace }, nil } -func (t *muxTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *muxTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { for _, t := range t.tracers { if t.OnOpcode != nil { t.OnOpcode(pc, op, gas, cost, scope, rData, depth, err) @@ -85,7 +85,7 @@ func (t *muxTracer) OnOpcode(pc uint64, op tracing.OpCode, gas, cost uint64, sco } } -func (t *muxTracer) OnFault(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, depth int, err error) { +func (t *muxTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { for _, t := range t.tracers { if t.OnFault != nil { t.OnFault(pc, op, gas, cost, scope, depth, err) @@ -101,7 +101,7 @@ func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) } } -func (t *muxTracer) OnEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *muxTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { if t.OnEnter != nil { t.OnEnter(typ, from, to, input, gas, value) diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 97b29493a7b7..e90a1729bbcf 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -99,7 +99,7 @@ func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. } // OnOpcode implements the EVMLogger interface to trace a single step of VM execution. -func (t *prestateTracer) OnOpcode(pc uint64, opcode tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (t *prestateTracer) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { if err != nil { return } From a476051c48daa26f43e4dfcceb099a760deae993 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 7 Mar 2024 12:15:57 +0100 Subject: [PATCH 37/50] add fn refundGas --- core/vm/contract.go | 11 +++++++++++ core/vm/instructions.go | 36 ++++++------------------------------ 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index 860d0f246754..8a927777b6ad 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -171,6 +171,17 @@ func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasC return true } +// RefundGas refunds gas to the contract +func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) { + if gas == 0 { + return + } + if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored { + logger.OnGasChange(c.Gas, c.Gas+gas, reason) + } + c.Gas += gas +} + // Address returns the contracts address func (c *Contract) Address() common.Address { return c.self.Address() diff --git a/core/vm/instructions.go b/core/vm/instructions.go index ddf5e7374ba0..0b9ea37e9dbb 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -612,11 +612,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } scope.Stack.push(&stackvalue) - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) - } - - scope.Contract.Gas += returnGas + scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { interpreter.returnData = res // set REVERT data to return data buffer @@ -657,11 +653,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } scope.Stack.push(&stackvalue) - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) - } - - scope.Contract.Gas += returnGas + scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { interpreter.returnData = res // set REVERT data to return data buffer @@ -707,11 +699,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) - } - - scope.Contract.Gas += returnGas + scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) interpreter.returnData = ret return ret, nil @@ -747,11 +735,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) - } - - scope.Contract.Gas += returnGas + scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) interpreter.returnData = ret return ret, nil @@ -780,11 +764,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) - } - - scope.Contract.Gas += returnGas + scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) interpreter.returnData = ret return ret, nil @@ -813,11 +793,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil && interpreter.evm.Config.Tracer.OnGasChange != nil && returnGas > 0 { - interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, tracing.GasChangeCallLeftOverRefunded) - } - - scope.Contract.Gas += returnGas + scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded) interpreter.returnData = ret return ret, nil From d0a86c45b54663f62771d48200c2be2a2f822785 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 7 Mar 2024 13:08:27 +0100 Subject: [PATCH 38/50] rm canceler --- core/tracing/hooks.go | 7 ------- core/vm/evm.go | 1 - eth/tracers/js/goja.go | 35 +++++++++++++++++++++-------------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index b262b0aed7f7..df1da15f8786 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -45,12 +45,6 @@ type StateDB interface { GetRefund() uint64 } -// Canceler is an interface that wraps the Cancel method. -// It allows loggers to cancel EVM processing. -type Canceler interface { - Cancel() -} - // VMContext provides the context for the EVM execution. type VMContext struct { Coinbase common.Address @@ -61,7 +55,6 @@ type VMContext struct { GasPrice *big.Int ChainConfig *params.ChainConfig StateDB StateDB - VM Canceler } // BlockEvent is emitted upon tracing an incoming block. diff --git a/core/vm/evm.go b/core/vm/evm.go index 8baa69adfa7f..099ee6c877c6 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -554,6 +554,5 @@ func (evm *EVM) GetVMContext() *tracing.VMContext { GasPrice: evm.TxContext.GasPrice, ChainConfig: evm.ChainConfig(), StateDB: evm.StateDB, - VM: evm, } } diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 458b99f525af..86454d376ae4 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -239,7 +239,6 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String()) if err != nil { t.err = err - t.env.VM.Cancel() return } t.ctx["gasPrice"] = gasPriceBig @@ -248,6 +247,9 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from // OnTxEnd implements the Tracer interface and is invoked at the end of // transaction processing. func (t *jsTracer) OnTxEnd(receipt *types.Receipt, err error) { + if t.err != nil { + return + } if err != nil { // Don't override vm error if _, ok := t.ctx["error"]; !ok { @@ -260,9 +262,8 @@ func (t *jsTracer) OnTxEnd(receipt *types.Receipt, err error) { // onStart implements the Tracer interface to initialize the tracing operation. func (t *jsTracer) onStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - cancel := func(err error) { - t.err = err - t.env.VM.Cancel() + if t.err != nil { + return } if create { t.ctx["type"] = t.vm.ToValue("CREATE") @@ -271,19 +272,19 @@ func (t *jsTracer) onStart(from common.Address, to common.Address, create bool, } fromVal, err := t.toBuf(t.vm, from.Bytes()) if err != nil { - cancel(err) + t.err = err return } t.ctx["from"] = fromVal toVal, err := t.toBuf(t.vm, to.Bytes()) if err != nil { - cancel(err) + t.err = err return } t.ctx["to"] = toVal inputVal, err := t.toBuf(t.vm, input) if err != nil { - cancel(err) + t.err = err return } t.ctx["input"] = inputVal @@ -334,6 +335,9 @@ func (t *jsTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.O // onEnd is called after the call finishes to finalize the tracing. func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool) { + if t.err != nil { + return + } if err != nil { t.ctx["error"] = t.vm.ToValue(err.Error()) } @@ -341,6 +345,9 @@ func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *jsTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + if t.err != nil { + return + } if depth == 0 { t.onStart(from, to, vm.OpCode(typ) == vm.CREATE, input, gas, value) return @@ -348,9 +355,6 @@ func (t *jsTracer) OnEnter(depth int, typ byte, from common.Address, to common.A if !t.traceFrame { return } - if t.err != nil { - return - } t.frame.typ = vm.OpCode(typ).String() t.frame.from = from @@ -370,6 +374,9 @@ func (t *jsTracer) OnEnter(depth int, typ byte, from common.Address, to common.A // OnExit is called when EVM exits a scope, even if the scope didn't // execute any code. func (t *jsTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + if t.err != nil { + return + } if depth == 0 { t.onEnd(output, gasUsed, err, reverted) return @@ -389,6 +396,9 @@ func (t *jsTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, r // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error func (t *jsTracer) GetResult() (json.RawMessage, error) { + if t.err != nil { + return nil, t.err + } ctx := t.vm.ToValue(t.ctx) res, err := t.result(t.obj, ctx, t.dbValue) if err != nil { @@ -398,7 +408,7 @@ func (t *jsTracer) GetResult() (json.RawMessage, error) { if err != nil { return nil, err } - return json.RawMessage(encoded), t.err + return encoded, t.err } // Stop terminates execution of the tracer at the first opportune moment. @@ -411,9 +421,6 @@ func (t *jsTracer) Stop(err error) { // execution. func (t *jsTracer) onError(context string, err error) { t.err = wrapError(context, err) - // `env` is set on OnStart which comes before any JS execution. - // So it should be non-nil. - t.env.VM.Cancel() } func wrapError(context string, err error) error { From 77486e91fb8ee551cc58dee7b3acc77d460bc53c Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 13 Mar 2024 12:45:22 +0100 Subject: [PATCH 39/50] fix lint issue --- core/vm/evm.go | 2 ++ eth/tracers/logger/logger.go | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index 099ee6c877c6..cb7c1dad18a7 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -545,6 +545,8 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas tracer.OnExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) } +// GetVMContext provides context about the block being executed as well as state +// to the tracers. func (evm *EVM) GetVMContext() *tracing.VMContext { return &tracing.VMContext{ Coinbase: evm.Context.Coinbase, diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 791a3db2eb57..7e2e7aeff8bf 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -183,9 +183,7 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope var stck []uint256.Int if !l.cfg.DisableStack { stck = make([]uint256.Int, len(stack)) - for i, item := range stack { - stck[i] = item - } + copy(stck, stack) } contractAddr := scope.Address() stackLen := len(stack) From 77a701cca49f239379ff86b35dcc8c4a50bd18a1 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 13 Mar 2024 18:01:08 +0100 Subject: [PATCH 40/50] rm extra param # Conflicts: # core/vm/evm.go --- core/vm/evm.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index cb7c1dad18a7..aafc5777870f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -183,7 +183,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if evm.Config.Tracer != nil { evm.captureBegin(evm.depth == 0, CALL, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - evm.captureEnd(evm.depth == 0, CALL, startGas, leftOverGas, ret, err) + evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -255,7 +255,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if evm.Config.Tracer != nil { evm.captureBegin(false, CALLCODE, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - evm.captureEnd(false, CALLCODE, startGas, leftOverGas, ret, err) + evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -310,7 +310,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // DELEGATECALL inherits value from parent call evm.captureBegin(false, DELEGATECALL, caller.Address(), addr, input, gas, parent.value) defer func(startGas uint64) { - evm.captureEnd(false, DELEGATECALL, startGas, leftOverGas, ret, err) + evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -351,7 +351,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if evm.Config.Tracer != nil { evm.captureBegin(false, STATICCALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { - evm.captureEnd(false, STATICCALL, startGas, leftOverGas, ret, err) + evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -418,7 +418,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.Config.Tracer != nil { evm.captureBegin(evm.depth == 0, typ, caller.Address(), address, codeAndHash.code, gas, value) defer func(startGas uint64) { - evm.captureEnd(evm.depth == 0, typ, startGas, leftOverGas, ret, err) + evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) } // Depth check execution. Fail if we're trying to execute above the @@ -529,7 +529,7 @@ func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to co } } -func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) { +func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret []byte, err error) { tracer := evm.Config.Tracer if leftOverGas != 0 && tracer.OnGasChange != nil { @@ -542,7 +542,7 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) { reverted = false } - tracer.OnExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) } // GetVMContext provides context about the block being executed as well as state From 177074432db847c78081f61618f7e6e6ed390683 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 14 Mar 2024 08:58:51 -0400 Subject: [PATCH 41/50] Call `OnBlockchainInit` before `OnGenesisBlock` (#22) # Conflicts: # core/blockchain.go --- core/blockchain.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index cc88434e8f69..ee230123cdde 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -437,6 +437,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } } + if bc.logger != nil && bc.logger.OnBlockchainInit != nil { + bc.logger.OnBlockchainInit(chainConfig) + } + if bc.logger != nil && bc.logger.OnGenesisBlock != nil { if block := bc.CurrentBlock(); block.Number.Uint64() == 0 { alloc, err := getGenesisState(bc.db, block.Hash()) @@ -488,10 +492,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis } rawdb.WriteChainConfig(db, genesisHash, chainConfig) } - if bc.logger != nil && bc.logger.OnBlockchainInit != nil { - bc.logger.OnBlockchainInit(chainConfig) - } - // Start tx indexer/unindexer if required. + + // Start tx indexer if it's enabled. if txLookupLimit != nil { bc.txLookupLimit = *txLookupLimit From 28a80d617f42ebce4f437a0c959be588136fc0c9 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 14 Mar 2024 14:07:19 +0100 Subject: [PATCH 42/50] fix empty tracer config --- cmd/utils/flags.go | 6 +++--- eth/backend.go | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 41f782ed4f35..928dd2cb9db7 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2211,11 +2211,11 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} if ctx.IsSet(VMTraceFlag.Name) { if name := ctx.String(VMTraceFlag.Name); name != "" { - var config string + var config json.RawMessage if ctx.IsSet(VMTraceConfigFlag.Name) { - config = ctx.String(VMTraceConfigFlag.Name) + config = json.RawMessage(ctx.String(VMTraceConfigFlag.Name)) } - t, err := live.Directory.New(name, json.RawMessage(config)) + t, err := live.Directory.New(name, config) if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) } diff --git a/eth/backend.go b/eth/backend.go index 29af07cc5649..746266cd10a5 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -208,7 +208,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } ) if config.VMTrace != "" { - t, err := live.Directory.New(config.VMTrace, json.RawMessage(config.VMTraceConfig)) + var traceConfig json.RawMessage + if config.VMTraceConfig != "" { + traceConfig = json.RawMessage(config.VMTraceConfig) + } + t, err := live.Directory.New(config.VMTrace, traceConfig) if err != nil { return nil, fmt.Errorf("Failed to create tracer %s: %v", config.VMTrace, err) } From 3834f135a96e9e167bdb999a79d11f35fdd6e392 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 14 Mar 2024 16:53:22 -0400 Subject: [PATCH 43/50] Series of changes to get backport to 1.13.5 working --- accounts/abi/bind/backends/simulated.go | 3 +- cmd/evm/internal/t8ntool/transition.go | 4 +- cmd/geth/snapshot.go | 9 +- core/genesis.go | 68 +++------------ core/genesis_test.go | 2 +- core/state/dump.go | 24 +++-- core/state/state_object.go | 2 +- core/state/statedb.go | 6 +- core/tracing/hooks.go | 3 +- core/types/account.go | 87 +++++++++++++++++++ .../gen_account.go} | 44 +++++----- core/vm/instructions.go | 4 +- core/vm/interpreter.go | 4 +- eth/tracers/js/tracer_test.go | 2 +- eth/tracers/native/call.go | 34 ++------ eth/tracers/native/mux.go | 4 +- light/odr_test.go | 3 +- 17 files changed, 161 insertions(+), 142 deletions(-) create mode 100644 core/types/account.go rename core/{gen_genesis_account.go => types/gen_account.go} (61%) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 3bb0e8651b91..3fc346fc6804 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "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/filters" @@ -681,7 +682,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // Set infinite balance to the fake caller account. from := stateDB.GetOrNewStateObject(call.From) - from.SetBalance(math.MaxBig256, state.BalanceChangeUnspecified) + from.SetBalance(math.MaxBig256, tracing.BalanceChangeUnspecified) // Execute the call. msg := &core.Message{ diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index bb28620b0160..2600a6759e5a 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -186,9 +186,7 @@ func Transition(ctx *cli.Context) error { } prestate.Env = *inputData.Env - vmConfig := vm.Config{ - Tracer: tracer, - } + vmConfig := vm.Config{} // Construct the chainconfig var chainConfig *params.ChainConfig if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 25c6311c4c43..d30d394d9f1d 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -568,11 +568,10 @@ func dumpState(ctx *cli.Context) error { return err } da := &state.DumpAccount{ - Balance: account.Balance.String(), - Nonce: account.Nonce, - Root: account.Root.Bytes(), - CodeHash: account.CodeHash, - SecureKey: accIt.Hash().Bytes(), + Balance: account.Balance.String(), + Nonce: account.Nonce, + Root: account.Root.Bytes(), + CodeHash: account.CodeHash, } if !conf.SkipCode && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash)) diff --git a/core/genesis.go b/core/genesis.go index 16f8ec83a1f4..a6c51f3c2054 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -18,7 +18,6 @@ package core import ( "bytes" - "encoding/hex" "encoding/json" "errors" "fmt" @@ -41,7 +40,6 @@ import ( ) //go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go -//go:generate go run github.com/fjl/gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go var errGenesisNoConfig = errors.New("genesis has no chain configuration") @@ -106,23 +104,14 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) { return &genesis, nil } -// GenesisAlloc specifies the initial state that is part of the genesis block. -type GenesisAlloc map[common.Address]GenesisAccount +// Deprecated: use types.GenesisAccount instead. +type GenesisAccount = types.Account -func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error { - m := make(map[common.UnprefixedAddress]GenesisAccount) - if err := json.Unmarshal(data, &m); err != nil { - return err - } - *ga = make(GenesisAlloc) - for addr, a := range m { - (*ga)[common.Address(addr)] = a - } - return nil -} +// Deprecated: use types.GenesisAlloc instead. +type GenesisAlloc = types.GenesisAlloc -// hash computes the state root according to the genesis specification. -func (ga *GenesisAlloc) hash() (common.Hash, error) { +// hashAlloc computes the state root according to the genesis specification. +func hashAlloc(ga *types.GenesisAlloc) (common.Hash, error) { // Create an ephemeral in-memory database for computing hash, // all the derived states will be discarded to not pollute disk. db := state.NewDatabase(rawdb.NewMemoryDatabase()) @@ -143,10 +132,10 @@ func (ga *GenesisAlloc) hash() (common.Hash, error) { return statedb.Commit(0, false) } -// flush is very similar with hash, but the main difference is all the generated +// flushAlloc is very similar with hash, but the main difference is all the generated // states will be persisted into the given database. Also, the genesis state // specification will be flushed as well. -func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error { +func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error { statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil) if err != nil { return err @@ -215,15 +204,6 @@ func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc GenesisAll return nil, nil } -// GenesisAccount is an account in the state of the genesis block. -type GenesisAccount struct { - Code []byte `json:"code,omitempty"` - Storage map[common.Hash]common.Hash `json:"storage,omitempty"` - Balance *big.Int `json:"balance" gencodec:"required"` - Nonce uint64 `json:"nonce,omitempty"` - PrivateKey []byte `json:"secretKey,omitempty"` // for tests -} - // field type overrides for gencodec type genesisSpecMarshaling struct { Nonce math.HexOrDecimal64 @@ -239,34 +219,6 @@ type genesisSpecMarshaling struct { BlobGasUsed *math.HexOrDecimal64 } -type genesisAccountMarshaling struct { - Code hexutil.Bytes - Balance *math.HexOrDecimal256 - Nonce math.HexOrDecimal64 - Storage map[storageJSON]storageJSON - PrivateKey hexutil.Bytes -} - -// storageJSON represents a 256 bit byte array, but allows less than 256 bits when -// unmarshaling from hex. -type storageJSON common.Hash - -func (h *storageJSON) UnmarshalText(text []byte) error { - text = bytes.TrimPrefix(text, []byte("0x")) - if len(text) > 64 { - return fmt.Errorf("too many hex characters in storage key/value %q", text) - } - offset := len(h) - len(text)/2 // pad on the left - if _, err := hex.Decode(h[offset:], text); err != nil { - return fmt.Errorf("invalid hex storage key/value %q", text) - } - return nil -} - -func (h storageJSON) MarshalText() ([]byte, error) { - return hexutil.Bytes(h[:]).MarshalText() -} - // GenesisMismatchError is raised when trying to overwrite an existing // genesis block with an incompatible one. type GenesisMismatchError struct { @@ -449,7 +401,7 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { // ToBlock returns the genesis block according to genesis specification. func (g *Genesis) ToBlock() *types.Block { - root, err := g.Alloc.hash() + root, err := hashAlloc(&g.Alloc) if err != nil { panic(err) } @@ -526,7 +478,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block // All the checks has passed, flush the states derived from the genesis // specification as well as the specification itself into the provided // database. - if err := g.Alloc.flush(db, triedb, block.Hash()); err != nil { + if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil { return nil, err } rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty()) diff --git a/core/genesis_test.go b/core/genesis_test.go index fac88ff373b3..2a7eedf40d94 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -231,7 +231,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) { {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}}, {2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}}, } - hash, _ = alloc.hash() + hash, _ = hashAlloc(alloc) ) blob, _ := json.Marshal(alloc) rawdb.WriteGenesisStateSpec(db, hash, blob) diff --git a/core/state/dump.go b/core/state/dump.go index 1840e5b2205f..d3a7b1170e4e 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -104,14 +104,13 @@ type iterativeDump struct { // OnAccount implements DumpCollector interface func (d iterativeDump) OnAccount(addr *common.Address, account DumpAccount) { dumpAccount := &DumpAccount{ - Balance: account.Balance, - Nonce: account.Nonce, - Root: account.Root, - CodeHash: account.CodeHash, - Code: account.Code, - Storage: account.Storage, - SecureKey: account.SecureKey, - Address: addr, + Balance: account.Balance, + Nonce: account.Nonce, + Root: account.Root, + CodeHash: account.CodeHash, + Code: account.Code, + Storage: account.Storage, + Address: addr, } d.Encode(dumpAccount) } @@ -150,11 +149,10 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] panic(err) } account := DumpAccount{ - Balance: data.Balance.String(), - Nonce: data.Nonce, - Root: data.Root[:], - CodeHash: data.CodeHash, - SecureKey: it.Key, + Balance: data.Balance.String(), + Nonce: data.Nonce, + Root: data.Root[:], + CodeHash: data.CodeHash, } var ( addrBytes = s.trie.GetKey(it.Key) diff --git a/core/state/state_object.go b/core/state/state_object.go index cd718ae0580a..250c549f3a31 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -420,7 +420,7 @@ func (s *stateObject) AddBalance(amount *big.Int, reason tracing.BalanceChangeRe // SubBalance removes amount from s's balance. // It is used to remove funds from the origin account of a transfer. func (s *stateObject) SubBalance(amount *big.Int, reason tracing.BalanceChangeReason) { - if amount.IsZero() { + if amount.Sign() == 0 { return } s.SetBalance(new(big.Int).Sub(s.Balance(), amount), reason) diff --git a/core/state/statedb.go b/core/state/statedb.go index 054f02270868..22516af32245 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -384,7 +384,7 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool { // AddBalance adds amount to the account associated with addr. func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { - stateObject := s.getOrNewStateObject(addr) + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.AddBalance(amount, reason) } @@ -392,14 +392,14 @@ func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason tracin // SubBalance subtracts amount from the account associated with addr. func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { - stateObject := s.getOrNewStateObject(addr) + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SubBalance(amount, reason) } } func (s *StateDB) SetBalance(addr common.Address, amount *big.Int, reason tracing.BalanceChangeReason) { - stateObject := s.getOrNewStateObject(addr) + stateObject := s.GetOrNewStateObject(addr) if stateObject != nil { stateObject.SetBalance(amount, reason) } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index df1da15f8786..a2041f50a072 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -22,13 +22,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" ) // OpContext provides the context at which the opcode is being // executed in, including the memory, stack and various contract-level information. type OpContext interface { MemoryData() []byte - StackData() []big.Int + StackData() []uint256.Int Caller() common.Address Address() common.Address CallValue() *big.Int diff --git a/core/types/account.go b/core/types/account.go new file mode 100644 index 000000000000..bb0f4ca02e10 --- /dev/null +++ b/core/types/account.go @@ -0,0 +1,87 @@ +// 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 types + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/common/math" +) + +//go:generate go run github.com/fjl/gencodec -type Account -field-override accountMarshaling -out gen_account.go + +// Account represents an Ethereum account and its attached data. +// This type is used to specify accounts in the genesis block state, and +// is also useful for JSON encoding/decoding of accounts. +type Account struct { + Code []byte `json:"code,omitempty"` + Storage map[common.Hash]common.Hash `json:"storage,omitempty"` + Balance *big.Int `json:"balance" gencodec:"required"` + Nonce uint64 `json:"nonce,omitempty"` + + // used in tests + PrivateKey []byte `json:"secretKey,omitempty"` +} + +type accountMarshaling struct { + Code hexutil.Bytes + Balance *math.HexOrDecimal256 + Nonce math.HexOrDecimal64 + Storage map[storageJSON]storageJSON + PrivateKey hexutil.Bytes +} + +// storageJSON represents a 256 bit byte array, but allows less than 256 bits when +// unmarshaling from hex. +type storageJSON common.Hash + +func (h *storageJSON) UnmarshalText(text []byte) error { + text = bytes.TrimPrefix(text, []byte("0x")) + if len(text) > 64 { + return fmt.Errorf("too many hex characters in storage key/value %q", text) + } + offset := len(h) - len(text)/2 // pad on the left + if _, err := hex.Decode(h[offset:], text); err != nil { + return fmt.Errorf("invalid hex storage key/value %q", text) + } + return nil +} + +func (h storageJSON) MarshalText() ([]byte, error) { + return hexutil.Bytes(h[:]).MarshalText() +} + +// GenesisAlloc specifies the initial state of a genesis block. +type GenesisAlloc map[common.Address]Account + +func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error { + m := make(map[common.UnprefixedAddress]Account) + if err := json.Unmarshal(data, &m); err != nil { + return err + } + *ga = make(GenesisAlloc) + for addr, a := range m { + (*ga)[common.Address(addr)] = a + } + return nil +} diff --git a/core/gen_genesis_account.go b/core/types/gen_account.go similarity index 61% rename from core/gen_genesis_account.go rename to core/types/gen_account.go index a9d47e6ba355..4e475896a736 100644 --- a/core/gen_genesis_account.go +++ b/core/types/gen_account.go @@ -1,6 +1,6 @@ // Code generated by github.com/fjl/gencodec. DO NOT EDIT. -package core +package types import ( "encoding/json" @@ -12,62 +12,62 @@ import ( "github.com/ethereum/go-ethereum/common/math" ) -var _ = (*genesisAccountMarshaling)(nil) +var _ = (*accountMarshaling)(nil) // MarshalJSON marshals as JSON. -func (g GenesisAccount) MarshalJSON() ([]byte, error) { - type GenesisAccount struct { +func (a Account) MarshalJSON() ([]byte, error) { + type Account struct { Code hexutil.Bytes `json:"code,omitempty"` Storage map[storageJSON]storageJSON `json:"storage,omitempty"` Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` Nonce math.HexOrDecimal64 `json:"nonce,omitempty"` PrivateKey hexutil.Bytes `json:"secretKey,omitempty"` } - var enc GenesisAccount - enc.Code = g.Code - if g.Storage != nil { - enc.Storage = make(map[storageJSON]storageJSON, len(g.Storage)) - for k, v := range g.Storage { + var enc Account + enc.Code = a.Code + if a.Storage != nil { + enc.Storage = make(map[storageJSON]storageJSON, len(a.Storage)) + for k, v := range a.Storage { enc.Storage[storageJSON(k)] = storageJSON(v) } } - enc.Balance = (*math.HexOrDecimal256)(g.Balance) - enc.Nonce = math.HexOrDecimal64(g.Nonce) - enc.PrivateKey = g.PrivateKey + enc.Balance = (*math.HexOrDecimal256)(a.Balance) + enc.Nonce = math.HexOrDecimal64(a.Nonce) + enc.PrivateKey = a.PrivateKey return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. -func (g *GenesisAccount) UnmarshalJSON(input []byte) error { - type GenesisAccount struct { +func (a *Account) UnmarshalJSON(input []byte) error { + type Account struct { Code *hexutil.Bytes `json:"code,omitempty"` Storage map[storageJSON]storageJSON `json:"storage,omitempty"` Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"` Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"` PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"` } - var dec GenesisAccount + var dec Account if err := json.Unmarshal(input, &dec); err != nil { return err } if dec.Code != nil { - g.Code = *dec.Code + a.Code = *dec.Code } if dec.Storage != nil { - g.Storage = make(map[common.Hash]common.Hash, len(dec.Storage)) + a.Storage = make(map[common.Hash]common.Hash, len(dec.Storage)) for k, v := range dec.Storage { - g.Storage[common.Hash(k)] = common.Hash(v) + a.Storage[common.Hash(k)] = common.Hash(v) } } if dec.Balance == nil { - return errors.New("missing required field 'balance' for GenesisAccount") + return errors.New("missing required field 'balance' for Account") } - g.Balance = (*big.Int)(dec.Balance) + a.Balance = (*big.Int)(dec.Balance) if dec.Nonce != nil { - g.Nonce = uint64(*dec.Nonce) + a.Nonce = uint64(*dec.Nonce) } if dec.PrivateKey != nil { - g.PrivateKey = *dec.PrivateKey + a.PrivateKey = *dec.PrivateKey } return nil } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0b9ea37e9dbb..673908f44fa1 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -835,7 +835,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } if tracer.OnExit != nil { - tracer.OnExit([]byte{}, 0, nil, false) + tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false) } } return nil, errStopToken @@ -855,7 +855,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) } if tracer.OnExit != nil { - tracer.OnExit([]byte{}, 0, nil, false) + tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false) } } return nil, errStopToken diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 47d75a89c972..a8fdac855de7 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -17,6 +17,8 @@ package vm import ( + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/tracing" @@ -63,7 +65,7 @@ func (ctx *ScopeContext) Address() common.Address { return ctx.Contract.Address() } -func (ctx *ScopeContext) CallValue() *uint256.Int { +func (ctx *ScopeContext) CallValue() *big.Int { return ctx.Contract.Value() } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 9ca636325056..895d6be6843e 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -77,7 +77,7 @@ func runTrace(tracer *directory.Tracer, vmctx *vmContext, chaincfg *params.Chain tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value) ret, err := env.Interpreter().Run(contract, []byte{}, false) - tracer.OnEnd(ret, startGas-contract.Gas, err, true) + tracer.OnExit(0, ret, startGas-contract.Gas, err, true) // Rest gas assumes no refund tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) if err != nil { diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index d671d3d364d5..83db1139f18a 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -147,32 +147,7 @@ func newCallTracerObject(ctx *directory.Context, cfg json.RawMessage) (*callTrac } // First callframe contains tx context info // and is populated on start and end. - return &callTracer{callstack: make([]callFrame, 1), config: config}, nil -} - -// CaptureStart implements the EVMLogger interface to initialize the tracing operation. -func (t *callTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { - toCopy := to - t.callstack[0] = callFrame{ - Type: vm.CALL, - From: from, - To: &toCopy, - Input: common.CopyBytes(input), - Gas: t.gasLimit, - Value: value, - } - if create { - t.callstack[0].Type = vm.CREATE - } -} - -// CaptureEnd is called after the call finishes to finalize the tracing. -func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { - t.callstack[0].processOutput(output, err, reverted) -} - -// CaptureState implements the EVMLogger interface to trace a single step of VM execution. -func (t *callTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + return &callTracer{callstack: make([]callFrame, 0, 1), config: config}, nil } // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). @@ -195,6 +170,9 @@ func (t *callTracer) OnEnter(depth int, typ byte, from common.Address, to common Gas: gas, Value: value, } + if depth == 0 { + call.Gas = t.gasLimit + } t.callstack = append(t.callstack, call) } @@ -210,17 +188,19 @@ func (t *callTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, if t.config.OnlyTopCall { return } + size := len(t.callstack) if size <= 1 { return } - // pop call + // Pop call. call := t.callstack[size-1] t.callstack = t.callstack[:size-1] size -= 1 call.GasUsed = gasUsed call.processOutput(output, err, reverted) + // Nest call into parent. t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 527411351845..177082ffd952 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -104,7 +104,7 @@ func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) func (t *muxTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { for _, t := range t.tracers { if t.OnEnter != nil { - t.OnEnter(typ, from, to, input, gas, value) + t.OnEnter(depth, typ, from, to, input, gas, value) } } } @@ -112,7 +112,7 @@ func (t *muxTracer) OnEnter(depth int, typ byte, from common.Address, to common. func (t *muxTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { for _, t := range t.tracers { if t.OnExit != nil { - t.OnExit(output, gasUsed, err, reverted) + t.OnExit(depth, output, gasUsed, err, reverted) } } } diff --git a/light/odr_test.go b/light/odr_test.go index 44450fc2ff74..7296163e1def 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "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/crypto" @@ -201,7 +202,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain } // Perform read-only call. - st.SetBalance(testBankAddress, math.MaxBig256, state.BalanceChangeUnspecified) + st.SetBalance(testBankAddress, math.MaxBig256, tracing.BalanceChangeUnspecified) msg := &core.Message{ From: testBankAddress, To: &testContractAddr, From 17fcdfaadfdea4f3d4ff1445da492956303c74ef Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 15 Mar 2024 10:58:50 -0400 Subject: [PATCH 44/50] Fixed incorrect `captureBeging/captureEnd` in EVM --- core/vm/evm.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/vm/evm.go b/core/vm/evm.go index aafc5777870f..d5ce1c51203f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -181,7 +181,7 @@ func (evm *EVM) Interpreter() *EVMInterpreter { func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { // Capture the tracer start/end events in debug mode if evm.Config.Tracer != nil { - evm.captureBegin(evm.depth == 0, CALL, caller.Address(), addr, input, gas, value) + evm.captureBegin(evm.depth, CALL, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -253,7 +253,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - evm.captureBegin(false, CALLCODE, caller.Address(), addr, input, gas, value) + evm.captureBegin(evm.depth, CALLCODE, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -308,7 +308,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // that caller is something other than a Contract. parent := caller.(*Contract) // DELEGATECALL inherits value from parent call - evm.captureBegin(false, DELEGATECALL, caller.Address(), addr, input, gas, parent.value) + evm.captureBegin(evm.depth, DELEGATECALL, caller.Address(), addr, input, gas, parent.value) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -349,7 +349,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - evm.captureBegin(false, STATICCALL, caller.Address(), addr, input, gas, nil) + evm.captureBegin(evm.depth, STATICCALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -416,7 +416,7 @@ func (c *codeAndHash) Hash() common.Hash { // create creates a new contract using code as deployment code. func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { if evm.Config.Tracer != nil { - evm.captureBegin(evm.depth == 0, typ, caller.Address(), address, codeAndHash.code, gas, value) + evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value) defer func(startGas uint64) { evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err) }(gas) @@ -518,12 +518,11 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } -func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) { +func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) { tracer := evm.Config.Tracer if tracer.OnEnter != nil { - tracer.OnEnter(0, byte(typ), from, to, input, startGas, value) + tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value) } - if tracer.OnGasChange != nil { tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance) } @@ -531,7 +530,6 @@ func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to co func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret []byte, err error) { tracer := evm.Config.Tracer - if leftOverGas != 0 && tracer.OnGasChange != nil { tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned) } @@ -542,7 +540,9 @@ func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret [ if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) { reverted = false } - tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + if tracer.OnExit != nil { + tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted) + } } // GetVMContext provides context about the block being executed as well as state From f596461a2369f6863a9ede9beb9594ec89558f04 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 15 Mar 2024 14:06:11 -0400 Subject: [PATCH 45/50] Backported a bunch more of `eth/tracers` --- common/hexutil/json.go | 45 ++++++ core/genesis.go | 12 +- core/state/dump.go | 41 +++--- eth/api_backend.go | 2 +- eth/state_accessor.go | 4 +- eth/tracers/api.go | 111 ++++++++++++--- eth/tracers/api_test.go | 130 ++++++++++++++++-- eth/tracers/directory/util_test.go | 6 +- .../internal/tracetest/calltrace_test.go | 14 +- .../internal/tracetest/prestate_test.go | 6 + .../testdata/prestate_tracer/blob_tx.json | 63 +++++++++ .../prestate_tracer/create_post_eip158.json | 64 +++++++++ .../create_failed.json | 2 +- .../create_post_eip158.json | 82 +++++++++++ eth/tracers/js/goja.go | 28 +++- .../js/internal/tracers/call_tracer_legacy.js | 2 +- eth/tracers/logger/gen_structlog.go | 16 ++- eth/tracers/logger/logger.go | 6 +- eth/tracers/native/call_flat.go | 6 + les/api_backend.go | 2 +- les/state_accessor.go | 4 +- 21 files changed, 558 insertions(+), 88 deletions(-) create mode 100644 eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json create mode 100644 eth/tracers/internal/tracetest/testdata/prestate_tracer/create_post_eip158.json create mode 100644 eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_post_eip158.json diff --git a/common/hexutil/json.go b/common/hexutil/json.go index 50db208118ee..6906bcaf5395 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -23,6 +23,8 @@ import ( "math/big" "reflect" "strconv" + + "github.com/holiman/uint256" ) var ( @@ -30,6 +32,7 @@ var ( bigT = reflect.TypeOf((*Big)(nil)) uintT = reflect.TypeOf(Uint(0)) uint64T = reflect.TypeOf(Uint64(0)) + u256T = reflect.TypeOf((*uint256.Int)(nil)) ) // Bytes marshals/unmarshals as a JSON string with 0x prefix. @@ -91,6 +94,48 @@ func (b *Bytes) UnmarshalGraphQL(input interface{}) error { return err } +// U256 marshals/unmarshals as a JSON string with 0x prefix. +// The zero value marshals as "0x0". +type U256 uint256.Int + +// MarshalText implements encoding.TextMarshaler +func (b U256) MarshalText() ([]byte, error) { + u256 := (*uint256.Int)(&b) + return []byte(u256.Hex()), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (b *U256) UnmarshalJSON(input []byte) error { + // The uint256.Int.UnmarshalJSON method accepts "dec", "0xhex"; we must be + // more strict, hence we check string and invoke SetFromHex directly. + if !isString(input) { + return errNonString(u256T) + } + // The hex decoder needs to accept empty string ("") as '0', which uint256.Int + // would reject. + if len(input) == 2 { + (*uint256.Int)(b).Clear() + return nil + } + err := (*uint256.Int)(b).SetFromHex(string(input[1 : len(input)-1])) + if err != nil { + return &json.UnmarshalTypeError{Value: err.Error(), Type: u256T} + } + return nil +} + +// UnmarshalText implements encoding.TextUnmarshaler +func (b *U256) UnmarshalText(input []byte) error { + // The uint256.Int.UnmarshalText method accepts "dec", "0xhex"; we must be + // more strict, hence we check string and invoke SetFromHex directly. + return (*uint256.Int)(b).SetFromHex(string(input)) +} + +// String returns the hex encoding of b. +func (b *U256) String() string { + return (*uint256.Int)(b).Hex() +} + // UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out // determines the required input length. This function is commonly used to implement the // UnmarshalJSON method for fixed-size types. diff --git a/core/genesis.go b/core/genesis.go index a6c51f3c2054..aa788711f065 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -43,6 +43,12 @@ import ( var errGenesisNoConfig = errors.New("genesis has no chain configuration") +// use types.GenesisAccount instead (Deprecation not ported from latest Geth for now). +type GenesisAccount = types.Account + +// use types.GenesisAlloc instead (Deprecation not ported from latest Geth for now). +type GenesisAlloc = types.GenesisAlloc + // Genesis specifies the header fields, state of a genesis block. It also defines hard // fork switch-over blocks through the chain configuration. type Genesis struct { @@ -104,12 +110,6 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) { return &genesis, nil } -// Deprecated: use types.GenesisAccount instead. -type GenesisAccount = types.Account - -// Deprecated: use types.GenesisAlloc instead. -type GenesisAlloc = types.GenesisAlloc - // hashAlloc computes the state root according to the genesis specification. func hashAlloc(ga *types.GenesisAlloc) (common.Hash, error) { // Create an ephemeral in-memory database for computing hash, diff --git a/core/state/dump.go b/core/state/dump.go index d3a7b1170e4e..9ce6cd394b88 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -49,14 +49,15 @@ type DumpCollector interface { // DumpAccount represents an account in the state. type DumpAccount struct { - Balance string `json:"balance"` - Nonce uint64 `json:"nonce"` - Root hexutil.Bytes `json:"root"` - CodeHash hexutil.Bytes `json:"codeHash"` - Code hexutil.Bytes `json:"code,omitempty"` - Storage map[common.Hash]string `json:"storage,omitempty"` - Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode - AddressHash hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key + Balance string `json:"balance"` + Nonce uint64 `json:"nonce"` + Root hexutil.Bytes `json:"root"` + CodeHash hexutil.Bytes `json:"codeHash"` + Code hexutil.Bytes `json:"code,omitempty"` + Storage map[common.Hash]string `json:"storage,omitempty"` + Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode + SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key + } // Dump represents the full dump in a collected format, as one large map. @@ -104,13 +105,14 @@ type iterativeDump struct { // OnAccount implements DumpCollector interface func (d iterativeDump) OnAccount(addr *common.Address, account DumpAccount) { dumpAccount := &DumpAccount{ - Balance: account.Balance, - Nonce: account.Nonce, - Root: account.Root, - CodeHash: account.CodeHash, - Code: account.Code, - Storage: account.Storage, - Address: addr, + Balance: account.Balance, + Nonce: account.Nonce, + Root: account.Root, + CodeHash: account.CodeHash, + Code: account.Code, + Storage: account.Storage, + SecureKey: account.SecureKey, + Address: addr, } d.Encode(dumpAccount) } @@ -149,10 +151,11 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] panic(err) } account := DumpAccount{ - Balance: data.Balance.String(), - Nonce: data.Nonce, - Root: data.Root[:], - CodeHash: data.CodeHash, + Balance: data.Balance.String(), + Nonce: data.Nonce, + Root: data.Root[:], + CodeHash: data.CodeHash, + SecureKey: it.Key, } var ( addrBytes = s.trie.GetKey(it.Key) diff --git a/eth/api_backend.go b/eth/api_backend.go index 601e55515857..c59f32eec294 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -412,6 +412,6 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re return b.eth.stateAtBlock(ctx, block, reexec, base, readOnly, preferDisk) } -func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { +func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 24694df66c36..cf00e582bc86 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -216,7 +216,7 @@ func (eth *Ethereum) stateAtBlock(ctx context.Context, block *types.Block, reexe } // stateAtTransaction returns the execution environment of a certain transaction. -func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { +func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { // Short circuit if it's genesis block. if block.NumberU64() == 0 { return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis") @@ -243,7 +243,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) if idx == txIndex { - return msg, context, statedb, release, nil + return tx, context, statedb, release, nil } // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 447ba3119c3e..e6fb576416b7 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -88,7 +88,7 @@ type Backend interface { Engine() consensus.Engine ChainDb() ethdb.Database StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, StateReleaseFunc, error) - StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) + StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) } // API is the collection of tracing APIs exposed over the private debugging endpoint. @@ -166,6 +166,7 @@ type TraceCallConfig struct { TraceConfig StateOverrides *ethapi.StateOverride BlockOverrides *ethapi.BlockOverrides + TxIndex *hexutil.Uint } // StdTraceConfig holds extra parameters to standard-json trace functions. @@ -227,7 +228,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf } sub := notifier.CreateSubscription() - resCh := api.traceChain(from, to, config, notifier.Closed()) + resCh := api.traceChain(from, to, config, sub.Err()) go func() { for result := range resCh { notifier.Notify(sub.ID, result) @@ -241,7 +242,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf // the end block but excludes the start one. The return value will be one item per // transaction, dependent on the requested tracer. // The tracing procedure should be aborted in case the closed signal is received. -func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan interface{}) chan *blockTraceResult { +func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan error) chan *blockTraceResult { reexec := defaultTraceReexec if config != nil && config.Reexec != nil { reexec = *config.Reexec @@ -278,7 +279,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config) if err != nil { task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()} log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) @@ -610,7 +611,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac TxIndex: i, TxHash: tx.Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config) + res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config) if err != nil { return nil, err } @@ -627,7 +628,6 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat var ( txs = block.Transactions() blockHash = block.Hash() - blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) results = make([]*txTraceResult, len(txs)) pend sync.WaitGroup @@ -650,7 +650,12 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat TxIndex: task.index, TxHash: txs[task.index].Hash(), } - res, err := api.traceTx(ctx, msg, txctx, blockCtx, task.statedb, config) + // Reconstruct the block context for each transaction + // as the GetHash function of BlockContext is not safe for + // concurrent use. + // See: https://github.com/ethereum/go-ethereum/issues/29114 + blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config) if err != nil { results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()} continue @@ -662,6 +667,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat // Feed the transactions into the tracers and return var failed error + blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) txloop: for i, tx := range txs { // Send the trace task over for execution @@ -843,11 +849,15 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * if err != nil { return nil, err } - msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) + tx, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) if err != nil { return nil, err } defer release() + msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee()) + if err != nil { + return nil, err + } txctx := &directory.Context{ BlockHash: blockHash, @@ -855,17 +865,23 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * TxIndex: int(index), TxHash: hash, } - return api.traceTx(ctx, msg, txctx, vmctx, statedb, config) + return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config) } // TraceCall lets you trace a given eth_call. It collects the structured logs // created during the execution of EVM if the given transaction was added on // top of the provided block and returns them as a JSON object. +// If no transaction index is specified, the trace will be conducted on the state +// after executing the specified block. However, if a transaction index is provided, +// the trace will be conducted on the state after executing the specified transaction +// within the specified block. func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { // Try to retrieve the specified block var ( - err error - block *types.Block + err error + block *types.Block + statedb *state.StateDB + release StateReleaseFunc ) if hash, ok := blockNrOrHash.Hash(); ok { block, err = api.blockByHash(ctx, hash) @@ -890,7 +906,12 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil && config.Reexec != nil { reexec = *config.Reexec } - statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) + + if config != nil && config.TxIndex != nil { + _, _, statedb, release, err = api.backend.StateAtTransaction(ctx, block, int(*config.TxIndex), reexec) + } else { + statedb, release, err = api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) + } if err != nil { return nil, err } @@ -905,26 +926,30 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc config.BlockOverrides.Apply(&vmctx) } // Execute the trace - msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee()) + msg, err := args.ToMessage(api.backend.RPCGasCap(), vmctx.BaseFee) + if err != nil { + return nil, err + } + tx, err := argsToTransaction(api.backend, &args, msg) if err != nil { return nil, err } - var traceConfig *TraceConfig if config != nil { traceConfig = &config.TraceConfig } - return api.traceTx(ctx, msg, new(directory.Context), vmctx, statedb, traceConfig) + return api.traceTx(ctx, tx, msg, new(directory.Context), vmctx, statedb, traceConfig) } // traceTx configures a new tracer according to the provided configuration, and // executes the given message in the provided environment. The return value will // be tracer dependent. -func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { +func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { var ( tracer *directory.Tracer err error timeout = defaultTraceTimeout + usedGas uint64 ) if config == nil { config = &TraceConfig{} @@ -959,7 +984,8 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *direc // Call Prepare to clear out the statedb access list statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) - if _, err = core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)); err != nil { + _, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv) + if err != nil { return nil, fmt.Errorf("tracing failed: %w", err) } return tracer.GetResult() @@ -1024,3 +1050,54 @@ func overrideConfig(original *params.ChainConfig, override *params.ChainConfig) return copy, canon } + +// argsToTransaction produces a Transaction object given call arguments. +// The `msg` field must be converted from the same arguments. +func argsToTransaction(b Backend, args *ethapi.TransactionArgs, msg *core.Message) (*types.Transaction, error) { + chainID := b.ChainConfig().ChainID + if args.ChainID != nil { + if have := (*big.Int)(args.ChainID); have.Cmp(chainID) != 0 { + return nil, fmt.Errorf("chainId does not match node's (have=%v, want=%v)", have, chainID) + } + } + var data types.TxData + switch { + case args.MaxFeePerGas != nil: + al := types.AccessList{} + if args.AccessList != nil { + al = *args.AccessList + } + data = &types.DynamicFeeTx{ + To: args.To, + ChainID: chainID, + Nonce: msg.Nonce, + Gas: msg.GasLimit, + GasFeeCap: msg.GasFeeCap, + GasTipCap: msg.GasTipCap, + Value: msg.Value, + Data: msg.Data, + AccessList: al, + } + case args.AccessList != nil: + data = &types.AccessListTx{ + To: args.To, + ChainID: chainID, + Nonce: msg.Nonce, + Gas: msg.GasLimit, + GasPrice: msg.GasPrice, + Value: msg.Value, + Data: msg.Data, + AccessList: *args.AccessList, + } + default: + data = &types.LegacyTx{ + To: args.To, + Nonce: msg.Nonce, + Gas: msg.GasLimit, + GasPrice: msg.GasPrice, + Value: msg.Value, + Data: msg.Data, + } + } + return types.NewTx(data), nil +} diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 52db08e9de8c..7405d2e0350b 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -155,7 +155,7 @@ func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reex return statedb, release, nil } -func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) { +func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, StateReleaseFunc, error) { parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1) if parent == nil { return nil, vm.BlockContext{}, nil, nil, errBlockNotFound @@ -174,7 +174,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(block.Header(), b.chain, nil) if idx == txIndex { - return msg, context, statedb, release, nil + return tx, context, statedb, release, nil } vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{}) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { @@ -192,7 +192,7 @@ func TestTraceCall(t *testing.T) { accounts := newAccounts(3) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -200,13 +200,51 @@ func TestTraceCall(t *testing.T) { } genBlocks := 10 signer := types.HomesteadSigner{} + nonce := uint64(0) backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) + nonce++ + + if i == genBlocks-2 { + // Transfer from account[0] to account[2] + tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &accounts[2].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) + b.AddTx(tx) + nonce++ + + // Transfer from account[0] to account[1] again + tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) + b.AddTx(tx) + nonce++ + } }) + + uintPtr := func(i int) *hexutil.Uint { x := hexutil.Uint(i); return &x } + defer backend.teardown() api := NewAPI(backend) var testSuite = []struct { @@ -240,6 +278,51 @@ func TestTraceCall(t *testing.T) { expectErr: nil, expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, }, + // Upon the last state, default to the post block's state + { + blockNumber: rpc.BlockNumber(genBlocks - 1), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: nil, + expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + }, + // Before the first transaction, should be failed + { + blockNumber: rpc.BlockNumber(genBlocks - 1), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: &TraceCallConfig{TxIndex: uintPtr(0)}, + expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), + }, + // Before the target transaction, should be failed + { + blockNumber: rpc.BlockNumber(genBlocks - 1), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: &TraceCallConfig{TxIndex: uintPtr(1)}, + expectErr: fmt.Errorf("tracing failed: insufficient funds for gas * price + value: address %s have 1000000000000000000 want 1000000000000000100", accounts[2].addr), + }, + // After the target transaction, should be succeed + { + blockNumber: rpc.BlockNumber(genBlocks - 1), + call: ethapi.TransactionArgs{ + From: &accounts[2].addr, + To: &accounts[0].addr, + Value: (*hexutil.Big)(new(big.Int).Add(big.NewInt(params.Ether), big.NewInt(100))), + }, + config: &TraceCallConfig{TxIndex: uintPtr(2)}, + expectErr: nil, + expect: `{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}`, + }, // Standard JSON trace upon the non-existent block, error expects { blockNumber: rpc.BlockNumber(genBlocks + 1), @@ -297,8 +380,8 @@ func TestTraceCall(t *testing.T) { t.Errorf("test %d: expect error %v, got nothing", i, testspec.expectErr) continue } - if !reflect.DeepEqual(err, testspec.expectErr) { - t.Errorf("test %d: error mismatch, want %v, git %v", i, testspec.expectErr, err) + if !reflect.DeepEqual(err.Error(), testspec.expectErr.Error()) { + t.Errorf("test %d: error mismatch, want '%v', got '%v'", i, testspec.expectErr, err) } } else { if err != nil { @@ -327,7 +410,7 @@ func TestTraceTransaction(t *testing.T) { accounts := newAccounts(2) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, }, @@ -338,7 +421,14 @@ func TestTraceTransaction(t *testing.T) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) target = tx.Hash() }) @@ -375,7 +465,7 @@ func TestTraceBlock(t *testing.T) { accounts := newAccounts(3) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -388,7 +478,14 @@ func TestTraceBlock(t *testing.T) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) txHash = tx.Hash() }) @@ -458,7 +555,7 @@ func TestTracingWithOverrides(t *testing.T) { storageAccount := common.Address{0x13, 37} genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, @@ -478,7 +575,14 @@ func TestTracingWithOverrides(t *testing.T) { // Transfer from account[0] to account[1] // value: 1000 wei // fee: 0 wei - tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) + tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{ + Nonce: uint64(i), + To: &accounts[1].addr, + Value: big.NewInt(1000), + Gas: params.TxGas, + GasPrice: b.BaseFee(), + Data: nil}), + signer, accounts[0].key) b.AddTx(tx) }) defer backend.chain.Stop() @@ -819,7 +923,7 @@ func TestTraceChain(t *testing.T) { accounts := newAccounts(3) genesis := &core.Genesis{ Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{ + Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, diff --git a/eth/tracers/directory/util_test.go b/eth/tracers/directory/util_test.go index 904c749d06a3..731eeb832fc8 100644 --- a/eth/tracers/directory/util_test.go +++ b/eth/tracers/directory/util_test.go @@ -33,9 +33,9 @@ func TestMemCopying(t *testing.T) { {0, 100, 0, "", 0}, // No need to pad (0 size) {100, 50, 100, "", 100}, // Should pad 100-150 {100, 50, 5, "", 5}, // Wanted range fully within memory - {100, -50, 0, "offset or size must not be negative", 0}, // Errror - {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror - {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror + {100, -50, 0, "offset or size must not be negative", 0}, // Error + {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Error + {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Error } { mem := vm.NewMemory() diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 8ca877aea53a..a45ba2bf144f 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -223,10 +223,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { b.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - msg, err := core.TransactionToMessage(tx, signer, nil) - if err != nil { - b.Fatalf("failed to prepare transaction for tracing: %v", err) - } origin, _ := signer.Sender(tx) txContext := vm.TxContext{ Origin: origin, @@ -241,6 +237,10 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { Difficulty: (*big.Int)(test.Context.Difficulty), GasLimit: uint64(test.Context.GasLimit), } + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) + if err != nil { + b.Fatalf("failed to prepare transaction for tracing: %v", err) + } triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) defer triedb.Close() @@ -370,11 +370,11 @@ func TestInternals(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), - core.GenesisAlloc{ - to: core.GenesisAccount{ + types.GenesisAlloc{ + to: types.Account{ Code: tc.code, }, - origin: core.GenesisAccount{ + origin: types.Account{ Balance: big.NewInt(500000000000000), }, }, false, rawdb.HashScheme) diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 48a95f43c138..9bad16bc4c5e 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -107,6 +108,11 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { ) defer triedb.Close() + if test.Genesis.ExcessBlobGas != nil && test.Genesis.BlobGasUsed != nil { + excessBlobGas := eip4844.CalcExcessBlobGas(*test.Genesis.ExcessBlobGas, *test.Genesis.BlobGasUsed) + context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas) + } + tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig) if err != nil { t.Fatalf("failed to create call tracer: %v", err) diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json new file mode 100644 index 000000000000..315481aff536 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json @@ -0,0 +1,63 @@ +{ + "genesis": { + "baseFeePerGas": "7", + "blobGasUsed": "0", + "difficulty": "0", + "excessBlobGas": "36306944", + "extraData": "0xd983010e00846765746888676f312e32312e308664617277696e", + "gasLimit": "15639172", + "hash": "0xc682259fda061bb9ce8ccb491d5b2d436cb73daf04e1025dd116d045ce4ad28c", + "miner": "0x0000000000000000000000000000000000000000", + "mixHash": "0xae1a5ba939a4c9ac38aabeff361169fb55a6fc2c9511457e0be6eff9514faec0", + "nonce": "0x0000000000000000", + "number": "315", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x577f42ab21ccfd946511c57869ace0bdf7c217c36f02b7cd3459df0ed1cffc1a", + "timestamp": "1709626771", + "totalDifficulty": "1", + "withdrawals": [], + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "alloc": { + "0x0000000000000000000000000000000000000000": { + "balance": "0x272e0528" + }, + "0x0c2c51a0990aee1d73c1228de158688341557508": { + "balance": "0xde0b6b3a7640000" + } + }, + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true + } + }, + "context": { + "number": "316", + "difficulty": "0", + "timestamp": "1709626785", + "gasLimit": "15654443", + "miner": "0x0000000000000000000000000000000000000000" + }, + "input": "0x03f8b1820539806485174876e800825208940c2c51a0990aee1d73c1228de1586883415575088080c083020000f842a00100c9fbdf97f747e85847b4f3fff408f89c26842f77c882858bf2c89923849aa00138e3896f3c27f2389147507f8bcec52028b0efca6ee842ed83c9158873943880a0dbac3f97a532c9b00e6239b29036245a5bfbb96940b9d848634661abee98b945a03eec8525f261c2e79798f7b45a5d6ccaefa24576d53ba5023e919b86841c0675", + "result": { + "0x0000000000000000000000000000000000000000": { "balance": "0x272e0528" }, + "0x0c2c51a0990aee1d73c1228de158688341557508": { + "balance": "0xde0b6b3a7640000" + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_post_eip158.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_post_eip158.json new file mode 100644 index 000000000000..205b472dabe4 --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer/create_post_eip158.json @@ -0,0 +1,64 @@ +{ + "genesis": { + "baseFeePerGas": "7", + "difficulty": "2", + "extraData": "0xd983010d0e846765746888676f312e32312e318664617277696e0000000000001713699f05f79a59abec177c7a87b90ceda79b72ff5edc9197dd7627a447cde45b079bbc3765a236cdf680e2d4d2247135d0e6bb6fd92b50638b92504ddb274f00", + "gasLimit": "30000000", + "hash": "0x6ad5258175c66f4e883d238a92a08428d8ebcbeac631ab7b972634cc05effab3", + "miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "number": "39137", + "stateRoot": "0x715f00df764dbadd4863247a215ac44b5420beafde3ec458b15db7aafa89be0c", + "timestamp": "1709022192", + "totalDifficulty": "78275", + "alloc": { + "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": { + "balance": "0x10f06447a8d44dba190", + "nonce": "2" + }, + "0x82211934c340b29561381392348d48413e15adc8": { + "balance": "0x6abd7a808913ed2", + "nonce": "64" + } + }, + "config": { + "chainId": 12345, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "clique": { + "period": 5, + "epoch": 30000 + } + } + }, + "context": { + "number": "39138", + "difficulty": "2", + "timestamp": "1709022197", + "gasLimit": "30000000", + "miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0" + }, + "input": "0x02f902af823039408459682f008459682f088302b3538080b90254608060405234801561001057600080fd5b50610234806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033c001a0a8cf4729b7e4664687abb3e2559853d7d489eb441519be2a17493061fb4c3a03a04b5a904ba8a6e59c6c40049c4d14a73233aeb8a45b38403199f304630dc0d453", + "result": { + "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": { + "balance": "0x10f06447a8d44dba190", + "nonce": 2 + }, + "0x82211934c340b29561381392348d48413e15adc8": { + "balance": "0x6abd7a808913ed2", + "nonce": 64 + } + } +} diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json index e80dad5667ef..561ead05b6f8 100644 --- a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json @@ -83,7 +83,7 @@ }, "post": { "0x808b4da0be6c9512e948521452227efc619bea52": { - "balance": "0x2cd72a36dd031f089", + "balance": "0x2cd987071ba2346b6", "nonce": 1223933 }, "0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": { diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_post_eip158.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_post_eip158.json new file mode 100644 index 000000000000..83266f6669cf --- /dev/null +++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_post_eip158.json @@ -0,0 +1,82 @@ +{ + "genesis": { + "baseFeePerGas": "7", + "difficulty": "2", + "extraData": "0xd983010d0e846765746888676f312e32312e318664617277696e0000000000001713699f05f79a59abec177c7a87b90ceda79b72ff5edc9197dd7627a447cde45b079bbc3765a236cdf680e2d4d2247135d0e6bb6fd92b50638b92504ddb274f00", + "gasLimit": "30000000", + "hash": "0x6ad5258175c66f4e883d238a92a08428d8ebcbeac631ab7b972634cc05effab3", + "miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "number": "39137", + "stateRoot": "0x715f00df764dbadd4863247a215ac44b5420beafde3ec458b15db7aafa89be0c", + "timestamp": "1709022192", + "totalDifficulty": "78275", + "alloc": { + "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": { + "balance": "0x10f06447a8d44dba190", + "nonce": "2" + }, + "0x82211934c340b29561381392348d48413e15adc8": { + "balance": "0x6abd7a808913ed2", + "nonce": "64" + } + }, + "config": { + "chainId": 12345, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "clique": { + "period": 5, + "epoch": 30000 + } + } + }, + "context": { + "number": "39138", + "difficulty": "2", + "timestamp": "1709022197", + "gasLimit": "30000000", + "miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0" + }, + "input": "0x02f902af823039408459682f008459682f088302b3538080b90254608060405234801561001057600080fd5b50610234806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033c001a0a8cf4729b7e4664687abb3e2559853d7d489eb441519be2a17493061fb4c3a03a04b5a904ba8a6e59c6c40049c4d14a73233aeb8a45b38403199f304630dc0d453", + "tracerConfig": { + "diffMode": true + }, + "result": { + "post": { + "0x1bda2f8e4735507930bd6cfe873bf0bf0f4ab1de": { + "code": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033", + "nonce": 1 + }, + "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": { + "balance": "0x10f0645688331eb5690" + }, + "0x82211934c340b29561381392348d48413e15adc8": { + "balance": "0x6aae9b21b6ee855", + "nonce": 65 + } + }, + "pre": { + "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": { + "balance": "0x10f06447a8d44dba190", + "nonce": 2 + }, + "0x82211934c340b29561381392348d48413e15adc8": { + "balance": "0x6abd7a808913ed2", + "nonce": 64 + } + } + } +} diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 86454d376ae4..5c52ba1f1129 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -144,19 +144,29 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir vm: vm, ctx: make(map[string]goja.Value), } + + t.setTypeConverters() + t.setBuiltinFunctions() + if ctx == nil { ctx = new(directory.Context) } if ctx.BlockHash != (common.Hash{}) { - t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes()) + blockHash, err := t.toBuf(vm, ctx.BlockHash.Bytes()) + if err != nil { + return nil, err + } + t.ctx["blockHash"] = blockHash if ctx.TxHash != (common.Hash{}) { t.ctx["txIndex"] = vm.ToValue(ctx.TxIndex) - t.ctx["txHash"] = vm.ToValue(ctx.TxHash.Bytes()) + txHash, err := t.toBuf(vm, ctx.TxHash.Bytes()) + if err != nil { + return nil, err + } + t.ctx["txHash"] = txHash } } - t.setTypeConverters() - t.setBuiltinFunctions() ret, err := vm.RunString("(" + code + ")") if err != nil { return nil, err @@ -341,6 +351,12 @@ func (t *jsTracer) onEnd(output []byte, gasUsed uint64, err error, reverted bool if err != nil { t.ctx["error"] = t.vm.ToValue(err.Error()) } + outputVal, err := t.toBuf(t.vm, output) + if err != nil { + t.err = err + return + } + t.ctx["output"] = outputVal } // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). @@ -520,13 +536,13 @@ func (t *jsTracer) setBuiltinFunctions() { } return false }) - vm.Set("slice", func(slice goja.Value, start, end int) goja.Value { + vm.Set("slice", func(slice goja.Value, start, end int64) goja.Value { b, err := t.fromBuf(vm, slice, false) if err != nil { vm.Interrupt(err) return nil } - if start < 0 || start > end || end > len(b) { + if start < 0 || start > end || end > int64(len(b)) { vm.Interrupt(fmt.Sprintf("Tracer accessed out of bound memory: available %d, offset %d, size %d", len(b), start, end-start)) return nil } diff --git a/eth/tracers/js/internal/tracers/call_tracer_legacy.js b/eth/tracers/js/internal/tracers/call_tracer_legacy.js index 451a644b917a..0760bb1e3f64 100644 --- a/eth/tracers/js/internal/tracers/call_tracer_legacy.js +++ b/eth/tracers/js/internal/tracers/call_tracer_legacy.js @@ -219,7 +219,7 @@ return this.finalize(result); }, - // finalize recreates a call object using the final desired field oder for json + // finalize recreates a call object using the final desired field order for json // serialization. This is a nicety feature to pass meaningfully ordered results // to users who don't interpret it, just display it. finalize: function(call) { diff --git a/eth/tracers/logger/gen_structlog.go b/eth/tracers/logger/gen_structlog.go index df06a9ee6b66..b406cb344546 100644 --- a/eth/tracers/logger/gen_structlog.go +++ b/eth/tracers/logger/gen_structlog.go @@ -23,7 +23,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) { GasCost math.HexOrDecimal64 `json:"gasCost"` Memory hexutil.Bytes `json:"memory,omitempty"` MemorySize int `json:"memSize"` - Stack []uint256.Int `json:"stack"` + Stack []hexutil.U256 `json:"stack"` ReturnData hexutil.Bytes `json:"returnData,omitempty"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` @@ -39,7 +39,12 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.GasCost = math.HexOrDecimal64(s.GasCost) enc.Memory = s.Memory enc.MemorySize = s.MemorySize - enc.Stack = s.Stack + if s.Stack != nil { + enc.Stack = make([]hexutil.U256, len(s.Stack)) + for k, v := range s.Stack { + enc.Stack[k] = hexutil.U256(v) + } + } enc.ReturnData = s.ReturnData enc.Storage = s.Storage enc.Depth = s.Depth @@ -59,7 +64,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { GasCost *math.HexOrDecimal64 `json:"gasCost"` Memory *hexutil.Bytes `json:"memory,omitempty"` MemorySize *int `json:"memSize"` - Stack []uint256.Int `json:"stack"` + Stack []hexutil.U256 `json:"stack"` ReturnData *hexutil.Bytes `json:"returnData,omitempty"` Storage map[common.Hash]common.Hash `json:"-"` Depth *int `json:"depth"` @@ -89,7 +94,10 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.MemorySize = *dec.MemorySize } if dec.Stack != nil { - s.Stack = dec.Stack + s.Stack = make([]uint256.Int, len(dec.Stack)) + for k, v := range dec.Stack { + s.Stack[k] = uint256.Int(v) + } } if dec.ReturnData != nil { s.ReturnData = *dec.ReturnData diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 7e2e7aeff8bf..9d6f4c8a196d 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -85,6 +85,7 @@ type structLogMarshaling struct { GasCost math.HexOrDecimal64 Memory hexutil.Bytes ReturnData hexutil.Bytes + Stack []hexutil.U256 OpName string `json:"opName"` // adds call to OpName() in MarshalJSON ErrorString string `json:"error,omitempty"` // adds call to ErrorString() in MarshalJSON } @@ -418,11 +419,6 @@ func (t *mdLogger) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.O fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err) } -func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { - fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", - output, gasUsed, err) -} - // ExecutionResult groups all structured logs emitted by the EVM // while replaying a transaction in debug mode as well as transaction // execution status, the amount of gas used and the return value diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 47d388d312f3..cd53736ed9ee 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -155,6 +155,9 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory. func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { t.tracer.OnEnter(depth, typ, from, to, input, gas, value) + if depth == 0 { + return + } // Child calls must have a value, even if it's zero. // Practically speaking, only STATICCALL has nil value. Set it to zero. if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil { @@ -167,6 +170,9 @@ func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to co func (t *flatCallTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { t.tracer.OnExit(depth, output, gasUsed, err, reverted) + if depth == 0 { + return + } // Parity traces don't include CALL/STATICCALLs to precompiles. // By default we remove them from the callstack. if t.config.IncludePrecompiles { diff --git a/les/api_backend.go b/les/api_backend.go index 3e9dbadce86b..07ec02552e20 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -332,6 +332,6 @@ func (b *LesApiBackend) StateAtBlock(ctx context.Context, block *types.Block, re return b.eth.stateAtBlock(ctx, block, reexec) } -func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { +func (b *LesApiBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { return b.eth.stateAtTransaction(ctx, block, txIndex, reexec) } diff --git a/les/state_accessor.go b/les/state_accessor.go index 9a8214ac2f8f..a9c8e2dea852 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -39,7 +39,7 @@ func (leth *LightEthereum) stateAtBlock(ctx context.Context, block *types.Block, } // stateAtTransaction returns the execution environment of a certain transaction. -func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { +func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) { // Short circuit if it's genesis block. if block.NumberU64() == 0 { return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis") @@ -65,7 +65,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. context := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) statedb.SetTxContext(tx.Hash(), idx) if idx == txIndex { - return msg, context, statedb, release, nil + return tx, context, statedb, release, nil } // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, leth.blockchain.Config(), vm.Config{}) From 7ea867c72711788688c89baf1a7aae44221088ca Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 15 Mar 2024 17:16:44 -0400 Subject: [PATCH 46/50] Reverted some changes that are not required anymore with the new tracing interface The new tracing interface avoids an import cycle we have before around `core`, `core/vm` and `core/state`. So I can revert some of the changes needed previously. --- core/state/statedb.go | 5 ++++- core/state/statedb_test.go | 20 ++++++++++---------- core/vm/interface.go | 5 +---- eth/gasestimator/gasestimator.go | 6 ++---- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 6552f59b7d65..37565f97b30e 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/state/snapshot" "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/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -701,7 +702,7 @@ func (s *StateDB) CreateAccount(addr common.Address) { // Copy creates a deep, independent copy of the state. // Snapshots of the copied state cannot be applied to the copy. -func (s *StateDB) Copy() interface{} { +func (s *StateDB) Copy() vm.StateDB { // Copy all the basic fields, initialize the memory ones state := &StateDB{ db: s.db, @@ -1409,6 +1410,8 @@ func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount) return ret } +func (s *StateDB) SetEVM(evm *vm.EVM) {} + // copySet returns a deep-copied set. func copySet[k comparable](set map[k][]byte) map[k][]byte { copied := make(map[k][]byte, len(set)) diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 1f3a145c5d1d..213b32ca0214 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -174,24 +174,24 @@ func TestCopy(t *testing.T) { orig.Finalise(false) // Copy the state - copy := orig.Copy().(*StateDB) + copy := orig.Copy() // Copy the copy state - ccopy := copy.Copy().(*StateDB) + ccopy := copy.Copy() // modify all in memory for i := byte(0); i < 255; i++ { origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) + copyObj := copy.(*StateDB).GetOrNewStateObject(common.BytesToAddress([]byte{i})) + ccopyObj := ccopy.(*StateDB).GetOrNewStateObject(common.BytesToAddress([]byte{i})) origObj.AddBalance(big.NewInt(2*int64(i)), tracing.BalanceChangeUnspecified) copyObj.AddBalance(big.NewInt(3*int64(i)), tracing.BalanceChangeUnspecified) ccopyObj.AddBalance(big.NewInt(4*int64(i)), tracing.BalanceChangeUnspecified) orig.updateStateObject(origObj) - copy.updateStateObject(copyObj) - ccopy.updateStateObject(copyObj) + copy.(*StateDB).updateStateObject(copyObj) + ccopy.(*StateDB).updateStateObject(copyObj) } // Finalise the changes on all concurrently @@ -203,15 +203,15 @@ func TestCopy(t *testing.T) { var wg sync.WaitGroup wg.Add(3) go finalise(&wg, orig) - go finalise(&wg, copy) - go finalise(&wg, ccopy) + go finalise(&wg, copy.(*StateDB)) + go finalise(&wg, ccopy.(*StateDB)) wg.Wait() // Verify that the three states have been updated independently for i := byte(0); i < 255; i++ { origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) - ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) + copyObj := copy.(*StateDB).GetOrNewStateObject(common.BytesToAddress([]byte{i})) + ccopyObj := ccopy.(*StateDB).GetOrNewStateObject(common.BytesToAddress([]byte{i})) if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 { t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) diff --git a/core/vm/interface.go b/core/vm/interface.go index c2a4fe7e96f1..8d6431310bac 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -88,15 +88,12 @@ type StateDB interface { Finalise(deleteEmptyObjects bool) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, error) SetTxContext(thash common.Hash, ti int) - Copy() interface{} + Copy() StateDB IntermediateRoot(deleteEmptyObjects bool) common.Hash GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log TxIndex() int Preimages() map[common.Hash][]byte SetLogger(logger *tracing.Hooks) -} - -type EVMAssignable interface { SetEVM(evm *EVM) } diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index d06afd032a0c..1c19930d81a5 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -209,13 +209,11 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio msgContext = core.NewEVMTxContext(call) evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil) - dirtyState = opts.State.Copy().(vm.StateDB) + dirtyState = opts.State.Copy() evm = vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) ) + dirtyState.SetEVM(evm) - if assignable, ok := dirtyState.(vm.EVMAssignable); ok { - assignable.SetEVM(evm) - } // Monitor the outer context and interrupt the EVM upon cancellation. To avoid // a dangling goroutine until the outer estimation finishes, create an internal // context for the lifetime of this method call. From 313a43d09da98180d565b005641a9a4eff27a67e Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 18 Mar 2024 10:36:12 +0100 Subject: [PATCH 47/50] cmd/evm: fix tracing This commit fixes several missing interface-checks, where the code did not properly verify a Hook before calling it. It also fixes an error where the tracer was not set in the vmconfig during t8n initialization. It also adds a testcase which verifies the tracing-output during t8n executio. The testcase currently only checks the jsonlogger, but can be extended. This change also removes a spurious 'null' output in the tracing, when tracing result is null. --- cmd/evm/internal/t8ntool/execution.go | 14 +-- cmd/evm/t8n_test.go | 89 +++++++++++++++++++ cmd/evm/testdata/31/README.md | 1 + cmd/evm/testdata/31/alloc.json | 16 ++++ cmd/evm/testdata/31/env.json | 20 +++++ ...7543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl | 6 ++ cmd/evm/testdata/31/txs.json | 14 +++ 7 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 cmd/evm/testdata/31/README.md create mode 100644 cmd/evm/testdata/31/alloc.json create mode 100644 cmd/evm/testdata/31/env.json create mode 100644 cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl create mode 100644 cmd/evm/testdata/31/txs.json diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 52e5bbe9de92..88ac13d1f076 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -228,7 +228,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if err != nil { return nil, nil, nil, err } - if vmConfig.Tracer != nil { + if tracer != nil { vmConfig.Tracer = tracer.Hooks } statedb.SetTxContext(tx.Hash(), txIndex) @@ -240,7 +240,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, ) evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig) - if tracer != nil { + if tracer != nil && tracer.OnTxStart != nil { tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) } // (ret []byte, usedGas uint64, failed bool, err error) @@ -251,7 +251,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) gaspool.SetGas(prevGas) if tracer != nil { - tracer.OnTxEnd(nil, err) + if tracer.OnTxEnd != nil { + tracer.OnTxEnd(nil, err) + } if err := writeTraceResult(tracer, traceOutput); err != nil { log.Warn("Error writing tracer output", "err", err) } @@ -298,7 +300,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, receipt.TransactionIndex = uint(txIndex) receipts = append(receipts, receipt) if tracer != nil { - tracer.Hooks.OnTxEnd(receipt, nil) + if tracer.Hooks.OnTxEnd != nil { + tracer.Hooks.OnTxEnd(receipt, nil) + } writeTraceResult(tracer, traceOutput) } } @@ -418,7 +422,7 @@ func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime func writeTraceResult(tracer *directory.Tracer, f io.WriteCloser) error { defer f.Close() result, err := tracer.GetResult() - if err != nil { + if err != nil || result == nil { return err } err = json.NewEncoder(f).Encode(result) diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index 03503d11c3b1..bb36434f52c3 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -17,9 +17,12 @@ package main import ( + "bufio" "encoding/json" "fmt" + "io" "os" + "path/filepath" "reflect" "strings" "testing" @@ -320,6 +323,92 @@ func TestT8n(t *testing.T) { } } +func lineIterator(path string) func() (string, error) { + data, err := os.ReadFile(path) + if err != nil { + return func() (string, error) { return err.Error(), err } + } + scanner := bufio.NewScanner(strings.NewReader(string(data))) + return func() (string, error) { + if scanner.Scan() { + return scanner.Text(), nil + } + if err := scanner.Err(); err != nil { + return "", err + } + return "", io.EOF // scanner gobbles io.EOF, but we want it + } +} + +// TestT8nTracing is a test that checks the tracing-output from t8n. +func TestT8nTracing(t *testing.T) { + t.Parallel() + tt := new(testT8n) + tt.TestCmd = cmdtest.NewTestCmd(t, tt) + for i, tc := range []struct { + base string + input t8nInput + expExitCode int + expectedTraces []string + }{ + { + base: "./testdata/31", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Cancun", "", + }, + expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"}, + }, + } { + args := []string{"t8n"} + args = append(args, tc.input.get(tc.base)...) + // Add tracing-args + args = append(args, "--trace") + // Place the output somewhere we can find it + outdir := t.TempDir() + + args = append(args, "--output.basedir", outdir) + + var qArgs []string // quoted args for debugging purposes + for _, arg := range args { + if len(arg) == 0 { + qArgs = append(qArgs, `""`) + } else { + qArgs = append(qArgs, arg) + } + } + tt.Logf("args: %v\n", strings.Join(qArgs, " ")) + tt.Run("evm-test", args...) + tt.WaitExit() + // Compare the expected traces + for _, traceFile := range tc.expectedTraces { + haveFn := lineIterator(filepath.Join(outdir, traceFile)) + wantFn := lineIterator(filepath.Join(tc.base, traceFile)) + + for line := 0; ; line++ { + want, wErr := wantFn() + have, hErr := haveFn() + if want != have { + t.Fatalf("test %d, trace %v, line %d\nwant: %v\nhave: %v\n", + i, traceFile, line, want, have) + } + if wErr != nil && hErr != nil { + break + } + if wErr != nil { + t.Fatal(wErr) + } + if hErr != nil { + t.Fatal(hErr) + } + t.Logf("%v\n", want) + } + } + if have, want := tt.ExitStatus(), tc.expExitCode; have != want { + t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want) + } + } +} + type t9nInput struct { inTxs string stFork string diff --git a/cmd/evm/testdata/31/README.md b/cmd/evm/testdata/31/README.md new file mode 100644 index 000000000000..305e4f52da07 --- /dev/null +++ b/cmd/evm/testdata/31/README.md @@ -0,0 +1 @@ +This test does some EVM execution, and can be used to test the tracers and trace-outputs. diff --git a/cmd/evm/testdata/31/alloc.json b/cmd/evm/testdata/31/alloc.json new file mode 100644 index 000000000000..bad5481c4a31 --- /dev/null +++ b/cmd/evm/testdata/31/alloc.json @@ -0,0 +1,16 @@ +{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x016345785d8a0000", + "code" : "0x", + "nonce" : "0x00", + "storage" : { + } + }, + "0x1111111111111111111111111111111111111111" : { + "balance" : "0x1", + "code" : "0x604060406040604000", + "nonce" : "0x00", + "storage" : { + } + } +} \ No newline at end of file diff --git a/cmd/evm/testdata/31/env.json b/cmd/evm/testdata/31/env.json new file mode 100644 index 000000000000..09b5f12d8834 --- /dev/null +++ b/cmd/evm/testdata/31/env.json @@ -0,0 +1,20 @@ +{ + "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentNumber" : "0x01", + "currentTimestamp" : "0x03e8", + "currentGasLimit" : "0x1000000000", + "previousHash" : "0xe4e2a30b340bec696242b67584264f878600dce98354ae0b6328740fd4ff18da", + "currentDataGasUsed" : "0x2000", + "parentTimestamp" : "0x00", + "parentDifficulty" : "0x00", + "parentUncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "parentBeaconBlockRoot" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "currentRandom" : "0x0000000000000000000000000000000000000000000000000000000000020000", + "withdrawals" : [ + ], + "parentBaseFee" : "0x08", + "parentGasUsed" : "0x00", + "parentGasLimit" : "0x1000000000", + "parentExcessBlobGas" : "0x1000", + "parentBlobGasUsed" : "0x2000" +} \ No newline at end of file diff --git a/cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl b/cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl new file mode 100644 index 000000000000..26e5c7ee4ef5 --- /dev/null +++ b/cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl @@ -0,0 +1,6 @@ +{"pc":0,"op":96,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":2,"op":96,"gas":"0x13495","gasCost":"0x3","memSize":0,"stack":["0x40"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":4,"op":96,"gas":"0x13492","gasCost":"0x3","memSize":0,"stack":["0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":6,"op":96,"gas":"0x1348f","gasCost":"0x3","memSize":0,"stack":["0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"PUSH1"} +{"pc":8,"op":0,"gas":"0x1348c","gasCost":"0x0","memSize":0,"stack":["0x40","0x40","0x40","0x40"],"depth":1,"refund":0,"opName":"STOP"} +{"output":"","gasUsed":"0xc"} diff --git a/cmd/evm/testdata/31/txs.json b/cmd/evm/testdata/31/txs.json new file mode 100644 index 000000000000..473c1526f40b --- /dev/null +++ b/cmd/evm/testdata/31/txs.json @@ -0,0 +1,14 @@ +[ + { + "gas": "0x186a0", + "gasPrice": "0x600", + "input": "0x", + "nonce": "0x0", + "to": "0x1111111111111111111111111111111111111111", + "value": "0x1", + "v" : "0x0", + "r" : "0x0", + "s" : "0x0", + "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" + } +] From a44b99b71607a3f64806b7e65182c7210cb8e658 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 18 Mar 2024 11:58:51 +0100 Subject: [PATCH 48/50] cmd/evm: add testcase for js-tracer --- cmd/evm/t8n_test.go | 23 +++++++++++++++---- ...47543268a5aaf2a6b32a69d2c6d978c45dcfb.json | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index bb36434f52c3..06134d2a6a8f 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -349,6 +349,7 @@ func TestT8nTracing(t *testing.T) { base string input t8nInput expExitCode int + extraArgs []string expectedTraces []string }{ { @@ -356,17 +357,30 @@ func TestT8nTracing(t *testing.T) { input: t8nInput{ "alloc.json", "txs.json", "env.json", "Cancun", "", }, + extraArgs: []string{"--trace"}, expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.jsonl"}, }, + { + base: "./testdata/31", + input: t8nInput{ + "alloc.json", "txs.json", "env.json", "Cancun", "", + }, + extraArgs: []string{"--trace.tracer", ` +{ + result: function(){ + return "hello world" + }, + fault: function(){} +}`}, + expectedTraces: []string{"trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json"}, + }, } { args := []string{"t8n"} args = append(args, tc.input.get(tc.base)...) - // Add tracing-args - args = append(args, "--trace") // Place the output somewhere we can find it outdir := t.TempDir() - args = append(args, "--output.basedir", outdir) + args = append(args, tc.extraArgs...) var qArgs []string // quoted args for debugging purposes for _, arg := range args { @@ -378,7 +392,8 @@ func TestT8nTracing(t *testing.T) { } tt.Logf("args: %v\n", strings.Join(qArgs, " ")) tt.Run("evm-test", args...) - tt.WaitExit() + t.Log(string(tt.Output())) + // Compare the expected traces for _, traceFile := range tc.expectedTraces { haveFn := lineIterator(filepath.Join(outdir, traceFile)) diff --git a/cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json b/cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json new file mode 100644 index 000000000000..cd4bc1ab64cc --- /dev/null +++ b/cmd/evm/testdata/31/trace-0-0x88f5fbd1524731a81e49f637aa847543268a5aaf2a6b32a69d2c6d978c45dcfb.json @@ -0,0 +1 @@ +"hello world" From 8ae03dd42a5ec17fd828cf1a084b943f7f4afee5 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 18 Mar 2024 13:11:21 +0100 Subject: [PATCH 49/50] core/vm: documentation + minor refactoring --- core/vm/interpreter.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index a8fdac855de7..c4d62b9e85ee 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -43,6 +43,8 @@ type ScopeContext struct { Contract *Contract } +// MemoryData returns the underlying memory slice. Callers must not modify the contents +// of the returned data. func (ctx *ScopeContext) MemoryData() []byte { if ctx.Memory == nil { return nil @@ -50,6 +52,8 @@ func (ctx *ScopeContext) MemoryData() []byte { return ctx.Memory.Data() } +// MemoryData returns the stack data. Callers must not modify the contents +// of the returned data. func (ctx *ScopeContext) StackData() []uint256.Int { if ctx.Stack == nil { return nil @@ -57,18 +61,23 @@ func (ctx *ScopeContext) StackData() []uint256.Int { return ctx.Stack.Data() } +// Caller returns the current caller. func (ctx *ScopeContext) Caller() common.Address { return ctx.Contract.Caller() } +// Address returns the address where this scope of execution is taking place. func (ctx *ScopeContext) Address() common.Address { return ctx.Contract.Address() } +// CallValue returns the value supplied with this call. func (ctx *ScopeContext) CallValue() *big.Int { return ctx.Contract.Value() } +// CallInput returns the input/calldata with this call. Callers must not modify +// the contents of the returned data. func (ctx *ScopeContext) CallInput() []byte { return ctx.Contract.Input } @@ -189,15 +198,15 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( contract.Input = input if debug { - defer func() { - if err != nil { - if !logged { - if in.evm.Config.Tracer.OnOpcode != nil { - in.evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) - } - } else if in.evm.Config.Tracer.OnFault != nil { - in.evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) - } + defer func() { // this deferred method handles exit-with-error + if err == nil { + return + } + if !logged && in.evm.Config.Tracer.OnOpcode != nil { + in.evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err)) + } + if logged && in.evm.Config.Tracer.OnFault != nil { + in.evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err)) } }() } From c76d4f01cecf4fa096549c886056ac74e2608cbe Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 18 Mar 2024 13:26:53 +0100 Subject: [PATCH 50/50] eth/tracers, cmd/evm: unexport JSONlogger --- cmd/evm/blockrunner.go | 2 +- cmd/evm/internal/t8ntool/transition.go | 4 ++-- cmd/evm/runner.go | 2 +- cmd/evm/staterunner.go | 2 +- core/blockchain_test.go | 4 ++-- eth/tracers/api.go | 2 +- eth/tracers/logger/logger_json.go | 18 +++++++----------- tests/state_test.go | 2 +- 8 files changed, 16 insertions(+), 20 deletions(-) diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 07af0bc9a24a..94527e7f6617 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -58,7 +58,7 @@ func blockTestCmd(ctx *cli.Context) error { DisableStack: ctx.Bool(DisableStackFlag.Name), DisableStorage: ctx.Bool(DisableStorageFlag.Name), EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), - }, os.Stderr).Hooks() + }, os.Stderr) } // Load the test content from the input file src, err := os.ReadFile(ctx.Args().First()) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index 2600a6759e5a..9424a62c1fff 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -124,10 +124,10 @@ func Transition(ctx *cli.Context) error { if err != nil { return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err)) } - logger := logger.NewJSONLogger(logConfig, traceFile).Hooks() + logger := logger.NewJSONLogger(logConfig, traceFile) tracer := &directory.Tracer{ Hooks: logger, - // JSONLogger streams out result to file. + // jsonLogger streams out result to file. GetResult: func() (json.RawMessage, error) { return nil, nil }, Stop: func(err error) {}, } diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 3d481d5fe70c..d1d28be3bdc5 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -128,7 +128,7 @@ func runCmd(ctx *cli.Context) error { blobBaseFee = new(big.Int) // TODO (MariusVanDerWijden) implement blob fee in state tests ) if ctx.Bool(MachineFlag.Name) { - tracer = logger.NewJSONLogger(logconfig, os.Stdout).Hooks() + tracer = logger.NewJSONLogger(logconfig, os.Stdout) } else if ctx.Bool(DebugFlag.Name) { debugLogger = logger.NewStructLogger(logconfig) tracer = debugLogger.Hooks() diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 0bb8f73439ac..e99bae246fac 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -61,7 +61,7 @@ func stateTestCmd(ctx *cli.Context) error { var cfg vm.Config switch { case ctx.Bool(MachineFlag.Name): - cfg.Tracer = logger.NewJSONLogger(config, os.Stderr).Hooks() + cfg.Tracer = logger.NewJSONLogger(config, os.Stderr) case ctx.Bool(DebugFlag.Name): cfg.Tracer = logger.NewStructLogger(config).Hooks() diff --git a/core/blockchain_test.go b/core/blockchain_test.go index b5fa0cffa26a..85c9a292cc6a 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -3248,7 +3248,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout).Hooks(), + Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) @@ -3330,7 +3330,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) { }) // Import the canonical chain chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{ - Tracer: logger.NewJSONLogger(nil, os.Stdout).Hooks(), + Tracer: logger.NewJSONLogger(nil, os.Stdout), }, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index e6fb576416b7..7cab53d1c5a6 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -783,7 +783,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Swap out the noop logger to the standard tracer writer = bufio.NewWriter(dump) vmConf = vm.Config{ - Tracer: logger.NewJSONLogger(&logConfig, writer).Hooks(), + Tracer: logger.NewJSONLogger(&logConfig, writer), EnablePreimageRecording: true, } } diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index e9a450896950..6fac2d115922 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -27,7 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" ) -type JSONLogger struct { +type jsonLogger struct { encoder *json.Encoder cfg *Config env *tracing.VMContext @@ -35,15 +35,11 @@ type JSONLogger struct { // NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects // into the provided stream. -func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger { - l := &JSONLogger{encoder: json.NewEncoder(writer), cfg: cfg} +func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks { + l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg} if l.cfg == nil { l.cfg = &Config{} } - return l -} - -func (l *JSONLogger) Hooks() *tracing.Hooks { return &tracing.Hooks{ OnTxStart: l.OnTxStart, OnExit: l.OnExit, @@ -52,12 +48,12 @@ func (l *JSONLogger) Hooks() *tracing.Hooks { } } -func (l *JSONLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { +func (l *jsonLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) { // TODO: Add rData to this interface as well l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err) } -func (l *JSONLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { +func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { memory := scope.MemoryData() stack := scope.StackData() @@ -83,7 +79,7 @@ func (l *JSONLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracin l.encoder.Encode(log) } -func (l *JSONLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { +func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { if depth > 0 { return } @@ -99,6 +95,6 @@ func (l *JSONLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg}) } -func (l *JSONLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (l *jsonLogger) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { l.env = env } diff --git a/tests/state_test.go b/tests/state_test.go index bf098055d4ec..094dafcafd7a 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -152,7 +152,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { } buf := new(bytes.Buffer) w := bufio.NewWriter(buf) - config.Tracer = logger.NewJSONLogger(&logger.Config{}, w).Hooks() + config.Tracer = logger.NewJSONLogger(&logger.Config{}, w) err2 := test(config) if !reflect.DeepEqual(err, err2) { t.Errorf("different error for second run: %v", err2)