Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eth/tracers: implement trace live filter #30255

Draft
wants to merge 50 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
3a9b93e
core: bc.logger is duplicate with bc.vmconfig.tracer, use function in…
jsvisa Aug 10, 2024
770ac7e
core: set logger after inited
jsvisa Aug 10, 2024
5c88fb1
cmd: set tracing logger after blockchain inited
jsvisa Aug 10, 2024
a2360d6
eth: set live tracer after inited
jsvisa Aug 10, 2024
d75c962
cmd,eth: register live tracer with Backend interface
jsvisa Aug 10, 2024
f76f25f
fix test
jsvisa Aug 12, 2024
4881b88
eth/tracers: live tracer return apis
jsvisa Aug 12, 2024
ecc2a37
cmd: register live tracer apis
jsvisa Aug 12, 2024
e819b13
Revert "cmd: register live tracer apis"
jsvisa Aug 22, 2024
efe8364
Revert "eth/tracers: live tracer return apis"
jsvisa Aug 22, 2024
560f2dc
Revert "fix test"
jsvisa Aug 22, 2024
61ac985
Revert "cmd,eth: register live tracer with Backend interface"
jsvisa Aug 22, 2024
0daacd3
Revert "eth: set live tracer after inited"
jsvisa Aug 22, 2024
7eec558
Revert "cmd: set tracing logger after blockchain inited"
jsvisa Aug 22, 2024
0f9fe5f
Revert "core: set logger after inited"
jsvisa Aug 22, 2024
efbdfbb
eth,cmd: set tracer's backend after eth initalized
jsvisa Aug 22, 2024
561c43d
eth/tracers: pass stack into live tracer
jsvisa Sep 25, 2024
9ae1444
pass eth.Backend into live tracer
jsvisa Sep 25, 2024
d5632f5
eth/tracers: declare muxTracer as public
jsvisa Jul 28, 2024
68b63d4
eth/tracers: read from kvdb directly
jsvisa Aug 12, 2024
3e598cb
freeze into frdb
jsvisa Aug 13, 2024
9781126
batch delete kvdb
jsvisa Aug 13, 2024
cbd1e10
feat(filter): implement a simple rlp.Encode/Decoder
jsvisa Aug 17, 2024
1aac054
fix(callflat): set ctx in live mode
jsvisa Aug 19, 2024
b759f84
check given tracer
jsvisa Aug 19, 2024
efa411f
native/callflat: register parity tracer
jsvisa Aug 19, 2024
c1f6d4e
special for parity traces
jsvisa Aug 19, 2024
53f1a9e
Revert "special for parity traces"
jsvisa Aug 19, 2024
b6dadd5
add prestate, flatcall, parity tracers
jsvisa Aug 19, 2024
8ba2c3d
return parity without txhash
jsvisa Aug 19, 2024
8750e2d
native/flat: always set context from receipt
jsvisa Aug 20, 2024
d358f39
Update eth/tracers/live/filter.go
jsvisa Aug 26, 2024
0c6e9a2
freeze: head is not used
jsvisa Aug 20, 2024
b62bf22
trace_tx
jsvisa Aug 21, 2024
f08d9a0
live/filter: register api inner
jsvisa Sep 27, 2024
92a0bc0
eth/tracers: nopersist txhash and flatten parity trace
jsvisa Sep 27, 2024
b002b3f
trace: implement trace_filter
jsvisa Sep 27, 2024
1d46050
live/filter: encode number
jsvisa Sep 27, 2024
be482ae
live: add testcase
jsvisa Sep 27, 2024
7fabd0a
internal/ethapi: make NewRPCTransaction as public
jsvisa Sep 27, 2024
5bb32c5
add nonce live tracer
jsvisa Sep 27, 2024
f4114d4
eth/tracers/live: rename filter to live
jsvisa Sep 27, 2024
d638fd5
enable nonce tracer
jsvisa Sep 27, 2024
c48a298
live: implement maxKeepBlocks
jsvisa Sep 28, 2024
186d6d8
eth/traces/native: adapt prestate for live tracing
jsvisa Sep 28, 2024
3cbadcd
trace/live: filter with address
jsvisa Oct 3, 2024
acdf44b
core/txpool: get tx by sender and nonce
jsvisa Oct 3, 2024
7c1d246
core/tracing: GetPoolTransactionBySenderAndNonce
jsvisa Oct 3, 2024
36c98a3
trace: retrieve from txpool
jsvisa Oct 3, 2024
c8dfa31
live: skip if freeze failed
jsvisa Oct 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2192,7 +2192,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
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, stack, nil)
if err != nil {
Fatalf("Failed to create tracer %q: %v", name, err)
}
Expand Down
55 changes: 31 additions & 24 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ type BlockChain struct {
prefetcher Prefetcher
processor Processor // Block transaction processor interface
vmConfig vm.Config
logger *tracing.Hooks
}

// NewBlockChain returns a fully initialised block chain using information
Expand Down Expand Up @@ -301,7 +300,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
engine: engine,
vmConfig: vmConfig,
logger: vmConfig.Tracer,
}
var err error
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
Expand Down Expand Up @@ -411,10 +409,11 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
// it in advance.
bc.engine.VerifyHeader(bc, bc.CurrentHeader())

if bc.logger != nil && bc.logger.OnBlockchainInit != nil {
bc.logger.OnBlockchainInit(chainConfig)
logger := bc.logger()
if logger != nil && logger.OnBlockchainInit != nil {
logger.OnBlockchainInit(chainConfig)
}
if bc.logger != nil && bc.logger.OnGenesisBlock != nil {
if logger != nil && logger.OnGenesisBlock != nil {
if block := bc.CurrentBlock(); block.Number.Uint64() == 0 {
alloc, err := getGenesisState(bc.db, block.Hash())
if err != nil {
Expand All @@ -423,7 +422,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
if alloc == nil {
return nil, errors.New("live blockchain tracer requires genesis alloc to be set")
}
bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
logger.OnGenesisBlock(bc.genesisBlock, alloc)
}
}

Expand Down Expand Up @@ -1156,8 +1155,8 @@ func (bc *BlockChain) Stop() {
}
}
// Allow tracers to clean-up and release resources.
if bc.logger != nil && bc.logger.OnClose != nil {
bc.logger.OnClose()
if logger := bc.logger(); logger != nil && logger.OnClose != nil {
logger.OnClose()
}
// Close the trie database, release all the held resources as the last step.
if err := bc.triedb.Close(); err != nil {
Expand Down Expand Up @@ -1714,6 +1713,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
// Track the singleton witness from this chain insertion (if any)
var witness *stateless.Witness

bclogger := bc.logger()
for ; block != nil && err == nil || errors.Is(err, ErrKnownBlock); block, err = it.next() {
// If the chain is terminating, stop processing blocks
if bc.insertStopped() {
Expand Down Expand Up @@ -1753,8 +1753,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
return nil, it.index, err
}
stats.processed++
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
bc.logger.OnSkippedBlock(tracing.BlockEvent{
if bclogger != nil && bclogger.OnSkippedBlock != nil {
bclogger.OnSkippedBlock(tracing.BlockEvent{
Block: block,
TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1),
Finalized: bc.CurrentFinalBlock(),
Expand All @@ -1776,7 +1776,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
if err != nil {
return nil, it.index, err
}
statedb.SetLogger(bc.logger)
statedb.SetLogger(bclogger)

// If we are past Byzantium, enable prefetching to pull in trie node paths
// while processing transactions. Before Byzantium the prefetcher is mostly
Expand Down Expand Up @@ -1881,19 +1881,21 @@ 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 && bc.logger.OnBlockStart != nil {
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
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)
}()
if logger := bc.logger(); logger != nil {
if logger.OnBlockStart != nil {
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
logger.OnBlockStart(tracing.BlockEvent{
Block: block,
TD: td,
Finalized: bc.CurrentFinalBlock(),
Safe: bc.CurrentSafeBlock(),
})
}
if logger.OnBlockEnd != nil {
defer func() {
logger.OnBlockEnd(blockEndErr)
}()
}
}

// Process block using the parent state as reference point
Expand Down Expand Up @@ -2537,3 +2539,8 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load())
}

// logger returns the tracing logger
func (bc *BlockChain) logger() *tracing.Hooks {
return bc.vmConfig.Tracer
}
21 changes: 21 additions & 0 deletions core/tracing/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
package tracing

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256"
)

Expand Down Expand Up @@ -68,6 +70,19 @@ type BlockEvent struct {
Safe *types.Header
}

// Backend interface provides the common API services (that are provided by
// both full and light clients) with access to necessary functions.
type Backend interface {
CurrentHeader() *types.Header
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
GetPoolTransactionBySenderAndNonce(sender common.Address, nonce uint64) *types.Transaction
ChainConfig() *params.ChainConfig
}

type (
/*
- VM events -
Expand Down Expand Up @@ -171,6 +186,7 @@ type (
)

type Hooks struct {
Backend Backend
// VM events
OnTxStart TxStartHook
OnTxEnd TxEndHook
Expand All @@ -196,6 +212,11 @@ type Hooks struct {
OnLog LogHook
}

// SetBackend sets a backend to the hooks, use this backend to read chain data.
func (h *Hooks) SetBackend(backend Backend) {
h.Backend = backend
}

// BalanceChangeReason is used to indicate the reason for a balance change, useful
// for tracing and reporting.
type BalanceChangeReason byte
Expand Down
20 changes: 20 additions & 0 deletions core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,26 @@ func (p *BlobPool) Get(hash common.Hash) *types.Transaction {
return item
}

// GetBySenderAndNonce returns a transaction of a sender and its corresponding nonce.
func (p *BlobPool) GetBySenderAndNonce(sender common.Address, nonce uint64) *types.Transaction {
p.lock.RLock()
defer p.lock.RUnlock()

txs, ok := p.index[sender]
if !ok {
return nil
}

next := p.state.GetNonce(sender)
offset := int(nonce - next)

if offset < 0 || offset >= len(txs) {
return nil
}

return p.Get(txs[offset].hash)
}

// Add inserts a set of blob transactions into the pool if they pass validation (both
// consensus validity and pool restrictions).
func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
Expand Down
19 changes: 19 additions & 0 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,25 @@ func (pool *LegacyPool) Get(hash common.Hash) *types.Transaction {
return tx
}

// GetBySenderAndNonce returns a transaction of a sender and it's corresponding nonce.
func (pool *LegacyPool) GetBySenderAndNonce(sender common.Address, nonce uint64) *types.Transaction {
// Check if the transaction is in the pending pool
if txList := pool.pending[sender]; txList != nil {
if tx := txList.txs.items[nonce]; tx != nil {
return tx
}
}

// Check if the transaction is in the queue pool
if txList := pool.queue[sender]; txList != nil {
if tx := txList.txs.items[nonce]; tx != nil {
return tx
}
}

return nil
}

// get returns a transaction if it is contained in the pool and nil otherwise.
func (pool *LegacyPool) get(hash common.Hash) *types.Transaction {
return pool.all.Get(hash)
Expand Down
3 changes: 3 additions & 0 deletions core/txpool/subpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ type SubPool interface {
// Get returns a transaction if it is contained in the pool, or nil otherwise.
Get(hash common.Hash) *types.Transaction

// GetBySenderAndNonce returns a transaction of a sender and it's corresponding nonce.
GetBySenderAndNonce(sender common.Address, nonce uint64) *types.Transaction

// Add enqueues a batch of transactions into the pool if they are valid. Due
// to the large transaction churn, add may postpone fully integrating the tx
// to a later point to batch multiple ones together.
Expand Down
10 changes: 10 additions & 0 deletions core/txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,16 @@ func (p *TxPool) Get(hash common.Hash) *types.Transaction {
return nil
}

// GetBySenderAndNonce returns a transaction of a sender and it's corresponding nonce.
func (p *TxPool) GetBySenderAndNonce(sender common.Address, nonce uint64) *types.Transaction {
for _, subpool := range p.subpools {
if tx := subpool.GetBySenderAndNonce(sender, nonce); tx != nil {
return tx
}
}
return nil
}

// Add enqueues a batch of transactions into the pool if they are valid. Due
// to the large transaction churn, add may postpone fully integrating the tx
// to a later point to batch multiple ones together.
Expand Down
5 changes: 5 additions & 0 deletions eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
return b.eth.txPool.Get(hash)
}

// GetPoolTransactionBySenderAndNonce retrieves the pool transaction by the sender and it's nonce
func (b *EthAPIBackend) GetPoolTransactionBySenderAndNonce(sender common.Address, nonce uint64) *types.Transaction {
return b.eth.txPool.GetBySenderAndNonce(sender, nonce)
}

// GetTransaction retrieves the lookup along with the transaction itself associate
// with the given transaction hash.
//
Expand Down
13 changes: 8 additions & 5 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
}
}

eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
if eth.APIBackend.allowUnprotectedTxs {
log.Info("Unprotected transactions allowed")
}

var (
vmConfig = vm.Config{
EnablePreimageRecording: config.EnablePreimageRecording,
Expand All @@ -201,7 +207,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if config.VMTraceJsonConfig != "" {
traceConfig = json.RawMessage(config.VMTraceJsonConfig)
}
t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig)
t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig, stack, eth.APIBackend)
if err != nil {
return nil, fmt.Errorf("failed to create tracer %s: %v", config.VMTrace, err)
}
Expand Down Expand Up @@ -254,10 +260,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
eth.miner = miner.New(eth, config.Miner, eth.engine)
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))

eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil}
if eth.APIBackend.allowUnprotectedTxs {
log.Info("Unprotected transactions allowed")
}
// Start the gas price oracle after blockchain is fully loaded
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice)

// Start the RPC service
Expand Down
2 changes: 1 addition & 1 deletion eth/tracers/internal/tracetest/supply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG
traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl")

// Load supply tracer
tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath)))
tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath)), nil, nil)
if err != nil {
return nil, nil, fmt.Errorf("failed to create call tracer: %v", err)
}
Expand Down
12 changes: 9 additions & 3 deletions eth/tracers/live.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import (
"errors"

"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/rpc"
)

type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error)
// LiveApiRegister is the interface that used to register JSON-RPC APIs
type LiveApiRegister interface {
RegisterAPIs(apis []rpc.API)
}

type ctorFunc func(config json.RawMessage, stack LiveApiRegister, backend tracing.Backend) (*tracing.Hooks, error)

// LiveDirectory is the collection of tracers which can be used
// during normal block import operations.
Expand All @@ -23,9 +29,9 @@ func (d *liveDirectory) Register(name string, f ctorFunc) {
}

// 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, stack LiveApiRegister, backend tracing.Backend) (*tracing.Hooks, error) {
if f, ok := d.elems[name]; ok {
return f(config)
return f(config, stack, backend)
}
return nil, errors.New("not found")
}
Loading