eth/tracers: various fixes (#30540)
Breaking changes: - The ChainConfig was exposed to tracers via VMContext passed in `OnTxStart`. This is unnecessary specially looking through the lens of live tracers as chain config remains the same throughout the lifetime of the program. It was there so that native API-invoked tracers could access it. So instead we moved it to the constructor of API tracers. Non-breaking: - Change the default config of the tracers to be `{}` instead of nil. This way an extra nil check can be avoided. Refactoring: - Rename `supply` struct to `supplyTracer`. - Un-export some hook definitions.
This commit is contained in:
parent
18a591811f
commit
978ca5fc5e
|
@ -132,7 +132,7 @@ type rejectedTx struct {
|
||||||
// Apply applies a set of transactions to a pre-state
|
// Apply applies a set of transactions to a pre-state
|
||||||
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
txIt txIterator, miningReward int64,
|
txIt txIterator, miningReward int64,
|
||||||
getTracerFn func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) {
|
getTracerFn func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) {
|
||||||
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
|
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
|
||||||
// required blockhashes
|
// required blockhashes
|
||||||
var hashError error
|
var hashError error
|
||||||
|
@ -242,7 +242,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tracer, traceOutput, err := getTracerFn(txIndex, tx.Hash())
|
tracer, traceOutput, err := getTracerFn(txIndex, tx.Hash(), chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,9 @@ type input struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Transition(ctx *cli.Context) error {
|
func Transition(ctx *cli.Context) error {
|
||||||
var getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) { return nil, nil, nil }
|
var getTracer = func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
baseDir, err := createBasedir(ctx)
|
baseDir, err := createBasedir(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -97,7 +99,7 @@ func Transition(ctx *cli.Context) error {
|
||||||
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
||||||
Debug: true,
|
Debug: true,
|
||||||
}
|
}
|
||||||
getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
|
getTracer = func(txIndex int, txHash common.Hash, _ *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) {
|
||||||
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
|
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
|
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
|
||||||
|
@ -121,12 +123,12 @@ func Transition(ctx *cli.Context) error {
|
||||||
if ctx.IsSet(TraceTracerConfigFlag.Name) {
|
if ctx.IsSet(TraceTracerConfigFlag.Name) {
|
||||||
config = []byte(ctx.String(TraceTracerConfigFlag.Name))
|
config = []byte(ctx.String(TraceTracerConfigFlag.Name))
|
||||||
}
|
}
|
||||||
getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
|
getTracer = func(txIndex int, txHash common.Hash, chainConfig *params.ChainConfig) (*tracers.Tracer, io.WriteCloser, error) {
|
||||||
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String())))
|
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String())))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 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 := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config)
|
tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
|
return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -543,6 +543,7 @@ var (
|
||||||
VMTraceJsonConfigFlag = &cli.StringFlag{
|
VMTraceJsonConfigFlag = &cli.StringFlag{
|
||||||
Name: "vmtrace.jsonconfig",
|
Name: "vmtrace.jsonconfig",
|
||||||
Usage: "Tracer configuration (JSON)",
|
Usage: "Tracer configuration (JSON)",
|
||||||
|
Value: "{}",
|
||||||
Category: flags.VMCategory,
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
// API options.
|
// API options.
|
||||||
|
@ -1899,13 +1900,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
// VM tracing config.
|
// VM tracing config.
|
||||||
if ctx.IsSet(VMTraceFlag.Name) {
|
if ctx.IsSet(VMTraceFlag.Name) {
|
||||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||||
var config string
|
|
||||||
if ctx.IsSet(VMTraceJsonConfigFlag.Name) {
|
|
||||||
config = ctx.String(VMTraceJsonConfigFlag.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.VMTrace = name
|
cfg.VMTrace = name
|
||||||
cfg.VMTraceJsonConfig = config
|
cfg.VMTraceJsonConfig = ctx.String(VMTraceJsonConfigFlag.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2186,10 +2182,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
||||||
}
|
}
|
||||||
if ctx.IsSet(VMTraceFlag.Name) {
|
if ctx.IsSet(VMTraceFlag.Name) {
|
||||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||||
var config json.RawMessage
|
config := json.RawMessage(ctx.String(VMTraceJsonConfigFlag.Name))
|
||||||
if ctx.IsSet(VMTraceJsonConfigFlag.Name) {
|
|
||||||
config = json.RawMessage(ctx.String(VMTraceJsonConfigFlag.Name))
|
|
||||||
}
|
|
||||||
t, err := tracers.LiveDirectory.New(name, config)
|
t, err := tracers.LiveDirectory.New(name, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Failed to create tracer %q: %v", name, err)
|
Fatalf("Failed to create tracer %q: %v", name, err)
|
||||||
|
|
|
@ -55,9 +55,8 @@ type VMContext struct {
|
||||||
Time uint64
|
Time uint64
|
||||||
Random *common.Hash
|
Random *common.Hash
|
||||||
// Effective tx gas price
|
// Effective tx gas price
|
||||||
GasPrice *big.Int
|
GasPrice *big.Int
|
||||||
ChainConfig *params.ChainConfig
|
StateDB StateDB
|
||||||
StateDB StateDB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockEvent is emitted upon tracing an incoming block.
|
// BlockEvent is emitted upon tracing an incoming block.
|
||||||
|
|
|
@ -613,7 +613,6 @@ func (evm *EVM) GetVMContext() *tracing.VMContext {
|
||||||
Time: evm.Context.Time,
|
Time: evm.Context.Time,
|
||||||
Random: evm.Context.Random,
|
Random: evm.Context.Random,
|
||||||
GasPrice: evm.TxContext.GasPrice,
|
GasPrice: evm.TxContext.GasPrice,
|
||||||
ChainConfig: evm.ChainConfig(),
|
|
||||||
StateDB: evm.StateDB,
|
StateDB: evm.StateDB,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,7 +395,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
||||||
cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
cfg.GasLimit = gas
|
cfg.GasLimit = gas
|
||||||
if len(tracerCode) > 0 {
|
if len(tracerCode) > 0 {
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil)
|
tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil, cfg.ChainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -887,7 +887,7 @@ func TestRuntimeJSTracer(t *testing.T) {
|
||||||
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
||||||
statedb.SetCode(common.HexToAddress("0xff"), suicideCode)
|
statedb.SetCode(common.HexToAddress("0xff"), suicideCode)
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
|
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -922,7 +922,7 @@ func TestJSTracerCreateTx(t *testing.T) {
|
||||||
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
|
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
|
||||||
|
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
|
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,7 +197,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if config.VMTrace != "" {
|
if config.VMTrace != "" {
|
||||||
var traceConfig json.RawMessage
|
traceConfig := json.RawMessage("{}")
|
||||||
if config.VMTraceJsonConfig != "" {
|
if config.VMTraceJsonConfig != "" {
|
||||||
traceConfig = json.RawMessage(config.VMTraceJsonConfig)
|
traceConfig = json.RawMessage(config.VMTraceJsonConfig)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1011,7 +1011,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
|
||||||
Stop: logger.Stop,
|
Stop: logger.Stop,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
|
tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig, api.backend.ChainConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context contains some contextual infos for a transaction execution that is not
|
// Context contains some contextual infos for a transaction execution that is not
|
||||||
|
@ -44,8 +45,8 @@ type Tracer struct {
|
||||||
Stop func(err error)
|
Stop func(err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ctorFn func(*Context, json.RawMessage) (*Tracer, error)
|
type ctorFn func(*Context, json.RawMessage, *params.ChainConfig) (*Tracer, error)
|
||||||
type jsCtorFn func(string, *Context, json.RawMessage) (*Tracer, error)
|
type jsCtorFn func(string, *Context, json.RawMessage, *params.ChainConfig) (*Tracer, error)
|
||||||
|
|
||||||
type elem struct {
|
type elem struct {
|
||||||
ctor ctorFn
|
ctor ctorFn
|
||||||
|
@ -78,12 +79,15 @@ func (d *directory) RegisterJSEval(f jsCtorFn) {
|
||||||
// New returns a new instance of a tracer, by iterating through the
|
// New returns a new instance of a tracer, by iterating through the
|
||||||
// registered lookups. Name is either name of an existing tracer
|
// registered lookups. Name is either name of an existing tracer
|
||||||
// or an arbitrary JS code.
|
// 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, chainConfig *params.ChainConfig) (*Tracer, error) {
|
||||||
|
if len(cfg) == 0 {
|
||||||
|
cfg = json.RawMessage("{}")
|
||||||
|
}
|
||||||
if elem, ok := d.elems[name]; ok {
|
if elem, ok := d.elems[name]; ok {
|
||||||
return elem.ctor(ctx, cfg)
|
return elem.ctor(ctx, cfg, chainConfig)
|
||||||
}
|
}
|
||||||
// Assume JS code
|
// Assume JS code
|
||||||
return d.jsEval(name, ctx, cfg)
|
return d.jsEval(name, ctx, cfg, chainConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsJS will return true if the given tracer will evaluate
|
// IsJS will return true if the given tracer will evaluate
|
||||||
|
|
|
@ -120,7 +120,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
)
|
)
|
||||||
state.Close()
|
state.Close()
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -227,7 +227,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create call tracer: %v", err)
|
b.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ func TestInternals(t *testing.T) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
mkTracer := func(name string, cfg json.RawMessage) *tracers.Tracer {
|
mkTracer := func(name string, cfg json.RawMessage) *tracers.Tracer {
|
||||||
tr, err := tracers.DefaultDirectory.New(name, nil, cfg)
|
tr, err := tracers.DefaultDirectory.New(name, nil, cfg, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
|
||||||
defer state.Close()
|
defer state.Close()
|
||||||
|
|
||||||
// Create the tracer, the EVM environment and run it
|
// Create the tracer, the EVM environment and run it
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create call tracer: %v", err)
|
return fmt.Errorf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
|
||||||
)
|
)
|
||||||
defer state.Close()
|
defer state.Close()
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig, test.Genesis.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/internal"
|
"github.com/ethereum/go-ethereum/eth/tracers/internal"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
@ -46,10 +47,10 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
type ctorFn = func(*tracers.Context, json.RawMessage) (*tracers.Tracer, error)
|
type ctorFn = func(*tracers.Context, json.RawMessage, *params.ChainConfig) (*tracers.Tracer, error)
|
||||||
lookup := func(code string) ctorFn {
|
lookup := func(code string) ctorFn {
|
||||||
return func(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
return func(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
return newJsTracer(code, ctx, cfg)
|
return newJsTracer(code, ctx, cfg, chainConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, code := range assetTracers {
|
for name, code := range assetTracers {
|
||||||
|
@ -102,6 +103,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
|
||||||
type jsTracer struct {
|
type jsTracer struct {
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
env *tracing.VMContext
|
env *tracing.VMContext
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
toBig toBigFn // Converts a hex string into a JS bigint
|
toBig toBigFn // Converts a hex string into a JS bigint
|
||||||
toBuf toBufFn // Converts a []byte into a JS buffer
|
toBuf toBufFn // Converts a []byte into a JS buffer
|
||||||
fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte
|
fromBuf fromBufFn // Converts an array, hex string or Uint8Array to a []byte
|
||||||
|
@ -138,13 +140,14 @@ type jsTracer struct {
|
||||||
// The methods `result` and `fault` are required to be present.
|
// The methods `result` and `fault` are required to be present.
|
||||||
// The methods `step`, `enter`, and `exit` are optional, but note that
|
// The methods `step`, `enter`, and `exit` are optional, but note that
|
||||||
// `enter` and `exit` always go together.
|
// `enter` and `exit` always go together.
|
||||||
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
vm := goja.New()
|
vm := goja.New()
|
||||||
// By default field names are exported to JS as is, i.e. capitalized.
|
// By default field names are exported to JS as is, i.e. capitalized.
|
||||||
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
||||||
t := &jsTracer{
|
t := &jsTracer{
|
||||||
vm: vm,
|
vm: vm,
|
||||||
ctx: make(map[string]goja.Value),
|
ctx: make(map[string]goja.Value),
|
||||||
|
chainConfig: chainConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.setTypeConverters()
|
t.setTypeConverters()
|
||||||
|
@ -244,7 +247,7 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from
|
||||||
db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
|
db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf}
|
||||||
t.dbValue = db.setupObject()
|
t.dbValue = db.setupObject()
|
||||||
// Update list of precompiles based on current block
|
// Update list of precompiles based on current block
|
||||||
rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
rules := t.chainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
||||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||||
t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64())
|
t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64())
|
||||||
t.ctx["gas"] = t.vm.ToValue(tx.Gas())
|
t.ctx["gas"] = t.vm.ToValue(tx.Gas())
|
||||||
|
|
|
@ -90,11 +90,12 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
|
||||||
func TestTracer(t *testing.T) {
|
func TestTracer(t *testing.T) {
|
||||||
execTracer := func(code string, contract []byte) ([]byte, string) {
|
execTracer := func(code string, contract []byte) ([]byte, string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tracer, err := newJsTracer(code, nil, nil)
|
chainConfig := params.TestChainConfig
|
||||||
|
tracer, err := newJsTracer(code, nil, nil, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
ret, err := runTrace(tracer, testCtx(), params.TestChainConfig, contract)
|
ret, err := runTrace(tracer, testCtx(), chainConfig, contract)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err.Error() // Stringify to allow comparison without nil checks
|
return nil, err.Error() // Stringify to allow comparison without nil checks
|
||||||
}
|
}
|
||||||
|
@ -167,7 +168,8 @@ func TestTracer(t *testing.T) {
|
||||||
|
|
||||||
func TestHalt(t *testing.T) {
|
func TestHalt(t *testing.T) {
|
||||||
timeout := errors.New("stahp")
|
timeout := errors.New("stahp")
|
||||||
tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil, nil)
|
chainConfig := params.TestChainConfig
|
||||||
|
tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil, nil, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -175,20 +177,21 @@ func TestHalt(t *testing.T) {
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
tracer.Stop(timeout)
|
tracer.Stop(timeout)
|
||||||
}()
|
}()
|
||||||
if _, err = runTrace(tracer, testCtx(), params.TestChainConfig, nil); !strings.Contains(err.Error(), "stahp") {
|
if _, err = runTrace(tracer, testCtx(), chainConfig, nil); !strings.Contains(err.Error(), "stahp") {
|
||||||
t.Errorf("Expected timeout error, got %v", err)
|
t.Errorf("Expected timeout error, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHaltBetweenSteps(t *testing.T) {
|
func TestHaltBetweenSteps(t *testing.T) {
|
||||||
tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil, nil)
|
chainConfig := params.TestChainConfig
|
||||||
|
tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil, nil, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
scope := &vm.ScopeContext{
|
scope := &vm.ScopeContext{
|
||||||
Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0),
|
Contract: vm.NewContract(&account{}, &account{}, uint256.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.Hooks})
|
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
|
||||||
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
|
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.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0))
|
||||||
tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
|
tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
|
||||||
|
@ -206,11 +209,12 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||||
func TestNoStepExec(t *testing.T) {
|
func TestNoStepExec(t *testing.T) {
|
||||||
execTracer := func(code string) []byte {
|
execTracer := func(code string) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tracer, err := newJsTracer(code, nil, nil)
|
chainConfig := params.TestChainConfig
|
||||||
|
tracer, err := newJsTracer(code, nil, nil, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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.Hooks})
|
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks})
|
||||||
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
|
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.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0))
|
||||||
tracer.OnExit(0, nil, 0, nil, false)
|
tracer.OnExit(0, nil, 0, nil, false)
|
||||||
|
@ -241,7 +245,7 @@ func TestIsPrecompile(t *testing.T) {
|
||||||
chaincfg.IstanbulBlock = big.NewInt(200)
|
chaincfg.IstanbulBlock = big.NewInt(200)
|
||||||
chaincfg.BerlinBlock = big.NewInt(300)
|
chaincfg.BerlinBlock = big.NewInt(300)
|
||||||
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
|
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
|
||||||
tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil)
|
tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil, chaincfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -255,7 +259,7 @@ func TestIsPrecompile(t *testing.T) {
|
||||||
t.Errorf("tracer should not consider blake2f as precompile in byzantium")
|
t.Errorf("tracer should not consider blake2f as precompile in byzantium")
|
||||||
}
|
}
|
||||||
|
|
||||||
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil)
|
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil, chaincfg)
|
||||||
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
|
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
|
||||||
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg, nil)
|
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -267,15 +271,16 @@ func TestIsPrecompile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnterExit(t *testing.T) {
|
func TestEnterExit(t *testing.T) {
|
||||||
|
chainConfig := params.TestChainConfig
|
||||||
// test that either both or none of enter() and exit() are defined
|
// test that either both or none of enter() and exit() are defined
|
||||||
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil {
|
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil, chainConfig); err == nil {
|
||||||
t.Fatal("tracer creation should've failed without exit() definition")
|
t.Fatal("tracer creation should've failed without exit() definition")
|
||||||
}
|
}
|
||||||
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil {
|
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil, chainConfig); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// test that the enter and exit method are correctly invoked and the values passed
|
// test that the enter and exit method are correctly invoked and the values passed
|
||||||
tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil)
|
tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -297,7 +302,8 @@ func TestEnterExit(t *testing.T) {
|
||||||
|
|
||||||
func TestSetup(t *testing.T) {
|
func TestSetup(t *testing.T) {
|
||||||
// Test empty config
|
// Test empty config
|
||||||
_, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil)
|
chainConfig := params.TestChainConfig
|
||||||
|
_, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -307,12 +313,12 @@ func TestSetup(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Test no setup func
|
// Test no setup func
|
||||||
_, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg)
|
_, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Test config value
|
// Test config value
|
||||||
tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg)
|
tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package tracers
|
package tracers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -24,6 +40,9 @@ func (d *liveDirectory) Register(name string, f ctorFunc) {
|
||||||
|
|
||||||
// New instantiates a tracer by name.
|
// New instantiates a tracer by name.
|
||||||
func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) {
|
func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) {
|
||||||
|
if len(config) == 0 {
|
||||||
|
config = json.RawMessage("{}")
|
||||||
|
}
|
||||||
if f, ok := d.elems[name]; ok {
|
if f, ok := d.elems[name]; ok {
|
||||||
return f(config)
|
return f(config)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package live
|
package live
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package live
|
package live
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -19,7 +35,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tracers.LiveDirectory.Register("supply", newSupply)
|
tracers.LiveDirectory.Register("supply", newSupplyTracer)
|
||||||
}
|
}
|
||||||
|
|
||||||
type supplyInfoIssuance struct {
|
type supplyInfoIssuance struct {
|
||||||
|
@ -63,7 +79,7 @@ type supplyTxCallstack struct {
|
||||||
burn *big.Int
|
burn *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type supply struct {
|
type supplyTracer struct {
|
||||||
delta supplyInfo
|
delta supplyInfo
|
||||||
txCallstack []supplyTxCallstack // Callstack for current transaction
|
txCallstack []supplyTxCallstack // Callstack for current transaction
|
||||||
logger *lumberjack.Logger
|
logger *lumberjack.Logger
|
||||||
|
@ -74,12 +90,10 @@ type supplyTracerConfig struct {
|
||||||
MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes.
|
MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes.
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) {
|
func newSupplyTracer(cfg json.RawMessage) (*tracing.Hooks, error) {
|
||||||
var config supplyTracerConfig
|
var config supplyTracerConfig
|
||||||
if cfg != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
return nil, fmt.Errorf("failed to parse config: %v", err)
|
||||||
return nil, fmt.Errorf("failed to parse config: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if config.Path == "" {
|
if config.Path == "" {
|
||||||
return nil, errors.New("supply tracer output path is required")
|
return nil, errors.New("supply tracer output path is required")
|
||||||
|
@ -93,19 +107,19 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) {
|
||||||
logger.MaxSize = config.MaxSize
|
logger.MaxSize = config.MaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
t := &supply{
|
t := &supplyTracer{
|
||||||
delta: newSupplyInfo(),
|
delta: newSupplyInfo(),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
return &tracing.Hooks{
|
return &tracing.Hooks{
|
||||||
OnBlockStart: t.OnBlockStart,
|
OnBlockStart: t.onBlockStart,
|
||||||
OnBlockEnd: t.OnBlockEnd,
|
OnBlockEnd: t.onBlockEnd,
|
||||||
OnGenesisBlock: t.OnGenesisBlock,
|
OnGenesisBlock: t.onGenesisBlock,
|
||||||
OnTxStart: t.OnTxStart,
|
OnTxStart: t.onTxStart,
|
||||||
OnBalanceChange: t.OnBalanceChange,
|
OnBalanceChange: t.onBalanceChange,
|
||||||
OnEnter: t.OnEnter,
|
OnEnter: t.onEnter,
|
||||||
OnExit: t.OnExit,
|
OnExit: t.onExit,
|
||||||
OnClose: t.OnClose,
|
OnClose: t.onClose,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,11 +142,11 @@ func newSupplyInfo() supplyInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) resetDelta() {
|
func (s *supplyTracer) resetDelta() {
|
||||||
s.delta = newSupplyInfo()
|
s.delta = newSupplyInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnBlockStart(ev tracing.BlockEvent) {
|
func (s *supplyTracer) onBlockStart(ev tracing.BlockEvent) {
|
||||||
s.resetDelta()
|
s.resetDelta()
|
||||||
|
|
||||||
s.delta.Number = ev.Block.NumberU64()
|
s.delta.Number = ev.Block.NumberU64()
|
||||||
|
@ -155,11 +169,11 @@ func (s *supply) OnBlockStart(ev tracing.BlockEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnBlockEnd(err error) {
|
func (s *supplyTracer) onBlockEnd(err error) {
|
||||||
s.write(s.delta)
|
s.write(s.delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
|
func (s *supplyTracer) onGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
|
||||||
s.resetDelta()
|
s.resetDelta()
|
||||||
|
|
||||||
s.delta.Number = b.NumberU64()
|
s.delta.Number = b.NumberU64()
|
||||||
|
@ -174,7 +188,7 @@ func (s *supply) OnGenesisBlock(b *types.Block, alloc types.GenesisAlloc) {
|
||||||
s.write(s.delta)
|
s.write(s.delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) {
|
func (s *supplyTracer) onBalanceChange(a common.Address, prevBalance, newBalance *big.Int, reason tracing.BalanceChangeReason) {
|
||||||
diff := new(big.Int).Sub(newBalance, prevBalance)
|
diff := new(big.Int).Sub(newBalance, prevBalance)
|
||||||
|
|
||||||
// NOTE: don't handle "BalanceIncreaseGenesisBalance" because it is handled in OnGenesisBlock
|
// NOTE: don't handle "BalanceIncreaseGenesisBalance" because it is handled in OnGenesisBlock
|
||||||
|
@ -193,12 +207,12 @@ func (s *supply) OnBalanceChange(a common.Address, prevBalance, newBalance *big.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
func (s *supplyTracer) onTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
s.txCallstack = make([]supplyTxCallstack, 0, 1)
|
s.txCallstack = make([]supplyTxCallstack, 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// internalTxsHandler handles internal transactions burned amount
|
// internalTxsHandler handles internal transactions burned amount
|
||||||
func (s *supply) internalTxsHandler(call *supplyTxCallstack) {
|
func (s *supplyTracer) internalTxsHandler(call *supplyTxCallstack) {
|
||||||
// Handle Burned amount
|
// Handle Burned amount
|
||||||
if call.burn != nil {
|
if call.burn != nil {
|
||||||
s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn)
|
s.delta.Burn.Misc.Add(s.delta.Burn.Misc, call.burn)
|
||||||
|
@ -211,7 +225,7 @@ func (s *supply) internalTxsHandler(call *supplyTxCallstack) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (s *supplyTracer) onEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
call := supplyTxCallstack{
|
call := supplyTxCallstack{
|
||||||
calls: make([]supplyTxCallstack, 0),
|
calls: make([]supplyTxCallstack, 0),
|
||||||
}
|
}
|
||||||
|
@ -226,7 +240,7 @@ func (s *supply) OnEnter(depth int, typ byte, from common.Address, to common.Add
|
||||||
s.txCallstack = append(s.txCallstack, call)
|
s.txCallstack = append(s.txCallstack, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
func (s *supplyTracer) onExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
|
||||||
if depth == 0 {
|
if depth == 0 {
|
||||||
// No need to handle Burned amount if transaction is reverted
|
// No need to handle Burned amount if transaction is reverted
|
||||||
if !reverted {
|
if !reverted {
|
||||||
|
@ -252,13 +266,13 @@ func (s *supply) OnExit(depth int, output []byte, gasUsed uint64, err error, rev
|
||||||
s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call)
|
s.txCallstack[size-1].calls = append(s.txCallstack[size-1].calls, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) OnClose() {
|
func (s *supplyTracer) onClose() {
|
||||||
if err := s.logger.Close(); err != nil {
|
if err := s.logger.Close(); err != nil {
|
||||||
log.Warn("failed to close supply tracer log file", "error", err)
|
log.Warn("failed to close supply tracer log file", "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *supply) write(data any) {
|
func (s *supplyTracer) write(data any) {
|
||||||
supply, ok := data.(supplyInfo)
|
supply, ok := data.(supplyInfo)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warn("failed to cast supply tracer data on write to log file")
|
log.Warn("failed to cast supply tracer data on write to log file")
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -48,17 +49,19 @@ func init() {
|
||||||
// 0xc281d19e-0: 1
|
// 0xc281d19e-0: 1
|
||||||
// }
|
// }
|
||||||
type fourByteTracer struct {
|
type fourByteTracer struct {
|
||||||
ids map[string]int // ids aggregates the 4byte ids found
|
ids map[string]int // ids aggregates the 4byte ids found
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
activePrecompiles []common.Address // Updated on tx start based on given rules
|
activePrecompiles []common.Address // Updated on tx start based on given rules
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFourByteTracer returns a native go tracer which collects
|
// newFourByteTracer returns a native go tracer which collects
|
||||||
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
||||||
func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) {
|
func newFourByteTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
t := &fourByteTracer{
|
t := &fourByteTracer{
|
||||||
ids: make(map[string]int),
|
ids: make(map[string]int),
|
||||||
|
chainConfig: chainConfig,
|
||||||
}
|
}
|
||||||
return &tracers.Tracer{
|
return &tracers.Tracer{
|
||||||
Hooks: &tracing.Hooks{
|
Hooks: &tracing.Hooks{
|
||||||
|
@ -88,7 +91,7 @@ func (t *fourByteTracer) store(id []byte, size int) {
|
||||||
|
|
||||||
func (t *fourByteTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
func (t *fourByteTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
|
||||||
// Update list of precompiles based on current block
|
// Update list of precompiles based on current block
|
||||||
rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
rules := t.chainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
||||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
|
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
|
||||||
|
@ -125,7 +126,7 @@ type callTracerConfig struct {
|
||||||
|
|
||||||
// newCallTracer returns a native go tracer which tracks
|
// newCallTracer returns a native go tracer which tracks
|
||||||
// call frames of a tx, and implements vm.EVMLogger.
|
// call frames of a tx, and implements vm.EVMLogger.
|
||||||
func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
func newCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
t, err := newCallTracerObject(ctx, cfg)
|
t, err := newCallTracerObject(ctx, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -145,10 +146,8 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer,
|
||||||
|
|
||||||
func newCallTracerObject(ctx *tracers.Context, cfg json.RawMessage) (*callTracer, error) {
|
func newCallTracerObject(ctx *tracers.Context, cfg json.RawMessage) (*callTracer, error) {
|
||||||
var config callTracerConfig
|
var config callTracerConfig
|
||||||
if cfg != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// First callframe contains tx context info
|
// First callframe contains tx context info
|
||||||
// and is populated on start and end.
|
// and is populated on start and end.
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go
|
//go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go
|
||||||
|
@ -114,6 +115,7 @@ type flatCallResultMarshaling struct {
|
||||||
type flatCallTracer struct {
|
type flatCallTracer struct {
|
||||||
tracer *callTracer
|
tracer *callTracer
|
||||||
config flatCallTracerConfig
|
config flatCallTracerConfig
|
||||||
|
chainConfig *params.ChainConfig
|
||||||
ctx *tracers.Context // Holds tracer context data
|
ctx *tracers.Context // Holds tracer context data
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
activePrecompiles []common.Address // Updated on tx start based on given rules
|
activePrecompiles []common.Address // Updated on tx start based on given rules
|
||||||
|
@ -125,22 +127,20 @@ type flatCallTracerConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFlatCallTracer returns a new flatCallTracer.
|
// newFlatCallTracer returns a new flatCallTracer.
|
||||||
func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
var config flatCallTracerConfig
|
var config flatCallTracerConfig
|
||||||
if cfg != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create inner call tracer with default configuration, don't forward
|
// Create inner call tracer with default configuration, don't forward
|
||||||
// the OnlyTopCall or WithLog to inner for now
|
// the OnlyTopCall or WithLog to inner for now
|
||||||
t, err := newCallTracerObject(ctx, nil)
|
t, err := newCallTracerObject(ctx, json.RawMessage("{}"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ft := &flatCallTracer{tracer: t, ctx: ctx, config: config}
|
ft := &flatCallTracer{tracer: t, ctx: ctx, config: config, chainConfig: chainConfig}
|
||||||
return &tracers.Tracer{
|
return &tracers.Tracer{
|
||||||
Hooks: &tracing.Hooks{
|
Hooks: &tracing.Hooks{
|
||||||
OnTxStart: ft.OnTxStart,
|
OnTxStart: ft.OnTxStart,
|
||||||
|
@ -206,7 +206,7 @@ func (t *flatCallTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction
|
||||||
}
|
}
|
||||||
t.tracer.OnTxStart(env, tx, from)
|
t.tracer.OnTxStart(env, tx, from)
|
||||||
// Update list of precompiles based on current block
|
// Update list of precompiles based on current block
|
||||||
rules := env.ChainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
rules := t.chainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time)
|
||||||
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
t.activePrecompiles = vm.ActivePrecompiles(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCallFlatStop(t *testing.T) {
|
func TestCallFlatStop(t *testing.T) {
|
||||||
tracer, err := tracers.DefaultDirectory.New("flatCallTracer", &tracers.Context{}, nil)
|
tracer, err := tracers.DefaultDirectory.New("flatCallTracer", &tracers.Context{}, nil, params.MainnetChainConfig)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// this error should be returned by GetResult
|
// this error should be returned by GetResult
|
||||||
|
@ -47,9 +47,7 @@ func TestCallFlatStop(t *testing.T) {
|
||||||
Data: nil,
|
Data: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tracer.OnTxStart(&tracing.VMContext{
|
tracer.OnTxStart(&tracing.VMContext{}, tx, common.Address{})
|
||||||
ChainConfig: params.MainnetChainConfig,
|
|
||||||
}, tx, common.Address{})
|
|
||||||
|
|
||||||
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, nil, 0, big.NewInt(0))
|
tracer.OnEnter(0, byte(vm.CALL), common.Address{}, common.Address{}, nil, 0, big.NewInt(0))
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -38,17 +39,15 @@ type muxTracer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMuxTracer returns a new mux tracer.
|
// newMuxTracer returns a new mux tracer.
|
||||||
func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
var config map[string]json.RawMessage
|
var config map[string]json.RawMessage
|
||||||
if cfg != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
objects := make([]*tracers.Tracer, 0, len(config))
|
objects := make([]*tracers.Tracer, 0, len(config))
|
||||||
names := make([]string, 0, len(config))
|
names := make([]string, 0, len(config))
|
||||||
for k, v := range config {
|
for k, v := range config {
|
||||||
t, err := tracers.DefaultDirectory.New(k, ctx, v)
|
t, err := tracers.DefaultDirectory.New(k, ctx, v, chainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -35,7 +36,7 @@ func init() {
|
||||||
type noopTracer struct{}
|
type noopTracer struct{}
|
||||||
|
|
||||||
// newNoopTracer returns a new noop tracer.
|
// newNoopTracer returns a new noop tracer.
|
||||||
func newNoopTracer(ctx *tracers.Context, _ json.RawMessage) (*tracers.Tracer, error) {
|
func newNoopTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
t := &noopTracer{}
|
t := &noopTracer{}
|
||||||
return &tracers.Tracer{
|
return &tracers.Tracer{
|
||||||
Hooks: &tracing.Hooks{
|
Hooks: &tracing.Hooks{
|
||||||
|
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers/internal"
|
"github.com/ethereum/go-ethereum/eth/tracers/internal"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
|
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
|
||||||
|
@ -74,12 +75,10 @@ type prestateTracerConfig struct {
|
||||||
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (*tracers.Tracer, error) {
|
func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
|
||||||
var config prestateTracerConfig
|
var config prestateTracerConfig
|
||||||
if cfg != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t := &prestateTracer{
|
t := &prestateTracer{
|
||||||
pre: stateMap{},
|
pre: stateMap{},
|
||||||
|
|
Loading…
Reference in New Issue