Skip to content

Commit 4878aff

Browse files
s1namaouehjsvisaholiman
authored andcommitted
eth/tracers: live chain tracing with hooks (ethereum#29189)
Here we add a Go API for running tracing plugins within the main block import process. As an advanced user of geth, you can now create a Go file in eth/tracers/live/, and within that file register your custom tracer implementation. Then recompile geth and select your tracer on the command line. Hooks defined in the tracer will run whenever a block is processed. The hook system is defined in package core/tracing. It uses a struct with callbacks, instead of requiring an interface, for several reasons: - We plan to keep this API stable long-term. The core/tracing hook API does not depend on on deep geth internals. - There are a lot of hooks, and tracers will only need some of them. Using a struct allows you to implement only the hooks you want to actually use. All existing tracers in eth/tracers/native have been rewritten to use the new hook system. This change breaks compatibility with the vm.EVMLogger interface that we used to have. If you are a user of vm.EVMLogger, please migrate to core/tracing, and sorry for breaking your stuff. But we just couldn't have both the old and new tracing APIs coexist in the EVM. --------- Co-authored-by: Matthieu Vachon <matthieu.o.vachon@gmail.com> Co-authored-by: Delweng <delweng@gmail.com> Co-authored-by: Martin HS <martin@swende.se>
1 parent 6c85fbe commit 4878aff

File tree

95 files changed

+2776
-1237
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+2776
-1237
lines changed

cmd/evm/blockrunner.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626

2727
"github.com/ethereum/go-ethereum/core"
2828
"github.com/ethereum/go-ethereum/core/rawdb"
29-
"github.com/ethereum/go-ethereum/core/vm"
29+
"github.com/ethereum/go-ethereum/core/tracing"
3030
"github.com/ethereum/go-ethereum/eth/tracers/logger"
3131
"github.com/ethereum/go-ethereum/tests"
3232
"github.com/urfave/cli/v2"
@@ -51,7 +51,7 @@ func blockTestCmd(ctx *cli.Context) error {
5151
return errors.New("path-to-test argument required")
5252
}
5353

54-
var tracer vm.EVMLogger
54+
var tracer *tracing.Hooks
5555
// Configure the EVM logger
5656
if ctx.Bool(MachineFlag.Name) {
5757
tracer = logger.NewJSONLogger(&logger.Config{

cmd/evm/internal/t8ntool/execution.go

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package t8ntool
1818

1919
import (
20+
"encoding/json"
2021
"fmt"
22+
"io"
2123
"math/big"
2224

2325
"github.com/ethereum/go-ethereum/common"
@@ -28,9 +30,11 @@ import (
2830
"github.com/ethereum/go-ethereum/core"
2931
"github.com/ethereum/go-ethereum/core/rawdb"
3032
"github.com/ethereum/go-ethereum/core/state"
33+
"github.com/ethereum/go-ethereum/core/tracing"
3134
"github.com/ethereum/go-ethereum/core/types"
3235
"github.com/ethereum/go-ethereum/core/vm"
3336
"github.com/ethereum/go-ethereum/crypto"
37+
"github.com/ethereum/go-ethereum/eth/tracers"
3438
"github.com/ethereum/go-ethereum/ethdb"
3539
"github.com/ethereum/go-ethereum/log"
3640
"github.com/ethereum/go-ethereum/params"
@@ -119,7 +123,7 @@ type rejectedTx struct {
119123
// Apply applies a set of transactions to a pre-state
120124
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
121125
txIt txIterator, miningReward int64,
122-
getTracerFn func(txIndex int, txHash common.Hash) (vm.EVMLogger, error)) (*state.StateDB, *ExecutionResult, []byte, error) {
126+
getTracerFn func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error)) (*state.StateDB, *ExecutionResult, []byte, error) {
123127
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
124128
// required blockhashes
125129
var hashError error
@@ -222,11 +226,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
222226
continue
223227
}
224228
}
225-
tracer, err := getTracerFn(txIndex, tx.Hash())
229+
tracer, traceOutput, err := getTracerFn(txIndex, tx.Hash())
226230
if err != nil {
227231
return nil, nil, nil, err
228232
}
229-
vmConfig.Tracer = tracer
233+
if tracer != nil {
234+
vmConfig.Tracer = tracer.Hooks
235+
}
230236
statedb.SetTxContext(tx.Hash(), txIndex)
231237

232238
var (
@@ -236,13 +242,24 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
236242
)
237243
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
238244

245+
if tracer != nil && tracer.OnTxStart != nil {
246+
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
247+
}
239248
// (ret []byte, usedGas uint64, failed bool, err error)
240249
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
241250
if err != nil {
242251
statedb.RevertToSnapshot(snapshot)
243252
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
244253
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
245254
gaspool.SetGas(prevGas)
255+
if tracer != nil {
256+
if tracer.OnTxEnd != nil {
257+
tracer.OnTxEnd(nil, err)
258+
}
259+
if err := writeTraceResult(tracer, traceOutput); err != nil {
260+
log.Warn("Error writing tracer output", "err", err)
261+
}
262+
}
246263
continue
247264
}
248265
includedTxs = append(includedTxs, tx)
@@ -285,6 +302,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
285302
//receipt.BlockNumber
286303
receipt.TransactionIndex = uint(txIndex)
287304
receipts = append(receipts, receipt)
305+
if tracer != nil {
306+
if tracer.Hooks.OnTxEnd != nil {
307+
tracer.Hooks.OnTxEnd(receipt, nil)
308+
}
309+
writeTraceResult(tracer, traceOutput)
310+
}
288311
}
289312

290313
txIndex++
@@ -310,15 +333,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
310333
reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta))
311334
reward.Mul(reward, blockReward)
312335
reward.Div(reward, big.NewInt(8))
313-
statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward))
336+
statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward), tracing.BalanceIncreaseRewardMineUncle)
314337
}
315-
statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward))
338+
statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward), tracing.BalanceIncreaseRewardMineBlock)
316339
}
317340
// Apply withdrawals
318341
for _, w := range pre.Env.Withdrawals {
319342
// Amount is in gwei, turn into wei
320343
amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
321-
statedb.AddBalance(w.Address, uint256.MustFromBig(amount))
344+
statedb.AddBalance(w.Address, uint256.MustFromBig(amount), tracing.BalanceIncreaseWithdrawal)
322345
}
323346
// Commit block
324347
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
@@ -361,7 +384,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
361384
for addr, a := range accounts {
362385
statedb.SetCode(addr, a.Code)
363386
statedb.SetNonce(addr, a.Nonce)
364-
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance))
387+
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance)
365388
for k, v := range a.Storage {
366389
statedb.SetState(addr, k, v)
367390
}
@@ -398,3 +421,16 @@ func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime
398421
}
399422
return ethash.CalcDifficulty(config, currentTime, parent)
400423
}
424+
425+
func writeTraceResult(tracer *tracers.Tracer, f io.WriteCloser) error {
426+
defer f.Close()
427+
result, err := tracer.GetResult()
428+
if err != nil || result == nil {
429+
return err
430+
}
431+
err = json.NewEncoder(f).Encode(result)
432+
if err != nil {
433+
return err
434+
}
435+
return nil
436+
}

cmd/evm/internal/t8ntool/tracewriter.go

Lines changed: 0 additions & 81 deletions
This file was deleted.

cmd/evm/internal/t8ntool/transition.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"encoding/json"
2121
"errors"
2222
"fmt"
23+
"io"
2324
"math/big"
2425
"os"
2526
"path/filepath"
@@ -80,7 +81,7 @@ type input struct {
8081
}
8182

8283
func Transition(ctx *cli.Context) error {
83-
var getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) { return nil, nil }
84+
var getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) { return nil, nil, nil }
8485

8586
baseDir, err := createBasedir(ctx)
8687
if err != nil {
@@ -95,28 +96,35 @@ func Transition(ctx *cli.Context) error {
9596
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
9697
Debug: true,
9798
}
98-
getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) {
99+
getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
99100
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
100101
if err != nil {
101-
return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
102+
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
102103
}
103-
return &traceWriter{logger.NewJSONLogger(logConfig, traceFile), traceFile}, nil
104+
logger := logger.NewJSONLogger(logConfig, traceFile)
105+
tracer := &tracers.Tracer{
106+
Hooks: logger,
107+
// jsonLogger streams out result to file.
108+
GetResult: func() (json.RawMessage, error) { return nil, nil },
109+
Stop: func(err error) {},
110+
}
111+
return tracer, traceFile, nil
104112
}
105113
} else if ctx.IsSet(TraceTracerFlag.Name) {
106114
var config json.RawMessage
107115
if ctx.IsSet(TraceTracerConfigFlag.Name) {
108116
config = []byte(ctx.String(TraceTracerConfigFlag.Name))
109117
}
110-
getTracer = func(txIndex int, txHash common.Hash) (vm.EVMLogger, error) {
118+
getTracer = func(txIndex int, txHash common.Hash) (*tracers.Tracer, io.WriteCloser, error) {
111119
traceFile, err := os.Create(filepath.Join(baseDir, fmt.Sprintf("trace-%d-%v.json", txIndex, txHash.String())))
112120
if err != nil {
113-
return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
121+
return nil, nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
114122
}
115123
tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name), nil, config)
116124
if err != nil {
117-
return nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
125+
return nil, nil, NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %w", err))
118126
}
119-
return &traceWriter{tracer, traceFile}, nil
127+
return tracer, traceFile, nil
120128
}
121129
}
122130
// We need to load three things: alloc, env and transactions. May be either in

cmd/evm/runner.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/ethereum/go-ethereum/core"
3434
"github.com/ethereum/go-ethereum/core/rawdb"
3535
"github.com/ethereum/go-ethereum/core/state"
36+
"github.com/ethereum/go-ethereum/core/tracing"
3637
"github.com/ethereum/go-ethereum/core/vm"
3738
"github.com/ethereum/go-ethereum/core/vm/runtime"
3839
"github.com/ethereum/go-ethereum/eth/tracers/logger"
@@ -116,7 +117,7 @@ func runCmd(ctx *cli.Context) error {
116117
}
117118

118119
var (
119-
tracer vm.EVMLogger
120+
tracer *tracing.Hooks
120121
debugLogger *logger.StructLogger
121122
statedb *state.StateDB
122123
chainConfig *params.ChainConfig
@@ -130,7 +131,7 @@ func runCmd(ctx *cli.Context) error {
130131
tracer = logger.NewJSONLogger(logconfig, os.Stdout)
131132
} else if ctx.Bool(DebugFlag.Name) {
132133
debugLogger = logger.NewStructLogger(logconfig)
133-
tracer = debugLogger
134+
tracer = debugLogger.Hooks()
134135
} else {
135136
debugLogger = logger.NewStructLogger(logconfig)
136137
}

cmd/evm/staterunner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func stateTestCmd(ctx *cli.Context) error {
6363
cfg.Tracer = logger.NewJSONLogger(config, os.Stderr)
6464

6565
case ctx.Bool(DebugFlag.Name):
66-
cfg.Tracer = logger.NewStructLogger(config)
66+
cfg.Tracer = logger.NewStructLogger(config).Hooks()
6767
}
6868
// Load the test content from the input file
6969
if len(ctx.Args().First()) != 0 {

0 commit comments

Comments
 (0)