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/42] 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 bcab461a43..5fb9631f1b 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/42] 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 | 166 +++++++-------- 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, 2140 insertions(+), 842 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 a26ee12e0a..3bb0e8651b 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 5cac5f07f8..5b9a2e417c 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 de19dbc851..16c9cd2e89 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 0000000000..8a850f1d98 --- /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 d517592e5c..e41ff313e0 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 5663963e3c..2627c6caf3 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 027dac7bd6..fa1ec6f279 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 2d4fe3dc06..7aee35753d 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 e9a7c7c110..6878a46e39 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 e856f4e6ce..9ab9026fa8 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 8eb9863da1..5c00e5c7bc 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 96995616de..bc393a9cbc 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 f458da8257..f9278d1908 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 bc6f8112f0..5f9892902b 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 31c111b73e..2949162bfe 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 c4801dc797..8b724ac2cf 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 1045815fab..edd323f67d 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 0000000000..6978e67075 --- /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 d42d2c34d8..f9cc0109a2 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 2553133dea..26a2838977 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 195e463c28..618b1cf042 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 c4704257c7..592d34b466 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 ad829a0c8f..f6074917e3 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 6196e77817..9ccb1475ea 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 b190567e92..a376c790c3 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 9a4333f723..b468dce3af 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 612fdd7813..d734467443 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 8914301e14..da830c4a77 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 a73c1bb8a7..bf4f52b035 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 a8f3dd7d86..a175df0324 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 e4b03bd74f..f7dae713d1 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 574bb9bef6..bb5dc039f0 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 f40e2c8f9e..04aa8a116d 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 fbbf19e178..9f6605f030 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 088b18aaa4..4960d08c77 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 56ff350201..b9c5b153b7 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 26814d3d2f..e96e4502fb 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 28da2e80e6..3eff0d25db 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 2667908a84..e86bb5d98e 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 04c6409ebd..55c9bf34b9 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 d10457e7fa..65fbd289df 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 796d3b4434..6e244759b8 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 3d3444a871..b93afbab06 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 09559f0ac1..1ba7552d76 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 5e8f58efdb..3ff1ae0013 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 300d904a99..30f0b23a56 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 0f78af9a01..52db08e9de 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 0000000000..dc6927b142 --- /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 3beecd8abf..e784b6c846 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(gasLimit uint64) {} +func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {} -func (*noopTracer) CaptureTxEnd(restGas uint64) {} +func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} + +func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { +} + +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) 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 7b43b7cf83..b16d9d2fe6 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 0000000000..92de735291 --- /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 0000000000..c72096708d --- /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 0000000000..8c3d5d275f --- /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 5c74baacd1..e518874549 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) } - st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) - if _, err := st.TransitionDb(); err != nil { + txContext := vm.TxContext{ + Origin: origin, + GasPrice: tx.GasPrice(), + } + 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 423167b13c..4b56dfd6b9 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 0000000000..306f107190 --- /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 b4fa5b6272..9a9189e399 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 9b45b52fe9..ed3688a942 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 c796804a4b..a2386ea9c7 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 4de08f2cca..611e50e2c0 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 28e96684b2..f3a7d9a946 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 74fd87cc6c..3c5d6d9f2b 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 96060d5545..6911ed4b32 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 0000000000..c46fe080f7 --- /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 0000000000..909a1eabe3 --- /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 95d292c924..1913f352dd 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 d22d140988..c3a9508202 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 bf6427faf6..11c44fbcc4 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 0000000000..fc6e759005 --- /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 766ee4e4b9..7df66066b5 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 4c9b910a27..ae64dc8625 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 a2cb4cd9fc..1bf88f8899 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 3192a15cba..a244e3f9ca 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 5a2c4f9111..c7c45cfaa4 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 f85cf6206a..43bd0198da 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 266ab99001..26f114f44f 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 db8ddd6438..1175cc768a 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) + t.CaptureExit(output, gasUsed, err, reverted) } } -func (t *muxTracer) CaptureTxStart(gasLimit uint64) { +func (t *muxTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) { for _, t := range t.tracers { - t.CaptureTxStart(gasLimit) + t.CaptureTxStart(env, tx, from) } } -func (t *muxTracer) CaptureTxEnd(restGas uint64) { +func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) { for _, t := range t.tracers { - t.CaptureTxEnd(restGas) + 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.OnStorageChange(a, k, prev, new) + } +} + +func (t *muxTracer) OnLog(log *types.Log) { + for _, t := range t.tracers { + t.OnLog(log) + } +} + +func (t *muxTracer) OnNewAccount(a common.Address, reset bool) { + for _, t := range t.tracers { + t.OnNewAccount(a, reset) } } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 82451c40a6..8608ab69dc 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) CaptureTxEnd(restGas uint64) { - if !t.config.DiffMode { - return +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(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 b4989ec984..ddfdb81ec6 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 38a7924124..4f792260ed 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 c415d73e7e..44450fc2ff 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 f680702814..a5ea9571a9 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 745a3c6b28..d959165e54 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/42] 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 f9278d1908..42648345d5 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 618b1cf042..838cbbe9a9 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 b468dce3af..231f63f969 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 0000000000..822fbbaea3 --- /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 fc6e759005..0000000000 --- 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/42] 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 838cbbe9a9..0c2a5b5aef 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 e784b6c846..5f6546cfbe 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 822fbbaea3..2984c6ee83 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 1175cc768a..70af63e752 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/42] 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 42648345d5..92e122cbaa 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 2984c6ee83..beb532a423 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/42] 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 fa1ec6f279..3f5303259b 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 6878a46e39..81f16458f8 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 5f9892902b..bc6f8112f0 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 dc6927b142..011d27f887 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 beb532a423..4ff4153c60 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/42] 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 92e122cbaa..33c9b0f7c9 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/42] 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 ae64dc8625..3c22bd7886 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 318dc843145303db11f2631a8f9b281a26b77be8 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 14 Feb 2024 16:52:10 +0100 Subject: [PATCH 09/42] 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 2627c6caf3..5c53bd2962 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 3f5303259b..95d59a04fa 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 7aee35753d..bb3e49b2ed 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 81f16458f8..2490e79fe5 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 011d27f887..39d1e99caa 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 10/42] 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 4ff4153c60..cc597adfc5 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 11/42] 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 33c9b0f7c9..74a226b78b 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 231f63f969..76dd0fdb1e 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 cc597adfc5..c565feced0 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 12/42] 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 74a226b78b..e08c939f1e 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 c565feced0..fe835e1d81 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 13/42] 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 95d59a04fa..d6ab18d805 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 2490e79fe5..abff65a09f 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 e08c939f1e..bb5a36ba44 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 f9cc0109a2..b6decf7717 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 0c2a5b5aef..5eabbcaeb3 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 3eff0d25db..7750517e06 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 1ba7552d76..abb6a7e7c2 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 3ff1ae0013..b5e2f2368c 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 39d1e99caa..f6656ac79f 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 fe835e1d81..f9b8e19def 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 14/42] 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 d6ab18d805..95d59a04fa 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 abff65a09f..2490e79fe5 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 9ab9026fa8..42bf2bd7f9 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 5c00e5c7bc..e09bfa10a0 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 bc393a9cbc..1f97527588 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 bb5a36ba44..babfcaf8f2 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 2949162bfe..dbaeb67f28 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 8b724ac2cf..515f8d78a9 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 edd323f67d..8e58bb134e 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 9ce6cd394b..1840e5b220 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 6978e67075..0000000000 --- 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 b6decf7717..2c7ffbf896 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 5eabbcaeb3..27bca3d2e8 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 76dd0fdb1e..776a104223 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 d734467443..f149d2a3a1 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 f7dae713d1..fbfc3b125d 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 bb5dc039f0..799665f0cb 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 4960d08c77..b291bcfbc6 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 b9c5b153b7..31296aa1a5 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 e96e4502fb..94aa015ed9 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 7750517e06..5f86299637 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 e86bb5d98e..269aed509c 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 55c9bf34b9..e9f26a88c1 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 abb6a7e7c2..1ba7552d76 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 b5e2f2368c..11e43e41b1 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 30f0b23a56..422b672086 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 f6656ac79f..9e89c0f36c 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 5f6546cfbe..9c28770494 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 b16d9d2fe6..4953c46c56 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 c3a9508202..f6ed3ed229 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 f9b8e19def..e099ef9ac5 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 7df66066b5..fdc990dfee 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 3c22bd7886..3e8a68e6e5 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 1bf88f8899..fecfa0cb73 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 c7c45cfaa4..aa2dbd906a 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 43bd0198da..74a13038d9 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 26f114f44f..4cd5308e40 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 70af63e752..501f598385 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 8608ab69dc..88cd0009fa 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 4f792260ed..39d79413bd 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 a5ea9571a9..95190d5c5d 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 15/42] 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 b291bcfbc6..a3fd2da531 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 9e89c0f36c..fca3654141 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 f6ed3ed229..3e0a4b592f 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 16/42] 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 92de735291..25f95642c8 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 c72096708d..904c749d06 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 88cd0009fa..7ac77ffaa9 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 17/42] 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 25f95642c8..f5a2cd80d3 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 3e0a4b592f..62115f40a2 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 18/42] 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 ff65574586..9c2f4578c8 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 5b9a2e417c..ea8069ba66 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 8a850f1d98..0000000000 --- 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 e41ff313e0..135b89fc1b 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 31296aa1a5..3a681775bb 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 5f86299637..6da530cd06 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 e518874549..fcc56180d1 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 4b56dfd6b9..7ae99d2b9d 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 9a9189e399..08e1eccc3c 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 11c44fbcc4..c5a3a7e2f8 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 a244e3f9ca..cd70f1a970 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 ddfdb81ec6..e3ce396374 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 ad1d34fb2b..5a021b245a 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 d959165e54..25b50a79e3 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 19/42] 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 ea8069ba66..bfcd9fde7d 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 45fc985351..e247f11f18 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 618ddf2ede..7d969ebad2 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 bc6f8112f0..aa41ddab8c 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 26a2838977..fe65b28bb4 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 592d34b466..5a6009103f 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 f6074917e3..0824c2b07e 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 9ccb1475ea..86c790a16a 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 a376c790c3..2a1956392d 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 da830c4a77..a5848fdf0b 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 bf4f52b035..621987b384 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 a175df0324..719b71bb34 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 65fbd289df..aba137c50c 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 6e244759b8..2997ca5351 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 b93afbab06..f14b96b436 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 3e8a68e6e5..d1db00f992 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 094dafcafd..05e7543bff 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 20/42] 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 9c2f4578c8..8e34deb7f2 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 135b89fc1b..20b5eee1b1 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 e247f11f18..53532aa763 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 7d969ebad2..5e26ff2ee6 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 aa41ddab8c..c3f77d70da 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 422b672086..0fb13696c3 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 d1db00f992..143db5752d 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 fecfa0cb73..43ba082beb 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 cd70f1a970..ddb558832d 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 e3ce396374..7f509b8ae6 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 05e7543bff..9759f9cb7a 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 21/42] 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 8e34deb7f2..ed439b6fb2 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 bfcd9fde7d..7129ba88de 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 53532aa763..259ea5220e 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 42bf2bd7f9..9f677cf861 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 e09bfa10a0..a31d9c0f1c 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 1f97527588..17e1b799d1 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 babfcaf8f2..b3f72bd213 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 515f8d78a9..52f8b0febf 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 8e58bb134e..16f8ec83a1 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 2c7ffbf896..cd718ae058 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 fe65b28bb4..d749e3219d 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 27bca3d2e8..0047a9c8e1 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 5a6009103f..b4b09a5cf7 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 0824c2b07e..28e850ba86 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 86c790a16a..9ee0010730 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 2a1956392d..a4abe18cc4 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 776a104223..d3ca3dde34 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 f149d2a3a1..b908adeb4c 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 0000000000..6f9795af3b --- /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 a5848fdf0b..e18b1c4caa 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 621987b384..dc62822c9f 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 719b71bb34..12b759490e 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 fbfc3b125d..291a426b3b 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 799665f0cb..6b2512570a 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 a3fd2da531..8064de4a86 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 3a681775bb..d06b59b121 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 94aa015ed9..f6d9cb30ad 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 6da530cd06..99d95f0160 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 e9f26a88c1..5986de2e7e 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 f14b96b436..4a5ecf3b7b 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 11e43e41b1..7abac3bec5 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 fca3654141..a097754310 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 9c28770494..a6bb457d2f 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 4953c46c56..78dc9ae6e7 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 62115f40a2..578b1a10af 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 c5a3a7e2f8..ac8fbe23fc 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 e099ef9ac5..ea843a38e7 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 fdc990dfee..51e03ba25d 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 143db5752d..8165ce5087 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 43ba082beb..76278c2bca 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 aa2dbd906a..8f83f068b2 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 74a13038d9..831b39cf7d 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 4cd5308e40..d0914e04a8 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 501f598385..92099133bf 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 7ac77ffaa9..f6483eb2cb 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 39d79413bd..305975775f 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 5a021b245a..37b89afc51 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 25b50a79e3..a12714181b 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 22/42] 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 ed439b6fb2..1f7d07f31a 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 20b5eee1b1..97a1101544 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 259ea5220e..47ae707063 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 5e26ff2ee6..0bb8f73439 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 c3f77d70da..b5fa0cffa2 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 6f9795af3b..6a89003677 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 0fb13696c3..d3f565edbe 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 51e03ba25d..a69c8b3048 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 8165ce5087..2398a803d8 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 76278c2bca..246b91bdf3 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 ddb558832d..c2982ab036 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 7f509b8ae6..505db4fa4e 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 305975775f..d1ac21b9d8 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 9759f9cb7a..bf098055d4 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 23/42] 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 6a89003677..3d78009f95 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 99d95f0160..b19e79531f 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 a6bb457d2f..2c250a066a 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 578b1a10af..c9bf5ab5b9 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 ea843a38e7..b5d8c3ae5b 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 a69c8b3048..7e76080a94 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 2398a803d8..3867749d46 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 246b91bdf3..72eda302d8 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 831b39cf7d..0b6790ce89 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 d0914e04a8..9eef26ca5e 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 92099133bf..68b600b923 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 f6483eb2cb..765356f10f 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 24/42] 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 3d78009f95..f7488e537d 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 25/42] 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 f7488e537d..e8584c2b64 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 26/42] 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 269aed509c..0000000000 --- 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 27/42] 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 7129ba88de..4e72d11b5b 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 d3ca3dde34..59e3c80c2a 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 e8584c2b64..f912a6c87d 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 8064de4a86..ba314b51da 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 d06b59b121..0f85f424e5 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 b19e79531f..4d867fbc05 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 aba137c50c..49415e50fa 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 2997ca5351..fdf8abbc1f 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 d3f565edbe..d0eefa84b1 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 2c250a066a..637f0da326 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 fcc56180d1..01c5ae1fdb 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 7ae99d2b9d..8cf9eae541 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 08e1eccc3c..a055ba334d 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 c9bf5ab5b9..3d96253b71 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 ac8fbe23fc..c1546ca055 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 b5d8c3ae5b..05560cfb24 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 7e76080a94..92df195fe0 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 3867749d46..6e694368b1 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 72eda302d8..5067fb8a58 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 8f83f068b2..a0a4a6ff4f 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 0b6790ce89..d42f36666b 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 9eef26ca5e..c0ec5c312e 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 68b600b923..4516ab1dff 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 765356f10f..5e053236a9 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 28/42] 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 | 48 ++++++---------- 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, 206 insertions(+), 281 deletions(-) diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index 1f7d07f31a..07af0bc9a2 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 4e72d11b5b..52e5bbe9de 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 97a1101544..bb28620b01 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 47ae707063..3d481d5fe7 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 b3f72bd213..cc88434e8f 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 0047a9c8e1..054f022708 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 f912a6c87d..872d4af2dd 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 291a426b3b..860d0f2467 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 6b2512570a..821f33327a 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 4d867fbc05..bee43ac1cd 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 7abac3bec5..33ee536f88 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 d0eefa84b1..447ba3119c 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 a097754310..cedf81c35e 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 637f0da326..551c6635e1 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) { -} +func (t *NoopTracer) OnKeccakPreimage(hash common.Hash, data []byte) {} -// 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) {} - -// 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 78dc9ae6e7..766278974c 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 01c5ae1fdb..8ca877aea5 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 8cf9eae541..afffb80bf8 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 a055ba334d..48a95f43c1 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 3d96253b71..4bac8edfeb 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 c1546ca055..5893c6844f 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 05560cfb24..afb52a9448 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) { +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) { } -// 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) { +func (t *noop) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { } -// 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 tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (t *noop) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { } -// 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) CaptureTxStart(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 92df195fe0..4ac49fd91f 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 6e694368b1..c0b66d8c27 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 5067fb8a58..6744ac729c 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 c2982ab036..d3bfc13a03 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 a0a4a6ff4f..7add963e16 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 d42f36666b..07e86ab1f0 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 c0ec5c312e..d321278732 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 4516ab1dff..a54b5611af 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 5e053236a9..d51780da21 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 37b89afc51..e39cbf5aae 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 29/42] 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 4bac8edfeb..9e7c699f40 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 4ac49fd91f..f4206d524c 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 c0b66d8c27..fb4a008ca2 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 6744ac729c..a1c81c8856 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 7add963e16..e6e9ad0e51 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 07e86ab1f0..72a07fc854 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 d321278732..e5e83bc2e8 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 d51780da21..97b29493a7 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 30/42] 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 872d4af2dd..ca3d17c1a9 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 0f85f424e5..1a5a29dd43 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 551c6635e1..39049dace4 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 afb52a9448..31e9ce7e3e 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 a54b5611af..885cc3a830 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 31/42] 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 95d59a04fa..2f49c486f9 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 2490e79fe5..41f782ed4f 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 1ba7552d76..29af07cc56 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 33ee536f88..27754f7c15 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 32/42] 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 ca3d17c1a9..b262b0aed7 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 ba314b51da..8baa69adfa 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 1a5a29dd43..ddf5e7374b 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 bee43ac1cd..47d75a89c9 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 39049dace4..d0edf12e21 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 9e7c699f40..458b99f525 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 5893c6844f..9ca6363250 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 31e9ce7e3e..2236674201 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 f4206d524c..fc7713b245 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 fb4a008ca2..791a3db2eb 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 a1c81c8856..e9a4508969 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 e6e9ad0e51..550b0115a3 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 72a07fc854..d671d3d364 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 e5e83bc2e8..47d388d312 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 885cc3a830..5274113518 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 97b29493a7..e90a1729bb 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 33/42] 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 860d0f2467..8a927777b6 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 ddf5e7374b..0b9ea37e9d 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 34/42] 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 b262b0aed7..df1da15f87 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 8baa69adfa..099ee6c877 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 458b99f525..86454d376a 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 35/42] 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 099ee6c877..cb7c1dad18 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 791a3db2eb..7e2e7aeff8 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 36/42] 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 cb7c1dad18..aafc577787 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 37/42] 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 cc88434e8f..ee230123cd 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 38/42] 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 41f782ed4f..928dd2cb9d 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 29af07cc56..746266cd10 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 39/42] 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 3bb0e8651b..3fc346fc68 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 bb28620b01..2600a6759e 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 25c6311c4c..d30d394d9f 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 16f8ec83a1..a6c51f3c20 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 fac88ff373..2a7eedf40d 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 1840e5b220..d3a7b1170e 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 cd718ae058..250c549f3a 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 054f022708..22516af322 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 df1da15f87..a2041f50a0 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 0000000000..bb0f4ca02e --- /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 a9d47e6ba3..4e475896a7 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 0b9ea37e9d..673908f44f 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 47d75a89c9..a8fdac855d 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 9ca6363250..895d6be684 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 d671d3d364..83db1139f1 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 5274113518..177082ffd9 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 44450fc2ff..7296163e1d 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 d673f1927c6278419d01728c78a6c5f2f543ca11 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Thu, 14 Mar 2024 16:58:22 -0400 Subject: [PATCH 40/42] Added Firehose 3.0 backport tracer --- buf.gen.yaml | 6 + core/state/statedb.go | 3 + core/tracing/hooks.go | 7 + eth/tracers/firehose.go | 1975 +++++++++++++++ eth/tracers/firehose_test.go | 288 +++ go.mod | 2 +- pb/sf/ethereum/type/v2/type.go | 27 + pb/sf/ethereum/type/v2/type.pb.go | 3691 +++++++++++++++++++++++++++++ 8 files changed, 5998 insertions(+), 1 deletion(-) create mode 100644 buf.gen.yaml create mode 100644 eth/tracers/firehose.go create mode 100644 eth/tracers/firehose_test.go create mode 100644 pb/sf/ethereum/type/v2/type.go create mode 100644 pb/sf/ethereum/type/v2/type.pb.go diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000000..6275bdee31 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,6 @@ +version: v1 +plugins: + - plugin: buf.build/protocolbuffers/go:v1.31.0 + out: pb + opt: paths=source_relative + diff --git a/core/state/statedb.go b/core/state/statedb.go index 22516af322..a4081c9c05 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -645,6 +645,9 @@ 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 != nil { + s.logger.OnNewAccount(addr, prev != nil) + } if prev == nil { s.journal.append(createObjectChange{account: &addr}) } else { diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index a2041f50a0..869dbc1b56 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -163,6 +163,13 @@ type Hooks struct { OnCodeChange CodeChangeHook OnStorageChange StorageChangeHook OnLog LogHook + + // Firehose backward compatibility + // This hook exist because some current Firehose supported chains requires it + // but this field is going to be deprecated and newer chains will not produced + // those events anymore. The hook is registered conditionally based on the + // tracer configuration. + OnNewAccount func(address common.Address, previousExisted bool) } // BalanceChangeReason is used to indicate the reason for a balance change, useful diff --git a/eth/tracers/firehose.go b/eth/tracers/firehose.go new file mode 100644 index 0000000000..dbb96f51e6 --- /dev/null +++ b/eth/tracers/firehose.go @@ -0,0 +1,1975 @@ +package tracers + +import ( + "bytes" + "cmp" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "os" + "regexp" + "runtime/debug" + "strconv" + "strings" + "sync/atomic" + "time" + + "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/crypto" + "github.com/ethereum/go-ethereum/eth/tracers/directory/live" + "github.com/ethereum/go-ethereum/params" + pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2" + "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" +) + +var firehoseTracerLogLevel = strings.ToLower(os.Getenv("GETH_FIREHOSE_TRACER_LOG_LEVEL")) +var isFirehoseDebugEnabled = firehoseTracerLogLevel == "debug" || firehoseTracerLogLevel == "trace" +var isFirehoseTracerEnabled = firehoseTracerLogLevel == "trace" + +var emptyCommonAddress = common.Address{} +var emptyCommonHash = common.Hash{} + +func init() { + staticFirehoseChainValidationOnInit() + + live.Directory.Register("firehose", newFirehoseTracer) +} + +func newFirehoseTracer(cfg json.RawMessage) (*tracing.Hooks, error) { + firehoseDebug("New firehose tracer") + + var config FirehoseConfig + if len([]byte(cfg)) > 0 { + if err := json.Unmarshal(cfg, &config); err != nil { + return nil, fmt.Errorf("failed to parse Firehose config: %w", err) + } + } + + tracer := NewFirehose() + + return &tracing.Hooks{ + OnBlockchainInit: tracer.OnBlockchainInit, + OnGenesisBlock: tracer.OnGenesisBlock, + OnBlockStart: tracer.OnBlockStart, + OnBlockEnd: tracer.OnBlockEnd, + OnSkippedBlock: tracer.OnSkippedBlock, + + OnTxStart: tracer.OnTxStart, + OnTxEnd: tracer.OnTxEnd, + OnEnter: tracer.OnCallEnter, + OnExit: tracer.OnCallExit, + OnOpcode: tracer.OnOpcode, + OnFault: tracer.OnOpcodeFault, + + OnBalanceChange: tracer.OnBalanceChange, + OnNonceChange: tracer.OnNonceChange, + OnCodeChange: tracer.OnCodeChange, + OnStorageChange: tracer.OnStorageChange, + OnGasChange: tracer.OnGasChange, + OnLog: tracer.OnLog, + OnNewAccount: tracer.OnNewAccount, + }, nil +} + +type FirehoseConfig struct { + // Nothing for now +} + +type Firehose struct { + // Global state + outputBuffer *bytes.Buffer + initSent *atomic.Bool + chainConfig *params.ChainConfig + hasher crypto.KeccakState // Keccak256 hasher instance shared across tracer needs (non-concurrent safe) + hasherBuf common.Hash // Keccak256 hasher result array shared across tracer needs (non-concurrent safe) + + // Block state + block *pbeth.Block + blockBaseFee *big.Int + blockOrdinal *Ordinal + blockFinality *FinalityStatus + blockRules params.Rules + + // Transaction state + evm *tracing.VMContext + transaction *pbeth.TransactionTrace + transactionLogIndex uint32 + inSystemCall bool + blockIsPrecompiledAddr func(addr common.Address) bool + + // Call state + callStack *CallStack + deferredCallState *DeferredCallState + latestCallEnterSuicided bool +} + +const FirehoseProtocolVersion = "3.0" + +func NewFirehose() *Firehose { + return &Firehose{ + // Global state + outputBuffer: bytes.NewBuffer(make([]byte, 0, 100*1024*1024)), + initSent: new(atomic.Bool), + chainConfig: nil, + hasher: crypto.NewKeccakState(), + + // Block state + blockOrdinal: &Ordinal{}, + blockFinality: &FinalityStatus{}, + + // Transaction state + transactionLogIndex: 0, + + // Call state + callStack: NewCallStack(), + deferredCallState: NewDeferredCallState(), + latestCallEnterSuicided: false, + } +} + +// resetBlock resets the block state only, do not reset transaction or call state +func (f *Firehose) resetBlock() { + f.block = nil + f.blockBaseFee = nil + f.blockOrdinal.Reset() + f.blockFinality.Reset() + f.blockIsPrecompiledAddr = nil + f.blockRules = params.Rules{} +} + +// resetTransaction resets the transaction state and the call state in one shot +func (f *Firehose) resetTransaction() { + f.transaction = nil + f.evm = nil + f.transactionLogIndex = 0 + f.inSystemCall = false + + f.callStack.Reset() + f.latestCallEnterSuicided = false + f.deferredCallState.Reset() +} + +func (f *Firehose) OnBlockchainInit(chainConfig *params.ChainConfig) { + f.chainConfig = chainConfig + + if wasNeverSent := f.initSent.CompareAndSwap(false, true); wasNeverSent { + printToFirehose("INIT", FirehoseProtocolVersion, "geth", params.Version) + } else { + f.panicInvalidState("The OnBlockchainInit callback was called more than once") + } +} + +func (f *Firehose) OnBlockStart(event tracing.BlockEvent) { + b := event.Block + firehoseDebug("block start number=%d hash=%s", b.NumberU64(), b.Hash()) + + f.ensureBlockChainInit() + + f.blockRules = f.chainConfig.Rules(b.Number(), f.chainConfig.TerminalTotalDifficultyPassed, b.Time()) + f.blockIsPrecompiledAddr = getActivePrecompilesChecker(f.blockRules) + + f.block = &pbeth.Block{ + Hash: b.Hash().Bytes(), + Number: b.Number().Uint64(), + Header: newBlockHeaderFromChainHeader(b.Header(), firehoseBigIntFromNative(new(big.Int).Add(event.TD, b.Difficulty()))), + Size: b.Size(), + // Known Firehose issue: If you fix all known Firehose issue for a new chain, don't forget to bump `Ver` to `4`! + Ver: 3, + } + + for _, uncle := range b.Uncles() { + // TODO: check if td should be part of uncles + f.block.Uncles = append(f.block.Uncles, newBlockHeaderFromChainHeader(uncle, nil)) + } + + if f.block.Header.BaseFeePerGas != nil { + f.blockBaseFee = f.block.Header.BaseFeePerGas.Native() + } + + f.blockFinality.populateFromChain(event.Finalized) +} + +func (f *Firehose) OnSkippedBlock(event tracing.BlockEvent) { + // Blocks that are skipped from blockchain that were knwon and should contain 0 transactions. + // It happened in the past, on Polygon if I recall right, that we missed block because some block + // went in this code path. + // + // See https: //github.com/streamingfast/go-ethereum/blob/a46903cf0cad829479ded66b369017914bf82314/core/blockchain.go#L1797-L1814 + if event.Block.Transactions().Len() > 0 { + panic(fmt.Sprintf("The tracer received an `OnSkippedBlock` block #%d (%s) with transactions (%d), this according to core/blockchain.go should never happen and is an error", + event.Block.NumberU64(), + event.Block.Hash().Hex(), + event.Block.Transactions().Len(), + )) + } + + // Trace the block as normal, worst case the Firehose system will simply discard it at some point + f.OnBlockStart(event) + f.OnBlockEnd(nil) +} + +func getActivePrecompilesChecker(rules params.Rules) func(addr common.Address) bool { + activePrecompiles := vm.ActivePrecompiles(rules) + + activePrecompilesMap := make(map[common.Address]bool, len(activePrecompiles)) + for _, addr := range activePrecompiles { + activePrecompilesMap[addr] = true + } + + return func(addr common.Address) bool { + _, found := activePrecompilesMap[addr] + return found + } +} + +func (f *Firehose) OnBlockEnd(err error) { + firehoseDebug("block ending err=%s", errorView(err)) + + if err == nil { + f.ensureInBlockAndNotInTrx() + f.printBlockToFirehose(f.block, f.blockFinality) + } else { + // An error occurred, could have happen in transaction/call context, we must not check if in trx/call, only check in block + f.ensureInBlock() + } + + f.resetBlock() + f.resetTransaction() + + firehoseDebug("block end") +} + +func (f *Firehose) OnBeaconBlockRootStart(root common.Hash) { + firehoseDebug("system call start for=%s", "beacon_block_root") + f.ensureInBlockAndNotInTrx() + + f.inSystemCall = true + f.transaction = &pbeth.TransactionTrace{} +} + +func (f *Firehose) OnBeaconBlockRootEnd() { + f.ensureInBlockAndInTrx() + f.ensureInSystemCall() + + f.block.SystemCalls = append(f.block.SystemCalls, f.transaction.Calls...) + + f.resetTransaction() +} + +func (f *Firehose) OnTxStart(evm *tracing.VMContext, tx *types.Transaction, from common.Address) { + firehoseDebug("trx start hash=%s type=%d gas=%d input=%s", tx.Hash(), tx.Type(), tx.Gas(), inputView(tx.Data())) + + f.ensureInBlockAndNotInTrxAndNotInCall() + + f.evm = evm + var to common.Address + if tx.To() == nil { + to = crypto.CreateAddress(from, evm.StateDB.GetNonce(from)) + } else { + to = *tx.To() + } + + f.onTxStart(tx, tx.Hash(), from, to) +} + +// onTxStart is used internally a two places, in the normal "tracer" and in the "OnGenesisBlock", +// we manually pass some override to the `tx` because genesis block has a different way of creating +// the transaction that wraps the genesis block. +func (f *Firehose) onTxStart(tx *types.Transaction, hash common.Hash, from, to common.Address) { + v, r, s := tx.RawSignatureValues() + + var blobGas *uint64 + if tx.Type() == types.BlobTxType { + blobGas = ptr(tx.BlobGas()) + } + + f.transaction = &pbeth.TransactionTrace{ + BeginOrdinal: f.blockOrdinal.Next(), + Hash: hash.Bytes(), + From: from.Bytes(), + To: to.Bytes(), + Nonce: tx.Nonce(), + GasLimit: tx.Gas(), + GasPrice: gasPrice(tx, f.blockBaseFee), + Value: firehoseBigIntFromNative(tx.Value()), + Input: tx.Data(), + V: emptyBytesToNil(v.Bytes()), + R: normalizeSignaturePoint(r.Bytes()), + S: normalizeSignaturePoint(s.Bytes()), + Type: transactionTypeFromChainTxType(tx.Type()), + AccessList: newAccessListFromChain(tx.AccessList()), + MaxFeePerGas: maxFeePerGas(tx), + MaxPriorityFeePerGas: maxPriorityFeePerGas(tx), + BlobGas: blobGas, + BlobGasFeeCap: firehoseBigIntFromNative(tx.BlobGasFeeCap()), + BlobHashes: newBlobHashesFromChain(tx.BlobHashes()), + } +} + +func (f *Firehose) OnTxEnd(receipt *types.Receipt, err error) { + firehoseDebug("trx ending") + f.ensureInBlockAndInTrx() + + f.block.TransactionTraces = append(f.block.TransactionTraces, f.completeTransaction(receipt)) + + // The reset must be done as the very last thing as the CallStack needs to be + // properly populated for the `completeTransaction` call above to complete correctly. + f.resetTransaction() + + firehoseDebug("trx end") +} + +func (f *Firehose) completeTransaction(receipt *types.Receipt) *pbeth.TransactionTrace { + firehoseDebug("completing transaction call_count=%d receipt=%s", len(f.transaction.Calls), (*receiptView)(receipt)) + + // Sorting needs to happen first, before we populate the state reverted + slices.SortFunc(f.transaction.Calls, func(i, j *pbeth.Call) int { + return cmp.Compare(i.Index, j.Index) + }) + + rootCall := f.transaction.Calls[0] + + if !f.deferredCallState.IsEmpty() { + f.deferredCallState.MaybePopulateCallAndReset("root", rootCall) + } + + // Receipt can be nil if an error occurred during the transaction execution, right now we don't have it + if receipt != nil { + f.transaction.Index = uint32(receipt.TransactionIndex) + f.transaction.GasUsed = receipt.GasUsed + f.transaction.Receipt = newTxReceiptFromChain(receipt, f.transaction.Type) + f.transaction.Status = transactionStatusFromChainTxReceipt(receipt.Status) + } + + // It's possible that the transaction was reverted, but we still have a receipt, in that case, we must + // check the root call + if rootCall.StatusReverted { + f.transaction.Status = pbeth.TransactionTraceStatus_REVERTED + } + + // Order is important, we must populate the state reverted before we remove the log block index and re-assign ordinals + f.populateStateReverted() + f.removeLogBlockIndexOnStateRevertedCalls() + f.assignOrdinalAndIndexToReceiptLogs() + + // Known Firehose issue: This field has never been populated in the old Firehose instrumentation, so it's the same thing for now + // f.transaction.ReturnData = rootCall.ReturnData + f.transaction.EndOrdinal = f.blockOrdinal.Next() + + return f.transaction +} + +func (f *Firehose) populateStateReverted() { + // Calls are ordered by execution index. So the algo is quite simple. + // We loop through the flat calls, at each call, if the parent is present + // and reverted, the current call is reverted. Otherwise, if the current call + // is failed, the state is reverted. In all other cases, we simply continue + // our iteration loop. + // + // This works because we see the parent before its children, and since we + // trickle down the state reverted value down the children, checking the parent + // of a call will always tell us if the whole chain of parent/child should + // be reverted + // + calls := f.transaction.Calls + for _, call := range f.transaction.Calls { + var parent *pbeth.Call + if call.ParentIndex > 0 { + parent = calls[call.ParentIndex-1] + } + + call.StateReverted = (parent != nil && parent.StateReverted) || call.StatusFailed + } +} + +func (f *Firehose) removeLogBlockIndexOnStateRevertedCalls() { + for _, call := range f.transaction.Calls { + if call.StateReverted { + for _, log := range call.Logs { + log.BlockIndex = 0 + } + } + } +} + +func (f *Firehose) assignOrdinalAndIndexToReceiptLogs() { + firehoseTrace("assigning ordinal and index to logs") + defer func() { + firehoseTrace("assigning ordinal and index to logs terminated") + }() + + trx := f.transaction + + receiptsLogs := trx.Receipt.Logs + + callLogs := []*pbeth.Log{} + for _, call := range trx.Calls { + firehoseTrace("checking call reverted=%t logs=%d", call.StateReverted, len(call.Logs)) + if call.StateReverted { + continue + } + + callLogs = append(callLogs, call.Logs...) + } + + slices.SortFunc(callLogs, func(i, j *pbeth.Log) int { + return cmp.Compare(i.Ordinal, j.Ordinal) + }) + + if len(callLogs) != len(receiptsLogs) { + panic(fmt.Errorf( + "mismatch between Firehose call logs and Ethereum transaction %s receipt logs at block #%d, transaction receipt has %d logs but there is %d Firehose call logs", + hex.EncodeToString(trx.Hash), + f.block.Number, + len(receiptsLogs), + len(callLogs), + )) + } + + for i := 0; i < len(callLogs); i++ { + callLog := callLogs[i] + receiptsLog := receiptsLogs[i] + + result := &validationResult{} + // Ordinal must **not** be checked as we are assigning it here below after the validations + validateBytesField(result, "Address", callLog.Address, receiptsLog.Address) + validateUint32Field(result, "BlockIndex", callLog.BlockIndex, receiptsLog.BlockIndex) + validateBytesField(result, "Data", callLog.Data, receiptsLog.Data) + validateArrayOfBytesField(result, "Topics", callLog.Topics, receiptsLog.Topics) + + if len(result.failures) > 0 { + result.panicOnAnyFailures("mismatch between Firehose call log and Ethereum transaction receipt log at index %d", i) + } + + receiptsLog.Index = callLog.Index + receiptsLog.Ordinal = callLog.Ordinal + } +} + +// OnCallEnter implements the EVMLogger interface to initialize the tracing operation. +func (f *Firehose) OnCallEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + opCode := vm.OpCode(typ) + + var callType pbeth.CallType + if isRootCall := depth == 0; isRootCall { + callType = rootCallType(opCode == vm.CREATE) + } else { + // The invokation for vm.SELFDESTRUCT is called while already in another call and is recorded specially + // in the Geth tracer and generates `OnEnter/OnExit` callbacks. However in Firehose, self destruction + // simply sets the call as having called suicided so there is no extra call. + // + // So we ignore `OnEnter/OnExit` callbacks for `SELFDESTRUCT` opcode, we ignore it here and set + // a special sentinel variable that will tell `OnExit` to ignore itself. + if opCode == vm.SELFDESTRUCT { + f.ensureInCall() + f.callStack.Peek().Suicide = true + + // The next OnCallExit must be ignored, this variable will make the next OnCallExit to be ignored + f.latestCallEnterSuicided = true + return + } + + callType = callTypeFromOpCode(opCode) + if callType == pbeth.CallType_UNSPECIFIED { + panic(fmt.Errorf("unexpected call type, received OpCode %s but only call related opcode (CALL, CREATE, CREATE2, STATIC, DELEGATECALL and CALLCODE) or SELFDESTRUCT is accepted", opCode)) + } + } + + f.callStart(computeCallSource(depth), callType, from, to, input, gas, value) +} + +// OnCallExit is called after the call finishes to finalize the tracing. +func (f *Firehose) OnCallExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + f.callEnd(computeCallSource(depth), output, gasUsed, err, reverted) +} + +// OnOpcode implements the EVMLogger interface to trace a single step of VM execution. +func (f *Firehose) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + firehoseTrace("on opcode op=%s gas=%d cost=%d, err=%s", op, gas, cost, errorView(err)) + + if activeCall := f.callStack.Peek(); activeCall != nil { + opCode := vm.OpCode(op) + f.captureInterpreterStep(activeCall, pc, opCode, gas, cost, scope, rData, depth, err) + + // The rest of the logic expects that a call succeeded, nothing to do more here if the interpreter failed on this OpCode + if err != nil { + return + } + + // The gas change must come first to retain Firehose backward compatibility. Indeed, before Firehose 3.0, + // we had a specific method `OnKeccakPreimage` that was called during the KECCAK256 opcode. However, in + // the new model, we do it through `OnOpcode`. + // + // The gas change recording in the previous Firehose patch was done before calling `OnKeccakPreimage` so + // we must do the same here. + if cost > 0 { + if reason, found := opCodeToGasChangeReasonMap[opCode]; found { + activeCall.GasChanges = append(activeCall.GasChanges, f.newGasChange("state", gas, gas-cost, reason)) + } + } + + if opCode == vm.KECCAK256 { + f.onOpcodeSha3(activeCall, scope.StackData(), Memory(scope.MemoryData())) + } + } +} + +type Memory []byte + +func (m Memory) GetPtr(offset, size int64) []byte { + if size == 0 { + return nil + } + + if len(m) > int(offset) { + return m[offset : offset+size] + } + + return nil +} + +var opCodeToGasChangeReasonMap = map[vm.OpCode]pbeth.GasChange_Reason{ + vm.CREATE: pbeth.GasChange_REASON_CONTRACT_CREATION, + vm.CREATE2: pbeth.GasChange_REASON_CONTRACT_CREATION2, + vm.CALL: pbeth.GasChange_REASON_CALL, + vm.STATICCALL: pbeth.GasChange_REASON_STATIC_CALL, + vm.CALLCODE: pbeth.GasChange_REASON_CALL_CODE, + vm.DELEGATECALL: pbeth.GasChange_REASON_DELEGATE_CALL, + vm.RETURN: pbeth.GasChange_REASON_RETURN, + vm.REVERT: pbeth.GasChange_REASON_REVERT, + vm.LOG0: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG1: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG2: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG3: pbeth.GasChange_REASON_EVENT_LOG, + vm.LOG4: pbeth.GasChange_REASON_EVENT_LOG, + vm.SELFDESTRUCT: pbeth.GasChange_REASON_SELF_DESTRUCT, + vm.CALLDATACOPY: pbeth.GasChange_REASON_CALL_DATA_COPY, + vm.CODECOPY: pbeth.GasChange_REASON_CODE_COPY, + vm.EXTCODECOPY: pbeth.GasChange_REASON_EXT_CODE_COPY, + vm.RETURNDATACOPY: pbeth.GasChange_REASON_RETURN_DATA_COPY, +} + +// onOpcodeSha3 is called during the SHA3 (a.k.a KECCAK256) opcode it's known +// in Firehose tracer as Keccak preimages. The preimage is the input data that +// was used to produce the given keccak hash. +func (f *Firehose) onOpcodeSha3(call *pbeth.Call, stack []uint256.Int, memory Memory) { + if call.KeccakPreimages == nil { + call.KeccakPreimages = make(map[string]string) + } + + offset, size := stack[0], stack[1] + preImage := memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) + + // We should have exclusive access to the hasher, we can safely reset it. + f.hasher.Reset() + f.hasher.Write(preImage) + f.hasher.Read(f.hasherBuf[:]) + + // Known Firehose issue: It appears the old Firehose instrumentation have a bug + // where when the keccak256 preimage is empty, it is written as "." which is + // completely wrong. + // + // To keep the same behavior, we will write the preimage as a "." when the encoded + // data is an empty string. + // + // For new chain, this code should be remove so that we just keep `hex.EncodeToString(data)`. + encodedData := hex.EncodeToString(preImage) + if encodedData == "" { + encodedData = "." + } + + call.KeccakPreimages[hex.EncodeToString(f.hasherBuf[:])] = encodedData +} + +// OnOpcodeFault implements the EVMLogger interface to trace an execution fault. +func (f *Firehose) OnOpcodeFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { + if activeCall := f.callStack.Peek(); activeCall != nil { + f.captureInterpreterStep(activeCall, pc, vm.OpCode(op), gas, cost, scope, nil, depth, err) + } +} + +func (f *Firehose) captureInterpreterStep(activeCall *pbeth.Call, pc uint64, op vm.OpCode, gas, cost uint64, _ tracing.OpContext, rData []byte, depth int, err error) { + _, _, _, _, _, _, _ = pc, op, gas, cost, rData, depth, err + + // for call, we need to process the executed code here + // since in old firehose executed code calculation depends if the code exist + if activeCall.CallType == pbeth.CallType_CALL && !activeCall.ExecutedCode { + firehoseTrace("Intepreter step for callType_CALL") + activeCall.ExecutedCode = len(activeCall.Input) > 0 + } +} + +func (f *Firehose) callStart(source string, callType pbeth.CallType, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + firehoseDebug("call start source=%s index=%d type=%s input=%s", source, f.callStack.NextIndex(), callType, inputView(input)) + f.ensureInBlockAndInTrx() + + // Known Firehose issue: Contract creation call's input is always `nil` in old Firehose patch + // due to an oversight that having it in `CodeChange` would be sufficient but this is wrong + // as constructor's input are not part of the code change but part of the call input. + // + // New chain integration should remove this `if` statement completely. + if callType == pbeth.CallType_CREATE { + input = nil + } + + call := &pbeth.Call{ + // Known Firehose issue: Ref 042a2ff03fd623f151d7726314b8aad6 (see below) + // + // New chain integration should uncomment the code below and remove the `if` statement of the the other ref + // BeginOrdinal: f.blockOrdinal.Next(), + CallType: callType, + Depth: 0, + Caller: from.Bytes(), + Address: to.Bytes(), + // We need to clone `input` received by the tracer as it's re-used within Geth! + Input: bytes.Clone(input), + Value: firehoseBigIntFromNative(value), + GasLimit: gas, + } + + call.ExecutedCode = f.getExecutedCode(f.evm, call) + + // Known Firehose issue: The BeginOrdinal of the genesis block root call is never actually + // incremented and it's always 0. + // + // New chain integration should remove this `if` statement and uncomment code of other ref + // above. + // + // Ref 042a2ff03fd623f151d7726314b8aad6 + if f.block.Number != 0 { + call.BeginOrdinal = f.blockOrdinal.Next() + } + + if err := f.deferredCallState.MaybePopulateCallAndReset(source, call); err != nil { + panic(err) + } + + // Known Firehose issue: The `BeginOrdinal` of the root call is incremented but must + // be assigned back to 0 because of a bug in the console reader. remove on new chain. + // + // New chain integration should remove this `if` statement + if source == "root" { + call.BeginOrdinal = 0 + } + + f.callStack.Push(call) +} + +func (f *Firehose) getExecutedCode(evm *tracing.VMContext, call *pbeth.Call) bool { + precompile := f.blockIsPrecompiledAddr(common.BytesToAddress(call.Address)) + + if evm != nil && call.CallType == pbeth.CallType_CALL { + if !evm.StateDB.Exist(common.BytesToAddress(call.Address)) && + !precompile && f.blockRules.IsEIP158 && + (call.Value == nil || call.Value.Native().Sign() == 0) { + firehoseDebug("executed code IsSpuriousDragon callTyp=%s inputLength=%d", call.CallType.String(), len(call.Input) > 0) + return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 + } + } + + if precompile { + firehoseDebug("executed code isprecompile callTyp=%s inputLength=%d", call.CallType.String(), len(call.Input) > 0) + return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 + } + + if call.CallType == pbeth.CallType_CALL { + firehoseDebug("executed code callType_CALL") + // calculation for executed code will happen in captureInterpreterStep + return false + } + + firehoseDebug("executed code default callTyp=%s inputLength=%d", call.CallType.String(), len(call.Input) > 0) + return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 +} + +func (f *Firehose) callEnd(source string, output []byte, gasUsed uint64, err error, reverted bool) { + firehoseDebug("call end source=%s index=%d output=%s gasUsed=%d err=%s reverted=%t", source, f.callStack.ActiveIndex(), outputView(output), gasUsed, errorView(err), reverted) + + if f.latestCallEnterSuicided { + if source != "child" { + panic(fmt.Errorf("unexpected source for suicided call end, expected child but got %s, suicide are always produced on a 'child' source", source)) + } + + // Geth native tracer does a `OnEnter(SELFDESTRUCT, ...)/OnExit(...)`, we must skip the `OnExit` call + // in that case because we did not push it on our CallStack. + f.latestCallEnterSuicided = false + return + } + + f.ensureInBlockAndInTrxAndInCall() + + call := f.callStack.Pop() + call.GasConsumed = gasUsed + + // For create call, we do not save the returned value which is the actual contract's code + if call.CallType != pbeth.CallType_CREATE { + call.ReturnData = bytes.Clone(output) + } + + // Known Firehose issue: How we computed `executed_code` before was not working for contract's that only + // deal with ETH transfer through Solidity `receive()` built-in since those call have `len(input) == 0` + // + // New chain should turn the logic into: + // + // if !call.ExecutedCode && f.isPrecompiledAddr(common.BytesToAddress(call.Address)) { + // call.ExecutedCode = true + // } + // + // At this point, `call.ExecutedCode` is tied to `EVMInterpreter#Run` execution (in `core/vm/interpreter.go`) + // and is `true` if the run/loop of the interpreter executed. + // + // This means that if `false` the interpreter did not run at all and we would had emitted a + // `account_without_code` event in the old Firehose patch which you have set `call.ExecutecCode` + // to false + // + // For precompiled address however, interpreter does not run so determine there was a bug in Firehose instrumentation where we would + // if call.ExecutedCode || (f.isPrecompiledAddr != nil && f.isPrecompiledAddr(common.BytesToAddress(call.Address))) { + // // In this case, we are sure that some code executed. This translates in the old Firehose instrumentation + // // that it would have **never** emitted an `account_without_code`. + // // + // // When no `account_without_code` was executed in the previous Firehose instrumentation, + // // the `call.ExecutedCode` defaulted to the condition below + // call.ExecutedCode = call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 + // } else { + // // In all other cases, we are sure that no code executed. This translates in the old Firehose instrumentation + // // that it would have emitted an `account_without_code` and it would have then forced set the `call.ExecutedCode` + // // to `false`. + // call.ExecutedCode = false + // } + + if reverted { + failureReason := "" + if err != nil { + failureReason = err.Error() + } + + call.FailureReason = failureReason + call.StatusFailed = true + + // We also treat ErrInsufficientBalance and ErrDepth as reverted in Firehose model + // because they do not cost any gas. + call.StatusReverted = errors.Is(err, vm.ErrExecutionReverted) || errors.Is(err, vm.ErrInsufficientBalance) || errors.Is(err, vm.ErrDepth) + + // Known Firehose issue: FIXME Document! + if !call.ExecutedCode && (errors.Is(err, vm.ErrInsufficientBalance) || errors.Is(err, vm.ErrDepth)) { + call.ExecutedCode = call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 + } + } + + // Known Firehose issue: The EndOrdinal of the genesis block root call is never actually + // incremented and it's always 0. + // + // New chain should turn the logic into: + // + // call.EndOrdinal = f.blockOrdinal.Next() + // + // Removing the condition around the `EndOrdinal` assignment (keeping it!) + if f.block.Number != 0 { + call.EndOrdinal = f.blockOrdinal.Next() + } + + f.transaction.Calls = append(f.transaction.Calls, call) +} + +func computeCallSource(depth int) string { + if depth == 0 { + return "root" + } + + return "child" +} + +func (f *Firehose) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) { + f.ensureBlockChainInit() + + f.OnBlockStart(tracing.BlockEvent{Block: b, TD: big.NewInt(0), Finalized: nil, Safe: nil}) + f.onTxStart(types.NewTx(&types.LegacyTx{}), emptyCommonHash, emptyCommonAddress, emptyCommonAddress) + f.OnCallEnter(0, byte(vm.CALL), emptyCommonAddress, emptyCommonAddress, nil, 0, nil) + + for _, addr := range sortedKeys(alloc) { + account := alloc[addr] + + f.OnNewAccount(addr, false) + + if account.Balance != nil && account.Balance.Sign() != 0 { + activeCall := f.callStack.Peek() + activeCall.BalanceChanges = append(activeCall.BalanceChanges, f.newBalanceChange("genesis", addr, common.Big0, account.Balance, pbeth.BalanceChange_REASON_GENESIS_BALANCE)) + } + + if len(account.Code) > 0 { + f.OnCodeChange(addr, emptyCommonHash, nil, common.BytesToHash(crypto.Keccak256(account.Code)), account.Code) + } + + if account.Nonce > 0 { + f.OnNonceChange(addr, 0, account.Nonce) + } + + for _, key := range sortedKeys(account.Storage) { + f.OnStorageChange(addr, key, emptyCommonHash, account.Storage[key]) + } + } + + f.OnCallExit(0, nil, 0, nil, false) + f.OnTxEnd(&types.Receipt{ + PostState: b.Root().Bytes(), + Status: types.ReceiptStatusSuccessful, + }, nil) + f.OnBlockEnd(nil) +} + +type bytesGetter interface { + comparable + Bytes() []byte +} + +func sortedKeys[K bytesGetter, V any](m map[K]V) []K { + keys := maps.Keys(m) + slices.SortFunc(keys, func(i, j K) int { + return bytes.Compare(i.Bytes(), j.Bytes()) + }) + + return keys +} + +func (f *Firehose) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) { + if reason == tracing.BalanceChangeUnspecified { + // We ignore those, if they are mislabelled, too bad so particular attention needs to be ported to this + return + } + + // Known Firehose issue: It's possible to burn Ether by sending some ether to a suicided account. In those case, + // at theend of block producing, StateDB finalize the block by burning ether from the account. This is something + // we were not tracking in the old Firehose instrumentation. + // + // New chain integration should remove this `if` statement all along. + if reason == tracing.BalanceDecreaseSelfdestructBurn { + return + } + + f.ensureInBlockOrTrx() + + change := f.newBalanceChange("tracer", a, prev, new, balanceChangeReasonFromChain(reason)) + + if f.transaction != nil { + activeCall := f.callStack.Peek() + + // There is an initial transfer happening will the call is not yet started, we track it manually + if activeCall == nil { + f.deferredCallState.balanceChanges = append(f.deferredCallState.balanceChanges, change) + return + } + + activeCall.BalanceChanges = append(activeCall.BalanceChanges, change) + } else { + f.block.BalanceChanges = append(f.block.BalanceChanges, change) + } +} + +func (f *Firehose) newBalanceChange(tag string, address common.Address, oldValue, newValue *big.Int, reason pbeth.BalanceChange_Reason) *pbeth.BalanceChange { + firehoseTrace("balance changed tag=%s before=%d after=%d reason=%s", tag, oldValue, newValue, reason) + + if reason == pbeth.BalanceChange_REASON_UNKNOWN { + panic(fmt.Errorf("received unknown balance change reason %s", reason)) + } + + return &pbeth.BalanceChange{ + Ordinal: f.blockOrdinal.Next(), + Address: address.Bytes(), + OldValue: firehoseBigIntFromNative(oldValue), + NewValue: firehoseBigIntFromNative(newValue), + Reason: reason, + } +} + +func (f *Firehose) OnNonceChange(a common.Address, prev, new uint64) { + f.ensureInBlockAndInTrx() + + activeCall := f.callStack.Peek() + change := &pbeth.NonceChange{ + Address: a.Bytes(), + OldValue: prev, + NewValue: new, + Ordinal: f.blockOrdinal.Next(), + } + + // There is an initial nonce change happening when the call is not yet started, we track it manually + if activeCall == nil { + f.deferredCallState.nonceChanges = append(f.deferredCallState.nonceChanges, change) + return + } + + activeCall.NonceChanges = append(activeCall.NonceChanges, change) +} + +func (f *Firehose) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) { + f.ensureInBlockOrTrx() + + change := &pbeth.CodeChange{ + Address: a.Bytes(), + OldHash: prevCodeHash.Bytes(), + OldCode: prev, + NewHash: codeHash.Bytes(), + NewCode: code, + Ordinal: f.blockOrdinal.Next(), + } + + if f.transaction != nil { + activeCall := f.callStack.Peek() + if activeCall == nil { + f.panicInvalidState("caller expected to be in call state but we were not, this is a bug") + } + + activeCall.CodeChanges = append(activeCall.CodeChanges, change) + } else { + f.block.CodeChanges = append(f.block.CodeChanges, change) + } +} + +func (f *Firehose) OnStorageChange(a common.Address, k, prev, new common.Hash) { + f.ensureInBlockAndInTrxAndInCall() + + activeCall := f.callStack.Peek() + activeCall.StorageChanges = append(activeCall.StorageChanges, &pbeth.StorageChange{ + Address: a.Bytes(), + Key: k.Bytes(), + OldValue: prev.Bytes(), + NewValue: new.Bytes(), + Ordinal: f.blockOrdinal.Next(), + }) +} + +func (f *Firehose) OnLog(l *types.Log) { + f.ensureInBlockAndInTrxAndInCall() + + topics := make([][]byte, len(l.Topics)) + for i, topic := range l.Topics { + topics[i] = topic.Bytes() + } + + activeCall := f.callStack.Peek() + firehoseTrace("adding log to call address=%s call=%d (has already %d logs)", l.Address, activeCall.Index, len(activeCall.Logs)) + + activeCall.Logs = append(activeCall.Logs, &pbeth.Log{ + Address: l.Address.Bytes(), + Topics: topics, + Data: l.Data, + Index: f.transactionLogIndex, + BlockIndex: uint32(l.Index), + Ordinal: f.blockOrdinal.Next(), + }) + + f.transactionLogIndex++ +} + +func (f *Firehose) OnNewAccount(a common.Address, previousDataExists bool) { + f.ensureInBlockOrTrx() + if f.transaction == nil { + // We receive OnNewAccount on finalization of the block which means there is no + // transaction active. In that case, we do not track the account creation because + // the "old" Firehose didn't but mainly because we don't have `AccountCreation` at + // the block level so what can we do... + + // This fix was applied on Erigon branch after chain's comparison. I need to check + // with what the old patch was doing to write a meaningful comment here and ensure + // they got the logic right + f.blockOrdinal.Next() + return + } + + // Known Firehose issue: The current Firehose instrumentation emits multiple + // time the same `OnNewAccount` event for the same account when such account + // exists in the past. For now, do nothing and keep the legacy behavior. + _ = previousDataExists + + if call := f.callStack.Peek(); call != nil && call.CallType == pbeth.CallType_STATIC && f.blockIsPrecompiledAddr(common.Address(call.Address)) { + // Old Firehose ignore those, we do the same + return + } + + accountCreation := &pbeth.AccountCreation{ + Account: a.Bytes(), + Ordinal: f.blockOrdinal.Next(), + } + + activeCall := f.callStack.Peek() + if activeCall == nil { + f.deferredCallState.accountCreations = append(f.deferredCallState.accountCreations, accountCreation) + return + } + + activeCall.AccountCreations = append(activeCall.AccountCreations, accountCreation) +} + +func (f *Firehose) OnGasChange(old, new uint64, reason tracing.GasChangeReason) { + f.ensureInBlockAndInTrx() + + if old == new { + return + } + + if reason == tracing.GasChangeCallOpCode { + // We ignore those because we track OpCode gas consumption manually by tracking the gas value at `OnOpcode` call + return + } + + // Known Firehose issue: New geth native tracer added more gas change, some that we were indeed missing and + // should have included in our previous patch. + // + // For new chain, this code should be remove so that they are included and useful to user. + // + // Ref eb1916a67d9bea03df16a7a3e2cfac72 + if reason == tracing.GasChangeTxInitialBalance || + reason == tracing.GasChangeTxRefunds || + reason == tracing.GasChangeTxLeftOverReturned || + reason == tracing.GasChangeCallInitialBalance || + reason == tracing.GasChangeCallLeftOverReturned { + return + } + + activeCall := f.callStack.Peek() + change := f.newGasChange("tracer", old, new, gasChangeReasonFromChain(reason)) + + // There is an initial gas consumption happening will the call is not yet started, we track it manually + if activeCall == nil { + f.deferredCallState.gasChanges = append(f.deferredCallState.gasChanges, change) + return + } + + activeCall.GasChanges = append(activeCall.GasChanges, change) +} + +func (f *Firehose) newGasChange(tag string, oldValue, newValue uint64, reason pbeth.GasChange_Reason) *pbeth.GasChange { + firehoseTrace("gas consumed tag=%s before=%d after=%d reason=%s", tag, oldValue, newValue, reason) + + // Should already be checked by the caller, but we keep it here for safety if the code ever change + if reason == pbeth.GasChange_REASON_UNKNOWN { + panic(fmt.Errorf("received unknown gas change reason %s", reason)) + } + + return &pbeth.GasChange{ + OldValue: oldValue, + NewValue: newValue, + Ordinal: f.blockOrdinal.Next(), + Reason: reason, + } +} + +func (f *Firehose) ensureBlockChainInit() { + if f.chainConfig == nil { + f.panicInvalidState("the OnBlockchainInit hook should have been called at this point") + } +} + +func (f *Firehose) ensureInBlock() { + if f.block == nil { + f.panicInvalidState("caller expected to be in block state but we were not, this is a bug") + } + + if f.chainConfig == nil { + f.panicInvalidState("the OnBlockchainInit hook should have been called at this point") + } +} + +func (f *Firehose) ensureNotInBlock() { + if f.block != nil { + f.panicInvalidState("caller expected to not be in block state but we were, this is a bug") + } +} + +// Suppress lint warning about unusued method, we keep it in the patch because it's used in other +// network which pulls this branch. +var _ = new(Firehose).ensureNotInBlock + +func (f *Firehose) ensureInBlockAndInTrx() { + f.ensureInBlock() + + if f.transaction == nil { + f.panicInvalidState("caller expected to be in transaction state but we were not, this is a bug") + } +} + +func (f *Firehose) ensureInBlockAndNotInTrx() { + f.ensureInBlock() + + if f.transaction != nil { + f.panicInvalidState("caller expected to not be in transaction state but we were, this is a bug") + } +} + +func (f *Firehose) ensureInBlockAndNotInTrxAndNotInCall() { + f.ensureInBlock() + + if f.transaction != nil { + f.panicInvalidState("caller expected to not be in transaction state but we were, this is a bug") + } + + if f.callStack.HasActiveCall() { + f.panicInvalidState("caller expected to not be in call state but we were, this is a bug") + } +} + +func (f *Firehose) ensureInBlockOrTrx() { + if f.transaction == nil && f.block == nil { + f.panicInvalidState("caller expected to be in either block or transaction state but we were not, this is a bug") + } +} + +func (f *Firehose) ensureInBlockAndInTrxAndInCall() { + if f.transaction == nil || f.block == nil { + f.panicInvalidState("caller expected to be in block and in transaction but we were not, this is a bug") + } + + if !f.callStack.HasActiveCall() { + f.panicInvalidState("caller expected to be in call state but we were not, this is a bug") + } +} + +func (f *Firehose) ensureInCall() { + if f.block == nil { + f.panicInvalidState("caller expected to be in call state but we were not, this is a bug") + } +} + +func (f *Firehose) ensureInSystemCall() { + if !f.inSystemCall { + f.panicInvalidState("call expected to be in system call state but we were not, this is a bug") + } +} + +func (f *Firehose) panicInvalidState(msg string) string { + panic(fmt.Errorf("%s (init=%t, inBlock=%t, inTransaction=%t, inCall=%t)", msg, f.chainConfig != nil, f.block != nil, f.transaction != nil, f.callStack.HasActiveCall())) +} + +// printToFirehose is an easy way to print to Firehose format, it essentially +// adds the "FIRE" prefix to the input and joins the input with spaces as well +// as adding a newline at the end. +// +// It flushes this through [flushToFirehose] to the `os.Stdout` writer. +func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *FinalityStatus) { + marshalled, err := proto.Marshal(block) + if err != nil { + panic(fmt.Errorf("failed to marshal block: %w", err)) + } + + f.outputBuffer.Reset() + + previousHash := block.PreviousID() + previousNum := 0 + if block.Number > 0 { + previousNum = int(block.Number) - 1 + } + + libNum := finalityStatus.LastIrreversibleBlockNumber + if finalityStatus.IsEmpty() { + // FIXME: We should have access to the genesis block to perform this operation to ensure we never go below the + // the genesis block + if block.Number >= 200 { + libNum = block.Number - 200 + } else { + libNum = 0 + } + } + + // **Important* The final space in the Sprintf template is mandatory! + f.outputBuffer.WriteString(fmt.Sprintf("FIRE BLOCK %d %s %d %s %d %d ", block.Number, hex.EncodeToString(block.Hash), previousNum, previousHash, libNum, block.Time().UnixNano())) + + encoder := base64.NewEncoder(base64.StdEncoding, f.outputBuffer) + if _, err = encoder.Write(marshalled); err != nil { + panic(fmt.Errorf("write to encoder should have been infaillible: %w", err)) + } + + if err := encoder.Close(); err != nil { + panic(fmt.Errorf("closing encoder should have been infaillible: %w", err)) + } + + f.outputBuffer.WriteString("\n") + + flushToFirehose(f.outputBuffer.Bytes(), os.Stdout) +} + +// printToFirehose is an easy way to print to Firehose format, it essentially +// adds the "FIRE" prefix to the input and joins the input with spaces as well +// as adding a newline at the end. +// +// It flushes this through [flushToFirehose] to the `os.Stdout` writer. +func printToFirehose(input ...string) { + flushToFirehose([]byte("FIRE "+strings.Join(input, " ")+"\n"), os.Stdout) +} + +// flushToFirehose sends data to Firehose via `io.Writter` checking for errors +// and retrying if necessary. +// +// If error is still present after 10 retries, prints an error message to `writer` +// as well as writing file `/tmp/firehose_writer_failed_print.log` with the same +// error message. +func flushToFirehose(in []byte, writer io.Writer) { + var written int + var err error + loops := 10 + for i := 0; i < loops; i++ { + written, err = writer.Write(in) + + if len(in) == written { + return + } + + in = in[written:] + if i == loops-1 { + break + } + } + + errstr := fmt.Sprintf("\nFIREHOSE FAILED WRITING %dx: %s\n", loops, err) + os.WriteFile("/tmp/firehose_writer_failed_print.log", []byte(errstr), 0644) + fmt.Fprint(writer, errstr) +} + +// FIXME: Create a unit test that is going to fail as soon as any header is added in +func newBlockHeaderFromChainHeader(h *types.Header, td *pbeth.BigInt) *pbeth.BlockHeader { + var withdrawalsHashBytes []byte + if hash := h.WithdrawalsHash; hash != nil { + withdrawalsHashBytes = hash.Bytes() + } + + var parentBeaconRootBytes []byte + if root := h.ParentBeaconRoot; root != nil { + parentBeaconRootBytes = root.Bytes() + } + + pbHead := &pbeth.BlockHeader{ + Hash: h.Hash().Bytes(), + Number: h.Number.Uint64(), + ParentHash: h.ParentHash.Bytes(), + UncleHash: h.UncleHash.Bytes(), + Coinbase: h.Coinbase.Bytes(), + StateRoot: h.Root.Bytes(), + TransactionsRoot: h.TxHash.Bytes(), + ReceiptRoot: h.ReceiptHash.Bytes(), + LogsBloom: h.Bloom.Bytes(), + Difficulty: firehoseBigIntFromNative(h.Difficulty), + TotalDifficulty: td, + GasLimit: h.GasLimit, + GasUsed: h.GasUsed, + Timestamp: timestamppb.New(time.Unix(int64(h.Time), 0)), + ExtraData: h.Extra, + MixHash: h.MixDigest.Bytes(), + Nonce: h.Nonce.Uint64(), + BaseFeePerGas: firehoseBigIntFromNative(h.BaseFee), + WithdrawalsRoot: withdrawalsHashBytes, + BlobGasUsed: h.BlobGasUsed, + ExcessBlobGas: h.ExcessBlobGas, + ParentBeaconRoot: parentBeaconRootBytes, + + // Only set on Polygon fork(s) + TxDependency: nil, + } + + if pbHead.Difficulty == nil { + pbHead.Difficulty = &pbeth.BigInt{Bytes: []byte{0}} + } + + return pbHead +} + +// FIXME: Bring back Firehose test that ensures no new tx type are missed +func transactionTypeFromChainTxType(txType uint8) pbeth.TransactionTrace_Type { + switch txType { + case types.AccessListTxType: + return pbeth.TransactionTrace_TRX_TYPE_ACCESS_LIST + case types.DynamicFeeTxType: + return pbeth.TransactionTrace_TRX_TYPE_DYNAMIC_FEE + case types.LegacyTxType: + return pbeth.TransactionTrace_TRX_TYPE_LEGACY + case types.BlobTxType: + return pbeth.TransactionTrace_TRX_TYPE_BLOB + default: + panic(fmt.Errorf("unknown transaction type %d", txType)) + } +} + +func transactionStatusFromChainTxReceipt(txStatus uint64) pbeth.TransactionTraceStatus { + switch txStatus { + case types.ReceiptStatusSuccessful: + return pbeth.TransactionTraceStatus_SUCCEEDED + case types.ReceiptStatusFailed: + return pbeth.TransactionTraceStatus_FAILED + default: + panic(fmt.Errorf("unknown transaction status %d", txStatus)) + } +} + +func rootCallType(create bool) pbeth.CallType { + if create { + return pbeth.CallType_CREATE + } + + return pbeth.CallType_CALL +} + +func callTypeFromOpCode(typ vm.OpCode) pbeth.CallType { + switch typ { + case vm.CALL: + return pbeth.CallType_CALL + case vm.STATICCALL: + return pbeth.CallType_STATIC + case vm.DELEGATECALL: + return pbeth.CallType_DELEGATE + case vm.CREATE, vm.CREATE2: + return pbeth.CallType_CREATE + case vm.CALLCODE: + return pbeth.CallType_CALLCODE + } + + return pbeth.CallType_UNSPECIFIED +} + +func newTxReceiptFromChain(receipt *types.Receipt, txType pbeth.TransactionTrace_Type) (out *pbeth.TransactionReceipt) { + out = &pbeth.TransactionReceipt{ + StateRoot: receipt.PostState, + CumulativeGasUsed: receipt.CumulativeGasUsed, + LogsBloom: receipt.Bloom[:], + } + + if txType == pbeth.TransactionTrace_TRX_TYPE_BLOB { + out.BlobGasUsed = &receipt.BlobGasUsed + out.BlobGasPrice = firehoseBigIntFromNative(receipt.BlobGasPrice) + } + + if len(receipt.Logs) > 0 { + out.Logs = make([]*pbeth.Log, len(receipt.Logs)) + for i, log := range receipt.Logs { + out.Logs[i] = &pbeth.Log{ + Address: log.Address.Bytes(), + Topics: func() [][]byte { + if len(log.Topics) == 0 { + return nil + } + + out := make([][]byte, len(log.Topics)) + for i, topic := range log.Topics { + out[i] = topic.Bytes() + } + return out + }(), + Data: log.Data, + Index: uint32(i), + BlockIndex: uint32(log.Index), + + // Ordinal on transaction receipt logs is populated at the very end, so pairing + // between call logs and receipt logs is made + } + } + } + + return out +} + +func newAccessListFromChain(accessList types.AccessList) (out []*pbeth.AccessTuple) { + if len(accessList) == 0 { + return nil + } + + out = make([]*pbeth.AccessTuple, len(accessList)) + for i, tuple := range accessList { + out[i] = &pbeth.AccessTuple{ + Address: tuple.Address.Bytes(), + StorageKeys: func() [][]byte { + out := make([][]byte, len(tuple.StorageKeys)) + for i, key := range tuple.StorageKeys { + out[i] = key.Bytes() + } + return out + }(), + } + } + + return +} + +func newBlobHashesFromChain(blobHashes []common.Hash) (out [][]byte) { + if len(blobHashes) == 0 { + return nil + } + + out = make([][]byte, len(blobHashes)) + for i, blobHash := range blobHashes { + out[i] = blobHash.Bytes() + } + + return +} + +var balanceChangeReasonToPb = map[tracing.BalanceChangeReason]pbeth.BalanceChange_Reason{ + tracing.BalanceIncreaseRewardMineUncle: pbeth.BalanceChange_REASON_REWARD_MINE_UNCLE, + tracing.BalanceIncreaseRewardMineBlock: pbeth.BalanceChange_REASON_REWARD_MINE_BLOCK, + tracing.BalanceIncreaseDaoContract: pbeth.BalanceChange_REASON_DAO_REFUND_CONTRACT, + tracing.BalanceDecreaseDaoAccount: pbeth.BalanceChange_REASON_DAO_ADJUST_BALANCE, + tracing.BalanceChangeTransfer: pbeth.BalanceChange_REASON_TRANSFER, + tracing.BalanceIncreaseGenesisBalance: pbeth.BalanceChange_REASON_GENESIS_BALANCE, + tracing.BalanceDecreaseGasBuy: pbeth.BalanceChange_REASON_GAS_BUY, + tracing.BalanceIncreaseRewardTransactionFee: pbeth.BalanceChange_REASON_REWARD_TRANSACTION_FEE, + tracing.BalanceIncreaseGasReturn: pbeth.BalanceChange_REASON_GAS_REFUND, + tracing.BalanceChangeTouchAccount: pbeth.BalanceChange_REASON_TOUCH_ACCOUNT, + tracing.BalanceIncreaseSelfdestruct: pbeth.BalanceChange_REASON_SUICIDE_REFUND, + tracing.BalanceDecreaseSelfdestruct: pbeth.BalanceChange_REASON_SUICIDE_WITHDRAW, + tracing.BalanceDecreaseSelfdestructBurn: pbeth.BalanceChange_REASON_BURN, + tracing.BalanceIncreaseWithdrawal: pbeth.BalanceChange_REASON_WITHDRAWAL, + + tracing.BalanceChangeUnspecified: pbeth.BalanceChange_REASON_UNKNOWN, +} + +func balanceChangeReasonFromChain(reason tracing.BalanceChangeReason) pbeth.BalanceChange_Reason { + if r, ok := balanceChangeReasonToPb[reason]; ok { + return r + } + + panic(fmt.Errorf("unknown tracer balance change reason value '%d', check state.BalanceChangeReason so see to which constant it refers to", reason)) +} + +var gasChangeReasonToPb = map[tracing.GasChangeReason]pbeth.GasChange_Reason{ + // Known Firehose issue: Those are new gas change trace that we were missing initially in our old + // Firehose patch. See Known Firehose issue referenced eb1916a67d9bea03df16a7a3e2cfac72 for details + // search for the id within this project to find back all links). + // + // New chain should uncomment the code below and remove the same assigments to UNKNOWN + // + // tracing.GasChangeTxInitialBalance: pbeth.GasChange_REASON_TX_INITIAL_BALANCE, + // tracing.GasChangeTxRefunds: pbeth.GasChange_REASON_TX_REFUNDS, + // tracing.GasChangeTxLeftOverReturned: pbeth.GasChange_REASON_TX_LEFT_OVER_RETURNED, + // tracing.GasChangeCallInitialBalance: pbeth.GasChange_REASON_CALL_INITIAL_BALANCE, + // tracing.GasChangeCallLeftOverReturned: pbeth.GasChange_REASON_CALL_LEFT_OVER_RETURNED, + tracing.GasChangeTxInitialBalance: pbeth.GasChange_REASON_UNKNOWN, + tracing.GasChangeTxRefunds: pbeth.GasChange_REASON_UNKNOWN, + tracing.GasChangeTxLeftOverReturned: pbeth.GasChange_REASON_UNKNOWN, + tracing.GasChangeCallInitialBalance: pbeth.GasChange_REASON_UNKNOWN, + tracing.GasChangeCallLeftOverReturned: pbeth.GasChange_REASON_UNKNOWN, + + tracing.GasChangeTxIntrinsicGas: pbeth.GasChange_REASON_INTRINSIC_GAS, + tracing.GasChangeCallContractCreation: pbeth.GasChange_REASON_CONTRACT_CREATION, + tracing.GasChangeCallContractCreation2: pbeth.GasChange_REASON_CONTRACT_CREATION2, + tracing.GasChangeCallCodeStorage: pbeth.GasChange_REASON_CODE_STORAGE, + tracing.GasChangeCallPrecompiledContract: pbeth.GasChange_REASON_PRECOMPILED_CONTRACT, + tracing.GasChangeCallStorageColdAccess: pbeth.GasChange_REASON_STATE_COLD_ACCESS, + tracing.GasChangeCallLeftOverRefunded: pbeth.GasChange_REASON_REFUND_AFTER_EXECUTION, + tracing.GasChangeCallFailedExecution: pbeth.GasChange_REASON_FAILED_EXECUTION, + + // Ignored, we track them manually, newGasChange ensure that we panic if we see Unknown + tracing.GasChangeCallOpCode: pbeth.GasChange_REASON_UNKNOWN, +} + +func gasChangeReasonFromChain(reason tracing.GasChangeReason) pbeth.GasChange_Reason { + if r, ok := gasChangeReasonToPb[reason]; ok { + if r == pbeth.GasChange_REASON_UNKNOWN { + panic(fmt.Errorf("tracer gas change reason value '%d' mapped to %s which is not accepted", reason, r)) + } + + return r + } + + panic(fmt.Errorf("unknown tracer gas change reason value '%d', check vm.GasChangeReason so see to which constant it refers to", reason)) +} + +func maxFeePerGas(tx *types.Transaction) *pbeth.BigInt { + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + return nil + + case types.DynamicFeeTxType, types.BlobTxType: + return firehoseBigIntFromNative(tx.GasFeeCap()) + + } + + panic(errUnhandledTransactionType("maxFeePerGas", tx.Type())) +} + +func maxPriorityFeePerGas(tx *types.Transaction) *pbeth.BigInt { + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + return nil + + case types.DynamicFeeTxType, types.BlobTxType: + return firehoseBigIntFromNative(tx.GasTipCap()) + } + + panic(errUnhandledTransactionType("maxPriorityFeePerGas", tx.Type())) +} + +func gasPrice(tx *types.Transaction, baseFee *big.Int) *pbeth.BigInt { + switch tx.Type() { + case types.LegacyTxType, types.AccessListTxType: + return firehoseBigIntFromNative(tx.GasPrice()) + + case types.DynamicFeeTxType, types.BlobTxType: + if baseFee == nil { + return firehoseBigIntFromNative(tx.GasPrice()) + } + + return firehoseBigIntFromNative(math.BigMin(new(big.Int).Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())) + } + + panic(errUnhandledTransactionType("gasPrice", tx.Type())) +} + +func FirehoseDebug(msg string, args ...interface{}) { + firehoseDebug(msg, args...) +} + +func firehoseDebug(msg string, args ...interface{}) { + if isFirehoseDebugEnabled { + fmt.Fprintf(os.Stderr, "[Firehose] "+msg+"\n", args...) + } +} + +func firehoseTrace(msg string, args ...interface{}) { + if isFirehoseTracerEnabled { + fmt.Fprintf(os.Stderr, "[Firehose] "+msg+"\n", args...) + } +} + +// Ignore unused, we keep it around for debugging purposes +var _ = firehoseDebugPrintStack + +func firehoseDebugPrintStack() { + if isFirehoseDebugEnabled { + fmt.Fprintf(os.Stderr, "[Firehose] Stacktrace\n") + + // PrintStack prints to Stderr + debug.PrintStack() + } +} + +func errUnhandledTransactionType(tag string, value uint8) error { + return fmt.Errorf("unhandled transaction type's %d for firehose.%s(), carefully review the patch, if this new transaction type add new fields, think about adding them to Firehose Block format, when you see this message, it means something changed in the chain model and great care and thinking most be put here to properly understand the changes and the consequences they bring for the instrumentation", value, tag) +} + +type Ordinal struct { + value uint64 +} + +// Reset resets the ordinal to zero. +func (o *Ordinal) Reset() { + o.value = 0 +} + +// Next gives you the next sequential ordinal value that you should +// use to assign to your exeuction trace (block, transaction, call, etc). +func (o *Ordinal) Next() (out uint64) { + o.value++ + + return o.value +} + +type CallStack struct { + index uint32 + stack []*pbeth.Call + depth int +} + +func NewCallStack() *CallStack { + return &CallStack{} +} + +func (s *CallStack) Reset() { + s.index = 0 + s.stack = s.stack[:0] + s.depth = 0 +} + +func (s *CallStack) HasActiveCall() bool { + return len(s.stack) > 0 +} + +// Push a call onto the stack. The `Index` and `ParentIndex` of this call are +// assigned by this method which knowns how to find the parent call and deal with +// it. +func (s *CallStack) Push(call *pbeth.Call) { + s.index++ + call.Index = s.index + + call.Depth = uint32(s.depth) + s.depth++ + + // If a current call is active, it's the parent of this call + if parent := s.Peek(); parent != nil { + call.ParentIndex = parent.Index + } + + s.stack = append(s.stack, call) +} + +func (s *CallStack) ActiveIndex() uint32 { + if len(s.stack) == 0 { + return 0 + } + + return s.stack[len(s.stack)-1].Index +} + +func (s *CallStack) NextIndex() uint32 { + return s.index + 1 +} + +func (s *CallStack) Pop() (out *pbeth.Call) { + if len(s.stack) == 0 { + panic(fmt.Errorf("pop from empty call stack")) + } + + out = s.stack[len(s.stack)-1] + s.stack = s.stack[:len(s.stack)-1] + s.depth-- + + return +} + +// Peek returns the top of the stack without removing it, it's the +// activate call. +func (s *CallStack) Peek() *pbeth.Call { + if len(s.stack) == 0 { + return nil + } + + return s.stack[len(s.stack)-1] +} + +// DeferredCallState is a helper struct that can be used to accumulate call's state +// that is recorded before the Call has been started. This happens on the "starting" +// portion of the call/created. +type DeferredCallState struct { + accountCreations []*pbeth.AccountCreation + balanceChanges []*pbeth.BalanceChange + gasChanges []*pbeth.GasChange + nonceChanges []*pbeth.NonceChange +} + +func NewDeferredCallState() *DeferredCallState { + return &DeferredCallState{} +} + +func (d *DeferredCallState) MaybePopulateCallAndReset(source string, call *pbeth.Call) error { + if d.IsEmpty() { + return nil + } + + if source != "root" { + return fmt.Errorf("unexpected source for deferred call state, expected root but got %s, deferred call's state are always produced on the 'root' call", source) + } + + // We must happen because it's populated at beginning of the call as well as at the very end + call.AccountCreations = append(call.AccountCreations, d.accountCreations...) + call.BalanceChanges = append(call.BalanceChanges, d.balanceChanges...) + call.GasChanges = append(call.GasChanges, d.gasChanges...) + call.NonceChanges = append(call.NonceChanges, d.nonceChanges...) + + d.Reset() + + return nil +} + +func (d *DeferredCallState) IsEmpty() bool { + return len(d.accountCreations) == 0 && len(d.balanceChanges) == 0 && len(d.gasChanges) == 0 && len(d.nonceChanges) == 0 +} + +func (d *DeferredCallState) Reset() { + d.accountCreations = nil + d.balanceChanges = nil + d.gasChanges = nil + d.nonceChanges = nil +} + +func errorView(err error) _errorView { + return _errorView{err} +} + +type _errorView struct { + err error +} + +func (e _errorView) String() string { + if e.err == nil { + return "" + } + + return e.err.Error() +} + +type inputView []byte + +func (b inputView) String() string { + if len(b) == 0 { + return "" + } + + if len(b) < 4 { + return common.Bytes2Hex(b) + } + + method := b[:4] + rest := b[4:] + + if len(rest)%32 == 0 { + return fmt.Sprintf("%s (%d params)", common.Bytes2Hex(method), len(rest)/32) + } + + // Contract input starts with pre-defined chracters AFAIK, we could show them more nicely + + return fmt.Sprintf("%d bytes", len(rest)) +} + +type outputView []byte + +func (b outputView) String() string { + if len(b) == 0 { + return "" + } + + return fmt.Sprintf("%d bytes", len(b)) +} + +type receiptView types.Receipt + +func (r *receiptView) String() string { + if r == nil { + return "" + } + + status := "unknown" + switch r.Status { + case types.ReceiptStatusSuccessful: + status = "success" + case types.ReceiptStatusFailed: + status = "failed" + } + + return fmt.Sprintf("[status=%s, gasUsed=%d, logs=%d]", status, r.GasUsed, len(r.Logs)) +} + +func emptyBytesToNil(in []byte) []byte { + if len(in) == 0 { + return nil + } + + return in +} + +func normalizeSignaturePoint(value []byte) []byte { + if len(value) == 0 { + return nil + } + + if len(value) < 32 { + offset := 32 - len(value) + + out := make([]byte, 32) + copy(out[offset:32], value) + + return out + } + + return value[0:32] +} + +func firehoseBigIntFromNative(in *big.Int) *pbeth.BigInt { + if in == nil || in.Sign() == 0 { + return nil + } + + return &pbeth.BigInt{Bytes: in.Bytes()} +} + +type FinalityStatus struct { + LastIrreversibleBlockNumber uint64 + LastIrreversibleBlockHash []byte +} + +func (s *FinalityStatus) populateFromChain(finalHeader *types.Header) { + if finalHeader == nil { + s.Reset() + return + } + + s.LastIrreversibleBlockNumber = finalHeader.Number.Uint64() + s.LastIrreversibleBlockHash = finalHeader.Hash().Bytes() +} + +func (s *FinalityStatus) Reset() { + s.LastIrreversibleBlockNumber = 0 + s.LastIrreversibleBlockHash = nil +} + +func (s *FinalityStatus) IsEmpty() bool { + return s.LastIrreversibleBlockNumber == 0 && len(s.LastIrreversibleBlockHash) == 0 +} + +var errFirehoseUnknownType = errors.New("firehose unknown tx type") +var sanitizeRegexp = regexp.MustCompile(`[\t( ){2,}]+`) + +func staticFirehoseChainValidationOnInit() { + firehoseKnownTxTypes := map[byte]bool{ + types.LegacyTxType: true, + types.AccessListTxType: true, + types.DynamicFeeTxType: true, + types.BlobTxType: true, + } + + for txType := byte(0); txType < 255; txType++ { + err := validateFirehoseKnownTransactionType(txType, firehoseKnownTxTypes[txType]) + if err != nil { + panic(fmt.Errorf(sanitizeRegexp.ReplaceAllString(` + If you see this panic message, it comes from a sanity check of Firehose instrumentation + around Ethereum transaction types. + + Over time, Ethereum added new transaction types but there is no easy way for Firehose to + report a compile time check that a new transaction's type must be handled. As such, we + have a runtime check at initialization of the process that encode/decode each possible + transaction's receipt and check proper handling. + + This panic means that a transaction that Firehose don't know about has most probably + been added and you must take **great care** to instrument it. One of the most important place + to look is in 'firehose.StartTransaction' where it should be properly handled. Think + carefully, read the EIP and ensure that any new "semantic" the transactions type's is + bringing is handled and instrumented (it might affect Block and other execution units also). + + For example, when London fork appeared, semantic of 'GasPrice' changed and it required + a different computation for 'GasPrice' when 'DynamicFeeTx' transaction were added. If you determined + it was indeed a new transaction's type, fix 'firehoseKnownTxTypes' variable above to include it + as a known Firehose type (after proper instrumentation of course). + + It's also possible the test itself is now flaky, we do 'receipt := types.Receipt{Type: }' + then 'buffer := receipt.EncodeRLP(...)' and then 'receipt.DecodeRLP(buffer)'. This should catch + new transaction types but could be now generate false positive. + + Received error: %w + `, " "), err)) + } + } +} + +func validateFirehoseKnownTransactionType(txType byte, isKnownFirehoseTxType bool) error { + writerBuffer := bytes.NewBuffer(nil) + + receipt := types.Receipt{Type: txType} + err := receipt.EncodeRLP(writerBuffer) + if err != nil { + if err == types.ErrTxTypeNotSupported { + if isKnownFirehoseTxType { + return fmt.Errorf("firehose known type but encoding RLP of receipt led to 'types.ErrTxTypeNotSupported'") + } + + // It's not a known type and encoding reported the same, so validation is OK + return nil + } + + // All other cases results in an error as we should have been able to encode it to RLP + return fmt.Errorf("encoding RLP: %w", err) + } + + readerBuffer := bytes.NewBuffer(writerBuffer.Bytes()) + err = receipt.DecodeRLP(rlp.NewStream(readerBuffer, 0)) + if err != nil { + if err == types.ErrTxTypeNotSupported { + if isKnownFirehoseTxType { + return fmt.Errorf("firehose known type but decoding of RLP of receipt led to 'types.ErrTxTypeNotSupported'") + } + + // It's not a known type and decoding reported the same, so validation is OK + return nil + } + + // All other cases results in an error as we should have been able to decode it from RLP + return fmt.Errorf("decoding RLP: %w", err) + } + + // If we reach here, encoding/decoding accepted the transaction's type, so let's ensure we expected the same + if !isKnownFirehoseTxType { + return fmt.Errorf("unknown tx type value %d: %w", txType, errFirehoseUnknownType) + } + + return nil +} + +type validationResult struct { + failures []string +} + +func (r *validationResult) panicOnAnyFailures(msg string, args ...any) { + if len(r.failures) > 0 { + panic(fmt.Errorf(fmt.Sprintf(msg, args...)+": validation failed:\n %s", strings.Join(r.failures, "\n"))) + } +} + +// We keep them around, planning in the future to use them (they existed in the previous Firehose patch) +var _, _, _, _ = validateAddressField, validateBigIntField, validateHashField, validateUint64Field + +func validateAddressField(into *validationResult, field string, a, b common.Address) { + validateField(into, field, a, b, a == b, common.Address.String) +} + +func validateBigIntField(into *validationResult, field string, a, b *big.Int) { + equal := false + if a == nil && b == nil { + equal = true + } else if a == nil || b == nil { + equal = false + } else { + equal = a.Cmp(b) == 0 + } + + validateField(into, field, a, b, equal, func(x *big.Int) string { + if x == nil { + return "" + } else { + return x.String() + } + }) +} + +func validateBytesField(into *validationResult, field string, a, b []byte) { + validateField(into, field, a, b, bytes.Equal(a, b), common.Bytes2Hex) +} + +func validateArrayOfBytesField(into *validationResult, field string, a, b [][]byte) { + if len(a) != len(b) { + into.failures = append(into.failures, fmt.Sprintf("%s [(actual element) %d != %d (expected element)]", field, len(a), len(b))) + return + } + + for i := range a { + validateBytesField(into, fmt.Sprintf("%s[%d]", field, i), a[i], b[i]) + } +} + +func validateHashField(into *validationResult, field string, a, b common.Hash) { + validateField(into, field, a, b, a == b, common.Hash.String) +} + +func validateUint32Field(into *validationResult, field string, a, b uint32) { + validateField(into, field, a, b, a == b, func(x uint32) string { return strconv.FormatUint(uint64(x), 10) }) +} + +func validateUint64Field(into *validationResult, field string, a, b uint64) { + validateField(into, field, a, b, a == b, func(x uint64) string { return strconv.FormatUint(x, 10) }) +} + +// validateField, pays the price for failure message construction only when field are not equal +func validateField[T any](into *validationResult, field string, a, b T, equal bool, toString func(x T) string) { + if !equal { + into.failures = append(into.failures, fmt.Sprintf("%s [(actual) %s %s %s (expected)]", field, toString(a), "!=", toString(b))) + } +} + +func ptr[T any](t T) *T { + return &t +} diff --git a/eth/tracers/firehose_test.go b/eth/tracers/firehose_test.go new file mode 100644 index 0000000000..3349cc5357 --- /dev/null +++ b/eth/tracers/firehose_test.go @@ -0,0 +1,288 @@ +package tracers + +import ( + "encoding/json" + "fmt" + "math/big" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/exp/maps" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestFirehoseCallStack_Push(t *testing.T) { + type actionRunner func(t *testing.T, s *CallStack) + + push := func(call *pbeth.Call) actionRunner { return func(_ *testing.T, s *CallStack) { s.Push(call) } } + pop := func() actionRunner { return func(_ *testing.T, s *CallStack) { s.Pop() } } + check := func(r actionRunner) actionRunner { return func(t *testing.T, s *CallStack) { r(t, s) } } + + tests := []struct { + name string + actions []actionRunner + }{ + { + "push/pop emtpy", []actionRunner{ + push(&pbeth.Call{}), + pop(), + check(func(t *testing.T, s *CallStack) { + require.Len(t, s.stack, 0) + }), + }, + }, + { + "push/push/push", []actionRunner{ + push(&pbeth.Call{}), + push(&pbeth.Call{}), + push(&pbeth.Call{}), + check(func(t *testing.T, s *CallStack) { + require.Len(t, s.stack, 3) + + require.Equal(t, 1, int(s.stack[0].Index)) + require.Equal(t, 0, int(s.stack[0].ParentIndex)) + + require.Equal(t, 2, int(s.stack[1].Index)) + require.Equal(t, 1, int(s.stack[1].ParentIndex)) + + require.Equal(t, 3, int(s.stack[2].Index)) + require.Equal(t, 2, int(s.stack[2].ParentIndex)) + }), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewCallStack() + + for _, action := range tt.actions { + action(t, s) + } + }) + } +} + +func Test_validateKnownTransactionTypes(t *testing.T) { + tests := []struct { + name string + txType byte + knownType bool + want error + }{ + {"legacy", 0, true, nil}, + {"access_list", 1, true, nil}, + {"inexistant", 255, false, nil}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateFirehoseKnownTransactionType(tt.txType, tt.knownType) + if tt.want == nil && err != nil { + t.Fatalf("Transaction of type %d expected to validate properly but received error %q", tt.txType, err) + } else if tt.want != nil && err == nil { + t.Fatalf("Transaction of type %d expected to validate improperly but generated no error", tt.txType) + } else if tt.want != nil && err != nil && tt.want.Error() != err.Error() { + t.Fatalf("Transaction of type %d expected to validate improperly but generated error %q does not match expected error %q", tt.txType, err, tt.want) + } + }) + } +} + +var ignorePbFieldNames = map[string]bool{ + "Hash": true, + "TotalDifficulty": true, + "state": true, + "unknownFields": true, + "sizeCache": true, + + // This was a Polygon specific field that existed for a while and has since been + // removed. It can be safely ignored in all protocols now. + "TxDependency": true, +} + +var pbFieldNameToGethMapping = map[string]string{ + "WithdrawalsRoot": "WithdrawalsHash", + "MixHash": "MixDigest", + "BaseFeePerGas": "BaseFee", + "StateRoot": "Root", + "ExtraData": "Extra", + "Timestamp": "Time", + "ReceiptRoot": "ReceiptHash", + "TransactionsRoot": "TxHash", + "LogsBloom": "Bloom", +} + +var ( + pbHeaderType = reflect.TypeFor[pbeth.BlockHeader]() + gethHeaderType = reflect.TypeFor[types.Header]() +) + +func Test_TypesHeader_AllConsensusFieldsAreKnown(t *testing.T) { + // This exact hash varies from protocol to protocol and also sometimes from one version to the other. + // When adding support for a new hard-fork that adds new block header fields, it's normal that this value + // changes. If you are sure the two struct are the same, then you can update the expected hash below + // to the new value. + expectedHash := common.HexToHash("5341947c531e5c9cf38202784b16ac66484fe1838aa6e825436b22321b927296") + + gethHeaderValue := reflect.New(gethHeaderType) + fillAllFieldsWithNonEmptyValues(t, gethHeaderValue, reflect.VisibleFields(gethHeaderType)) + gethHeader := gethHeaderValue.Interface().(*types.Header) + + // If you hit this assertion, it means that the fields `types.Header` of go-ethereum differs now + // versus last time this test was edited. + // + // It's important to understand that in Ethereum Block Header (e.g. `*types.Header`), the `Hash` is + // actually a computed value based on the other fields in the struct, so if you change any field, + // the hash will change also. + // + // On hard-fork, it happens that new fields are added, this test serves as a way to "detect" in codde + // that the expected fields of `types.Header` changed + require.Equal(t, expectedHash, gethHeader.Hash(), + "Geth Header Hash mistmatch, got %q but expecting %q on *types.Header:\n\nGeth Header (from fillNonDefault(new(*types.Header)))\n%s", + gethHeader.Hash().Hex(), + expectedHash, + asIndentedJSON(t, gethHeader), + ) +} + +func Test_FirehoseAndGethHeaderFieldMatches(t *testing.T) { + pbFields := filter(reflect.VisibleFields(pbHeaderType), func(f reflect.StructField) bool { + return !ignorePbFieldNames[f.Name] + }) + + gethFields := reflect.VisibleFields(gethHeaderType) + + pbFieldCount := len(pbFields) + gethFieldCount := len(gethFields) + + pbFieldNames := extractStructFieldNames(pbFields) + gethFieldNames := extractStructFieldNames(gethFields) + + // If you reach this assertion, it means that the fields count in the protobuf and go-ethereum are different. + // It is super important that you properly update the mapping from pbeth.BlockHeader to go-ethereum/core/types.Header + // that is done in `codecHeaderToGethHeader` function in `executor/provider_statedb.go`. + require.Equal( + t, + pbFieldCount, + gethFieldCount, + fieldsCountMistmatchMessage(t, pbFieldNames, gethFieldNames)) + + for pbFieldName := range pbFieldNames { + pbFieldRenamedName, found := pbFieldNameToGethMapping[pbFieldName] + if !found { + pbFieldRenamedName = pbFieldName + } + + assert.Contains(t, gethFieldNames, pbFieldRenamedName, "pbField.Name=%q (original %q) not found in gethFieldNames", pbFieldRenamedName, pbFieldName) + } +} + +func fillAllFieldsWithNonEmptyValues(t *testing.T, structValue reflect.Value, fields []reflect.StructField) { + t.Helper() + + for _, field := range fields { + fieldValue := structValue.Elem().FieldByName(field.Name) + require.True(t, fieldValue.IsValid(), "field %q not found", field.Name) + + switch fieldValue.Interface().(type) { + case []byte: + fieldValue.Set(reflect.ValueOf([]byte{1})) + case uint64: + fieldValue.Set(reflect.ValueOf(uint64(1))) + case *uint64: + var mockValue uint64 = 1 + fieldValue.Set(reflect.ValueOf(&mockValue)) + case *common.Hash: + var mockValue common.Hash = common.HexToHash("0x01") + fieldValue.Set(reflect.ValueOf(&mockValue)) + case common.Hash: + fieldValue.Set(reflect.ValueOf(common.HexToHash("0x01"))) + case common.Address: + fieldValue.Set(reflect.ValueOf(common.HexToAddress("0x01"))) + case types.Bloom: + fieldValue.Set(reflect.ValueOf(types.BytesToBloom([]byte{1}))) + case types.BlockNonce: + fieldValue.Set(reflect.ValueOf(types.EncodeNonce(1))) + case *big.Int: + fieldValue.Set(reflect.ValueOf(big.NewInt(1))) + case *pbeth.BigInt: + fieldValue.Set(reflect.ValueOf(&pbeth.BigInt{Bytes: []byte{1}})) + case *timestamppb.Timestamp: + fieldValue.Set(reflect.ValueOf(×tamppb.Timestamp{Seconds: 1})) + default: + // If you reach this panic in test, simply add a case above with a sane non-default + // value for the type in question. + t.Fatalf("unsupported type %T", fieldValue.Interface()) + } + } +} + +func fieldsCountMistmatchMessage(t *testing.T, pbFieldNames map[string]bool, gethFieldNames map[string]bool) string { + t.Helper() + + pbRemappedFieldNames := make(map[string]bool, len(pbFieldNames)) + for pbFieldName := range pbFieldNames { + pbFieldRenamedName, found := pbFieldNameToGethMapping[pbFieldName] + if !found { + pbFieldRenamedName = pbFieldName + } + + pbRemappedFieldNames[pbFieldRenamedName] = true + } + + return fmt.Sprintf( + "Field count mistmatch between `pbeth.BlockHeader` (has %d fields) and `*types.Header` (has %d fields)\n\n"+ + "Fields in `pbeth.Blockheader`:\n%s\n\n"+ + "Fields in `*types.Header`:\n%s\n\n"+ + "Missing in `pbeth.BlockHeader`:\n%s\n\n"+ + "Missing in `*types.Header`:\n%s", + len(pbRemappedFieldNames), + len(gethFieldNames), + asIndentedJSON(t, maps.Keys(pbRemappedFieldNames)), + asIndentedJSON(t, maps.Keys(gethFieldNames)), + asIndentedJSON(t, missingInSet(gethFieldNames, pbRemappedFieldNames)), + asIndentedJSON(t, missingInSet(pbRemappedFieldNames, gethFieldNames)), + ) +} + +func asIndentedJSON(t *testing.T, v any) string { + t.Helper() + out, err := json.MarshalIndent(v, "", " ") + require.NoError(t, err) + + return string(out) +} + +func missingInSet(a, b map[string]bool) []string { + missing := make([]string, 0) + for name := range a { + if !b[name] { + missing = append(missing, name) + } + } + + return missing +} + +func extractStructFieldNames(fields []reflect.StructField) map[string]bool { + result := make(map[string]bool, len(fields)) + for _, field := range fields { + result[field.Name] = true + } + return result +} + +func filter[S ~[]T, T any](s S, f func(T) bool) (out S) { + out = make(S, 0, len(s)/4) + for i, v := range s { + if f(v) { + out = append(out, s[i]) + } + } + + return out +} diff --git a/go.mod b/go.mod index 385d5afdc8..86b4d1fdbe 100644 --- a/go.mod +++ b/go.mod @@ -70,6 +70,7 @@ require ( golang.org/x/text v0.13.0 golang.org/x/time v0.3.0 golang.org/x/tools v0.13.0 + google.golang.org/protobuf v1.27.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -138,7 +139,6 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 // indirect - google.golang.org/protobuf v1.27.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/pb/sf/ethereum/type/v2/type.go b/pb/sf/ethereum/type/v2/type.go new file mode 100644 index 0000000000..38b6881fc1 --- /dev/null +++ b/pb/sf/ethereum/type/v2/type.go @@ -0,0 +1,27 @@ +package pbeth + +import ( + "encoding/hex" + "math/big" + "time" +) + +var b0 = big.NewInt(0) + +func (b *Block) PreviousID() string { + return hex.EncodeToString(b.Header.ParentHash) +} + +func (b *Block) Time() time.Time { + return b.Header.Timestamp.AsTime() +} + +func (m *BigInt) Native() *big.Int { + if m == nil { + return b0 + } + + z := new(big.Int) + z.SetBytes(m.Bytes) + return z +} diff --git a/pb/sf/ethereum/type/v2/type.pb.go b/pb/sf/ethereum/type/v2/type.pb.go new file mode 100644 index 0000000000..8103b2f182 --- /dev/null +++ b/pb/sf/ethereum/type/v2/type.pb.go @@ -0,0 +1,3691 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc (unknown) +// source: sf/ethereum/type/v2/type.proto + +package pbeth + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TransactionTraceStatus int32 + +const ( + TransactionTraceStatus_UNKNOWN TransactionTraceStatus = 0 + TransactionTraceStatus_SUCCEEDED TransactionTraceStatus = 1 + TransactionTraceStatus_FAILED TransactionTraceStatus = 2 + TransactionTraceStatus_REVERTED TransactionTraceStatus = 3 +) + +// Enum value maps for TransactionTraceStatus. +var ( + TransactionTraceStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SUCCEEDED", + 2: "FAILED", + 3: "REVERTED", + } + TransactionTraceStatus_value = map[string]int32{ + "UNKNOWN": 0, + "SUCCEEDED": 1, + "FAILED": 2, + "REVERTED": 3, + } +) + +func (x TransactionTraceStatus) Enum() *TransactionTraceStatus { + p := new(TransactionTraceStatus) + *p = x + return p +} + +func (x TransactionTraceStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TransactionTraceStatus) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[0].Descriptor() +} + +func (TransactionTraceStatus) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[0] +} + +func (x TransactionTraceStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TransactionTraceStatus.Descriptor instead. +func (TransactionTraceStatus) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{0} +} + +type CallType int32 + +const ( + CallType_UNSPECIFIED CallType = 0 + CallType_CALL CallType = 1 // direct? what's the name for `Call` alone? + CallType_CALLCODE CallType = 2 + CallType_DELEGATE CallType = 3 + CallType_STATIC CallType = 4 + CallType_CREATE CallType = 5 // create2 ? any other form of calls? +) + +// Enum value maps for CallType. +var ( + CallType_name = map[int32]string{ + 0: "UNSPECIFIED", + 1: "CALL", + 2: "CALLCODE", + 3: "DELEGATE", + 4: "STATIC", + 5: "CREATE", + } + CallType_value = map[string]int32{ + "UNSPECIFIED": 0, + "CALL": 1, + "CALLCODE": 2, + "DELEGATE": 3, + "STATIC": 4, + "CREATE": 5, + } +) + +func (x CallType) Enum() *CallType { + p := new(CallType) + *p = x + return p +} + +func (x CallType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CallType) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[1].Descriptor() +} + +func (CallType) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[1] +} + +func (x CallType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CallType.Descriptor instead. +func (CallType) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{1} +} + +type Block_DetailLevel int32 + +const ( + Block_DETAILLEVEL_EXTENDED Block_DetailLevel = 0 + // DETAILLEVEL_TRACE = 1; // TBD + Block_DETAILLEVEL_BASE Block_DetailLevel = 2 +) + +// Enum value maps for Block_DetailLevel. +var ( + Block_DetailLevel_name = map[int32]string{ + 0: "DETAILLEVEL_EXTENDED", + 2: "DETAILLEVEL_BASE", + } + Block_DetailLevel_value = map[string]int32{ + "DETAILLEVEL_EXTENDED": 0, + "DETAILLEVEL_BASE": 2, + } +) + +func (x Block_DetailLevel) Enum() *Block_DetailLevel { + p := new(Block_DetailLevel) + *p = x + return p +} + +func (x Block_DetailLevel) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Block_DetailLevel) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[2].Descriptor() +} + +func (Block_DetailLevel) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[2] +} + +func (x Block_DetailLevel) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Block_DetailLevel.Descriptor instead. +func (Block_DetailLevel) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{0, 0} +} + +type TransactionTrace_Type int32 + +const ( + // All transactions that ever existed prior Berlin fork before EIP-2718 was implemented. + TransactionTrace_TRX_TYPE_LEGACY TransactionTrace_Type = 0 + // Transaction that specicy an access list of contract/storage_keys that is going to be used + // in this transaction. + // + // Added in Berlin fork (EIP-2930). + TransactionTrace_TRX_TYPE_ACCESS_LIST TransactionTrace_Type = 1 + // Transaction that specifis an access list just like TRX_TYPE_ACCESS_LIST but in addition defines the + // max base gas gee and max priority gas fee to pay for this transaction. Transaction's of those type are + // executed against EIP-1559 rules which dictates a dynamic gas cost based on the congestion of the network. + TransactionTrace_TRX_TYPE_DYNAMIC_FEE TransactionTrace_Type = 2 + // Transaction which contain a large amount of data that cannot be accessed by EVM execution, but whose commitment + // can be accessed. The format is intended to be fully compatible with the format that will be used in full sharding. + // + // Transaction that defines specifis an access list just like TRX_TYPE_ACCESS_LIST and enables dynamic fee just like + // TRX_TYPE_DYNAMIC_FEE but in addition defines the fields 'max_fee_per_data_gas' of type 'uint256' and the fields + // 'blob_versioned_hashes' field represents a list of hash outputs from 'kzg_to_versioned_hash'. + // + // Activated in Dencun + TransactionTrace_TRX_TYPE_BLOB TransactionTrace_Type = 3 + // Arbitrum-specific transactions + TransactionTrace_TRX_TYPE_ARBITRUM_DEPOSIT TransactionTrace_Type = 100 + TransactionTrace_TRX_TYPE_ARBITRUM_UNSIGNED TransactionTrace_Type = 101 + TransactionTrace_TRX_TYPE_ARBITRUM_CONTRACT TransactionTrace_Type = 102 + TransactionTrace_TRX_TYPE_ARBITRUM_RETRY TransactionTrace_Type = 104 + TransactionTrace_TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE TransactionTrace_Type = 105 + TransactionTrace_TRX_TYPE_ARBITRUM_INTERNAL TransactionTrace_Type = 106 + TransactionTrace_TRX_TYPE_ARBITRUM_LEGACY TransactionTrace_Type = 120 +) + +// Enum value maps for TransactionTrace_Type. +var ( + TransactionTrace_Type_name = map[int32]string{ + 0: "TRX_TYPE_LEGACY", + 1: "TRX_TYPE_ACCESS_LIST", + 2: "TRX_TYPE_DYNAMIC_FEE", + 3: "TRX_TYPE_BLOB", + 100: "TRX_TYPE_ARBITRUM_DEPOSIT", + 101: "TRX_TYPE_ARBITRUM_UNSIGNED", + 102: "TRX_TYPE_ARBITRUM_CONTRACT", + 104: "TRX_TYPE_ARBITRUM_RETRY", + 105: "TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE", + 106: "TRX_TYPE_ARBITRUM_INTERNAL", + 120: "TRX_TYPE_ARBITRUM_LEGACY", + } + TransactionTrace_Type_value = map[string]int32{ + "TRX_TYPE_LEGACY": 0, + "TRX_TYPE_ACCESS_LIST": 1, + "TRX_TYPE_DYNAMIC_FEE": 2, + "TRX_TYPE_BLOB": 3, + "TRX_TYPE_ARBITRUM_DEPOSIT": 100, + "TRX_TYPE_ARBITRUM_UNSIGNED": 101, + "TRX_TYPE_ARBITRUM_CONTRACT": 102, + "TRX_TYPE_ARBITRUM_RETRY": 104, + "TRX_TYPE_ARBITRUM_SUBMIT_RETRYABLE": 105, + "TRX_TYPE_ARBITRUM_INTERNAL": 106, + "TRX_TYPE_ARBITRUM_LEGACY": 120, + } +) + +func (x TransactionTrace_Type) Enum() *TransactionTrace_Type { + p := new(TransactionTrace_Type) + *p = x + return p +} + +func (x TransactionTrace_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TransactionTrace_Type) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[3].Descriptor() +} + +func (TransactionTrace_Type) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[3] +} + +func (x TransactionTrace_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TransactionTrace_Type.Descriptor instead. +func (TransactionTrace_Type) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{5, 0} +} + +// Obtain all balanche change reasons under deep mind repository: +// +// ```shell +// ack -ho 'BalanceChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq +// ``` +type BalanceChange_Reason int32 + +const ( + BalanceChange_REASON_UNKNOWN BalanceChange_Reason = 0 + BalanceChange_REASON_REWARD_MINE_UNCLE BalanceChange_Reason = 1 + BalanceChange_REASON_REWARD_MINE_BLOCK BalanceChange_Reason = 2 + BalanceChange_REASON_DAO_REFUND_CONTRACT BalanceChange_Reason = 3 + BalanceChange_REASON_DAO_ADJUST_BALANCE BalanceChange_Reason = 4 + BalanceChange_REASON_TRANSFER BalanceChange_Reason = 5 + BalanceChange_REASON_GENESIS_BALANCE BalanceChange_Reason = 6 + BalanceChange_REASON_GAS_BUY BalanceChange_Reason = 7 + BalanceChange_REASON_REWARD_TRANSACTION_FEE BalanceChange_Reason = 8 + BalanceChange_REASON_REWARD_FEE_RESET BalanceChange_Reason = 14 + BalanceChange_REASON_GAS_REFUND BalanceChange_Reason = 9 + BalanceChange_REASON_TOUCH_ACCOUNT BalanceChange_Reason = 10 + BalanceChange_REASON_SUICIDE_REFUND BalanceChange_Reason = 11 + BalanceChange_REASON_SUICIDE_WITHDRAW BalanceChange_Reason = 13 + BalanceChange_REASON_CALL_BALANCE_OVERRIDE BalanceChange_Reason = 12 + // Used on chain(s) where some Ether burning happens + BalanceChange_REASON_BURN BalanceChange_Reason = 15 + BalanceChange_REASON_WITHDRAWAL BalanceChange_Reason = 16 +) + +// Enum value maps for BalanceChange_Reason. +var ( + BalanceChange_Reason_name = map[int32]string{ + 0: "REASON_UNKNOWN", + 1: "REASON_REWARD_MINE_UNCLE", + 2: "REASON_REWARD_MINE_BLOCK", + 3: "REASON_DAO_REFUND_CONTRACT", + 4: "REASON_DAO_ADJUST_BALANCE", + 5: "REASON_TRANSFER", + 6: "REASON_GENESIS_BALANCE", + 7: "REASON_GAS_BUY", + 8: "REASON_REWARD_TRANSACTION_FEE", + 14: "REASON_REWARD_FEE_RESET", + 9: "REASON_GAS_REFUND", + 10: "REASON_TOUCH_ACCOUNT", + 11: "REASON_SUICIDE_REFUND", + 13: "REASON_SUICIDE_WITHDRAW", + 12: "REASON_CALL_BALANCE_OVERRIDE", + 15: "REASON_BURN", + 16: "REASON_WITHDRAWAL", + } + BalanceChange_Reason_value = map[string]int32{ + "REASON_UNKNOWN": 0, + "REASON_REWARD_MINE_UNCLE": 1, + "REASON_REWARD_MINE_BLOCK": 2, + "REASON_DAO_REFUND_CONTRACT": 3, + "REASON_DAO_ADJUST_BALANCE": 4, + "REASON_TRANSFER": 5, + "REASON_GENESIS_BALANCE": 6, + "REASON_GAS_BUY": 7, + "REASON_REWARD_TRANSACTION_FEE": 8, + "REASON_REWARD_FEE_RESET": 14, + "REASON_GAS_REFUND": 9, + "REASON_TOUCH_ACCOUNT": 10, + "REASON_SUICIDE_REFUND": 11, + "REASON_SUICIDE_WITHDRAW": 13, + "REASON_CALL_BALANCE_OVERRIDE": 12, + "REASON_BURN": 15, + "REASON_WITHDRAWAL": 16, + } +) + +func (x BalanceChange_Reason) Enum() *BalanceChange_Reason { + p := new(BalanceChange_Reason) + *p = x + return p +} + +func (x BalanceChange_Reason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BalanceChange_Reason) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[4].Descriptor() +} + +func (BalanceChange_Reason) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[4] +} + +func (x BalanceChange_Reason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BalanceChange_Reason.Descriptor instead. +func (BalanceChange_Reason) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{11, 0} +} + +// Obtain all gas change reasons under deep mind repository: +// +// ```shell +// ack -ho 'GasChangeReason\(".*"\)' | grep -Eo '".*"' | sort | uniq +// ``` +type GasChange_Reason int32 + +const ( + GasChange_REASON_UNKNOWN GasChange_Reason = 0 + // REASON_CALL is the amount of gas that will be charged for a 'CALL' opcode executed by the EVM + GasChange_REASON_CALL GasChange_Reason = 1 + // REASON_CALL_CODE is the amount of gas that will be charged for a 'CALLCODE' opcode executed by the EVM + GasChange_REASON_CALL_CODE GasChange_Reason = 2 + // REASON_CALL_DATA_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM + GasChange_REASON_CALL_DATA_COPY GasChange_Reason = 3 + // REASON_CODE_COPY is the amount of gas that will be charged for a 'CALLDATACOPY' opcode executed by the EVM + GasChange_REASON_CODE_COPY GasChange_Reason = 4 + // REASON_CODE_STORAGE is the amount of gas that will be charged for code storage + GasChange_REASON_CODE_STORAGE GasChange_Reason = 5 + // REASON_CONTRACT_CREATION is the amount of gas that will be charged for a 'CREATE' opcode executed by the EVM and for the gas + // burned for a CREATE, today controlled by EIP150 rules + GasChange_REASON_CONTRACT_CREATION GasChange_Reason = 6 + // REASON_CONTRACT_CREATION2 is the amount of gas that will be charged for a 'CREATE2' opcode executed by the EVM and for the gas + // burned for a CREATE2, today controlled by EIP150 rules + GasChange_REASON_CONTRACT_CREATION2 GasChange_Reason = 7 + // REASON_DELEGATE_CALL is the amount of gas that will be charged for a 'DELEGATECALL' opcode executed by the EVM + GasChange_REASON_DELEGATE_CALL GasChange_Reason = 8 + // REASON_EVENT_LOG is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM + GasChange_REASON_EVENT_LOG GasChange_Reason = 9 + // REASON_EXT_CODE_COPY is the amount of gas that will be charged for a 'LOG' opcode executed by the EVM + GasChange_REASON_EXT_CODE_COPY GasChange_Reason = 10 + // REASON_FAILED_EXECUTION is the burning of the remaining gas when the execution failed without a revert + GasChange_REASON_FAILED_EXECUTION GasChange_Reason = 11 + // REASON_INTRINSIC_GAS 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 + GasChange_REASON_INTRINSIC_GAS GasChange_Reason = 12 + // GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution + GasChange_REASON_PRECOMPILED_CONTRACT GasChange_Reason = 13 + // REASON_REFUND_AFTER_EXECUTION is the amount of gas that will be refunded to the caller after the execution of the call, + // if there is left over at the end of execution + GasChange_REASON_REFUND_AFTER_EXECUTION GasChange_Reason = 14 + // REASON_RETURN is the amount of gas that will be charged for a 'RETURN' opcode executed by the EVM + GasChange_REASON_RETURN GasChange_Reason = 15 + // REASON_RETURN_DATA_COPY is the amount of gas that will be charged for a 'RETURNDATACOPY' opcode executed by the EVM + GasChange_REASON_RETURN_DATA_COPY GasChange_Reason = 16 + // REASON_REVERT is the amount of gas that will be charged for a 'REVERT' opcode executed by the EVM + GasChange_REASON_REVERT GasChange_Reason = 17 + // REASON_SELF_DESTRUCT is the amount of gas that will be charged for a 'SELFDESTRUCT' opcode executed by the EVM + GasChange_REASON_SELF_DESTRUCT GasChange_Reason = 18 + // REASON_STATIC_CALL is the amount of gas that will be charged for a 'STATICALL' opcode executed by the EVM + GasChange_REASON_STATIC_CALL GasChange_Reason = 19 + // REASON_STATE_COLD_ACCESS is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + // + // Added in Berlin fork (Geth 1.10+) + GasChange_REASON_STATE_COLD_ACCESS GasChange_Reason = 20 + // REASON_TX_INITIAL_BALANCE is the initial balance for the call which will be equal to the gasLimit of the call + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_TX_INITIAL_BALANCE GasChange_Reason = 21 + // REASON_TX_REFUNDS 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 only one such gas change per transaction. + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_TX_REFUNDS GasChange_Reason = 22 + // REASON_TX_LEFT_OVER_RETURNED 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. + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_TX_LEFT_OVER_RETURNED GasChange_Reason = 23 + // REASON_CALL_INITIAL_BALANCE 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. + // + // Added as new tracing reason in Geth, available only on some chains + GasChange_REASON_CALL_INITIAL_BALANCE GasChange_Reason = 24 + // REASON_CALL_LEFT_OVER_RETURNED 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. + GasChange_REASON_CALL_LEFT_OVER_RETURNED GasChange_Reason = 25 +) + +// Enum value maps for GasChange_Reason. +var ( + GasChange_Reason_name = map[int32]string{ + 0: "REASON_UNKNOWN", + 1: "REASON_CALL", + 2: "REASON_CALL_CODE", + 3: "REASON_CALL_DATA_COPY", + 4: "REASON_CODE_COPY", + 5: "REASON_CODE_STORAGE", + 6: "REASON_CONTRACT_CREATION", + 7: "REASON_CONTRACT_CREATION2", + 8: "REASON_DELEGATE_CALL", + 9: "REASON_EVENT_LOG", + 10: "REASON_EXT_CODE_COPY", + 11: "REASON_FAILED_EXECUTION", + 12: "REASON_INTRINSIC_GAS", + 13: "REASON_PRECOMPILED_CONTRACT", + 14: "REASON_REFUND_AFTER_EXECUTION", + 15: "REASON_RETURN", + 16: "REASON_RETURN_DATA_COPY", + 17: "REASON_REVERT", + 18: "REASON_SELF_DESTRUCT", + 19: "REASON_STATIC_CALL", + 20: "REASON_STATE_COLD_ACCESS", + 21: "REASON_TX_INITIAL_BALANCE", + 22: "REASON_TX_REFUNDS", + 23: "REASON_TX_LEFT_OVER_RETURNED", + 24: "REASON_CALL_INITIAL_BALANCE", + 25: "REASON_CALL_LEFT_OVER_RETURNED", + } + GasChange_Reason_value = map[string]int32{ + "REASON_UNKNOWN": 0, + "REASON_CALL": 1, + "REASON_CALL_CODE": 2, + "REASON_CALL_DATA_COPY": 3, + "REASON_CODE_COPY": 4, + "REASON_CODE_STORAGE": 5, + "REASON_CONTRACT_CREATION": 6, + "REASON_CONTRACT_CREATION2": 7, + "REASON_DELEGATE_CALL": 8, + "REASON_EVENT_LOG": 9, + "REASON_EXT_CODE_COPY": 10, + "REASON_FAILED_EXECUTION": 11, + "REASON_INTRINSIC_GAS": 12, + "REASON_PRECOMPILED_CONTRACT": 13, + "REASON_REFUND_AFTER_EXECUTION": 14, + "REASON_RETURN": 15, + "REASON_RETURN_DATA_COPY": 16, + "REASON_REVERT": 17, + "REASON_SELF_DESTRUCT": 18, + "REASON_STATIC_CALL": 19, + "REASON_STATE_COLD_ACCESS": 20, + "REASON_TX_INITIAL_BALANCE": 21, + "REASON_TX_REFUNDS": 22, + "REASON_TX_LEFT_OVER_RETURNED": 23, + "REASON_CALL_INITIAL_BALANCE": 24, + "REASON_CALL_LEFT_OVER_RETURNED": 25, + } +) + +func (x GasChange_Reason) Enum() *GasChange_Reason { + p := new(GasChange_Reason) + *p = x + return p +} + +func (x GasChange_Reason) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (GasChange_Reason) Descriptor() protoreflect.EnumDescriptor { + return file_sf_ethereum_type_v2_type_proto_enumTypes[5].Descriptor() +} + +func (GasChange_Reason) Type() protoreflect.EnumType { + return &file_sf_ethereum_type_v2_type_proto_enumTypes[5] +} + +func (x GasChange_Reason) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use GasChange_Reason.Descriptor instead. +func (GasChange_Reason) EnumDescriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{15, 0} +} + +type Block struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Hash is the block's hash. + Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` + // Number is the block's height at which this block was mined. + Number uint64 `protobuf:"varint,3,opt,name=number,proto3" json:"number,omitempty"` + // Size is the size in bytes of the RLP encoding of the block according to Ethereum + // rules. + Size uint64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` + // Header contain's the block's header information like its parent hash, the merkel root hash + // and all other information the form a block. + Header *BlockHeader `protobuf:"bytes,5,opt,name=header,proto3" json:"header,omitempty"` + // Uncles represents block produced with a valid solution but were not actually choosen + // as the canonical block for the given height so they are mostly "forked" blocks. + // + // If the Block has been produced using the Proof of Stake consensus algorithm, this + // field will actually be always empty. + Uncles []*BlockHeader `protobuf:"bytes,6,rep,name=uncles,proto3" json:"uncles,omitempty"` + // TransactionTraces hold the execute trace of all the transactions that were executed + // in this block. In in there that you will find most of the Ethereum data model. + // + // They are ordered by the order of execution of the transaction in the block. + TransactionTraces []*TransactionTrace `protobuf:"bytes,10,rep,name=transaction_traces,json=transactionTraces,proto3" json:"transaction_traces,omitempty"` + // BalanceChanges here is the array of ETH transfer that happened at the block level + // outside of the normal transaction flow of a block. The best example of this is mining + // reward for the block mined, the transfer of ETH to the miner happens outside the normal + // transaction flow of the chain and is recorded as a `BalanceChange` here since we cannot + // attached it to any transaction. + // + // Only available in DetailLevel: EXTENDED + BalanceChanges []*BalanceChange `protobuf:"bytes,11,rep,name=balance_changes,json=balanceChanges,proto3" json:"balance_changes,omitempty"` + // DetailLevel affects the data available in this block. + // + // EXTENDED describes the most complete block, with traces, balance changes, storage changes. It is extracted during the execution of the block. + // BASE describes a block that contains only the block header, transaction receipts and event logs: everything that can be extracted using the base JSON-RPC interface (https://ethereum.org/en/developers/docs/apis/json-rpc/#json-rpc-methods) + // + // Furthermore, the eth_getTransactionReceipt call has been avoided because it brings only minimal improvements at the cost of requiring an archive node or a full node with complete transaction index. + DetailLevel Block_DetailLevel `protobuf:"varint,12,opt,name=detail_level,json=detailLevel,proto3,enum=sf.ethereum.type.v2.Block_DetailLevel" json:"detail_level,omitempty"` + // CodeChanges here is the array of smart code change that happened that happened at the block level + // outside of the normal transaction flow of a block. Some Ethereum's fork like BSC and Polygon + // has some capabilities to upgrade internal smart contracts used usually to track the validator + // list. + // + // On hard fork, some procedure runs to upgrade the smart contract code to a new version. In those + // network, a `CodeChange` for each modified smart contract on upgrade would be present here. Note + // that this happen rarely, so the vast majority of block will have an empty list here. + // Only available in DetailLevel: EXTENDED + CodeChanges []*CodeChange `protobuf:"bytes,20,rep,name=code_changes,json=codeChanges,proto3" json:"code_changes,omitempty"` + // System calls are introduced in Cancun, along with blobs. They are executed outside of transactions but affect the state. + // Only available in DetailLevel: EXTENDED + SystemCalls []*Call `protobuf:"bytes,21,rep,name=system_calls,json=systemCalls,proto3" json:"system_calls,omitempty"` + // Ver represents that data model version of the block, it is used internally by Firehose on Ethereum + // as a validation that we are reading the correct version. + Ver int32 `protobuf:"varint,1,opt,name=ver,proto3" json:"ver,omitempty"` +} + +func (x *Block) Reset() { + *x = Block{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Block) ProtoMessage() {} + +func (x *Block) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Block.ProtoReflect.Descriptor instead. +func (*Block) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{0} +} + +func (x *Block) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *Block) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +func (x *Block) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *Block) GetHeader() *BlockHeader { + if x != nil { + return x.Header + } + return nil +} + +func (x *Block) GetUncles() []*BlockHeader { + if x != nil { + return x.Uncles + } + return nil +} + +func (x *Block) GetTransactionTraces() []*TransactionTrace { + if x != nil { + return x.TransactionTraces + } + return nil +} + +func (x *Block) GetBalanceChanges() []*BalanceChange { + if x != nil { + return x.BalanceChanges + } + return nil +} + +func (x *Block) GetDetailLevel() Block_DetailLevel { + if x != nil { + return x.DetailLevel + } + return Block_DETAILLEVEL_EXTENDED +} + +func (x *Block) GetCodeChanges() []*CodeChange { + if x != nil { + return x.CodeChanges + } + return nil +} + +func (x *Block) GetSystemCalls() []*Call { + if x != nil { + return x.SystemCalls + } + return nil +} + +func (x *Block) GetVer() int32 { + if x != nil { + return x.Ver + } + return 0 +} + +type BlockHeader struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ParentHash []byte `protobuf:"bytes,1,opt,name=parent_hash,json=parentHash,proto3" json:"parent_hash,omitempty"` + // Uncle hash of the block, some reference it as `sha3Uncles`, but `sha3“ is badly worded, so we prefer `uncle_hash`, also + // referred as `ommers` in EIP specification. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to `0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347`. + UncleHash []byte `protobuf:"bytes,2,opt,name=uncle_hash,json=uncleHash,proto3" json:"uncle_hash,omitempty"` + Coinbase []byte `protobuf:"bytes,3,opt,name=coinbase,proto3" json:"coinbase,omitempty"` + StateRoot []byte `protobuf:"bytes,4,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + TransactionsRoot []byte `protobuf:"bytes,5,opt,name=transactions_root,json=transactionsRoot,proto3" json:"transactions_root,omitempty"` + ReceiptRoot []byte `protobuf:"bytes,6,opt,name=receipt_root,json=receiptRoot,proto3" json:"receipt_root,omitempty"` + LogsBloom []byte `protobuf:"bytes,7,opt,name=logs_bloom,json=logsBloom,proto3" json:"logs_bloom,omitempty"` + // Difficulty is the difficulty of the Proof of Work algorithm that was required to compute a solution. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to `0x00`. + Difficulty *BigInt `protobuf:"bytes,8,opt,name=difficulty,proto3" json:"difficulty,omitempty"` + // TotalDifficulty is the sum of all previous blocks difficulty including this block difficulty. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to the terminal total difficulty + // that was required to transition to Proof of Stake algorithm, which varies per network. It is set to + // 58 750 000 000 000 000 000 000 on Ethereum Mainnet and to 10 790 000 on Ethereum Testnet Goerli. + TotalDifficulty *BigInt `protobuf:"bytes,17,opt,name=total_difficulty,json=totalDifficulty,proto3" json:"total_difficulty,omitempty"` + Number uint64 `protobuf:"varint,9,opt,name=number,proto3" json:"number,omitempty"` + GasLimit uint64 `protobuf:"varint,10,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + GasUsed uint64 `protobuf:"varint,11,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // ExtraData is free-form bytes included in the block by the "miner". While on Yellow paper of + // Ethereum this value is maxed to 32 bytes, other consensus algorithm like Clique and some other + // forks are using bigger values to carry special consensus data. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field is strictly enforced to be <= 32 bytes. + ExtraData []byte `protobuf:"bytes,13,opt,name=extra_data,json=extraData,proto3" json:"extra_data,omitempty"` + // MixHash is used to prove, when combined with the `nonce` that sufficient amount of computation has been + // achieved and that the solution found is valid. + MixHash []byte `protobuf:"bytes,14,opt,name=mix_hash,json=mixHash,proto3" json:"mix_hash,omitempty"` + // Nonce is used to prove, when combined with the `mix_hash` that sufficient amount of computation has been + // achieved and that the solution found is valid. + // + // If the Block containing this `BlockHeader` has been produced using the Proof of Stake + // consensus algorithm, this field will actually be constant and set to `0`. + Nonce uint64 `protobuf:"varint,15,opt,name=nonce,proto3" json:"nonce,omitempty"` + // Hash is the hash of the block which is actually the computation: + // + // Keccak256(rlp([ + // parent_hash, + // uncle_hash, + // coinbase, + // state_root, + // transactions_root, + // receipt_root, + // logs_bloom, + // difficulty, + // number, + // gas_limit, + // gas_used, + // timestamp, + // extra_data, + // mix_hash, + // nonce, + // base_fee_per_gas (to be included only if London fork is active) + // withdrawals_root (to be included only if Shangai fork is active) + // blob_gas_used (to be included only if Cancun fork is active) + // excess_blob_gas (to be included only if Cancun fork is active) + // parent_beacon_root (to be included only if Cancun fork is active) + // ])) + Hash []byte `protobuf:"bytes,16,opt,name=hash,proto3" json:"hash,omitempty"` + // Base fee per gas according to EIP-1559 (e.g. London Fork) rules, only set if London is present/active on the chain. + BaseFeePerGas *BigInt `protobuf:"bytes,18,opt,name=base_fee_per_gas,json=baseFeePerGas,proto3" json:"base_fee_per_gas,omitempty"` + // Withdrawals root hash according to EIP-4895 (e.g. Shangai Fork) rules, only set if Shangai is present/active on the chain. + // + // Only available in DetailLevel: EXTENDED + WithdrawalsRoot []byte `protobuf:"bytes,19,opt,name=withdrawals_root,json=withdrawalsRoot,proto3" json:"withdrawals_root,omitempty"` + // Only available in DetailLevel: EXTENDED + TxDependency *Uint64NestedArray `protobuf:"bytes,20,opt,name=tx_dependency,json=txDependency,proto3" json:"tx_dependency,omitempty"` + // BlobGasUsed was added by EIP-4844 and is ignored in legacy headers. + BlobGasUsed *uint64 `protobuf:"varint,22,opt,name=blob_gas_used,json=blobGasUsed,proto3,oneof" json:"blob_gas_used,omitempty"` + // ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers. + ExcessBlobGas *uint64 `protobuf:"varint,23,opt,name=excess_blob_gas,json=excessBlobGas,proto3,oneof" json:"excess_blob_gas,omitempty"` + // ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers. + ParentBeaconRoot []byte `protobuf:"bytes,24,opt,name=parent_beacon_root,json=parentBeaconRoot,proto3" json:"parent_beacon_root,omitempty"` +} + +func (x *BlockHeader) Reset() { + *x = BlockHeader{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockHeader) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockHeader) ProtoMessage() {} + +func (x *BlockHeader) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockHeader.ProtoReflect.Descriptor instead. +func (*BlockHeader) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{1} +} + +func (x *BlockHeader) GetParentHash() []byte { + if x != nil { + return x.ParentHash + } + return nil +} + +func (x *BlockHeader) GetUncleHash() []byte { + if x != nil { + return x.UncleHash + } + return nil +} + +func (x *BlockHeader) GetCoinbase() []byte { + if x != nil { + return x.Coinbase + } + return nil +} + +func (x *BlockHeader) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *BlockHeader) GetTransactionsRoot() []byte { + if x != nil { + return x.TransactionsRoot + } + return nil +} + +func (x *BlockHeader) GetReceiptRoot() []byte { + if x != nil { + return x.ReceiptRoot + } + return nil +} + +func (x *BlockHeader) GetLogsBloom() []byte { + if x != nil { + return x.LogsBloom + } + return nil +} + +func (x *BlockHeader) GetDifficulty() *BigInt { + if x != nil { + return x.Difficulty + } + return nil +} + +func (x *BlockHeader) GetTotalDifficulty() *BigInt { + if x != nil { + return x.TotalDifficulty + } + return nil +} + +func (x *BlockHeader) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +func (x *BlockHeader) GetGasLimit() uint64 { + if x != nil { + return x.GasLimit + } + return 0 +} + +func (x *BlockHeader) GetGasUsed() uint64 { + if x != nil { + return x.GasUsed + } + return 0 +} + +func (x *BlockHeader) GetTimestamp() *timestamppb.Timestamp { + if x != nil { + return x.Timestamp + } + return nil +} + +func (x *BlockHeader) GetExtraData() []byte { + if x != nil { + return x.ExtraData + } + return nil +} + +func (x *BlockHeader) GetMixHash() []byte { + if x != nil { + return x.MixHash + } + return nil +} + +func (x *BlockHeader) GetNonce() uint64 { + if x != nil { + return x.Nonce + } + return 0 +} + +func (x *BlockHeader) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *BlockHeader) GetBaseFeePerGas() *BigInt { + if x != nil { + return x.BaseFeePerGas + } + return nil +} + +func (x *BlockHeader) GetWithdrawalsRoot() []byte { + if x != nil { + return x.WithdrawalsRoot + } + return nil +} + +func (x *BlockHeader) GetTxDependency() *Uint64NestedArray { + if x != nil { + return x.TxDependency + } + return nil +} + +func (x *BlockHeader) GetBlobGasUsed() uint64 { + if x != nil && x.BlobGasUsed != nil { + return *x.BlobGasUsed + } + return 0 +} + +func (x *BlockHeader) GetExcessBlobGas() uint64 { + if x != nil && x.ExcessBlobGas != nil { + return *x.ExcessBlobGas + } + return 0 +} + +func (x *BlockHeader) GetParentBeaconRoot() []byte { + if x != nil { + return x.ParentBeaconRoot + } + return nil +} + +type Uint64NestedArray struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Val []*Uint64Array `protobuf:"bytes,1,rep,name=val,proto3" json:"val,omitempty"` +} + +func (x *Uint64NestedArray) Reset() { + *x = Uint64NestedArray{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Uint64NestedArray) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Uint64NestedArray) ProtoMessage() {} + +func (x *Uint64NestedArray) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Uint64NestedArray.ProtoReflect.Descriptor instead. +func (*Uint64NestedArray) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{2} +} + +func (x *Uint64NestedArray) GetVal() []*Uint64Array { + if x != nil { + return x.Val + } + return nil +} + +type Uint64Array struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Val []uint64 `protobuf:"varint,1,rep,packed,name=val,proto3" json:"val,omitempty"` +} + +func (x *Uint64Array) Reset() { + *x = Uint64Array{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Uint64Array) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Uint64Array) ProtoMessage() {} + +func (x *Uint64Array) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Uint64Array.ProtoReflect.Descriptor instead. +func (*Uint64Array) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{3} +} + +func (x *Uint64Array) GetVal() []uint64 { + if x != nil { + return x.Val + } + return nil +} + +type BigInt struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"` +} + +func (x *BigInt) Reset() { + *x = BigInt{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BigInt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BigInt) ProtoMessage() {} + +func (x *BigInt) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BigInt.ProtoReflect.Descriptor instead. +func (*BigInt) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{4} +} + +func (x *BigInt) GetBytes() []byte { + if x != nil { + return x.Bytes + } + return nil +} + +// TransactionTrace is full trace of execution of the transaction when the +// it actually executed on chain. +// +// It contains all the transaction details like `from`, `to`, `gas`, etc. +// as well as all the internal calls that were made during the transaction. +// +// The `calls` vector contains Call objects which have balance changes, events +// storage changes, etc. +// +// If ordering is important between elements, almost each message like `Log`, +// `Call`, `StorageChange`, etc. have an ordinal field that is represents "execution" +// order of the said element against all other elements in this block. +// +// Due to how the call tree works doing "naively", looping through all calls then +// through a Call's element like `logs` while not yielding the elements in the order +// they were executed on chain. A log in call could have been done before or after +// another in another call depending on the actual call tree. +// +// The `calls` are ordered by creation order and the call tree can be re-computing +// using fields found in `Call` object (parent/child relationship). +// +// Another important thing to note is that even if a transaction succeed, some calls +// within it could have been reverted internally, if this is important to you, you must +// check the field `state_reverted` on the `Call` to determine if it was fully committed +// to the chain or not. +type TransactionTrace struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // consensus + To []byte `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` + Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"` + // GasPrice represents the effective price that has been paid for each gas unit of this transaction. Over time, the + // Ethereum rules changes regarding GasPrice field here. Before London fork, the GasPrice was always set to the + // fixed gas price. After London fork, this value has different meaning depending on the transaction type (see `Type` field). + // + // In cases where `TransactionTrace.Type == TRX_TYPE_LEGACY || TRX_TYPE_ACCESS_LIST`, then GasPrice has the same meaning + // as before the London fork. + // + // In cases where `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE`, then GasPrice is the effective gas price paid + // for the transaction which is equals to `BlockHeader.BaseFeePerGas + TransactionTrace.` + GasPrice *BigInt `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice,proto3" json:"gas_price,omitempty"` + // GasLimit is the maximum of gas unit the sender of the transaction is willing to consume when perform the EVM + // execution of the whole transaction + GasLimit uint64 `protobuf:"varint,4,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + // Value is the amount of Ether transferred as part of this transaction. + Value *BigInt `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"` + // Input data the transaction will receive for execution of EVM. + Input []byte `protobuf:"bytes,6,opt,name=input,proto3" json:"input,omitempty"` + // V is the recovery ID value for the signature Y point. + V []byte `protobuf:"bytes,7,opt,name=v,proto3" json:"v,omitempty"` + // R is the signature's X point on the elliptic curve (32 bytes). + R []byte `protobuf:"bytes,8,opt,name=r,proto3" json:"r,omitempty"` + // S is the signature's Y point on the elliptic curve (32 bytes). + S []byte `protobuf:"bytes,9,opt,name=s,proto3" json:"s,omitempty"` + // GasUsed is the total amount of gas unit used for the whole execution of the transaction. + GasUsed uint64 `protobuf:"varint,10,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + // Type represents the Ethereum transaction type, available only since EIP-2718 & EIP-2930 activation which happened on Berlin fork. + // The value is always set even for transaction before Berlin fork because those before the fork are still legacy transactions. + Type TransactionTrace_Type `protobuf:"varint,12,opt,name=type,proto3,enum=sf.ethereum.type.v2.TransactionTrace_Type" json:"type,omitempty"` + // AcccessList represents the storage access this transaction has agreed to do in which case those storage + // access cost less gas unit per access. + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_ACCESS_LIST || TRX_TYPE_DYNAMIC_FEE` which + // is possible only if Berlin (TRX_TYPE_ACCESS_LIST) nor London (TRX_TYPE_DYNAMIC_FEE) fork are active on the chain. + AccessList []*AccessTuple `protobuf:"bytes,14,rep,name=access_list,json=accessList,proto3" json:"access_list,omitempty"` + // MaxFeePerGas is the maximum fee per gas the user is willing to pay for the transaction gas used. + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only + // if Londong fork is active on the chain. + // + // Only available in DetailLevel: EXTENDED + MaxFeePerGas *BigInt `protobuf:"bytes,11,opt,name=max_fee_per_gas,json=maxFeePerGas,proto3" json:"max_fee_per_gas,omitempty"` + // MaxPriorityFeePerGas is priority fee per gas the user to pay in extra to the miner on top of the block's + // base fee. + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_DYNAMIC_FEE` which is possible only + // if London fork is active on the chain. + // + // Only available in DetailLevel: EXTENDED + MaxPriorityFeePerGas *BigInt `protobuf:"bytes,13,opt,name=max_priority_fee_per_gas,json=maxPriorityFeePerGas,proto3" json:"max_priority_fee_per_gas,omitempty"` + // meta + Index uint32 `protobuf:"varint,20,opt,name=index,proto3" json:"index,omitempty"` + Hash []byte `protobuf:"bytes,21,opt,name=hash,proto3" json:"hash,omitempty"` + From []byte `protobuf:"bytes,22,opt,name=from,proto3" json:"from,omitempty"` + // Only available in DetailLevel: EXTENDED + ReturnData []byte `protobuf:"bytes,23,opt,name=return_data,json=returnData,proto3" json:"return_data,omitempty"` + // Only available in DetailLevel: EXTENDED + PublicKey []byte `protobuf:"bytes,24,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + BeginOrdinal uint64 `protobuf:"varint,25,opt,name=begin_ordinal,json=beginOrdinal,proto3" json:"begin_ordinal,omitempty"` + EndOrdinal uint64 `protobuf:"varint,26,opt,name=end_ordinal,json=endOrdinal,proto3" json:"end_ordinal,omitempty"` + // TransactionTraceStatus is the status of the transaction execution and will let you know if the transaction + // was successful or not. + // + // A successful transaction has been recorded to the blockchain's state for calls in it that were successful. + // This means it's possible only a subset of the calls were properly recorded, refer to [calls[].state_reverted] field + // to determine which calls were reverted. + // + // A quirks of the Ethereum protocol is that a transaction `FAILED` or `REVERTED` still affects the blockchain's + // state for **some** of the state changes. Indeed, in those cases, the transactions fees are still paid to the miner + // which means there is a balance change for the transaction's emitter (e.g. `from`) to pay the gas fees, an optional + // balance change for gas refunded to the transaction's emitter (e.g. `from`) and a balance change for the miner who + // received the transaction fees. There is also a nonce change for the transaction's emitter (e.g. `from`). + // + // This means that to properly record the state changes for a transaction, you need to conditionally procees the + // transaction's status. + // + // For a `SUCCEEDED` transaction, you iterate over the `calls` array and record the state changes for each call for + // which `state_reverted == false` (if a transaction succeeded, the call at #0 will always `state_reverted == false` + // because it aligns with the transaction). + // + // For a `FAILED` or `REVERTED` transaction, you iterate over the root call (e.g. at #0, will always exist) for + // balance changes you process those where `reason` is either `REASON_GAS_BUY`, `REASON_GAS_REFUND` or + // `REASON_REWARD_TRANSACTION_FEE` and for nonce change, still on the root call, you pick the nonce change which the + // smallest ordinal (if more than one). + Status TransactionTraceStatus `protobuf:"varint,30,opt,name=status,proto3,enum=sf.ethereum.type.v2.TransactionTraceStatus" json:"status,omitempty"` + Receipt *TransactionReceipt `protobuf:"bytes,31,opt,name=receipt,proto3" json:"receipt,omitempty"` + // Only available in DetailLevel: EXTENDED + Calls []*Call `protobuf:"bytes,32,rep,name=calls,proto3" json:"calls,omitempty"` + // BlobGas is the amount of gas the transaction is going to pay for the blobs, this is a computed value + // equivalent to `self.blob_gas_fee_cap * len(self.blob_hashes)` and provided in the model for convenience. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGas *uint64 `protobuf:"varint,33,opt,name=blob_gas,json=blobGas,proto3,oneof" json:"blob_gas,omitempty"` + // BlobGasFeeCap is the maximum fee per data gas the user is willing to pay for the data gas used. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGasFeeCap *BigInt `protobuf:"bytes,34,opt,name=blob_gas_fee_cap,json=blobGasFeeCap,proto3,oneof" json:"blob_gas_fee_cap,omitempty"` + // BlobHashes field represents a list of hash outputs from 'kzg_to_versioned_hash' which + // essentially is a version byte + the sha256 hash of the blob commitment (e.g. + // `BLOB_COMMITMENT_VERSION_KZG + sha256(commitment)[1:]`. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobHashes [][]byte `protobuf:"bytes,35,rep,name=blob_hashes,json=blobHashes,proto3" json:"blob_hashes,omitempty"` +} + +func (x *TransactionTrace) Reset() { + *x = TransactionTrace{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionTrace) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionTrace) ProtoMessage() {} + +func (x *TransactionTrace) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionTrace.ProtoReflect.Descriptor instead. +func (*TransactionTrace) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{5} +} + +func (x *TransactionTrace) GetTo() []byte { + if x != nil { + return x.To + } + return nil +} + +func (x *TransactionTrace) GetNonce() uint64 { + if x != nil { + return x.Nonce + } + return 0 +} + +func (x *TransactionTrace) GetGasPrice() *BigInt { + if x != nil { + return x.GasPrice + } + return nil +} + +func (x *TransactionTrace) GetGasLimit() uint64 { + if x != nil { + return x.GasLimit + } + return 0 +} + +func (x *TransactionTrace) GetValue() *BigInt { + if x != nil { + return x.Value + } + return nil +} + +func (x *TransactionTrace) GetInput() []byte { + if x != nil { + return x.Input + } + return nil +} + +func (x *TransactionTrace) GetV() []byte { + if x != nil { + return x.V + } + return nil +} + +func (x *TransactionTrace) GetR() []byte { + if x != nil { + return x.R + } + return nil +} + +func (x *TransactionTrace) GetS() []byte { + if x != nil { + return x.S + } + return nil +} + +func (x *TransactionTrace) GetGasUsed() uint64 { + if x != nil { + return x.GasUsed + } + return 0 +} + +func (x *TransactionTrace) GetType() TransactionTrace_Type { + if x != nil { + return x.Type + } + return TransactionTrace_TRX_TYPE_LEGACY +} + +func (x *TransactionTrace) GetAccessList() []*AccessTuple { + if x != nil { + return x.AccessList + } + return nil +} + +func (x *TransactionTrace) GetMaxFeePerGas() *BigInt { + if x != nil { + return x.MaxFeePerGas + } + return nil +} + +func (x *TransactionTrace) GetMaxPriorityFeePerGas() *BigInt { + if x != nil { + return x.MaxPriorityFeePerGas + } + return nil +} + +func (x *TransactionTrace) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *TransactionTrace) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *TransactionTrace) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TransactionTrace) GetReturnData() []byte { + if x != nil { + return x.ReturnData + } + return nil +} + +func (x *TransactionTrace) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *TransactionTrace) GetBeginOrdinal() uint64 { + if x != nil { + return x.BeginOrdinal + } + return 0 +} + +func (x *TransactionTrace) GetEndOrdinal() uint64 { + if x != nil { + return x.EndOrdinal + } + return 0 +} + +func (x *TransactionTrace) GetStatus() TransactionTraceStatus { + if x != nil { + return x.Status + } + return TransactionTraceStatus_UNKNOWN +} + +func (x *TransactionTrace) GetReceipt() *TransactionReceipt { + if x != nil { + return x.Receipt + } + return nil +} + +func (x *TransactionTrace) GetCalls() []*Call { + if x != nil { + return x.Calls + } + return nil +} + +func (x *TransactionTrace) GetBlobGas() uint64 { + if x != nil && x.BlobGas != nil { + return *x.BlobGas + } + return 0 +} + +func (x *TransactionTrace) GetBlobGasFeeCap() *BigInt { + if x != nil { + return x.BlobGasFeeCap + } + return nil +} + +func (x *TransactionTrace) GetBlobHashes() [][]byte { + if x != nil { + return x.BlobHashes + } + return nil +} + +// AccessTuple represents a list of storage keys for a given contract's address and is used +// for AccessList construction. +type AccessTuple struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + StorageKeys [][]byte `protobuf:"bytes,2,rep,name=storage_keys,json=storageKeys,proto3" json:"storage_keys,omitempty"` +} + +func (x *AccessTuple) Reset() { + *x = AccessTuple{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccessTuple) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccessTuple) ProtoMessage() {} + +func (x *AccessTuple) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AccessTuple.ProtoReflect.Descriptor instead. +func (*AccessTuple) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{6} +} + +func (x *AccessTuple) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *AccessTuple) GetStorageKeys() [][]byte { + if x != nil { + return x.StorageKeys + } + return nil +} + +type TransactionReceipt struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // State root is an intermediate state_root hash, computed in-between transactions to make + // **sure** you could build a proof and point to state in the middle of a block. Geth client + // uses `PostState + root + PostStateOrStatus“ while Parity used `status_code, root...“ this piles + // hardforks, see (read the EIPs first): + // - https://github.com/ethereum/EIPs/blob/master/EIPS/eip-658.md + // + // Moreover, the notion of `Outcome“ in parity, which segregates the two concepts, which are + // stored in the same field `status_code“ can be computed based on such a hack of the `state_root` + // field, following `EIP-658`. + // + // Before Byzantinium hard fork, this field is always empty. + StateRoot []byte `protobuf:"bytes,1,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty"` + CumulativeGasUsed uint64 `protobuf:"varint,2,opt,name=cumulative_gas_used,json=cumulativeGasUsed,proto3" json:"cumulative_gas_used,omitempty"` + LogsBloom []byte `protobuf:"bytes,3,opt,name=logs_bloom,json=logsBloom,proto3" json:"logs_bloom,omitempty"` + Logs []*Log `protobuf:"bytes,4,rep,name=logs,proto3" json:"logs,omitempty"` + // BlobGasUsed is the amount of blob gas that has been used within this transaction. At time + // of writing, this is equal to `self.blob_gas_fee_cap * len(self.blob_hashes)`. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGasUsed *uint64 `protobuf:"varint,5,opt,name=blob_gas_used,json=blobGasUsed,proto3,oneof" json:"blob_gas_used,omitempty"` + // BlobGasPrice is the amount to pay per blob item in the transaction. + // + // This is specified by https://eips.ethereum.org/EIPS/eip-4844 + // + // This will is populated only if `TransactionTrace.Type == TRX_TYPE_BLOB` which is possible only + // if Cancun fork is active on the chain. + BlobGasPrice *BigInt `protobuf:"bytes,6,opt,name=blob_gas_price,json=blobGasPrice,proto3,oneof" json:"blob_gas_price,omitempty"` +} + +func (x *TransactionReceipt) Reset() { + *x = TransactionReceipt{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionReceipt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionReceipt) ProtoMessage() {} + +func (x *TransactionReceipt) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionReceipt.ProtoReflect.Descriptor instead. +func (*TransactionReceipt) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{7} +} + +func (x *TransactionReceipt) GetStateRoot() []byte { + if x != nil { + return x.StateRoot + } + return nil +} + +func (x *TransactionReceipt) GetCumulativeGasUsed() uint64 { + if x != nil { + return x.CumulativeGasUsed + } + return 0 +} + +func (x *TransactionReceipt) GetLogsBloom() []byte { + if x != nil { + return x.LogsBloom + } + return nil +} + +func (x *TransactionReceipt) GetLogs() []*Log { + if x != nil { + return x.Logs + } + return nil +} + +func (x *TransactionReceipt) GetBlobGasUsed() uint64 { + if x != nil && x.BlobGasUsed != nil { + return *x.BlobGasUsed + } + return 0 +} + +func (x *TransactionReceipt) GetBlobGasPrice() *BigInt { + if x != nil { + return x.BlobGasPrice + } + return nil +} + +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Topics [][]byte `protobuf:"bytes,2,rep,name=topics,proto3" json:"topics,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + // Index is the index of the log relative to the transaction. This index + // is always populated regardless of the state revertion of the the call + // that emitted this log. + // + // Only available in DetailLevel: EXTENDED + Index uint32 `protobuf:"varint,4,opt,name=index,proto3" json:"index,omitempty"` + // BlockIndex represents the index of the log relative to the Block. + // + // An **important** notice is that this field will be 0 when the call + // that emitted the log has been reverted by the chain. + // + // Currently, there is two locations where a Log can be obtained: + // - block.transaction_traces[].receipt.logs[] + // - block.transaction_traces[].calls[].logs[] + // + // In the `receipt` case, the logs will be populated only when the call + // that emitted them has not been reverted by the chain and when in this + // position, the `blockIndex` is always populated correctly. + // + // In the case of `calls` case, for `call` where `stateReverted == true`, + // the `blockIndex` value will always be 0. + BlockIndex uint32 `protobuf:"varint,6,opt,name=blockIndex,proto3" json:"blockIndex,omitempty"` + Ordinal uint64 `protobuf:"varint,7,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{8} +} + +func (x *Log) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *Log) GetTopics() [][]byte { + if x != nil { + return x.Topics + } + return nil +} + +func (x *Log) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *Log) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *Log) GetBlockIndex() uint32 { + if x != nil { + return x.BlockIndex + } + return 0 +} + +func (x *Log) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type Call struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Index uint32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` + ParentIndex uint32 `protobuf:"varint,2,opt,name=parent_index,json=parentIndex,proto3" json:"parent_index,omitempty"` + Depth uint32 `protobuf:"varint,3,opt,name=depth,proto3" json:"depth,omitempty"` + CallType CallType `protobuf:"varint,4,opt,name=call_type,json=callType,proto3,enum=sf.ethereum.type.v2.CallType" json:"call_type,omitempty"` + Caller []byte `protobuf:"bytes,5,opt,name=caller,proto3" json:"caller,omitempty"` + Address []byte `protobuf:"bytes,6,opt,name=address,proto3" json:"address,omitempty"` + Value *BigInt `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"` + GasLimit uint64 `protobuf:"varint,8,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + GasConsumed uint64 `protobuf:"varint,9,opt,name=gas_consumed,json=gasConsumed,proto3" json:"gas_consumed,omitempty"` + ReturnData []byte `protobuf:"bytes,13,opt,name=return_data,json=returnData,proto3" json:"return_data,omitempty"` + Input []byte `protobuf:"bytes,14,opt,name=input,proto3" json:"input,omitempty"` + ExecutedCode bool `protobuf:"varint,15,opt,name=executed_code,json=executedCode,proto3" json:"executed_code,omitempty"` + Suicide bool `protobuf:"varint,16,opt,name=suicide,proto3" json:"suicide,omitempty"` + // hex representation of the hash -> preimage + KeccakPreimages map[string]string `protobuf:"bytes,20,rep,name=keccak_preimages,json=keccakPreimages,proto3" json:"keccak_preimages,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + StorageChanges []*StorageChange `protobuf:"bytes,21,rep,name=storage_changes,json=storageChanges,proto3" json:"storage_changes,omitempty"` + BalanceChanges []*BalanceChange `protobuf:"bytes,22,rep,name=balance_changes,json=balanceChanges,proto3" json:"balance_changes,omitempty"` + NonceChanges []*NonceChange `protobuf:"bytes,24,rep,name=nonce_changes,json=nonceChanges,proto3" json:"nonce_changes,omitempty"` + Logs []*Log `protobuf:"bytes,25,rep,name=logs,proto3" json:"logs,omitempty"` + CodeChanges []*CodeChange `protobuf:"bytes,26,rep,name=code_changes,json=codeChanges,proto3" json:"code_changes,omitempty"` + GasChanges []*GasChange `protobuf:"bytes,28,rep,name=gas_changes,json=gasChanges,proto3" json:"gas_changes,omitempty"` + // In Ethereum, a call can be either: + // - Successfull, execution passes without any problem encountered + // - Failed, execution failed, and remaining gas should be consumed + // - Reverted, execution failed, but only gas consumed so far is billed, remaining gas is refunded + // + // When a call is either `failed` or `reverted`, the `status_failed` field + // below is set to `true`. If the status is `reverted`, then both `status_failed` + // and `status_reverted` are going to be set to `true`. + StatusFailed bool `protobuf:"varint,10,opt,name=status_failed,json=statusFailed,proto3" json:"status_failed,omitempty"` + StatusReverted bool `protobuf:"varint,12,opt,name=status_reverted,json=statusReverted,proto3" json:"status_reverted,omitempty"` + // Populated when a call either failed or reverted, so when `status_failed == true`, + // see above for details about those flags. + FailureReason string `protobuf:"bytes,11,opt,name=failure_reason,json=failureReason,proto3" json:"failure_reason,omitempty"` + // This field represents wheter or not the state changes performed + // by this call were correctly recorded by the blockchain. + // + // On Ethereum, a transaction can record state changes even if some + // of its inner nested calls failed. This is problematic however since + // a call will invalidate all its state changes as well as all state + // changes performed by its child call. This means that even if a call + // has a status of `SUCCESS`, the chain might have reverted all the state + // changes it performed. + // + // ```text + // + // Trx 1 + // Call #1 + // Call #2 + // Call #3 + // |--- Failure here + // Call #4 + // + // ``` + // + // In the transaction above, while Call #2 and Call #3 would have the + // status `EXECUTED`. + // + // If you check all calls and check only `state_reverted` flag, you might be missing + // some balance changes and nonce changes. This is because when a full transaction fails + // in ethereum (e.g. `calls.all(x.state_reverted == true)`), there is still the transaction + // fee that are recorded to the chain. + // + // Refer to [TransactionTrace#status] field for more details about the handling you must + // perform. + StateReverted bool `protobuf:"varint,30,opt,name=state_reverted,json=stateReverted,proto3" json:"state_reverted,omitempty"` + BeginOrdinal uint64 `protobuf:"varint,31,opt,name=begin_ordinal,json=beginOrdinal,proto3" json:"begin_ordinal,omitempty"` + EndOrdinal uint64 `protobuf:"varint,32,opt,name=end_ordinal,json=endOrdinal,proto3" json:"end_ordinal,omitempty"` + AccountCreations []*AccountCreation `protobuf:"bytes,33,rep,name=account_creations,json=accountCreations,proto3" json:"account_creations,omitempty"` +} + +func (x *Call) Reset() { + *x = Call{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Call) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Call) ProtoMessage() {} + +func (x *Call) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Call.ProtoReflect.Descriptor instead. +func (*Call) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{9} +} + +func (x *Call) GetIndex() uint32 { + if x != nil { + return x.Index + } + return 0 +} + +func (x *Call) GetParentIndex() uint32 { + if x != nil { + return x.ParentIndex + } + return 0 +} + +func (x *Call) GetDepth() uint32 { + if x != nil { + return x.Depth + } + return 0 +} + +func (x *Call) GetCallType() CallType { + if x != nil { + return x.CallType + } + return CallType_UNSPECIFIED +} + +func (x *Call) GetCaller() []byte { + if x != nil { + return x.Caller + } + return nil +} + +func (x *Call) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *Call) GetValue() *BigInt { + if x != nil { + return x.Value + } + return nil +} + +func (x *Call) GetGasLimit() uint64 { + if x != nil { + return x.GasLimit + } + return 0 +} + +func (x *Call) GetGasConsumed() uint64 { + if x != nil { + return x.GasConsumed + } + return 0 +} + +func (x *Call) GetReturnData() []byte { + if x != nil { + return x.ReturnData + } + return nil +} + +func (x *Call) GetInput() []byte { + if x != nil { + return x.Input + } + return nil +} + +func (x *Call) GetExecutedCode() bool { + if x != nil { + return x.ExecutedCode + } + return false +} + +func (x *Call) GetSuicide() bool { + if x != nil { + return x.Suicide + } + return false +} + +func (x *Call) GetKeccakPreimages() map[string]string { + if x != nil { + return x.KeccakPreimages + } + return nil +} + +func (x *Call) GetStorageChanges() []*StorageChange { + if x != nil { + return x.StorageChanges + } + return nil +} + +func (x *Call) GetBalanceChanges() []*BalanceChange { + if x != nil { + return x.BalanceChanges + } + return nil +} + +func (x *Call) GetNonceChanges() []*NonceChange { + if x != nil { + return x.NonceChanges + } + return nil +} + +func (x *Call) GetLogs() []*Log { + if x != nil { + return x.Logs + } + return nil +} + +func (x *Call) GetCodeChanges() []*CodeChange { + if x != nil { + return x.CodeChanges + } + return nil +} + +func (x *Call) GetGasChanges() []*GasChange { + if x != nil { + return x.GasChanges + } + return nil +} + +func (x *Call) GetStatusFailed() bool { + if x != nil { + return x.StatusFailed + } + return false +} + +func (x *Call) GetStatusReverted() bool { + if x != nil { + return x.StatusReverted + } + return false +} + +func (x *Call) GetFailureReason() string { + if x != nil { + return x.FailureReason + } + return "" +} + +func (x *Call) GetStateReverted() bool { + if x != nil { + return x.StateReverted + } + return false +} + +func (x *Call) GetBeginOrdinal() uint64 { + if x != nil { + return x.BeginOrdinal + } + return 0 +} + +func (x *Call) GetEndOrdinal() uint64 { + if x != nil { + return x.EndOrdinal + } + return 0 +} + +func (x *Call) GetAccountCreations() []*AccountCreation { + if x != nil { + return x.AccountCreations + } + return nil +} + +type StorageChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + OldValue []byte `protobuf:"bytes,3,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue []byte `protobuf:"bytes,4,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Ordinal uint64 `protobuf:"varint,5,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *StorageChange) Reset() { + *x = StorageChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StorageChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StorageChange) ProtoMessage() {} + +func (x *StorageChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StorageChange.ProtoReflect.Descriptor instead. +func (*StorageChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{10} +} + +func (x *StorageChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *StorageChange) GetKey() []byte { + if x != nil { + return x.Key + } + return nil +} + +func (x *StorageChange) GetOldValue() []byte { + if x != nil { + return x.OldValue + } + return nil +} + +func (x *StorageChange) GetNewValue() []byte { + if x != nil { + return x.NewValue + } + return nil +} + +func (x *StorageChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type BalanceChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + OldValue *BigInt `protobuf:"bytes,2,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue *BigInt `protobuf:"bytes,3,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Reason BalanceChange_Reason `protobuf:"varint,4,opt,name=reason,proto3,enum=sf.ethereum.type.v2.BalanceChange_Reason" json:"reason,omitempty"` + Ordinal uint64 `protobuf:"varint,5,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *BalanceChange) Reset() { + *x = BalanceChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BalanceChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BalanceChange) ProtoMessage() {} + +func (x *BalanceChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BalanceChange.ProtoReflect.Descriptor instead. +func (*BalanceChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{11} +} + +func (x *BalanceChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *BalanceChange) GetOldValue() *BigInt { + if x != nil { + return x.OldValue + } + return nil +} + +func (x *BalanceChange) GetNewValue() *BigInt { + if x != nil { + return x.NewValue + } + return nil +} + +func (x *BalanceChange) GetReason() BalanceChange_Reason { + if x != nil { + return x.Reason + } + return BalanceChange_REASON_UNKNOWN +} + +func (x *BalanceChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type NonceChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + OldValue uint64 `protobuf:"varint,2,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue uint64 `protobuf:"varint,3,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Ordinal uint64 `protobuf:"varint,4,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *NonceChange) Reset() { + *x = NonceChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NonceChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NonceChange) ProtoMessage() {} + +func (x *NonceChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NonceChange.ProtoReflect.Descriptor instead. +func (*NonceChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{12} +} + +func (x *NonceChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *NonceChange) GetOldValue() uint64 { + if x != nil { + return x.OldValue + } + return 0 +} + +func (x *NonceChange) GetNewValue() uint64 { + if x != nil { + return x.NewValue + } + return 0 +} + +func (x *NonceChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type AccountCreation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Account []byte `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` + Ordinal uint64 `protobuf:"varint,2,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *AccountCreation) Reset() { + *x = AccountCreation{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccountCreation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccountCreation) ProtoMessage() {} + +func (x *AccountCreation) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AccountCreation.ProtoReflect.Descriptor instead. +func (*AccountCreation) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{13} +} + +func (x *AccountCreation) GetAccount() []byte { + if x != nil { + return x.Account + } + return nil +} + +func (x *AccountCreation) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +type CodeChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + OldHash []byte `protobuf:"bytes,2,opt,name=old_hash,json=oldHash,proto3" json:"old_hash,omitempty"` + OldCode []byte `protobuf:"bytes,3,opt,name=old_code,json=oldCode,proto3" json:"old_code,omitempty"` + NewHash []byte `protobuf:"bytes,4,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"` + NewCode []byte `protobuf:"bytes,5,opt,name=new_code,json=newCode,proto3" json:"new_code,omitempty"` + Ordinal uint64 `protobuf:"varint,6,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *CodeChange) Reset() { + *x = CodeChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CodeChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CodeChange) ProtoMessage() {} + +func (x *CodeChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CodeChange.ProtoReflect.Descriptor instead. +func (*CodeChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{14} +} + +func (x *CodeChange) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *CodeChange) GetOldHash() []byte { + if x != nil { + return x.OldHash + } + return nil +} + +func (x *CodeChange) GetOldCode() []byte { + if x != nil { + return x.OldCode + } + return nil +} + +func (x *CodeChange) GetNewHash() []byte { + if x != nil { + return x.NewHash + } + return nil +} + +func (x *CodeChange) GetNewCode() []byte { + if x != nil { + return x.NewCode + } + return nil +} + +func (x *CodeChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +// The gas change model represents the reason why some gas cost has occurred. +// The gas is computed per actual op codes. Doing them completely might prove +// overwhelming in most cases. +// +// Hence, we only index some of them, those that are costy like all the calls +// one, log events, return data, etc. +type GasChange struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OldValue uint64 `protobuf:"varint,1,opt,name=old_value,json=oldValue,proto3" json:"old_value,omitempty"` + NewValue uint64 `protobuf:"varint,2,opt,name=new_value,json=newValue,proto3" json:"new_value,omitempty"` + Reason GasChange_Reason `protobuf:"varint,3,opt,name=reason,proto3,enum=sf.ethereum.type.v2.GasChange_Reason" json:"reason,omitempty"` + Ordinal uint64 `protobuf:"varint,4,opt,name=ordinal,proto3" json:"ordinal,omitempty"` +} + +func (x *GasChange) Reset() { + *x = GasChange{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GasChange) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GasChange) ProtoMessage() {} + +func (x *GasChange) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GasChange.ProtoReflect.Descriptor instead. +func (*GasChange) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{15} +} + +func (x *GasChange) GetOldValue() uint64 { + if x != nil { + return x.OldValue + } + return 0 +} + +func (x *GasChange) GetNewValue() uint64 { + if x != nil { + return x.NewValue + } + return 0 +} + +func (x *GasChange) GetReason() GasChange_Reason { + if x != nil { + return x.Reason + } + return GasChange_REASON_UNKNOWN +} + +func (x *GasChange) GetOrdinal() uint64 { + if x != nil { + return x.Ordinal + } + return 0 +} + +// HeaderOnlyBlock is used to optimally unpack the [Block] structure (note the +// corresponding message number for the `header` field) while consuming less +// memory, when only the `header` is desired. +// +// WARN: this is a client-side optimization pattern and should be moved in the +// consuming code. +type HeaderOnlyBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Header *BlockHeader `protobuf:"bytes,5,opt,name=header,proto3" json:"header,omitempty"` +} + +func (x *HeaderOnlyBlock) Reset() { + *x = HeaderOnlyBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HeaderOnlyBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HeaderOnlyBlock) ProtoMessage() {} + +func (x *HeaderOnlyBlock) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HeaderOnlyBlock.ProtoReflect.Descriptor instead. +func (*HeaderOnlyBlock) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{16} +} + +func (x *HeaderOnlyBlock) GetHeader() *BlockHeader { + if x != nil { + return x.Header + } + return nil +} + +// BlockWithRefs is a lightweight block, with traces and transactions +// purged from the `block` within, and only. It is used in transports +// to pass block data around. +type BlockWithRefs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Block *Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` + TransactionTraceRefs *TransactionRefs `protobuf:"bytes,3,opt,name=transaction_trace_refs,json=transactionTraceRefs,proto3" json:"transaction_trace_refs,omitempty"` + Irreversible bool `protobuf:"varint,4,opt,name=irreversible,proto3" json:"irreversible,omitempty"` +} + +func (x *BlockWithRefs) Reset() { + *x = BlockWithRefs{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockWithRefs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockWithRefs) ProtoMessage() {} + +func (x *BlockWithRefs) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockWithRefs.ProtoReflect.Descriptor instead. +func (*BlockWithRefs) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{17} +} + +func (x *BlockWithRefs) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *BlockWithRefs) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +func (x *BlockWithRefs) GetTransactionTraceRefs() *TransactionRefs { + if x != nil { + return x.TransactionTraceRefs + } + return nil +} + +func (x *BlockWithRefs) GetIrreversible() bool { + if x != nil { + return x.Irreversible + } + return false +} + +type TransactionTraceWithBlockRef struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Trace *TransactionTrace `protobuf:"bytes,1,opt,name=trace,proto3" json:"trace,omitempty"` + BlockRef *BlockRef `protobuf:"bytes,2,opt,name=block_ref,json=blockRef,proto3" json:"block_ref,omitempty"` +} + +func (x *TransactionTraceWithBlockRef) Reset() { + *x = TransactionTraceWithBlockRef{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionTraceWithBlockRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionTraceWithBlockRef) ProtoMessage() {} + +func (x *TransactionTraceWithBlockRef) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionTraceWithBlockRef.ProtoReflect.Descriptor instead. +func (*TransactionTraceWithBlockRef) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{18} +} + +func (x *TransactionTraceWithBlockRef) GetTrace() *TransactionTrace { + if x != nil { + return x.Trace + } + return nil +} + +func (x *TransactionTraceWithBlockRef) GetBlockRef() *BlockRef { + if x != nil { + return x.BlockRef + } + return nil +} + +type TransactionRefs struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hashes [][]byte `protobuf:"bytes,1,rep,name=hashes,proto3" json:"hashes,omitempty"` +} + +func (x *TransactionRefs) Reset() { + *x = TransactionRefs{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransactionRefs) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransactionRefs) ProtoMessage() {} + +func (x *TransactionRefs) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransactionRefs.ProtoReflect.Descriptor instead. +func (*TransactionRefs) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{19} +} + +func (x *TransactionRefs) GetHashes() [][]byte { + if x != nil { + return x.Hashes + } + return nil +} + +type BlockRef struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Number uint64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"` +} + +func (x *BlockRef) Reset() { + *x = BlockRef{} + if protoimpl.UnsafeEnabled { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockRef) ProtoMessage() {} + +func (x *BlockRef) ProtoReflect() protoreflect.Message { + mi := &file_sf_ethereum_type_v2_type_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BlockRef.ProtoReflect.Descriptor instead. +func (*BlockRef) Descriptor() ([]byte, []int) { + return file_sf_ethereum_type_v2_type_proto_rawDescGZIP(), []int{20} +} + +func (x *BlockRef) GetHash() []byte { + if x != nil { + return x.Hash + } + return nil +} + +func (x *BlockRef) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + +var File_sf_ethereum_type_v2_type_proto protoreflect.FileDescriptor + +var file_sf_ethereum_type_v2_type_proto_rawDesc = []byte{ + 0x0a, 0x1e, 0x73, 0x66, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x2f, 0x76, 0x32, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x13, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x2e, 0x76, 0x32, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x05, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x38, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x06, 0x75, 0x6e, + 0x63, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x75, 0x6e, + 0x63, 0x6c, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0f, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x0c, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, + 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0b, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x12, 0x42, 0x0a, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, + 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x64, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, + 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x76, 0x65, 0x72, 0x22, 0x3d, 0x0a, 0x0b, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x4c, + 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x14, 0x0a, 0x10, 0x44, 0x45, 0x54, 0x41, 0x49, 0x4c, 0x4c, 0x45, 0x56, 0x45, 0x4c, 0x5f, 0x42, + 0x41, 0x53, 0x45, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x28, 0x10, 0x29, 0x4a, 0x04, 0x08, 0x29, 0x10, + 0x2a, 0x4a, 0x04, 0x08, 0x2a, 0x10, 0x2b, 0x22, 0xd2, 0x07, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x6e, 0x63, 0x6c, + 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x75, 0x6e, + 0x63, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, + 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x62, + 0x61, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, + 0x6f, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x6f, + 0x6f, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x6f, 0x6d, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x73, 0x42, 0x6c, 0x6f, 0x6f, + 0x6d, 0x12, 0x3b, 0x0a, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, + 0x6e, 0x74, 0x52, 0x0a, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x46, + 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, + 0x74, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, + 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x69, 0x66, 0x66, + 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1b, + 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, + 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, + 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x78, 0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x19, 0x0a, 0x08, 0x6d, 0x69, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x6d, 0x69, 0x78, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, + 0x6e, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x68, 0x61, 0x73, 0x68, 0x12, 0x44, 0x0a, 0x10, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, + 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x0d, 0x62, 0x61, 0x73, + 0x65, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x69, + 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, + 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x4b, 0x0a, 0x0d, 0x74, 0x78, 0x5f, 0x64, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x52, 0x0c, 0x74, 0x78, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x79, 0x12, 0x27, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, + 0x73, 0x65, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x6c, 0x6f, + 0x62, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0f, 0x65, + 0x78, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x17, + 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6c, + 0x6f, 0x62, 0x47, 0x61, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x18, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x52, 0x6f, 0x6f, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, + 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x65, 0x78, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x22, 0x47, 0x0a, 0x11, + 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x41, 0x72, 0x72, 0x61, + 0x79, 0x12, 0x32, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, 0x72, 0x61, 0x79, + 0x52, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x1f, 0x0a, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x04, 0x52, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x1e, 0x0a, 0x06, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x22, 0xab, 0x0b, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x74, + 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x12, 0x38, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, + 0x74, 0x52, 0x08, 0x67, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, + 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, + 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, + 0x67, 0x49, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, + 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x76, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x76, 0x12, + 0x0c, 0x0a, 0x01, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x72, 0x12, 0x0c, 0x0a, + 0x01, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x67, + 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, + 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x41, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, + 0x32, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x52, 0x0a, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0f, 0x6d, 0x61, 0x78, + 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, + 0x0c, 0x6d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x12, 0x53, 0x0a, + 0x18, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x66, 0x65, + 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x14, 0x6d, 0x61, + 0x78, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, + 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x14, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x15, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x18, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x18, 0x19, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4f, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x72, 0x64, + 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x4f, + 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x43, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x07, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x2f, + 0x0a, 0x05, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x20, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x05, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x12, + 0x1e, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x18, 0x21, 0x20, 0x01, 0x28, + 0x04, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x88, 0x01, 0x01, 0x12, + 0x49, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x66, 0x65, 0x65, 0x5f, + 0x63, 0x61, 0x70, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, + 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x48, 0x01, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, + 0x73, 0x46, 0x65, 0x65, 0x43, 0x61, 0x70, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x0a, 0x62, 0x6c, 0x6f, 0x62, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0xc4, 0x02, 0x0a, 0x04, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x52, 0x58, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x4c, 0x49, 0x53, + 0x54, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x02, 0x12, 0x11, 0x0a, + 0x0d, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4c, 0x4f, 0x42, 0x10, 0x03, + 0x12, 0x1d, 0x0a, 0x19, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, + 0x49, 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x10, 0x64, 0x12, + 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, + 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x49, 0x47, 0x4e, 0x45, 0x44, 0x10, 0x65, 0x12, + 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, + 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x54, 0x10, 0x66, 0x12, + 0x1b, 0x0a, 0x17, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, + 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x59, 0x10, 0x68, 0x12, 0x26, 0x0a, 0x22, + 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x42, 0x49, 0x54, 0x52, 0x55, + 0x4d, 0x5f, 0x53, 0x55, 0x42, 0x4d, 0x49, 0x54, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x59, 0x41, 0x42, + 0x4c, 0x45, 0x10, 0x69, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x41, 0x52, 0x42, 0x49, 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, + 0x41, 0x4c, 0x10, 0x6a, 0x12, 0x1c, 0x0a, 0x18, 0x54, 0x52, 0x58, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x41, 0x52, 0x42, 0x49, 0x54, 0x52, 0x55, 0x4d, 0x5f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, + 0x10, 0x78, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x42, + 0x13, 0x0a, 0x11, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x66, 0x65, 0x65, + 0x5f, 0x63, 0x61, 0x70, 0x22, 0x4a, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, + 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x22, 0xc6, 0x02, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x47, + 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x73, 0x5f, 0x62, + 0x6c, 0x6f, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6c, 0x6f, 0x67, 0x73, + 0x42, 0x6c, 0x6f, 0x6f, 0x6d, 0x12, 0x2c, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, + 0x6f, 0x67, 0x73, 0x12, 0x27, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, + 0x75, 0x73, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x6c, + 0x6f, 0x62, 0x47, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, 0x0e, + 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, + 0x74, 0x48, 0x01, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x62, 0x47, 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, + 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x67, 0x61, + 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x5f, + 0x67, 0x61, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x03, 0x4c, 0x6f, + 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x70, + 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1e, 0x0a, + 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, + 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, + 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0xb2, 0x0a, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, + 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x70, + 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x64, 0x65, 0x70, 0x74, 0x68, 0x12, + 0x3a, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, + 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x61, 0x6c, + 0x6c, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x31, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, + 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, + 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x67, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0b, 0x67, 0x61, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x73, 0x75, 0x69, 0x63, 0x69, 0x64, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, + 0x75, 0x69, 0x63, 0x69, 0x64, 0x65, 0x12, 0x59, 0x0a, 0x10, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, + 0x5f, 0x70, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2e, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x2e, 0x4b, 0x65, 0x63, 0x63, + 0x61, 0x6b, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x0f, 0x6b, 0x65, 0x63, 0x63, 0x61, 0x6b, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x73, 0x12, 0x4b, 0x0a, 0x0f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x66, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, + 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x4b, + 0x0a, 0x0f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x62, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0d, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x18, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0c, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, + 0x12, 0x42, 0x0a, 0x0c, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x64, + 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x0b, 0x67, 0x61, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x73, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x66, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, + 0x47, 0x61, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x67, 0x61, 0x73, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, + 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x76, 0x65, 0x72, + 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x61, 0x69, + 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x65, + 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x6c, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x4f, + 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x20, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, + 0x4f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x51, 0x0a, 0x11, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x21, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x4b, 0x65, + 0x63, 0x63, 0x61, 0x6b, 0x50, 0x72, 0x65, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, + 0x08, 0x1b, 0x10, 0x1c, 0x4a, 0x04, 0x08, 0x1d, 0x10, 0x1e, 0x4a, 0x04, 0x08, 0x32, 0x10, 0x33, + 0x4a, 0x04, 0x08, 0x33, 0x10, 0x34, 0x4a, 0x04, 0x08, 0x3c, 0x10, 0x3d, 0x22, 0x8f, 0x01, 0x0a, + 0x0d, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x6c, + 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6f, + 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0xcc, + 0x05, 0x0a, 0x0d, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x6f, 0x6c, + 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, 0x67, 0x49, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x69, + 0x67, 0x49, 0x6e, 0x74, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x41, + 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, + 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0xcf, 0x03, 0x0a, 0x06, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x4d, 0x49, 0x4e, 0x45, + 0x5f, 0x55, 0x4e, 0x43, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x4d, 0x49, 0x4e, 0x45, 0x5f, 0x42, + 0x4c, 0x4f, 0x43, 0x4b, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x44, 0x41, 0x4f, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, + 0x52, 0x41, 0x43, 0x54, 0x10, 0x03, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x44, 0x41, 0x4f, 0x5f, 0x41, 0x44, 0x4a, 0x55, 0x53, 0x54, 0x5f, 0x42, 0x41, 0x4c, 0x41, + 0x4e, 0x43, 0x45, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x10, 0x05, 0x12, 0x1a, 0x0a, 0x16, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x47, 0x45, 0x4e, 0x45, 0x53, 0x49, 0x53, 0x5f, 0x42, 0x41, 0x4c, + 0x41, 0x4e, 0x43, 0x45, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x47, 0x41, 0x53, 0x5f, 0x42, 0x55, 0x59, 0x10, 0x07, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x54, 0x52, 0x41, 0x4e, + 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x08, 0x12, 0x1b, 0x0a, + 0x17, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x46, + 0x45, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, 0x10, 0x0e, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, + 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x47, 0x41, 0x53, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, 0x44, 0x10, + 0x09, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x4f, 0x55, 0x43, + 0x48, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x0a, 0x12, 0x19, 0x0a, 0x15, 0x52, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x55, 0x49, 0x43, 0x49, 0x44, 0x45, 0x5f, 0x52, 0x45, + 0x46, 0x55, 0x4e, 0x44, 0x10, 0x0b, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x53, 0x55, 0x49, 0x43, 0x49, 0x44, 0x45, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, + 0x57, 0x10, 0x0d, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, + 0x4c, 0x4c, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x52, + 0x49, 0x44, 0x45, 0x10, 0x0c, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x42, 0x55, 0x52, 0x4e, 0x10, 0x0f, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x41, 0x4c, 0x10, 0x10, 0x22, 0x7b, 0x0a, + 0x0b, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x22, 0x45, 0x0a, 0x0f, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x22, 0xac, 0x01, 0x0a, 0x0a, 0x43, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6c, + 0x64, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x6c, + 0x64, 0x48, 0x61, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x64, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x6c, 0x64, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6e, + 0x65, 0x77, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6e, + 0x65, 0x77, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, + 0x22, 0xe0, 0x06, 0x0a, 0x09, 0x47, 0x61, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6e, + 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, + 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x47, + 0x61, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, + 0x6c, 0x22, 0xbf, 0x05, 0x0a, 0x06, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x0e, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x10, + 0x01, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, + 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x41, 0x53, 0x4f, + 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x43, 0x4f, 0x50, 0x59, + 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x44, + 0x45, 0x5f, 0x43, 0x4f, 0x50, 0x59, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x54, 0x4f, 0x52, 0x41, 0x47, 0x45, 0x10, + 0x05, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, + 0x52, 0x41, 0x43, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x06, 0x12, + 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x41, + 0x43, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x32, 0x10, 0x07, 0x12, 0x18, + 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41, 0x54, + 0x45, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x10, 0x08, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x09, 0x12, 0x18, + 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x58, 0x54, 0x5f, 0x43, 0x4f, 0x44, + 0x45, 0x5f, 0x43, 0x4f, 0x50, 0x59, 0x10, 0x0a, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, + 0x49, 0x4f, 0x4e, 0x10, 0x0b, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x49, 0x4e, 0x54, 0x52, 0x49, 0x4e, 0x53, 0x49, 0x43, 0x5f, 0x47, 0x41, 0x53, 0x10, 0x0c, 0x12, + 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45, 0x43, 0x4f, 0x4d, + 0x50, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x54, 0x10, 0x0d, + 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, + 0x44, 0x5f, 0x41, 0x46, 0x54, 0x45, 0x52, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x49, 0x4f, + 0x4e, 0x10, 0x0e, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, + 0x54, 0x55, 0x52, 0x4e, 0x10, 0x0f, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x43, 0x4f, 0x50, + 0x59, 0x10, 0x10, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x52, 0x45, + 0x56, 0x45, 0x52, 0x54, 0x10, 0x11, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x44, 0x45, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x10, 0x12, + 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x49, + 0x43, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x10, 0x13, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x41, 0x53, + 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x4f, 0x4c, 0x44, 0x5f, 0x41, 0x43, + 0x43, 0x45, 0x53, 0x53, 0x10, 0x14, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, + 0x5f, 0x54, 0x58, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x42, 0x41, 0x4c, 0x41, + 0x4e, 0x43, 0x45, 0x10, 0x15, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, + 0x54, 0x58, 0x5f, 0x52, 0x45, 0x46, 0x55, 0x4e, 0x44, 0x53, 0x10, 0x16, 0x12, 0x20, 0x0a, 0x1c, + 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x58, 0x5f, 0x4c, 0x45, 0x46, 0x54, 0x5f, 0x4f, + 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x45, 0x44, 0x10, 0x17, 0x12, 0x1f, + 0x0a, 0x1b, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x49, 0x4e, + 0x49, 0x54, 0x49, 0x41, 0x4c, 0x5f, 0x42, 0x41, 0x4c, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x18, 0x12, + 0x22, 0x0a, 0x1e, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4c, 0x4c, 0x5f, 0x4c, + 0x45, 0x46, 0x54, 0x5f, 0x4f, 0x56, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x45, + 0x44, 0x10, 0x19, 0x22, 0x4b, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x6e, 0x6c, + 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x38, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x22, 0xd1, 0x01, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, + 0x66, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x30, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x5a, 0x0a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x66, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x73, 0x52, 0x14, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x65, 0x66, 0x73, + 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x72, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x69, 0x62, 0x6c, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x72, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x62, 0x6c, 0x65, 0x22, 0x97, 0x01, 0x0a, 0x1c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x66, 0x12, 0x3b, 0x0a, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x52, 0x05, 0x74, 0x72, 0x61, + 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x66, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x66, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x65, 0x66, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x66, 0x22, 0x29, + 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, 0x36, 0x0a, 0x08, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x2a, 0x4e, 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x54, 0x72, 0x61, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x43, 0x43, + 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, + 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x56, 0x45, 0x52, 0x54, 0x45, 0x44, 0x10, + 0x03, 0x2a, 0x59, 0x0a, 0x08, 0x43, 0x61, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, + 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, + 0x0a, 0x04, 0x43, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x4c, 0x4c, + 0x43, 0x4f, 0x44, 0x45, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41, + 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x41, 0x54, 0x49, 0x43, 0x10, 0x04, + 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x05, 0x42, 0x4f, 0x5a, 0x4d, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x69, 0x6e, 0x67, 0x66, 0x61, 0x73, 0x74, 0x2f, 0x66, 0x69, 0x72, 0x65, 0x68, 0x6f, 0x73, + 0x65, 0x2d, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x70, 0x62, 0x2f, 0x73, 0x66, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x2f, 0x76, 0x32, 0x3b, 0x70, 0x62, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_sf_ethereum_type_v2_type_proto_rawDescOnce sync.Once + file_sf_ethereum_type_v2_type_proto_rawDescData = file_sf_ethereum_type_v2_type_proto_rawDesc +) + +func file_sf_ethereum_type_v2_type_proto_rawDescGZIP() []byte { + file_sf_ethereum_type_v2_type_proto_rawDescOnce.Do(func() { + file_sf_ethereum_type_v2_type_proto_rawDescData = protoimpl.X.CompressGZIP(file_sf_ethereum_type_v2_type_proto_rawDescData) + }) + return file_sf_ethereum_type_v2_type_proto_rawDescData +} + +var file_sf_ethereum_type_v2_type_proto_enumTypes = make([]protoimpl.EnumInfo, 6) +var file_sf_ethereum_type_v2_type_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_sf_ethereum_type_v2_type_proto_goTypes = []interface{}{ + (TransactionTraceStatus)(0), // 0: sf.ethereum.type.v2.TransactionTraceStatus + (CallType)(0), // 1: sf.ethereum.type.v2.CallType + (Block_DetailLevel)(0), // 2: sf.ethereum.type.v2.Block.DetailLevel + (TransactionTrace_Type)(0), // 3: sf.ethereum.type.v2.TransactionTrace.Type + (BalanceChange_Reason)(0), // 4: sf.ethereum.type.v2.BalanceChange.Reason + (GasChange_Reason)(0), // 5: sf.ethereum.type.v2.GasChange.Reason + (*Block)(nil), // 6: sf.ethereum.type.v2.Block + (*BlockHeader)(nil), // 7: sf.ethereum.type.v2.BlockHeader + (*Uint64NestedArray)(nil), // 8: sf.ethereum.type.v2.Uint64NestedArray + (*Uint64Array)(nil), // 9: sf.ethereum.type.v2.Uint64Array + (*BigInt)(nil), // 10: sf.ethereum.type.v2.BigInt + (*TransactionTrace)(nil), // 11: sf.ethereum.type.v2.TransactionTrace + (*AccessTuple)(nil), // 12: sf.ethereum.type.v2.AccessTuple + (*TransactionReceipt)(nil), // 13: sf.ethereum.type.v2.TransactionReceipt + (*Log)(nil), // 14: sf.ethereum.type.v2.Log + (*Call)(nil), // 15: sf.ethereum.type.v2.Call + (*StorageChange)(nil), // 16: sf.ethereum.type.v2.StorageChange + (*BalanceChange)(nil), // 17: sf.ethereum.type.v2.BalanceChange + (*NonceChange)(nil), // 18: sf.ethereum.type.v2.NonceChange + (*AccountCreation)(nil), // 19: sf.ethereum.type.v2.AccountCreation + (*CodeChange)(nil), // 20: sf.ethereum.type.v2.CodeChange + (*GasChange)(nil), // 21: sf.ethereum.type.v2.GasChange + (*HeaderOnlyBlock)(nil), // 22: sf.ethereum.type.v2.HeaderOnlyBlock + (*BlockWithRefs)(nil), // 23: sf.ethereum.type.v2.BlockWithRefs + (*TransactionTraceWithBlockRef)(nil), // 24: sf.ethereum.type.v2.TransactionTraceWithBlockRef + (*TransactionRefs)(nil), // 25: sf.ethereum.type.v2.TransactionRefs + (*BlockRef)(nil), // 26: sf.ethereum.type.v2.BlockRef + nil, // 27: sf.ethereum.type.v2.Call.KeccakPreimagesEntry + (*timestamppb.Timestamp)(nil), // 28: google.protobuf.Timestamp +} +var file_sf_ethereum_type_v2_type_proto_depIdxs = []int32{ + 7, // 0: sf.ethereum.type.v2.Block.header:type_name -> sf.ethereum.type.v2.BlockHeader + 7, // 1: sf.ethereum.type.v2.Block.uncles:type_name -> sf.ethereum.type.v2.BlockHeader + 11, // 2: sf.ethereum.type.v2.Block.transaction_traces:type_name -> sf.ethereum.type.v2.TransactionTrace + 17, // 3: sf.ethereum.type.v2.Block.balance_changes:type_name -> sf.ethereum.type.v2.BalanceChange + 2, // 4: sf.ethereum.type.v2.Block.detail_level:type_name -> sf.ethereum.type.v2.Block.DetailLevel + 20, // 5: sf.ethereum.type.v2.Block.code_changes:type_name -> sf.ethereum.type.v2.CodeChange + 15, // 6: sf.ethereum.type.v2.Block.system_calls:type_name -> sf.ethereum.type.v2.Call + 10, // 7: sf.ethereum.type.v2.BlockHeader.difficulty:type_name -> sf.ethereum.type.v2.BigInt + 10, // 8: sf.ethereum.type.v2.BlockHeader.total_difficulty:type_name -> sf.ethereum.type.v2.BigInt + 28, // 9: sf.ethereum.type.v2.BlockHeader.timestamp:type_name -> google.protobuf.Timestamp + 10, // 10: sf.ethereum.type.v2.BlockHeader.base_fee_per_gas:type_name -> sf.ethereum.type.v2.BigInt + 8, // 11: sf.ethereum.type.v2.BlockHeader.tx_dependency:type_name -> sf.ethereum.type.v2.Uint64NestedArray + 9, // 12: sf.ethereum.type.v2.Uint64NestedArray.val:type_name -> sf.ethereum.type.v2.Uint64Array + 10, // 13: sf.ethereum.type.v2.TransactionTrace.gas_price:type_name -> sf.ethereum.type.v2.BigInt + 10, // 14: sf.ethereum.type.v2.TransactionTrace.value:type_name -> sf.ethereum.type.v2.BigInt + 3, // 15: sf.ethereum.type.v2.TransactionTrace.type:type_name -> sf.ethereum.type.v2.TransactionTrace.Type + 12, // 16: sf.ethereum.type.v2.TransactionTrace.access_list:type_name -> sf.ethereum.type.v2.AccessTuple + 10, // 17: sf.ethereum.type.v2.TransactionTrace.max_fee_per_gas:type_name -> sf.ethereum.type.v2.BigInt + 10, // 18: sf.ethereum.type.v2.TransactionTrace.max_priority_fee_per_gas:type_name -> sf.ethereum.type.v2.BigInt + 0, // 19: sf.ethereum.type.v2.TransactionTrace.status:type_name -> sf.ethereum.type.v2.TransactionTraceStatus + 13, // 20: sf.ethereum.type.v2.TransactionTrace.receipt:type_name -> sf.ethereum.type.v2.TransactionReceipt + 15, // 21: sf.ethereum.type.v2.TransactionTrace.calls:type_name -> sf.ethereum.type.v2.Call + 10, // 22: sf.ethereum.type.v2.TransactionTrace.blob_gas_fee_cap:type_name -> sf.ethereum.type.v2.BigInt + 14, // 23: sf.ethereum.type.v2.TransactionReceipt.logs:type_name -> sf.ethereum.type.v2.Log + 10, // 24: sf.ethereum.type.v2.TransactionReceipt.blob_gas_price:type_name -> sf.ethereum.type.v2.BigInt + 1, // 25: sf.ethereum.type.v2.Call.call_type:type_name -> sf.ethereum.type.v2.CallType + 10, // 26: sf.ethereum.type.v2.Call.value:type_name -> sf.ethereum.type.v2.BigInt + 27, // 27: sf.ethereum.type.v2.Call.keccak_preimages:type_name -> sf.ethereum.type.v2.Call.KeccakPreimagesEntry + 16, // 28: sf.ethereum.type.v2.Call.storage_changes:type_name -> sf.ethereum.type.v2.StorageChange + 17, // 29: sf.ethereum.type.v2.Call.balance_changes:type_name -> sf.ethereum.type.v2.BalanceChange + 18, // 30: sf.ethereum.type.v2.Call.nonce_changes:type_name -> sf.ethereum.type.v2.NonceChange + 14, // 31: sf.ethereum.type.v2.Call.logs:type_name -> sf.ethereum.type.v2.Log + 20, // 32: sf.ethereum.type.v2.Call.code_changes:type_name -> sf.ethereum.type.v2.CodeChange + 21, // 33: sf.ethereum.type.v2.Call.gas_changes:type_name -> sf.ethereum.type.v2.GasChange + 19, // 34: sf.ethereum.type.v2.Call.account_creations:type_name -> sf.ethereum.type.v2.AccountCreation + 10, // 35: sf.ethereum.type.v2.BalanceChange.old_value:type_name -> sf.ethereum.type.v2.BigInt + 10, // 36: sf.ethereum.type.v2.BalanceChange.new_value:type_name -> sf.ethereum.type.v2.BigInt + 4, // 37: sf.ethereum.type.v2.BalanceChange.reason:type_name -> sf.ethereum.type.v2.BalanceChange.Reason + 5, // 38: sf.ethereum.type.v2.GasChange.reason:type_name -> sf.ethereum.type.v2.GasChange.Reason + 7, // 39: sf.ethereum.type.v2.HeaderOnlyBlock.header:type_name -> sf.ethereum.type.v2.BlockHeader + 6, // 40: sf.ethereum.type.v2.BlockWithRefs.block:type_name -> sf.ethereum.type.v2.Block + 25, // 41: sf.ethereum.type.v2.BlockWithRefs.transaction_trace_refs:type_name -> sf.ethereum.type.v2.TransactionRefs + 11, // 42: sf.ethereum.type.v2.TransactionTraceWithBlockRef.trace:type_name -> sf.ethereum.type.v2.TransactionTrace + 26, // 43: sf.ethereum.type.v2.TransactionTraceWithBlockRef.block_ref:type_name -> sf.ethereum.type.v2.BlockRef + 44, // [44:44] is the sub-list for method output_type + 44, // [44:44] is the sub-list for method input_type + 44, // [44:44] is the sub-list for extension type_name + 44, // [44:44] is the sub-list for extension extendee + 0, // [0:44] is the sub-list for field type_name +} + +func init() { file_sf_ethereum_type_v2_type_proto_init() } +func file_sf_ethereum_type_v2_type_proto_init() { + if File_sf_ethereum_type_v2_type_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_sf_ethereum_type_v2_type_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockHeader); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Uint64NestedArray); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Uint64Array); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BigInt); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionTrace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccessTuple); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionReceipt); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Call); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StorageChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BalanceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NonceChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccountCreation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CodeChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GasChange); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HeaderOnlyBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockWithRefs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionTraceWithBlockRef); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionRefs); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockRef); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_sf_ethereum_type_v2_type_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_sf_ethereum_type_v2_type_proto_msgTypes[5].OneofWrappers = []interface{}{} + file_sf_ethereum_type_v2_type_proto_msgTypes[7].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_sf_ethereum_type_v2_type_proto_rawDesc, + NumEnums: 6, + NumMessages: 22, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_sf_ethereum_type_v2_type_proto_goTypes, + DependencyIndexes: file_sf_ethereum_type_v2_type_proto_depIdxs, + EnumInfos: file_sf_ethereum_type_v2_type_proto_enumTypes, + MessageInfos: file_sf_ethereum_type_v2_type_proto_msgTypes, + }.Build() + File_sf_ethereum_type_v2_type_proto = out.File + file_sf_ethereum_type_v2_type_proto_rawDesc = nil + file_sf_ethereum_type_v2_type_proto_goTypes = nil + file_sf_ethereum_type_v2_type_proto_depIdxs = nil +} From 5cf634040644cad307c4756bdc19d52c90c6c6d7 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 15 Mar 2024 10:57:39 -0400 Subject: [PATCH 41/42] Fixed new keccak256 implementation that wasn't working properly --- eth/tracers/firehose.go | 97 ++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/eth/tracers/firehose.go b/eth/tracers/firehose.go index dbb96f51e6..98938211a4 100644 --- a/eth/tracers/firehose.go +++ b/eth/tracers/firehose.go @@ -35,7 +35,7 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -var firehoseTracerLogLevel = strings.ToLower(os.Getenv("GETH_FIREHOSE_TRACER_LOG_LEVEL")) +var firehoseTracerLogLevel = strings.ToLower(os.Getenv("FIREHOSE_ETHEREUM_TRACER_LOG_LEVEL")) var isFirehoseDebugEnabled = firehoseTracerLogLevel == "debug" || firehoseTracerLogLevel == "trace" var isFirehoseTracerEnabled = firehoseTracerLogLevel == "trace" @@ -80,7 +80,8 @@ func newFirehoseTracer(cfg json.RawMessage) (*tracing.Hooks, error) { OnStorageChange: tracer.OnStorageChange, OnGasChange: tracer.OnGasChange, OnLog: tracer.OnLog, - OnNewAccount: tracer.OnNewAccount, + + OnNewAccount: tracer.OnNewAccount, }, nil } @@ -522,23 +523,41 @@ func (f *Firehose) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing. } if opCode == vm.KECCAK256 { - f.onOpcodeSha3(activeCall, scope.StackData(), Memory(scope.MemoryData())) + f.onOpcodeKeccak256(activeCall, scope.StackData(), Memory(scope.MemoryData())) } } } -type Memory []byte - -func (m Memory) GetPtr(offset, size int64) []byte { - if size == 0 { - return nil +// onOpcodeKeccak256 is called during the SHA3 (a.k.a KECCAK256) opcode it's known +// in Firehose tracer as Keccak preimages. The preimage is the input data that +// was used to produce the given keccak hash. +func (f *Firehose) onOpcodeKeccak256(call *pbeth.Call, stack []uint256.Int, memory Memory) { + if call.KeccakPreimages == nil { + call.KeccakPreimages = make(map[string]string) } - if len(m) > int(offset) { - return m[offset : offset+size] + offset, size := stack[len(stack)-1], stack[len(stack)-2] + preImage := memory.GetPtrUint256(&offset, &size) + + // We should have exclusive access to the hasher, we can safely reset it. + f.hasher.Reset() + f.hasher.Write(preImage) + f.hasher.Read(f.hasherBuf[:]) + + // Known Firehose issue: It appears the old Firehose instrumentation have a bug + // where when the keccak256 preimage is empty, it is written as "." which is + // completely wrong. + // + // To keep the same behavior, we will write the preimage as a "." when the encoded + // data is an empty string. + // + // For new chain, this code should be remove so that we just keep `hex.EncodeToString(data)`. + encodedData := hex.EncodeToString(preImage) + if encodedData == "" { + encodedData = "." } - return nil + call.KeccakPreimages[hex.EncodeToString(f.hasherBuf[:])] = encodedData } var opCodeToGasChangeReasonMap = map[vm.OpCode]pbeth.GasChange_Reason{ @@ -562,38 +581,6 @@ var opCodeToGasChangeReasonMap = map[vm.OpCode]pbeth.GasChange_Reason{ vm.RETURNDATACOPY: pbeth.GasChange_REASON_RETURN_DATA_COPY, } -// onOpcodeSha3 is called during the SHA3 (a.k.a KECCAK256) opcode it's known -// in Firehose tracer as Keccak preimages. The preimage is the input data that -// was used to produce the given keccak hash. -func (f *Firehose) onOpcodeSha3(call *pbeth.Call, stack []uint256.Int, memory Memory) { - if call.KeccakPreimages == nil { - call.KeccakPreimages = make(map[string]string) - } - - offset, size := stack[0], stack[1] - preImage := memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) - - // We should have exclusive access to the hasher, we can safely reset it. - f.hasher.Reset() - f.hasher.Write(preImage) - f.hasher.Read(f.hasherBuf[:]) - - // Known Firehose issue: It appears the old Firehose instrumentation have a bug - // where when the keccak256 preimage is empty, it is written as "." which is - // completely wrong. - // - // To keep the same behavior, we will write the preimage as a "." when the encoded - // data is an empty string. - // - // For new chain, this code should be remove so that we just keep `hex.EncodeToString(data)`. - encodedData := hex.EncodeToString(preImage) - if encodedData == "" { - encodedData = "." - } - - call.KeccakPreimages[hex.EncodeToString(f.hasherBuf[:])] = encodedData -} - // OnOpcodeFault implements the EVMLogger interface to trace an execution fault. func (f *Firehose) OnOpcodeFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) { if activeCall := f.callStack.Peek(); activeCall != nil { @@ -675,13 +662,13 @@ func (f *Firehose) getExecutedCode(evm *tracing.VMContext, call *pbeth.Call) boo if !evm.StateDB.Exist(common.BytesToAddress(call.Address)) && !precompile && f.blockRules.IsEIP158 && (call.Value == nil || call.Value.Native().Sign() == 0) { - firehoseDebug("executed code IsSpuriousDragon callTyp=%s inputLength=%d", call.CallType.String(), len(call.Input) > 0) + firehoseDebug("executed code IsSpuriousDragon callType=%s inputLength=%d", call.CallType.String(), len(call.Input)) return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 } } if precompile { - firehoseDebug("executed code isprecompile callTyp=%s inputLength=%d", call.CallType.String(), len(call.Input) > 0) + firehoseDebug("executed code isprecompile callType=%s inputLength=%d", call.CallType.String(), len(call.Input)) return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 } @@ -691,7 +678,7 @@ func (f *Firehose) getExecutedCode(evm *tracing.VMContext, call *pbeth.Call) boo return false } - firehoseDebug("executed code default callTyp=%s inputLength=%d", call.CallType.String(), len(call.Input) > 0) + firehoseDebug("executed code default callType=%s inputLength=%d", call.CallType.String(), len(call.Input)) return call.CallType != pbeth.CallType_CREATE && len(call.Input) > 0 } @@ -1973,3 +1960,21 @@ func validateField[T any](into *validationResult, field string, a, b T, equal bo func ptr[T any](t T) *T { return &t } + +type Memory []byte + +func (m Memory) GetPtrUint256(offset, size *uint256.Int) []byte { + return m.GetPtr(int64(offset.Uint64()), int64(size.Uint64())) +} + +func (m Memory) GetPtr(offset, size int64) []byte { + if size == 0 { + return nil + } + + if len(m) > int(offset) { + return m[offset : offset+size] + } + + return nil +} From 17fcdfaadfdea4f3d4ff1445da492956303c74ef Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 15 Mar 2024 10:58:50 -0400 Subject: [PATCH 42/42] 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 aafc577787..d5ce1c5120 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