mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
Merge branch 'extended-tracer-backport-1.13.5' into extended-tracer-sei
# Conflicts: # accounts/abi/bind/backends/simulated.go # cmd/evm/blockrunner.go # cmd/geth/config.go # cmd/geth/snapshot.go # core/genesis.go # core/genesis_test.go # core/state_processor.go # core/vm/contracts.go # eth/api_backend.go # eth/state_accessor.go # eth/tracers/api.go # eth/tracers/api_test.go # eth/tracers/js/goja.go # les/api_backend.go # les/state_accessor.go # lib/ethapi/api.go # light/odr_test.go # tests/block_test_util.go
This commit is contained in:
commit
45cb5cd202
84 changed files with 1681 additions and 1206 deletions
|
|
@ -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.(*state.StateDB).GetOrNewStateObject(call.From)
|
||||
from.SetBalance(math.MaxBig256, state.BalanceChangeUnspecified)
|
||||
from.SetBalance(math.MaxBig256, tracing.BalanceChangeUnspecified)
|
||||
|
||||
// Execute the call.
|
||||
msg := &core.Message{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
estate "github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"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"
|
||||
|
|
@ -52,7 +52,7 @@ func blockTestCmd(ctx *cli.Context) error {
|
|||
return errors.New("path-to-test argument required")
|
||||
}
|
||||
|
||||
var tracer vm.EVMLogger
|
||||
var tracer *tracing.Hooks
|
||||
// Configure the EVM logger
|
||||
if ctx.Bool(MachineFlag.Name) {
|
||||
tracer = logger.NewJSONLogger(&logger.Config{
|
||||
|
|
@ -60,7 +60,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).Hooks()
|
||||
}
|
||||
// Load the test content from the input file
|
||||
src, err := os.ReadFile(ctx.Args().First())
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@
|
|||
package t8ntool
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -28,9 +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/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,13 @@ 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
|
||||
if vmConfig.Tracer != nil {
|
||||
vmConfig.Tracer = tracer.Hooks
|
||||
}
|
||||
statedb.SetTxContext(tx.Hash(), txIndex)
|
||||
|
||||
var (
|
||||
|
|
@ -235,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, 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)
|
||||
|
|
@ -245,7 +251,10 @@ 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)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
|
@ -289,7 +298,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.Hooks.OnTxEnd(receipt, nil)
|
||||
writeTraceResult(tracer, traceOutput)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,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, state.BalanceIncreaseRewardMineUncle)
|
||||
statedb.AddBalance(ommer.Address, reward, tracing.BalanceIncreaseRewardMineUncle)
|
||||
}
|
||||
statedb.AddBalance(pre.Env.Coinbase, minerReward, state.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, state.BalanceIncreaseWithdrawal)
|
||||
statedb.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
|
||||
}
|
||||
// Commit block
|
||||
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
|
||||
|
|
@ -367,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, state.BalanceIncreaseGenesisBalance)
|
||||
statedb.SetBalance(addr, a.Balance, tracing.BalanceIncreaseGenesisBalance)
|
||||
for k, v := range a.Storage {
|
||||
statedb.SetState(addr, k, v)
|
||||
}
|
||||
|
|
@ -404,3 +414,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
|
|
@ -88,12 +89,7 @@ func Transition(ctx *cli.Context) error {
|
|||
glogger.Verbosity(slog.Level(ctx.Int(VerbosityFlag.Name)))
|
||||
log.SetDefault(log.NewLogger(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))
|
||||
|
|
@ -125,32 +121,35 @@ 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).Hooks()
|
||||
tracer := &directory.Tracer{
|
||||
Hooks: 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
|
||||
|
|
@ -189,9 +188,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 {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,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/vm"
|
||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
|
|
@ -116,7 +117,7 @@ func runCmd(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
var (
|
||||
tracer vm.EVMLogger
|
||||
tracer *tracing.Hooks
|
||||
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).Hooks()
|
||||
} else if ctx.Bool(DebugFlag.Name) {
|
||||
debugLogger = logger.NewStructLogger(logconfig)
|
||||
tracer = debugLogger
|
||||
tracer = debugLogger.Hooks()
|
||||
} else {
|
||||
debugLogger = logger.NewStructLogger(logconfig)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).Hooks()
|
||||
|
||||
case ctx.Bool(DebugFlag.Name):
|
||||
cfg.Tracer = logger.NewStructLogger(config)
|
||||
cfg.Tracer = logger.NewStructLogger(config).Hooks()
|
||||
}
|
||||
// Load the test content from the input file
|
||||
if len(ctx.Args().First()) != 0 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory/live"
|
||||
"github.com/ethereum/go-ethereum/lib/ethapi"
|
||||
"github.com/ethereum/go-ethereum/lib/flags"
|
||||
"github.com/ethereum/go-ethereum/lib/version"
|
||||
|
|
@ -179,17 +178,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 != "" {
|
||||
t, err := live.Directory.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
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ var (
|
|||
utils.DeveloperPeriodFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.VMTraceFlag,
|
||||
utils.VMTraceConfigFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
utils.NoCompactionFlag,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
|
@ -502,7 +503,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",
|
||||
|
|
@ -1844,6 +1849,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
|
||||
|
|
@ -2124,7 +2141,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 json.RawMessage
|
||||
if ctx.IsSet(VMTraceConfigFlag.Name) {
|
||||
config = json.RawMessage(ctx.String(VMTraceConfigFlag.Name))
|
||||
}
|
||||
t, err := live.Directory.New(name, config)
|
||||
if err != nil {
|
||||
Fatalf("Failed to create tracer %q: %v", name, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"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/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -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, state.BalanceIncreaseWithdrawal)
|
||||
stateDB.AddBalance(w.Address, amount, tracing.BalanceIncreaseWithdrawal)
|
||||
}
|
||||
// No block reward which is issued by consensus layer instead.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"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/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -587,10 +587,10 @@ func accumulateRewards(config *params.ChainConfig, stateDB vm.StateDB, header *t
|
|||
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, tracing.BalanceIncreaseRewardMineUncle)
|
||||
|
||||
r.Div(blockReward, big32)
|
||||
reward.Add(reward, r)
|
||||
}
|
||||
stateDB.AddBalance(header.Coinbase, reward, state.BalanceIncreaseRewardMineBlock)
|
||||
stateDB.AddBalance(header.Coinbase, reward, tracing.BalanceIncreaseRewardMineBlock)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
"errors"
|
||||
"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/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -81,7 +81,7 @@ func ApplyDAOHardFork(statedb vm.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), tracing.BalanceIncreaseDaoContract)
|
||||
statedb.SetBalance(addr, new(big.Int), tracing.BalanceDecreaseDaoAccount)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ 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/ethdb"
|
||||
|
|
@ -185,23 +186,11 @@ 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
|
||||
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)
|
||||
OnBlockEnd(err error)
|
||||
OnGenesisBlock(genesis *types.Block, alloc GenesisAlloc)
|
||||
OnBeaconBlockRootStart(root common.Hash)
|
||||
OnBeaconBlockRootEnd()
|
||||
// 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
|
||||
|
|
@ -277,7 +266,7 @@ type BlockChain struct {
|
|||
processor Processor // Block transaction processor interface
|
||||
forker *ForkChoice
|
||||
vmConfig vm.Config
|
||||
logger BlockchainLogger
|
||||
logger *tracing.Hooks
|
||||
}
|
||||
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
|
|
@ -289,15 +278,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.
|
||||
|
|
@ -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: logger,
|
||||
logger: vmConfig.Tracer,
|
||||
}
|
||||
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
|
||||
bc.forker = NewForkChoice(bc, shouldPreserve)
|
||||
|
|
@ -456,7 +437,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
|||
}
|
||||
}
|
||||
|
||||
if bc.logger != nil {
|
||||
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())
|
||||
if err != nil {
|
||||
|
|
@ -507,10 +492,8 @@ 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.
|
||||
|
||||
// Start tx indexer if it's enabled.
|
||||
if txLookupLimit != nil {
|
||||
bc.txLookupLimit = *txLookupLimit
|
||||
|
||||
|
|
@ -1812,9 +1795,13 @@ 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(), true)
|
||||
bc.logger.OnBlockEnd(nil)
|
||||
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
|
||||
bc.logger.OnSkippedBlock(tracing.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,9 +1927,16 @@ 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(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock(), false)
|
||||
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||
Block: block,
|
||||
TD: td,
|
||||
Finalized: bc.CurrentFinalBlock(),
|
||||
Safe: bc.CurrentSafeBlock(),
|
||||
})
|
||||
}
|
||||
if bc.logger != nil && bc.logger.OnBlockEnd != nil {
|
||||
defer func() {
|
||||
bc.logger.OnBlockEnd(blockEndErr)
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -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).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),
|
||||
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)}, 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -22,7 +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/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
|
@ -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, tracing.BalanceChangeTransfer)
|
||||
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -30,6 +29,7 @@ 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/ethdb"
|
||||
|
|
@ -41,10 +41,15 @@ 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")
|
||||
|
||||
// use types.GenesisAccount instead (Deprecation not ported from latest Geth for now).
|
||||
type GenesisAccount = types.Account
|
||||
|
||||
// use types.GenesisAlloc instead (Deprecation not ported from latest Geth for now).
|
||||
type GenesisAlloc = types.GenesisAlloc
|
||||
|
||||
// Genesis specifies the header fields, state of a genesis block. It also defines hard
|
||||
// fork switch-over blocks through the chain configuration.
|
||||
type Genesis struct {
|
||||
|
|
@ -106,23 +111,8 @@ 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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// hash computes the state root according to the genesis specification.
|
||||
func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
|
||||
// hashAlloc computes the state root according to the genesis specification.
|
||||
func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||
// If a genesis-time verkle trie is requested, create a trie config
|
||||
// with the verkle trie enabled so that the tree can be initialized
|
||||
// as such.
|
||||
|
|
@ -142,7 +132,7 @@ func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
|
|||
}
|
||||
for addr, account := range *ga {
|
||||
if account.Balance != nil {
|
||||
statedb.AddBalance(addr, account.Balance, state.BalanceIncreaseGenesisBalance)
|
||||
statedb.AddBalance(addr, account.Balance, tracing.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
|
|
@ -153,17 +143,19 @@ func (ga *GenesisAlloc) hash(isVerkle bool) (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
|
||||
}
|
||||
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, tracing.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetNonce(addr, account.Nonce)
|
||||
|
|
@ -223,15 +215,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
|
||||
|
|
@ -247,34 +230,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 {
|
||||
|
|
@ -463,7 +418,7 @@ func (g *Genesis) IsVerkle() bool {
|
|||
|
||||
// ToBlock returns the genesis block according to genesis specification.
|
||||
func (g *Genesis) ToBlock() *types.Block {
|
||||
root, err := g.Alloc.hash(g.IsVerkle())
|
||||
root, err := hashAlloc(&g.Alloc, g.IsVerkle())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -540,7 +495,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())
|
||||
|
|
|
|||
|
|
@ -232,7 +232,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(false)
|
||||
hash, _ = hashAlloc(alloc, false)
|
||||
)
|
||||
blob, _ := json.Marshal(alloc)
|
||||
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
||||
|
|
|
|||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
)
|
||||
|
|
@ -24,6 +24,7 @@ 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/metrics"
|
||||
|
|
@ -243,7 +244,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)
|
||||
|
|
@ -408,7 +409,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 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 {
|
||||
|
|
@ -422,19 +423,19 @@ 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) {
|
||||
func (s *stateObject) SubBalance(amount *big.Int, reason tracing.BalanceChangeReason) {
|
||||
if amount.Sign() == 0 {
|
||||
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 tracing.BalanceChangeReason) {
|
||||
s.db.journal.append(balanceChange{
|
||||
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)
|
||||
|
|
@ -514,7 +515,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)
|
||||
|
|
@ -531,7 +532,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)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ 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/ethdb"
|
||||
|
|
@ -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), 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), BalanceChangeUnspecified)
|
||||
obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified)
|
||||
|
||||
// write some of them to the trie
|
||||
s.state.updateStateObject(obj1)
|
||||
|
|
@ -106,13 +107,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), 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), BalanceChangeUnspecified)
|
||||
obj3.SetBalance(big.NewInt(44), tracing.BalanceChangeUnspecified)
|
||||
obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00}))
|
||||
obj4.AddBalance(big.NewInt(1337), BalanceChangeUnspecified)
|
||||
obj4.AddBalance(big.NewInt(1337), tracing.BalanceChangeUnspecified)
|
||||
|
||||
// write some of them to the trie
|
||||
s.state.updateStateObject(obj1)
|
||||
|
|
@ -208,7 +209,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), tracing.BalanceChangeUnspecified)
|
||||
so0.SetNonce(43)
|
||||
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
||||
so0.selfDestructed = false
|
||||
|
|
@ -220,7 +221,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), tracing.BalanceChangeUnspecified)
|
||||
so1.SetNonce(53)
|
||||
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
|
||||
so1.selfDestructed = true
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ 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/log"
|
||||
|
|
@ -47,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:
|
||||
|
|
@ -77,7 +64,7 @@ type StateDB struct {
|
|||
prefetcher *triePrefetcher
|
||||
trie Trie
|
||||
hasher crypto.KeccakState
|
||||
logger StateLogger
|
||||
logger *tracing.Hooks
|
||||
snaps *snapshot.Tree // Nil if snapshot is not available
|
||||
snap snapshot.Snapshot // Nil if snapshot is not available
|
||||
|
||||
|
|
@ -188,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 StateLogger) {
|
||||
func (s *StateDB) SetLogger(l *tracing.Hooks) {
|
||||
s.logger = l
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +219,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)
|
||||
|
|
@ -396,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 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)
|
||||
|
|
@ -404,14 +391,14 @@ func (s *StateDB) AddBalance(addr common.Address, amount *big.Int, reason Balanc
|
|||
}
|
||||
|
||||
// SubBalance subtracts amount from the account associated with addr.
|
||||
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int, reason 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 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)
|
||||
|
|
@ -479,8 +466,8 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
|
|||
prev: stateObject.selfDestructed,
|
||||
prevbalance: new(big.Int).Set(stateObject.Balance()),
|
||||
})
|
||||
if s.logger != nil && prev.Sign() > 0 {
|
||||
s.logger.OnBalanceChange(addr, prev, n, BalanceDecreaseSelfdestruct)
|
||||
if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 {
|
||||
s.logger.OnBalanceChange(addr, prev, n, tracing.BalanceDecreaseSelfdestruct)
|
||||
}
|
||||
stateObject.markSelfdestructed()
|
||||
stateObject.data.Balance = new(big.Int)
|
||||
|
|
@ -865,8 +852,8 @@ 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 {
|
||||
s.logger.OnBalanceChange(obj.address, bal, new(big.Int), BalanceDecreaseSelfdestructBurn)
|
||||
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), tracing.BalanceDecreaseSelfdestructBurn)
|
||||
}
|
||||
// We need to maintain account deletions explicitly (will remain
|
||||
// set indefinitely). Note only the first occurred self-destruct
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ 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/rlp"
|
||||
|
|
@ -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]), tracing.BalanceChangeUnspecified)
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ 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/rlp"
|
||||
|
|
@ -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)), 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}))
|
||||
|
|
@ -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)), tracing.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)), tracing.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)), 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)
|
||||
|
|
@ -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]), tracing.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]), tracing.BalanceChangeUnspecified)
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
|
|
@ -536,7 +537,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), tracing.BalanceChangeUnspecified)
|
||||
|
||||
if len(s.state.journal.dirties) != 1 {
|
||||
t.Fatal("expected one dirty state object")
|
||||
|
|
@ -552,7 +553,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), tracing.BalanceChangeUnspecified)
|
||||
|
||||
if got := state.Copy().(*StateDB).GetBalance(addr).Uint64(); got != 42 {
|
||||
t.Fatalf("1st copy fail, expected 42, got %v", got)
|
||||
|
|
@ -575,7 +576,7 @@ 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.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
|
||||
|
||||
|
|
@ -648,7 +649,7 @@ 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.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
|
||||
|
||||
|
|
@ -717,7 +718,7 @@ 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.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
|
||||
|
||||
|
|
@ -766,7 +767,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), tracing.BalanceChangeUnspecified)
|
||||
|
||||
root, _ := state.Commit(0, false)
|
||||
state, _ = New(root, state.db, state.snaps)
|
||||
|
|
@ -776,7 +777,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), tracing.BalanceChangeUnspecified)
|
||||
state.RevertToSnapshot(id)
|
||||
|
||||
// Commit the entire state and make sure we don't crash and have the correct state
|
||||
|
|
@ -818,10 +819,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), tracing.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), tracing.BalanceChangeUnspecified)
|
||||
state.SetCode(a2, []byte{1, 2, 4})
|
||||
root, _ = state.Commit(0, false)
|
||||
t.Logf("root: %x", root)
|
||||
|
|
@ -846,7 +847,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), tracing.BalanceChangeUnspecified)
|
||||
root, err := state.Commit(0, false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got root :%x", root)
|
||||
|
|
@ -1114,13 +1115,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), 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), BalanceChangeUnspecified)
|
||||
state.SetBalance(addr, big.NewInt(2), tracing.BalanceChangeUnspecified)
|
||||
state.SetState(addr, slotB, common.BytesToHash([]byte{0x2}))
|
||||
root, _ := state.Commit(0, true)
|
||||
|
||||
|
|
@ -1146,7 +1147,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), tracing.BalanceChangeUnspecified)
|
||||
state.CreateAccount(addr)
|
||||
for i := 0; i < 1000; i++ {
|
||||
slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32())
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ 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/ethdb"
|
||||
|
|
@ -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)), tracing.BalanceChangeUnspecified)
|
||||
acc.balance = big.NewInt(int64(11 * i))
|
||||
|
||||
obj.SetNonce(uint64(42 * i))
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ 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"
|
||||
)
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ func filledStateDB() *StateDB {
|
|||
skey := common.HexToHash("aaa")
|
||||
sval := common.HexToHash("bbb")
|
||||
|
||||
state.SetBalance(addr, big.NewInt(42), BalanceChangeUnspecified) // Change the account 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++ {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"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"
|
||||
|
|
@ -77,7 +78,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.
|
|||
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() {
|
||||
|
|
@ -109,12 +110,21 @@ func (p *StateProcessor) Process(block *types.Block, statedb vm.StateDB, cfg vm.
|
|||
// 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 vm.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)
|
||||
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,
|
||||
Time: evm.Context.Time,
|
||||
Coinbase: evm.Context.Coinbase,
|
||||
Random: evm.Context.Random,
|
||||
}, tx, msg.From)
|
||||
if evm.Config.Tracer.OnTxEnd != nil {
|
||||
defer func() {
|
||||
evm.Config.Tracer.CaptureTxEnd(receipt, err)
|
||||
evm.Config.Tracer.OnTxEnd(receipt, err)
|
||||
}()
|
||||
}
|
||||
}
|
||||
// Create a new context to be used in the EVM environment.
|
||||
txContext := NewEVMTxContext(msg)
|
||||
evm.Reset(txContext, statedb)
|
||||
|
|
@ -182,13 +192,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 vm.StateDB, logger BlockchainLogger) {
|
||||
if logger != nil {
|
||||
logger.OnBeaconBlockRootStart(beaconRoot)
|
||||
defer func() {
|
||||
logger.OnBeaconBlockRootEnd()
|
||||
}()
|
||||
}
|
||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb vm.StateDB) {
|
||||
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
|
||||
// the new root
|
||||
msg := &Message{
|
||||
|
|
|
|||
|
|
@ -24,7 +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/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -263,14 +263,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, tracing.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, tracing.BalanceDecreaseGasBuy)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -393,8 +393,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, tracing.GasChangeTxIntrinsicGas)
|
||||
}
|
||||
st.gasRemaining -= gas
|
||||
|
||||
|
|
@ -445,7 +445,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, tracing.BalanceIncreaseRewardTransactionFee)
|
||||
}
|
||||
|
||||
return &ExecutionResult{
|
||||
|
|
@ -463,18 +463,18 @@ func (st *StateTransition) refundGas(refundQuotient uint64) 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, 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, state.BalanceIncreaseGasReturn)
|
||||
st.state.AddBalance(st.msg.From, remaining, tracing.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, tracing.GasChangeTxLeftOverReturned)
|
||||
}
|
||||
|
||||
// Also return remaining gas to the block gas counter so it is
|
||||
|
|
|
|||
275
core/tracing/hooks.go
Normal file
275
core/tracing/hooks.go
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// 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() *big.Int
|
||||
CallInput() []byte
|
||||
}
|
||||
|
||||
// StateDB gives tracers access to the whole state.
|
||||
type StateDB interface {
|
||||
GetBalance(common.Address) *big.Int
|
||||
GetNonce(common.Address) uint64
|
||||
GetCode(common.Address) []byte
|
||||
GetState(common.Address, common.Hash) common.Hash
|
||||
Exist(common.Address) bool
|
||||
GetRefund() uint64
|
||||
}
|
||||
|
||||
// VMContext provides the context for the EVM execution.
|
||||
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
|
||||
}
|
||||
|
||||
type (
|
||||
/*
|
||||
- VM events -
|
||||
*/
|
||||
|
||||
// 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.
|
||||
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)
|
||||
|
||||
// EnterHook is invoked when the processing of a message starts.
|
||||
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.
|
||||
// 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`.
|
||||
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 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 byte, gas, cost uint64, scope OpContext, depth int, err error)
|
||||
|
||||
// GasChangeHook is invoked when the gas changes.
|
||||
GasChangeHook = func(old, new uint64, reason GasChangeReason)
|
||||
|
||||
/*
|
||||
- Chain events -
|
||||
*/
|
||||
|
||||
// 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`.
|
||||
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.
|
||||
SkippedBlockHook = func(event BlockEvent)
|
||||
|
||||
// GenesisBlockHook is called when the genesis block is being processed.
|
||||
GenesisBlockHook = func(genesis *types.Block, alloc types.GenesisAlloc)
|
||||
|
||||
/*
|
||||
- State events -
|
||||
*/
|
||||
|
||||
// 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
|
||||
OnTxStart TxStartHook
|
||||
OnTxEnd TxEndHook
|
||||
OnEnter EnterHook
|
||||
OnExit ExitHook
|
||||
OnOpcode OpcodeHook
|
||||
OnFault FaultHook
|
||||
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
|
||||
// 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 = 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 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 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 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 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 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 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 GasChangeReason = 7
|
||||
// GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE.
|
||||
GasChangeCallContractCreation GasChangeReason = 8
|
||||
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2.
|
||||
GasChangeCallContractCreation2 GasChangeReason = 9
|
||||
// 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 `OnOpcode` handling.
|
||||
GasChangeCallOpCode GasChangeReason = 11
|
||||
// GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution.
|
||||
GasChangeCallPrecompiledContract GasChangeReason = 12
|
||||
// GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules.
|
||||
GasChangeCallStorageColdAccess GasChangeReason = 13
|
||||
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
|
||||
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.
|
||||
GasChangeIgnored GasChangeReason = 0xFF
|
||||
)
|
||||
|
|
@ -35,6 +35,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/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -513,17 +514,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), 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), state.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), 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), 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 +639,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), tracing.BalanceChangeUnspecified)
|
||||
statedb.Commit(0, true)
|
||||
|
||||
chain := &testBlockChain{
|
||||
|
|
@ -738,9 +739,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), 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 +819,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), 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 +1212,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), tracing.BalanceChangeUnspecified)
|
||||
statedb.SetNonce(addrs[acc], seed.nonce)
|
||||
|
||||
// Sign the seed transactions and store them in the data store
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ 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/event"
|
||||
|
|
@ -49,7 +50,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), state.BalanceChangeUnspecified)
|
||||
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))
|
||||
|
|
@ -91,7 +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()
|
||||
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified)
|
||||
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))
|
||||
|
|
@ -128,7 +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()
|
||||
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000), state.BalanceChangeUnspecified)
|
||||
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))
|
||||
|
|
@ -182,7 +183,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), state.BalanceChangeUnspecified)
|
||||
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)
|
||||
}
|
||||
|
|
@ -190,7 +191,7 @@ func TestTransactionZAttack(t *testing.T) {
|
|||
overDraftTxs := types.Transactions{}
|
||||
{
|
||||
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), tracing.BalanceChangeUnspecified)
|
||||
for j := 0; j < int(pool.config.GlobalSlots); j++ {
|
||||
overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key))
|
||||
}
|
||||
|
|
@ -227,7 +228,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), tracing.BalanceChangeUnspecified)
|
||||
futureTxs := types.Transactions{}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,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/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
|
|
@ -256,7 +257,7 @@ func (c *testChain) State() (vm.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), tracing.BalanceChangeUnspecified)
|
||||
*c.trigger = false
|
||||
}
|
||||
return stdb, nil
|
||||
|
|
@ -276,7 +277,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), tracing.BalanceChangeUnspecified)
|
||||
blockchain := &testChain{newTestBlockChain(params.TestChainConfig, 1000000000, statedb, new(event.Feed)), address, &trigger}
|
||||
|
||||
tx0 := transaction(0, 100000, key)
|
||||
|
|
@ -310,7 +311,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, tracing.BalanceChangeUnspecified)
|
||||
pool.mu.Unlock()
|
||||
}
|
||||
|
||||
|
|
@ -471,7 +472,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), tracing.BalanceChangeUnspecified)
|
||||
|
||||
pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed))
|
||||
<-pool.requestReset(nil, nil)
|
||||
|
|
@ -500,7 +501,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), tracing.BalanceChangeUnspecified)
|
||||
|
||||
pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed))
|
||||
<-pool.requestReset(nil, nil)
|
||||
|
|
@ -2663,7 +2664,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), tracing.BalanceChangeUnspecified)
|
||||
tx := transaction(uint64(0), 100000, key)
|
||||
batches[i] = tx
|
||||
}
|
||||
|
|
|
|||
87
core/types/account.go
Normal file
87
core/types/account.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -159,17 +160,28 @@ 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 *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
|
||||
if c.Gas < gas {
|
||||
return false
|
||||
}
|
||||
if logger != nil && reason != GasChangeIgnored {
|
||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
|
||||
}
|
||||
c.Gas -= gas
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ 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"
|
||||
bls12381 "github.com/ethereum/go-ethereum/crypto/bls12381"
|
||||
|
|
@ -43,7 +44,7 @@ type PrecompiledContract interface {
|
|||
}
|
||||
|
||||
type DynamicGasPrecompiledContract interface {
|
||||
RunAndCalculateGas(evm *EVM, sender common.Address, callingContract common.Address, input []byte, suppliedGas uint64, value *big.Int) (ret []byte, remainingGas uint64, err error) // Run runs the precompiled contract and calculate gas dynamically
|
||||
RunAndCalculateGas(evm *EVM, sender common.Address, callingContract common.Address, input []byte, suppliedGas uint64, value *big.Int, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) // Run runs the precompiled contract and calculate gas dynamically
|
||||
}
|
||||
|
||||
// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
|
||||
|
|
@ -172,16 +173,16 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
|
|||
// - the returned bytes,
|
||||
// - the _remaining_ gas,
|
||||
// - any error that occurred
|
||||
func RunPrecompiledContract(p PrecompiledContract, evm *EVM, sender common.Address, callingContract common.Address, input []byte, suppliedGas uint64, value *big.Int, logger EVMLogger) (ret []byte, remainingGas uint64, err error) {
|
||||
func RunPrecompiledContract(p PrecompiledContract, evm *EVM, sender common.Address, callingContract common.Address, input []byte, suppliedGas uint64, value *big.Int, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
|
||||
if dp, ok := p.(DynamicGasPrecompiledContract); ok {
|
||||
return dp.RunAndCalculateGas(evm, sender, callingContract, input, suppliedGas, value)
|
||||
return dp.RunAndCalculateGas(evm, sender, callingContract, input, suppliedGas, value, logger)
|
||||
}
|
||||
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, tracing.GasChangeCallPrecompiledContract)
|
||||
}
|
||||
suppliedGas -= gasCost
|
||||
output, err := p.Run(evm, sender, input, value)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"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/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -189,9 +189,9 @@ 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 == 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
|
||||
|
|
@ -238,8 +238,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, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
|
|
@ -261,9 +261,9 @@ 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(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
|
||||
|
|
@ -294,8 +294,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, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
|
|
@ -316,9 +316,9 @@ 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(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
|
||||
|
|
@ -344,8 +344,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, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
gas = 0
|
||||
}
|
||||
|
|
@ -360,9 +360,9 @@ 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(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
|
||||
|
|
@ -380,7 +380,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), tracing.BalanceChangeTouchAccount)
|
||||
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, evm, caller.Address(), caller.Address(), input, gas, nil, evm.Config.Tracer)
|
||||
|
|
@ -402,8 +402,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, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
gas = 0
|
||||
|
|
@ -427,9 +427,9 @@ 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 == 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
|
||||
|
|
@ -453,8 +453,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, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
|
||||
return nil, common.Address{}, 0, ErrContractAddressCollision
|
||||
|
|
@ -490,7 +490,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, tracing.GasChangeCallCodeStorage) {
|
||||
evm.StateDB.SetCode(address, ret)
|
||||
} else {
|
||||
err = ErrCodeStoreOutOfGas
|
||||
|
|
@ -503,7 +503,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, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -529,23 +529,20 @@ 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 isRoot {
|
||||
tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value)
|
||||
} else {
|
||||
tracer.CaptureEnter(typ, from, to, input, startGas, value)
|
||||
if tracer.OnEnter != nil {
|
||||
tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value)
|
||||
}
|
||||
if tracer.OnGasChange != nil {
|
||||
tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
|
||||
}
|
||||
|
||||
tracer.OnGasChange(0, startGas, GasChangeCallInitialBalance)
|
||||
}
|
||||
|
||||
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(leftOverGas, 0, GasChangeCallLeftOverReturned)
|
||||
if leftOverGas != 0 && tracer.OnGasChange != nil {
|
||||
tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
|
||||
}
|
||||
var reverted bool
|
||||
if err != nil {
|
||||
|
|
@ -554,9 +551,21 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas
|
|||
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)
|
||||
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
|
||||
// to the tracers.
|
||||
func (evm *EVM) GetVMContext() *tracing.VMContext {
|
||||
return &tracing.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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package vm
|
|||
|
||||
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/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -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.CaptureKeccakPreimage(common.BytesToHash(interpreter.hasherBuf[:]), data)
|
||||
}
|
||||
size.SetBytes(interpreter.hasherBuf[:])
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -594,7 +591,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, tracing.GasChangeCallContractCreation)
|
||||
//TODO: use uint256.Int instead of converting with toBig()
|
||||
var bigVal = big0
|
||||
if !value.IsZero() {
|
||||
|
|
@ -615,11 +612,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)
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -642,7 +635,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, tracing.GasChangeCallContractCreation2)
|
||||
// reuse size int for stackvalue
|
||||
stackvalue := size
|
||||
//TODO: use uint256.Int instead of converting with toBig()
|
||||
|
|
@ -660,11 +653,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)
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -710,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 && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
|
|
@ -750,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 && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
|
|
@ -783,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 && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
|
|
@ -816,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 && returnGas > 0 {
|
||||
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
|
||||
}
|
||||
|
||||
scope.Contract.Gas += returnGas
|
||||
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
interpreter.returnData = ret
|
||||
return ret, nil
|
||||
|
|
@ -855,11 +828,15 @@ 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, tracing.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, false)
|
||||
if tracer.OnEnter != nil {
|
||||
tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||
}
|
||||
if tracer.OnExit != nil {
|
||||
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
|
||||
}
|
||||
}
|
||||
return nil, errStopToken
|
||||
}
|
||||
|
|
@ -870,12 +847,16 @@ 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, 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 {
|
||||
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||
tracer.CaptureExit([]byte{}, 0, nil, false)
|
||||
if tracer.OnEnter != nil {
|
||||
tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
|
||||
}
|
||||
if tracer.OnExit != nil {
|
||||
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
|
||||
}
|
||||
}
|
||||
return nil, errStopToken
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"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/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, tracing.BalanceChangeReason)
|
||||
AddBalance(common.Address, *big.Int, tracing.BalanceChangeReason)
|
||||
GetBalance(common.Address) *big.Int
|
||||
|
||||
GetNonce(common.Address) uint64
|
||||
|
|
@ -83,7 +83,7 @@ type StateDB interface {
|
|||
// new methods
|
||||
Error() error
|
||||
GetStorageRoot(common.Address) common.Hash
|
||||
SetBalance(common.Address, *big.Int, state.BalanceChangeReason)
|
||||
SetBalance(common.Address, *big.Int, tracing.BalanceChangeReason)
|
||||
SetStorage(common.Address, map[common.Hash]common.Hash)
|
||||
Finalise(deleteEmptyObjects bool)
|
||||
Commit(block uint64, deleteEmptyObjects bool) (common.Hash, error)
|
||||
|
|
@ -93,7 +93,7 @@ type StateDB interface {
|
|||
GetLogs(hash common.Hash, blockNumber uint64, blockHash common.Hash) []*types.Log
|
||||
TxIndex() int
|
||||
Preimages() map[common.Hash][]byte
|
||||
SetLogger(logger state.StateLogger)
|
||||
SetLogger(logger *tracing.Hooks)
|
||||
}
|
||||
|
||||
type EVMAssignable interface {
|
||||
|
|
|
|||
|
|
@ -17,15 +17,19 @@
|
|||
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"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"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
|
||||
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
|
||||
|
|
@ -39,6 +43,36 @@ type ScopeContext struct {
|
|||
Contract *Contract
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) MemoryData() []byte {
|
||||
if ctx.Memory == nil {
|
||||
return nil
|
||||
}
|
||||
return ctx.Memory.Data()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) StackData() []uint256.Int {
|
||||
if ctx.Stack == nil {
|
||||
return nil
|
||||
}
|
||||
return ctx.Stack.Data()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) Caller() common.Address {
|
||||
return ctx.Contract.Caller()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) Address() common.Address {
|
||||
return ctx.Contract.Address()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) CallValue() *big.Int {
|
||||
return ctx.Contract.Value()
|
||||
}
|
||||
|
||||
func (ctx *ScopeContext) CallInput() []byte {
|
||||
return ctx.Contract.Input
|
||||
}
|
||||
|
||||
// EVMInterpreter represents an EVM interpreter
|
||||
type EVMInterpreter struct {
|
||||
evm *EVM
|
||||
|
|
@ -146,8 +180,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)
|
||||
|
|
@ -158,9 +192,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.OnOpcode != nil {
|
||||
in.evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
}
|
||||
} else if in.evm.Config.Tracer.OnFault != nil {
|
||||
in.evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
@ -185,7 +221,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, tracing.GasChangeIgnored) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
|
||||
|
|
@ -212,24 +248,32 @@ 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, tracing.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))
|
||||
if in.evm.Config.Tracer.OnGasChange != nil {
|
||||
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
|
||||
}
|
||||
if in.evm.Config.Tracer.OnOpcode != nil {
|
||||
in.evm.Config.Tracer.OnOpcode(pc, byte(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))
|
||||
if in.evm.Config.Tracer.OnGasChange != nil {
|
||||
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
|
||||
}
|
||||
if in.evm.Config.Tracer.OnOpcode != nil {
|
||||
in.evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
|
||||
logged = true
|
||||
}
|
||||
}
|
||||
|
||||
// execute the operation
|
||||
res, err = operation.execute(&pc, in, callContext)
|
||||
|
|
|
|||
|
|
@ -1,117 +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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 {
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
|
|
@ -21,6 +21,7 @@ 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/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, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.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, 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, 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)
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
|||
b.Fatal(err)
|
||||
}
|
||||
cfg.EVMConfig = vm.Config{
|
||||
Tracer: tracer,
|
||||
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),
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
Tracer: tracer.Hooks,
|
||||
}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -411,6 +411,6 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re
|
|||
return b.eth.stateAtBlock(ctx, block, reexec, base, readOnly, preferDisk)
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, vm.StateDB, tracers.StateReleaseFunc, error) {
|
||||
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, tracers.StateReleaseFunc, error) {
|
||||
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ 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/trie"
|
||||
|
|
@ -73,7 +74,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), tracing.BalanceChangeUnspecified)
|
||||
if _, ok := m[addr]; ok {
|
||||
t.Fatalf("bad")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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/lib/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,17 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
StateScheme: scheme,
|
||||
}
|
||||
)
|
||||
if config.VMTrace != "" {
|
||||
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)
|
||||
}
|
||||
vmConfig.Tracer = t
|
||||
}
|
||||
// Override the chain config with provided settings.
|
||||
var overrides core.ChainOverrides
|
||||
if config.OverrideCancun != nil {
|
||||
|
|
|
|||
|
|
@ -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/ethdb"
|
||||
|
|
@ -143,7 +142,8 @@ type Config struct {
|
|||
EnablePreimageRecording bool
|
||||
|
||||
// Enables VM tracing
|
||||
VMTracer vm.EVMLogger
|
||||
VMTrace string
|
||||
VMTraceConfig string
|
||||
|
||||
// Miscellaneous options
|
||||
DocRoot string `toml:"-"`
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ func (eth *Ethereum) stateAtBlock(ctx context.Context, block *types.Block, reexe
|
|||
}
|
||||
|
||||
// stateAtTransaction returns the execution environment of a certain transaction.
|
||||
func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, vm.StateDB, tracers.StateReleaseFunc, error) {
|
||||
func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, tracers.StateReleaseFunc, error) {
|
||||
// Short circuit if it's genesis block.
|
||||
if block.NumberU64() == 0 {
|
||||
return nil, vm.BlockContext{}, nil, nil, errors.New("no transaction in genesis")
|
||||
|
|
@ -243,7 +243,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
|
|||
txContext := core.NewEVMTxContext(msg)
|
||||
context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil)
|
||||
if idx == txIndex {
|
||||
return msg, context, statedb, release, nil
|
||||
return tx, context, statedb, release, nil
|
||||
}
|
||||
// Not yet the searched for transaction, execute on top of the current state
|
||||
vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{})
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ type Backend interface {
|
|||
Engine() consensus.Engine
|
||||
ChainDb() ethdb.Database
|
||||
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base vm.StateDB, readOnly bool, preferDisk bool) (vm.StateDB, StateReleaseFunc, error)
|
||||
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, vm.StateDB, StateReleaseFunc, error)
|
||||
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, StateReleaseFunc, error)
|
||||
}
|
||||
|
||||
// API is the collection of tracing APIs exposed over the private debugging endpoint.
|
||||
|
|
@ -228,7 +228,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
|
|||
}
|
||||
sub := notifier.CreateSubscription()
|
||||
|
||||
resCh := api.traceChain(from, to, config, notifier.Closed())
|
||||
resCh := api.traceChain(from, to, config, sub.Err())
|
||||
go func() {
|
||||
for result := range resCh {
|
||||
notifier.Notify(sub.ID, result)
|
||||
|
|
@ -242,7 +242,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
|
|||
// the end block but excludes the start one. The return value will be one item per
|
||||
// transaction, dependent on the requested tracer.
|
||||
// The tracing procedure should be aborted in case the closed signal is received.
|
||||
func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan interface{}) chan *blockTraceResult {
|
||||
func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan error) chan *blockTraceResult {
|
||||
reexec := defaultTraceReexec
|
||||
if config != nil && config.Reexec != nil {
|
||||
reexec = *config.Reexec
|
||||
|
|
@ -628,7 +628,6 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
|||
var (
|
||||
txs = block.Transactions()
|
||||
blockHash = block.Hash()
|
||||
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
||||
results = make([]*txTraceResult, len(txs))
|
||||
pend sync.WaitGroup
|
||||
|
|
@ -651,6 +650,11 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
|||
TxIndex: task.index,
|
||||
TxHash: txs[task.index].Hash(),
|
||||
}
|
||||
// Reconstruct the block context for each transaction
|
||||
// as the GetHash function of BlockContext is not safe for
|
||||
// concurrent use.
|
||||
// See: https://github.com/ethereum/go-ethereum/issues/29114
|
||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config)
|
||||
if err != nil {
|
||||
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
|
||||
|
|
@ -663,6 +667,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
|
|||
|
||||
// Feed the transactions into the tracers and return
|
||||
var failed error
|
||||
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
|
||||
txloop:
|
||||
for i, tx := range txs {
|
||||
// Send the trace task over for execution
|
||||
|
|
@ -778,16 +783,16 @@ 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).Hooks(),
|
||||
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.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()
|
||||
}
|
||||
|
|
@ -844,11 +849,15 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
|
||||
tx, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer release()
|
||||
msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txctx := &directory.Context{
|
||||
BlockHash: blockHash,
|
||||
|
|
@ -917,7 +926,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
|||
config.BlockOverrides.Apply(&vmctx)
|
||||
}
|
||||
// Execute the trace
|
||||
msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
|
||||
msg, err := args.ToMessage(api.backend.RPCGasCap(), vmctx.BaseFee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -937,7 +946,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
|||
// be tracer dependent.
|
||||
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb vm.StateDB, config *TraceConfig) (interface{}, error) {
|
||||
var (
|
||||
tracer directory.Tracer
|
||||
tracer *directory.Tracer
|
||||
err error
|
||||
timeout = defaultTraceTimeout
|
||||
usedGas uint64
|
||||
|
|
@ -946,15 +955,15 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
|||
config = &TraceConfig{}
|
||||
}
|
||||
// Default tracer is the struct logger
|
||||
tracer = logger.NewStructLogger(config.Config)
|
||||
tracer = logger.NewStructLogger(config.Config).Tracer()
|
||||
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.Hooks, NoBaseFee: true})
|
||||
statedb.SetLogger(tracer.Hooks)
|
||||
|
||||
// Define a meaningful timeout of a single transaction trace
|
||||
if config.Timeout != nil {
|
||||
|
|
@ -975,7 +984,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
|||
|
||||
// Call Prepare to clear out the statedb access list
|
||||
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
|
||||
if _, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv); err != nil {
|
||||
_, err = core.ApplyTransactionWithEVM(message, api.backend.ChainConfig(), new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, vmenv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tracing failed: %w", err)
|
||||
}
|
||||
return tracer.GetResult()
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reex
|
|||
return statedb, release, nil
|
||||
}
|
||||
|
||||
func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, vm.StateDB, StateReleaseFunc, error) {
|
||||
func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, vm.StateDB, StateReleaseFunc, error) {
|
||||
parent := b.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||
if parent == nil {
|
||||
return nil, vm.BlockContext{}, nil, nil, errBlockNotFound
|
||||
|
|
@ -173,7 +173,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
|
|||
txContext := core.NewEVMTxContext(msg)
|
||||
context := core.NewEVMBlockContext(block.Header(), b.chain, nil)
|
||||
if idx == txIndex {
|
||||
return msg, context, statedb, release, nil
|
||||
return tx, context, statedb, release, nil
|
||||
}
|
||||
vmenv := vm.NewEVM(context, txContext, statedb, b.chainConfig, vm.Config{})
|
||||
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
|
||||
|
|
@ -191,7 +191,7 @@ func TestTraceCall(t *testing.T) {
|
|||
accounts := newAccounts(3)
|
||||
genesis := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: core.GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||
|
|
@ -409,7 +409,7 @@ func TestTraceTransaction(t *testing.T) {
|
|||
accounts := newAccounts(2)
|
||||
genesis := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: core.GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
},
|
||||
|
|
@ -464,7 +464,7 @@ func TestTraceBlock(t *testing.T) {
|
|||
accounts := newAccounts(3)
|
||||
genesis := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: core.GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||
|
|
@ -554,7 +554,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
|||
storageAccount := common.Address{0x13, 37}
|
||||
genesis := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: core.GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||
|
|
@ -922,7 +922,7 @@ func TestTraceChain(t *testing.T) {
|
|||
accounts := newAccounts(3)
|
||||
genesis := &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: core.GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
|
||||
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package live
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
)
|
||||
|
||||
type ctorFunc func() (core.BlockchainLogger, 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.
|
||||
|
|
@ -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) (*tracing.Hooks, error) {
|
||||
if f, ok := d.elems[name]; ok {
|
||||
return f()
|
||||
return f(config)
|
||||
}
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
@ -35,46 +34,48 @@ 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{
|
||||
Hooks: &tracing.Hooks{
|
||||
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,
|
||||
}, 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 byte, 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 byte, 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 vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {}
|
||||
|
||||
func (t *NoopTracer) OnEnter(depth int, typ byte, 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 *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) {
|
||||
func (t *NoopTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
}
|
||||
|
||||
// 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 (*NoopTracer) OnTxStart(env *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 *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
}
|
||||
func (*NoopTracer) OnTxEnd(receipt *types.Receipt, err error) {}
|
||||
|
||||
func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {}
|
||||
|
||||
func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
|
||||
|
||||
func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
|
||||
func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
|
||||
}
|
||||
|
||||
func (*NoopTracer) OnNonceChange(a common.Address, prev, new uint64) {}
|
||||
|
|
|
|||
|
|
@ -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/core/tracing"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
*tracing.Hooks
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -28,20 +28,53 @@ 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, MemoryPtr(m, 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
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,14 +33,14 @@ func TestMemCopying(t *testing.T) {
|
|||
{0, 100, 0, "", 0}, // No need to pad (0 size)
|
||||
{100, 50, 100, "", 100}, // Should pad 100-150
|
||||
{100, 50, 5, "", 5}, // Wanted range fully within memory
|
||||
{100, -50, 0, "offset or size must not be negative", 0}, // Errror
|
||||
{0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror
|
||||
{10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror
|
||||
{100, -50, 0, "offset or size must not be negative", 0}, // Error
|
||||
{0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Error
|
||||
{10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Error
|
||||
|
||||
} {
|
||||
mem := vm.NewMemory()
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -125,11 +125,6 @@ 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(),
|
||||
}
|
||||
context = vm.BlockContext{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
|
|
@ -148,18 +143,19 @@ 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.Hooks)
|
||||
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.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 {
|
||||
|
|
@ -227,10 +223,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
|||
b.Fatalf("failed to parse testcase input: %v", err)
|
||||
}
|
||||
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
|
||||
msg, err := core.TransactionToMessage(tx, signer, nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
origin, _ := signer.Sender(tx)
|
||||
txContext := vm.TxContext{
|
||||
Origin: origin,
|
||||
|
|
@ -245,6 +237,10 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
|||
Difficulty: (*big.Int)(test.Context.Difficulty),
|
||||
GasLimit: uint64(test.Context.GasLimit),
|
||||
}
|
||||
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
|
||||
defer triedb.Close()
|
||||
|
||||
|
|
@ -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.Hooks})
|
||||
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
|
||||
}{
|
||||
{
|
||||
|
|
@ -374,16 +370,16 @@ func TestInternals(t *testing.T) {
|
|||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(),
|
||||
core.GenesisAlloc{
|
||||
to: core.GenesisAccount{
|
||||
types.GenesisAlloc{
|
||||
to: types.Account{
|
||||
Code: tc.code,
|
||||
},
|
||||
origin: core.GenesisAccount{
|
||||
origin: types.Account{
|
||||
Balance: big.NewInt(500000000000000),
|
||||
},
|
||||
}, false, rawdb.HashScheme)
|
||||
defer triedb.Close()
|
||||
statedb.SetLogger(tc.tracer)
|
||||
statedb.SetLogger(tc.tracer.Hooks)
|
||||
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
|
||||
To: &to,
|
||||
Value: big.NewInt(0),
|
||||
|
|
@ -397,17 +393,17 @@ 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.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)
|
||||
}
|
||||
tc.tracer.CaptureTxStart(evm, 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 {
|
||||
|
|
|
|||
|
|
@ -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,19 +101,19 @@ 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.Hooks)
|
||||
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.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()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -93,11 +94,6 @@ 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(),
|
||||
}
|
||||
context = vm.BlockContext{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
|
|
@ -112,22 +108,28 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
|
|||
)
|
||||
defer triedb.Close()
|
||||
|
||||
if test.Genesis.ExcessBlobGas != nil && test.Genesis.BlobGasUsed != nil {
|
||||
excessBlobGas := eip4844.CalcExcessBlobGas(*test.Genesis.ExcessBlobGas, *test.Genesis.BlobGasUsed)
|
||||
context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
|
||||
}
|
||||
|
||||
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create call tracer: %v", err)
|
||||
}
|
||||
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.Hooks)
|
||||
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.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 {
|
||||
|
|
|
|||
63
eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json
vendored
Normal file
63
eth/tracers/internal/tracetest/testdata/prestate_tracer/blob_tx.json
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"genesis": {
|
||||
"baseFeePerGas": "7",
|
||||
"blobGasUsed": "0",
|
||||
"difficulty": "0",
|
||||
"excessBlobGas": "36306944",
|
||||
"extraData": "0xd983010e00846765746888676f312e32312e308664617277696e",
|
||||
"gasLimit": "15639172",
|
||||
"hash": "0xc682259fda061bb9ce8ccb491d5b2d436cb73daf04e1025dd116d045ce4ad28c",
|
||||
"miner": "0x0000000000000000000000000000000000000000",
|
||||
"mixHash": "0xae1a5ba939a4c9ac38aabeff361169fb55a6fc2c9511457e0be6eff9514faec0",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "315",
|
||||
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x577f42ab21ccfd946511c57869ace0bdf7c217c36f02b7cd3459df0ed1cffc1a",
|
||||
"timestamp": "1709626771",
|
||||
"totalDifficulty": "1",
|
||||
"withdrawals": [],
|
||||
"withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"alloc": {
|
||||
"0x0000000000000000000000000000000000000000": {
|
||||
"balance": "0x272e0528"
|
||||
},
|
||||
"0x0c2c51a0990aee1d73c1228de158688341557508": {
|
||||
"balance": "0xde0b6b3a7640000"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"chainId": 1337,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"arrowGlacierBlock": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"cancunTime": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"terminalTotalDifficultyPassed": true
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"number": "316",
|
||||
"difficulty": "0",
|
||||
"timestamp": "1709626785",
|
||||
"gasLimit": "15654443",
|
||||
"miner": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"input": "0x03f8b1820539806485174876e800825208940c2c51a0990aee1d73c1228de1586883415575088080c083020000f842a00100c9fbdf97f747e85847b4f3fff408f89c26842f77c882858bf2c89923849aa00138e3896f3c27f2389147507f8bcec52028b0efca6ee842ed83c9158873943880a0dbac3f97a532c9b00e6239b29036245a5bfbb96940b9d848634661abee98b945a03eec8525f261c2e79798f7b45a5d6ccaefa24576d53ba5023e919b86841c0675",
|
||||
"result": {
|
||||
"0x0000000000000000000000000000000000000000": { "balance": "0x272e0528" },
|
||||
"0x0c2c51a0990aee1d73c1228de158688341557508": {
|
||||
"balance": "0xde0b6b3a7640000"
|
||||
}
|
||||
}
|
||||
}
|
||||
64
eth/tracers/internal/tracetest/testdata/prestate_tracer/create_post_eip158.json
vendored
Normal file
64
eth/tracers/internal/tracetest/testdata/prestate_tracer/create_post_eip158.json
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"genesis": {
|
||||
"baseFeePerGas": "7",
|
||||
"difficulty": "2",
|
||||
"extraData": "0xd983010d0e846765746888676f312e32312e318664617277696e0000000000001713699f05f79a59abec177c7a87b90ceda79b72ff5edc9197dd7627a447cde45b079bbc3765a236cdf680e2d4d2247135d0e6bb6fd92b50638b92504ddb274f00",
|
||||
"gasLimit": "30000000",
|
||||
"hash": "0x6ad5258175c66f4e883d238a92a08428d8ebcbeac631ab7b972634cc05effab3",
|
||||
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "39137",
|
||||
"stateRoot": "0x715f00df764dbadd4863247a215ac44b5420beafde3ec458b15db7aafa89be0c",
|
||||
"timestamp": "1709022192",
|
||||
"totalDifficulty": "78275",
|
||||
"alloc": {
|
||||
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
|
||||
"balance": "0x10f06447a8d44dba190",
|
||||
"nonce": "2"
|
||||
},
|
||||
"0x82211934c340b29561381392348d48413e15adc8": {
|
||||
"balance": "0x6abd7a808913ed2",
|
||||
"nonce": "64"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"chainId": 12345,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"arrowGlacierBlock": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"clique": {
|
||||
"period": 5,
|
||||
"epoch": 30000
|
||||
}
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"number": "39138",
|
||||
"difficulty": "2",
|
||||
"timestamp": "1709022197",
|
||||
"gasLimit": "30000000",
|
||||
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0"
|
||||
},
|
||||
"input": "0x02f902af823039408459682f008459682f088302b3538080b90254608060405234801561001057600080fd5b50610234806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033c001a0a8cf4729b7e4664687abb3e2559853d7d489eb441519be2a17493061fb4c3a03a04b5a904ba8a6e59c6c40049c4d14a73233aeb8a45b38403199f304630dc0d453",
|
||||
"result": {
|
||||
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
|
||||
"balance": "0x10f06447a8d44dba190",
|
||||
"nonce": 2
|
||||
},
|
||||
"0x82211934c340b29561381392348d48413e15adc8": {
|
||||
"balance": "0x6abd7a808913ed2",
|
||||
"nonce": 64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,7 +83,7 @@
|
|||
},
|
||||
"post": {
|
||||
"0x808b4da0be6c9512e948521452227efc619bea52": {
|
||||
"balance": "0x2cd72a36dd031f089",
|
||||
"balance": "0x2cd987071ba2346b6",
|
||||
"nonce": 1223933
|
||||
},
|
||||
"0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": {
|
||||
|
|
|
|||
82
eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_post_eip158.json
vendored
Normal file
82
eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_post_eip158.json
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"genesis": {
|
||||
"baseFeePerGas": "7",
|
||||
"difficulty": "2",
|
||||
"extraData": "0xd983010d0e846765746888676f312e32312e318664617277696e0000000000001713699f05f79a59abec177c7a87b90ceda79b72ff5edc9197dd7627a447cde45b079bbc3765a236cdf680e2d4d2247135d0e6bb6fd92b50638b92504ddb274f00",
|
||||
"gasLimit": "30000000",
|
||||
"hash": "0x6ad5258175c66f4e883d238a92a08428d8ebcbeac631ab7b972634cc05effab3",
|
||||
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0000000000000000",
|
||||
"number": "39137",
|
||||
"stateRoot": "0x715f00df764dbadd4863247a215ac44b5420beafde3ec458b15db7aafa89be0c",
|
||||
"timestamp": "1709022192",
|
||||
"totalDifficulty": "78275",
|
||||
"alloc": {
|
||||
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
|
||||
"balance": "0x10f06447a8d44dba190",
|
||||
"nonce": "2"
|
||||
},
|
||||
"0x82211934c340b29561381392348d48413e15adc8": {
|
||||
"balance": "0x6abd7a808913ed2",
|
||||
"nonce": "64"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"chainId": 12345,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"arrowGlacierBlock": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"clique": {
|
||||
"period": 5,
|
||||
"epoch": 30000
|
||||
}
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"number": "39138",
|
||||
"difficulty": "2",
|
||||
"timestamp": "1709022197",
|
||||
"gasLimit": "30000000",
|
||||
"miner": "0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0"
|
||||
},
|
||||
"input": "0x02f902af823039408459682f008459682f088302b3538080b90254608060405234801561001057600080fd5b50610234806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033c001a0a8cf4729b7e4664687abb3e2559853d7d489eb441519be2a17493061fb4c3a03a04b5a904ba8a6e59c6c40049c4d14a73233aeb8a45b38403199f304630dc0d453",
|
||||
"tracerConfig": {
|
||||
"diffMode": true
|
||||
},
|
||||
"result": {
|
||||
"post": {
|
||||
"0x1bda2f8e4735507930bd6cfe873bf0bf0f4ab1de": {
|
||||
"code": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806309ce9ccb1461003b5780633fb5c1cb14610059575b600080fd5b610043610075565b60405161005091906100e2565b60405180910390f35b610073600480360381019061006e919061012e565b61007b565b005b60005481565b80600081905550600a8111156100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906101de565b60405180910390fd5b50565b6000819050919050565b6100dc816100c9565b82525050565b60006020820190506100f760008301846100d3565b92915050565b600080fd5b61010b816100c9565b811461011657600080fd5b50565b60008135905061012881610102565b92915050565b600060208284031215610144576101436100fd565b5b600061015284828501610119565b91505092915050565b600082825260208201905092915050565b7f4e756d6265722069732067726561746572207468616e2031302c207472616e7360008201527f616374696f6e2072657665727465642e00000000000000000000000000000000602082015250565b60006101c860308361015b565b91506101d38261016c565b604082019050919050565b600060208201905081810360008301526101f7816101bb565b905091905056fea264697066735822122069018995fecf03bda91a88b6eafe41641709dee8b4a706fe301c8a569fe8c1b364736f6c63430008130033",
|
||||
"nonce": 1
|
||||
},
|
||||
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
|
||||
"balance": "0x10f0645688331eb5690"
|
||||
},
|
||||
"0x82211934c340b29561381392348d48413e15adc8": {
|
||||
"balance": "0x6aae9b21b6ee855",
|
||||
"nonce": 65
|
||||
}
|
||||
},
|
||||
"pre": {
|
||||
"0x2445e8c26a2bf3d1e59f1bb9b1d442caf90768e0": {
|
||||
"balance": "0x10f06447a8d44dba190",
|
||||
"nonce": 2
|
||||
},
|
||||
"0x82211934c340b29561381392348d48413e15adc8": {
|
||||
"balance": "0x6abd7a808913ed2",
|
||||
"nonce": 64
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,8 +23,10 @@ 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/holiman/uint256"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
|
@ -42,9 +44,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)
|
||||
}
|
||||
}
|
||||
|
|
@ -96,10 +98,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
|
||||
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
|
||||
|
|
@ -136,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 *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())
|
||||
|
|
@ -219,33 +219,47 @@ 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{
|
||||
Hooks: &tracing.Hooks{
|
||||
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 *vm.EVM, 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}
|
||||
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()
|
||||
return
|
||||
}
|
||||
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 t.err != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
// Don't override vm error
|
||||
if _, ok := t.ctx["error"]; !ok {
|
||||
|
|
@ -256,11 +270,10 @@ 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) {
|
||||
cancel := func(err error) {
|
||||
t.err = err
|
||||
t.env.Cancel()
|
||||
// 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) {
|
||||
if t.err != nil {
|
||||
return
|
||||
}
|
||||
if create {
|
||||
t.ctx["type"] = t.vm.ToValue("CREATE")
|
||||
|
|
@ -269,32 +282,32 @@ func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create b
|
|||
}
|
||||
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
|
||||
valueBig, err := t.toBig(t.vm, value.String())
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
t.err = err
|
||||
return
|
||||
}
|
||||
t.ctx["value"] = valueBig
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// OnOpcode implements the Tracer interface to trace a single step of VM execution.
|
||||
func (t *jsTracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||
if !t.traceStep {
|
||||
return
|
||||
}
|
||||
|
|
@ -303,10 +316,10 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
|
|||
}
|
||||
|
||||
log := t.log
|
||||
log.op.op = op
|
||||
log.memory.memory = scope.Memory
|
||||
log.stack.stack = scope.Stack
|
||||
log.contract.contract = scope.Contract
|
||||
log.op.op = vm.OpCode(op)
|
||||
log.memory.memory = scope.MemoryData()
|
||||
log.stack.stack = scope.StackData()
|
||||
log.contract.scope = scope
|
||||
log.pc = pc
|
||||
log.gas = gas
|
||||
log.cost = cost
|
||||
|
|
@ -318,20 +331,23 @@ 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) {
|
||||
// OnFault implements the Tracer interface to trace an execution fault
|
||||
func (t *jsTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 t.err != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.ctx["error"] = t.vm.ToValue(err.Error())
|
||||
}
|
||||
|
|
@ -343,16 +359,20 @@ func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted
|
|||
t.ctx["output"] = outputVal
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if !t.traceFrame {
|
||||
return
|
||||
}
|
||||
// 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
|
||||
}
|
||||
if !t.traceFrame {
|
||||
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)
|
||||
|
|
@ -367,9 +387,16 @@ 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
|
||||
// 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 t.err != nil {
|
||||
return
|
||||
}
|
||||
if depth == 0 {
|
||||
t.onEnd(output, gasUsed, err, reverted)
|
||||
return
|
||||
}
|
||||
if !t.traceFrame {
|
||||
return
|
||||
}
|
||||
|
|
@ -385,6 +412,9 @@ func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverte
|
|||
|
||||
// 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 {
|
||||
|
|
@ -394,7 +424,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.
|
||||
|
|
@ -407,9 +437,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 CaptureStart which comes before any JS execution.
|
||||
// So it should be non-nil.
|
||||
t.env.Cancel()
|
||||
}
|
||||
|
||||
func wrapError(context string, err error) error {
|
||||
|
|
@ -588,7 +615,7 @@ func (o *opObj) setupObject() *goja.Object {
|
|||
}
|
||||
|
||||
type memoryObj struct {
|
||||
memory *vm.Memory
|
||||
memory []byte
|
||||
vm *goja.Runtime
|
||||
toBig toBigFn
|
||||
toBuf toBufFn
|
||||
|
|
@ -639,14 +666,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 {
|
||||
|
|
@ -658,7 +685,7 @@ func (m *memoryObj) setupObject() *goja.Object {
|
|||
}
|
||||
|
||||
type stackObj struct {
|
||||
stack *vm.Stack
|
||||
stack []uint256.Int
|
||||
vm *goja.Runtime
|
||||
toBig toBigFn
|
||||
}
|
||||
|
|
@ -679,14 +706,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 {
|
||||
|
|
@ -697,7 +724,7 @@ func (s *stackObj) setupObject() *goja.Object {
|
|||
}
|
||||
|
||||
type dbObj struct {
|
||||
db vm.StateDB
|
||||
db tracing.StateDB
|
||||
vm *goja.Runtime
|
||||
toBig toBigFn
|
||||
toBuf toBufFn
|
||||
|
|
@ -789,14 +816,14 @@ func (do *dbObj) setupObject() *goja.Object {
|
|||
}
|
||||
|
||||
type contractObj struct {
|
||||
contract *vm.Contract
|
||||
scope tracing.OpContext
|
||||
vm *goja.Runtime
|
||||
toBig toBigFn
|
||||
toBuf toBufFn
|
||||
}
|
||||
|
||||
func (co *contractObj) GetCaller() goja.Value {
|
||||
caller := co.contract.Caller().Bytes()
|
||||
caller := co.scope.Caller().Bytes()
|
||||
res, err := co.toBuf(co.vm, caller)
|
||||
if err != nil {
|
||||
co.vm.Interrupt(err)
|
||||
|
|
@ -806,7 +833,7 @@ func (co *contractObj) GetCaller() goja.Value {
|
|||
}
|
||||
|
||||
func (co *contractObj) GetAddress() goja.Value {
|
||||
addr := co.contract.Address().Bytes()
|
||||
addr := co.scope.Address().Bytes()
|
||||
res, err := co.toBuf(co.vm, addr)
|
||||
if err != nil {
|
||||
co.vm.Interrupt(err)
|
||||
|
|
@ -816,7 +843,7 @@ func (co *contractObj) GetAddress() goja.Value {
|
|||
}
|
||||
|
||||
func (co *contractObj) GetValue() goja.Value {
|
||||
value := co.contract.Value()
|
||||
value := co.scope.CallValue()
|
||||
res, err := co.toBig(co.vm, value.String())
|
||||
if err != nil {
|
||||
co.vm.Interrupt(err)
|
||||
|
|
@ -826,7 +853,7 @@ func (co *contractObj) GetValue() goja.Value {
|
|||
}
|
||||
|
||||
func (co *contractObj) GetInput() goja.Value {
|
||||
input := common.CopyBytes(co.contract.Input)
|
||||
input := common.CopyBytes(co.scope.CallInput())
|
||||
res, err := co.toBuf(co.vm, input)
|
||||
if err != nil {
|
||||
co.vm.Interrupt(err)
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@
|
|||
return this.finalize(result);
|
||||
},
|
||||
|
||||
// finalize recreates a call object using the final desired field oder for json
|
||||
// finalize recreates a call object using the final desired field order for json
|
||||
// serialization. This is a nicety feature to pass meaningfully ordered results
|
||||
// to users who don't interpret it, just display it.
|
||||
finalize: function(call) {
|
||||
|
|
|
|||
|
|
@ -61,9 +61,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.Hooks})
|
||||
gasLimit uint64 = 31000
|
||||
startGas uint64 = 10000
|
||||
value = big.NewInt(0)
|
||||
|
|
@ -74,12 +74,12 @@ 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.CaptureStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
|
||||
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.CaptureEnd(ret, startGas-contract.Gas, err, true)
|
||||
tracer.OnExit(0, 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
|
||||
}
|
||||
|
|
@ -184,13 +184,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})
|
||||
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)
|
||||
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.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)
|
||||
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)
|
||||
|
|
@ -206,10 +206,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})
|
||||
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)
|
||||
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.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)
|
||||
|
|
@ -278,8 +278,8 @@ 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.CaptureExit([]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 {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
package live
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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/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"
|
||||
)
|
||||
|
|
@ -22,60 +21,62 @@ func init() {
|
|||
// as soon as we have a real live tracer.
|
||||
type noop struct{}
|
||||
|
||||
func newNoopTracer() (core.BlockchainLogger, error) {
|
||||
return &noop{}, nil
|
||||
func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) {
|
||||
t := &noop{}
|
||||
return &tracing.Hooks{
|
||||
OnTxStart: t.OnTxStart,
|
||||
OnTxEnd: t.OnTxEnd,
|
||||
OnEnter: t.OnEnter,
|
||||
OnExit: t.OnExit,
|
||||
OnOpcode: t.OnOpcode,
|
||||
OnFault: t.OnFault,
|
||||
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
|
||||
}
|
||||
|
||||
// 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 byte, 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 byte, 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 vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
func (t *noop) OnEnter(depth int, typ byte, 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 vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, 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 vm.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) OnTxEnd(receipt *types.Receipt, err error) {
|
||||
}
|
||||
|
||||
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, skip bool) {
|
||||
func (t *noop) OnBlockStart(ev tracing.BlockEvent) {
|
||||
}
|
||||
|
||||
func (t *noop) OnBlockEnd(err error) {
|
||||
}
|
||||
|
||||
func (t *noop) OnSkippedBlock(ev tracing.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 tracing.BalanceChangeReason) {
|
||||
}
|
||||
|
||||
func (t *noop) OnNonceChange(a common.Address, prev, new uint64) {
|
||||
|
|
@ -91,5 +92,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 tracing.GasChangeReason) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ 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"
|
||||
)
|
||||
|
||||
// accessList is an accumulator for the set of accounts and storage slots an EVM
|
||||
|
|
@ -102,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
|
||||
}
|
||||
|
|
@ -132,14 +131,20 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
|
|||
}
|
||||
}
|
||||
|
||||
// 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) Hooks() *tracing.Hooks {
|
||||
return &tracing.Hooks{
|
||||
OnOpcode: a.OnOpcode,
|
||||
}
|
||||
}
|
||||
|
||||
// OnOpcode captures all opcodes that touch storage or addresses and adds them to the accesslist.
|
||||
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)
|
||||
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.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())
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ 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"
|
||||
|
|
@ -108,9 +109,8 @@ 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
|
||||
env *tracing.VMContext
|
||||
|
||||
storage map[common.Address]Storage
|
||||
logs []StructLog
|
||||
|
|
@ -133,6 +133,23 @@ func NewStructLogger(cfg *Config) *StructLogger {
|
|||
return logger
|
||||
}
|
||||
|
||||
func (l *StructLogger) Hooks() *tracing.Hooks {
|
||||
return &tracing.Hooks{
|
||||
OnTxStart: l.OnTxStart,
|
||||
OnTxEnd: l.OnTxEnd,
|
||||
OnExit: l.OnExit,
|
||||
OnOpcode: l.OnOpcode,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *StructLogger) Tracer() *directory.Tracer {
|
||||
return &directory.Tracer{
|
||||
Hooks: l.Hooks(),
|
||||
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)
|
||||
|
|
@ -141,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, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||
// OnOpcode also tracks SLOAD/SSTORE ops to track storage change.
|
||||
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
|
||||
|
|
@ -154,49 +171,47 @@ 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.MemoryData()
|
||||
stack := scope.StackData()
|
||||
// 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[i] = item
|
||||
stck = make([]uint256.Int, len(stack))
|
||||
copy(stck, stack)
|
||||
}
|
||||
}
|
||||
stackData := stack.Data()
|
||||
stackLen := len(stackData)
|
||||
contractAddr := scope.Address()
|
||||
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
|
||||
|
|
@ -205,12 +220,15 @@ 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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
@ -247,11 +265,11 @@ 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) 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 {
|
||||
|
|
@ -319,10 +337,9 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
|
|||
}
|
||||
|
||||
type mdLogger struct {
|
||||
directory.NoopTracer
|
||||
out io.Writer
|
||||
cfg *Config
|
||||
env *vm.EVM
|
||||
env *tracing.VMContext
|
||||
}
|
||||
|
||||
// NewMarkdownLogger creates a logger which outputs information in a format adapted
|
||||
|
|
@ -335,11 +352,25 @@ 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) Hooks() *tracing.Hooks {
|
||||
return &tracing.Hooks{
|
||||
OnTxStart: t.OnTxStart,
|
||||
OnEnter: t.OnEnter,
|
||||
OnExit: t.OnExit,
|
||||
OnOpcode: t.OnOpcode,
|
||||
OnFault: t.OnFault,
|
||||
}
|
||||
}
|
||||
|
||||
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 byte, 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(),
|
||||
|
|
@ -356,15 +387,22 @@ 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) 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 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)
|
||||
|
||||
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, ","))
|
||||
|
|
@ -377,15 +415,10 @@ 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) 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)
|
||||
}
|
||||
|
||||
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||
output, gasUsed, err)
|
||||
}
|
||||
|
||||
// ExecutionResult groups all structured logs emitted by the EVM
|
||||
// while replaying a transaction in debug mode as well as transaction
|
||||
// execution status, the amount of gas used and the return value
|
||||
|
|
|
|||
|
|
@ -22,16 +22,15 @@ 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"
|
||||
)
|
||||
|
||||
type JSONLogger struct {
|
||||
directory.NoopTracer
|
||||
encoder *json.Encoder
|
||||
cfg *Config
|
||||
env *vm.EVM
|
||||
env *tracing.VMContext
|
||||
}
|
||||
|
||||
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
||||
|
|
@ -44,31 +43,39 @@ 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) {
|
||||
// TODO: Add rData to this interface as well
|
||||
l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
|
||||
func (l *JSONLogger) Hooks() *tracing.Hooks {
|
||||
return &tracing.Hooks{
|
||||
OnTxStart: l.OnTxStart,
|
||||
OnExit: l.OnExit,
|
||||
OnOpcode: l.OnOpcode,
|
||||
OnFault: l.OnFault,
|
||||
}
|
||||
}
|
||||
|
||||
// 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) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||
// TODO: Add rData to this interface as well
|
||||
l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err)
|
||||
}
|
||||
|
||||
func (l *JSONLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||
memory := scope.MemoryData()
|
||||
stack := scope.StackData()
|
||||
|
||||
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
|
||||
|
|
@ -76,8 +83,10 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
|
|||
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"`
|
||||
|
|
@ -90,6 +99,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) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||
l.env = env
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,13 +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.Hooks()})
|
||||
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.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)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ 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"
|
||||
|
|
@ -47,21 +48,26 @@ func init() {
|
|||
// 0xc281d19e-0: 1
|
||||
// }
|
||||
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
|
||||
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
|
||||
// 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{
|
||||
Hooks: &tracing.Hooks{
|
||||
OnTxStart: t.OnTxStart,
|
||||
OnEnter: t.OnEnter,
|
||||
},
|
||||
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,23 +86,14 @@ 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) OnTxStart(env *tracing.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)
|
||||
}
|
||||
|
||||
// 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(op vm.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 byte, 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 +101,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 {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ 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"
|
||||
|
|
@ -104,7 +105,6 @@ type callFrameMarshaling struct {
|
|||
}
|
||||
|
||||
type callTracer struct {
|
||||
directory.NoopTracer
|
||||
callstack []callFrame
|
||||
config callTracerConfig
|
||||
gasLimit uint64
|
||||
|
|
@ -120,7 +120,25 @@ 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{
|
||||
Hooks: &tracing.Hooks{
|
||||
OnTxStart: t.OnTxStart,
|
||||
OnTxEnd: t.OnTxEnd,
|
||||
OnEnter: t.OnEnter,
|
||||
OnExit: t.OnExit,
|
||||
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 {
|
||||
|
|
@ -129,38 +147,13 @@ func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Trace
|
|||
}
|
||||
// First callframe contains tx context info
|
||||
// and is populated on start and end.
|
||||
return &callTracer{callstack: make([]callFrame, 1), config: config}, nil
|
||||
return &callTracer{callstack: make([]callFrame, 0, 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 vm.OpCode, gas, cost uint64, scope *vm.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) {
|
||||
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 byte, 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
|
||||
|
|
@ -170,42 +163,59 @@ 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),
|
||||
Gas: gas,
|
||||
Value: value,
|
||||
}
|
||||
if depth == 0 {
|
||||
call.Gas = t.gasLimit
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (t *callTracer) CaptureTxStart(env *vm.EVM, 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
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ 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"
|
||||
|
|
@ -109,12 +110,11 @@ 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
|
||||
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 {
|
||||
|
|
@ -123,7 +123,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,42 +133,31 @@ 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")
|
||||
|
||||
ft := &flatCallTracer{tracer: t, ctx: ctx, config: config}
|
||||
return &directory.Tracer{
|
||||
Hooks: &tracing.Hooks{
|
||||
OnTxStart: ft.OnTxStart,
|
||||
OnTxEnd: ft.OnTxEnd,
|
||||
OnEnter: ft.OnEnter,
|
||||
OnExit: ft.OnExit,
|
||||
},
|
||||
Stop: ft.Stop,
|
||||
GetResult: ft.GetResult,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||
func (t *flatCallTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
t.tracer.OnEnter(depth, typ, from, to, input, gas, value)
|
||||
|
||||
if depth == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
return &flatCallTracer{tracer: t, ctx: ctx, config: config}, 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 vm.OpCode, gas, cost uint64, scope *vm.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) {
|
||||
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) {
|
||||
t.tracer.CaptureEnter(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.
|
||||
if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil {
|
||||
|
|
@ -176,11 +165,14 @@ 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
|
||||
// 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)
|
||||
|
||||
if depth == 0 {
|
||||
return
|
||||
}
|
||||
// Parity traces don't include CALL/STATICCALLs to precompiles.
|
||||
// By default we remove them from the callstack.
|
||||
if t.config.IncludePrecompiles {
|
||||
|
|
@ -200,15 +192,15 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, r
|
|||
}
|
||||
}
|
||||
|
||||
func (t *flatCallTracer) CaptureTxStart(env *vm.EVM, 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.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)
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||
)
|
||||
|
||||
|
|
@ -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,106 +56,121 @@ 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{
|
||||
Hooks: &tracing.Hooks{
|
||||
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,
|
||||
}, 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) {
|
||||
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 {
|
||||
t.CaptureStart(from, to, create, input, gas, value)
|
||||
if t.OnOpcode != nil {
|
||||
t.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||
func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
func (t *muxTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureEnd(output, gasUsed, err, reverted)
|
||||
if t.OnFault != nil {
|
||||
t.OnFault(pc, op, gas, cost, scope, depth, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
for _, t := range t.tracers {
|
||||
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) {
|
||||
for _, t := range t.tracers {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 tracing.GasChangeReason) {
|
||||
for _, t := range t.tracers {
|
||||
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) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureEnter(typ, from, to, input, gas, value)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureExit(output, gasUsed, err, reverted)
|
||||
if t.OnEnter != nil {
|
||||
t.OnEnter(depth, typ, from, to, input, gas, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction, from common.Address) {
|
||||
func (t *muxTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureTxStart(env, tx, from)
|
||||
if t.OnExit != nil {
|
||||
t.OnExit(depth, output, gasUsed, err, reverted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
|
||||
func (t *muxTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||
for _, t := range t.tracers {
|
||||
t.CaptureTxEnd(receipt, err)
|
||||
if t.OnTxStart != nil {
|
||||
t.OnTxStart(env, tx, from)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
|
||||
func (t *muxTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||
for _, t := range t.tracers {
|
||||
if t.OnTxEnd != nil {
|
||||
t.OnTxEnd(receipt, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnNonceChange(a common.Address, prev, new uint64) {
|
||||
for _, t := range t.tracers {
|
||||
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 {
|
||||
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 {
|
||||
if t.OnStorageChange != nil {
|
||||
t.OnStorageChange(a, k, prev, new)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *muxTracer) OnLog(log *types.Log) {
|
||||
for _, t := range t.tracers {
|
||||
if t.OnLog != nil {
|
||||
t.OnLog(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetResult returns an empty json object.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ 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"
|
||||
|
|
@ -57,8 +58,7 @@ type accountMarshaling struct {
|
|||
}
|
||||
|
||||
type prestateTracer struct {
|
||||
directory.NoopTracer
|
||||
env *vm.EVM
|
||||
env *tracing.VMContext
|
||||
pre stateMap
|
||||
post stateMap
|
||||
to common.Address
|
||||
|
|
@ -73,24 +73,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{
|
||||
Hooks: &tracing.Hooks{
|
||||
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, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, 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 byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -98,10 +107,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.StackData()
|
||||
stackLen := len(stackData)
|
||||
caller := scope.Contract.Address()
|
||||
caller := scope.Address()
|
||||
switch {
|
||||
case stackLen >= 1 && (op == vm.SLOAD || op == vm.SSTORE):
|
||||
slot := common.Hash(stackData[stackLen-1].Bytes32())
|
||||
|
|
@ -123,7 +132,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 := directory.GetMemoryCopyPadded(scope.Memory, 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
|
||||
|
|
@ -136,7 +145,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) 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))
|
||||
|
|
@ -147,10 +156,10 @@ 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) {
|
||||
func (t *prestateTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.Hooks()})
|
||||
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to prepare transaction for tracing: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ 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"
|
||||
|
|
@ -965,7 +966,7 @@ func (diff *StateOverride) Apply(statedb vm.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), tracing.BalanceChangeUnspecified)
|
||||
}
|
||||
if account.State != nil && account.StateDiff != nil {
|
||||
return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
|
||||
|
|
@ -1543,7 +1544,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.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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"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/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -107,7 +108,7 @@ type btHeaderMarshaling struct {
|
|||
ExcessBlobGas *math.HexOrDecimal64
|
||||
}
|
||||
|
||||
func (t *BlockTest) Run(snapshotter bool, scheme string, tracer vm.EVMLogger, postCheck func(error, *core.BlockChain)) (result error) {
|
||||
func (t *BlockTest) Run(snapshotter bool, scheme string, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) {
|
||||
config, ok := Forks[t.json.Network]
|
||||
if !ok {
|
||||
return UnsupportedForkError{t.json.Network}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,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).Hooks()
|
||||
err2 := test(config)
|
||||
if !reflect.DeepEqual(err, err2) {
|
||||
t.Errorf("different error for second run: %v", err2)
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ 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"
|
||||
|
|
@ -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), tracing.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, tracing.BalanceChangeUnspecified)
|
||||
for k, v := range a.Storage {
|
||||
statedb.SetState(addr, k, v)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue