From f4114d40472982dc28d9b3d2a3189de7c65e36a3 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 27 Sep 2024 13:57:07 +0000 Subject: [PATCH] eth/tracers/live: rename filter to live Signed-off-by: jsvisa --- eth/tracers/live/{filter.go => live.go} | 61 +++++----- eth/tracers/live/live_api_eth.go | 40 +++++++ .../live/{filter_api.go => live_api_trace.go} | 68 +++-------- .../live/{filter_freeze.go => live_freeze.go} | 10 +- eth/tracers/live/nonce.go | 108 ------------------ eth/tracers/live/utils.go | 45 ++++++++ .../{filter_api_test.go => utils_test.go} | 0 7 files changed, 138 insertions(+), 194 deletions(-) rename eth/tracers/live/{filter.go => live.go} (82%) create mode 100644 eth/tracers/live/live_api_eth.go rename eth/tracers/live/{filter_api.go => live_api_trace.go} (66%) rename eth/tracers/live/{filter_freeze.go => live_freeze.go} (92%) delete mode 100644 eth/tracers/live/nonce.go create mode 100644 eth/tracers/live/utils.go rename eth/tracers/live/{filter_api_test.go => utils_test.go} (100%) diff --git a/eth/tracers/live/filter.go b/eth/tracers/live/live.go similarity index 82% rename from eth/tracers/live/filter.go rename to eth/tracers/live/live.go index 54b470792d..ac3dc43787 100644 --- a/eth/tracers/live/filter.go +++ b/eth/tracers/live/live.go @@ -26,7 +26,7 @@ import ( ) func init() { - tracers.LiveDirectory.Register("filter", newFilter) + tracers.LiveDirectory.Register("live", newLive) } const ( @@ -57,7 +57,7 @@ func (tr *traceResult) DecodeRLP(s *rlp.Stream) error { return json.Unmarshal(temp.Result, &tr.Result) } -type filter struct { +type live struct { backend tracing.Backend kvdb ethdb.Database frdb *rawdb.Freezer @@ -74,7 +74,7 @@ type filter struct { offsetFile string } -type filterTracerConfig struct { +type liveTracerConfig struct { Path string `json:"path"` // Path to the directory where the tracer logs will be stored Config json.RawMessage `json:"config"` } @@ -111,15 +111,15 @@ func toKVKey(name string, number uint64, hash common.Hash) []byte { return key } -func newFilter(cfg json.RawMessage, stack tracers.LiveApiRegister, backend tracing.Backend) (*tracing.Hooks, error) { - var config filterTracerConfig +func newLive(cfg json.RawMessage, stack tracers.LiveApiRegister, backend tracing.Backend) (*tracing.Hooks, error) { + var config liveTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { return nil, fmt.Errorf("failed to parse config: %v", err) } } if config.Path == "" { - return nil, errors.New("filter tracer output path is required") + return nil, errors.New("live tracer output path is required") } t, err := native.NewMuxTracer(config.Config) @@ -159,7 +159,7 @@ func newFilter(cfg json.RawMessage, stack tracers.LiveApiRegister, backend traci return nil, fmt.Errorf("failed to read the frozen block numbers from the freezer db: %v", err) } - f := &filter{ + l := &live{ backend: backend, kvdb: kvdb, frdb: frdb, @@ -171,8 +171,8 @@ func newFilter(cfg json.RawMessage, stack tracers.LiveApiRegister, backend traci offsetFile: path.Join(frpath, "OFFSET"), } offset := 0 - if _, err := os.Stat(f.offsetFile); err == nil || os.IsExist(err) { - data, err := os.ReadFile(f.offsetFile) + if _, err := os.Stat(l.offsetFile); err == nil || os.IsExist(err) { + data, err := os.ReadFile(l.offsetFile) if err != nil { return nil, fmt.Errorf("failed to read the offset from the freezer db: %v", err) } @@ -181,15 +181,15 @@ func newFilter(cfg json.RawMessage, stack tracers.LiveApiRegister, backend traci return nil, fmt.Errorf("failed to convert offset: %v", err) } } - log.Info("Open filter tracer", "path", config.Path, "offset", offset, "tables", tables) + log.Info("Open live tracer", "path", config.Path, "offset", offset, "tables", tables) - f.latest.Store(tail + frozen + uint64(offset)) - f.offset.Store(uint64(offset)) + l.latest.Store(tail + frozen + uint64(offset)) + l.offset.Store(uint64(offset)) hooks := &tracing.Hooks{ - OnBlockStart: f.OnBlockStart, - OnBlockEnd: f.OnBlockEnd, - OnTxStart: f.OnTxStart, - OnTxEnd: f.OnTxEnd, + OnBlockStart: l.OnBlockStart, + OnBlockEnd: l.OnBlockEnd, + OnTxStart: l.OnTxStart, + OnTxEnd: l.OnTxEnd, // reuse the mux's hooks OnEnter: t.OnEnter, @@ -206,17 +206,21 @@ func newFilter(cfg json.RawMessage, stack tracers.LiveApiRegister, backend traci apis := []rpc.API{ { Namespace: "trace", - Service: &filterAPI{backend: backend, filter: f}, + Service: &traceAPI{backend: backend, live: l}, + }, + { + Namespace: "eth", + Service: ðAPI{backend: backend, live: l}, }, } stack.RegisterAPIs(apis) - go f.freeze() + go l.freeze() return hooks, nil } -func (f *filter) OnBlockStart(ev tracing.BlockEvent) { +func (f *live) OnBlockStart(ev tracing.BlockEvent) { // track the latest block number blknum := ev.Block.NumberU64() f.latest.Store(blknum) @@ -240,11 +244,16 @@ func (f *filter) OnBlockStart(ev tracing.BlockEvent) { }) } -func (f *filter) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { +func (f *live) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { + key := append(from.Bytes(), encodeNumber(tx.Nonce())...) + val := tx.Hash().Bytes() + if err := f.kvdb.Put(key, val); err != nil { + log.Warn("Failed to put nonce into kvdb", "err", err) + } f.tracer.OnTxStart(env, tx, from) } -func (f *filter) OnTxEnd(receipt *types.Receipt, err error) { +func (f *live) OnTxEnd(receipt *types.Receipt, err error) { f.tracer.OnTxEnd(receipt, err) for name, tt := range f.tracer.Tracers() { @@ -260,7 +269,7 @@ func (f *filter) OnTxEnd(receipt *types.Receipt, err error) { } } -func (f *filter) OnBlockEnd(err error) { +func (f *live) OnBlockEnd(err error) { if err != nil { log.Warn("OnBlockEnd", "latest", f.latest.Load(), "error", err) } @@ -289,7 +298,7 @@ func (f *filter) OnBlockEnd(err error) { } } -func (f *filter) readBlockTraces(ctx context.Context, name string, blknum uint64) ([]*traceResult, error) { +func (f *live) readBlockTraces(ctx context.Context, name string, blknum uint64) ([]*traceResult, error) { if blknum > f.latest.Load() { return nil, errors.New("notfound") } @@ -320,7 +329,7 @@ func (f *filter) readBlockTraces(ctx context.Context, name string, blknum uint64 return traces, err } -func (f *filter) readFromKVDB(ctx context.Context, name string, blknum uint64) ([]byte, error) { +func (f *live) readFromKVDB(ctx context.Context, name string, blknum uint64) ([]byte, error) { header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(blknum)) if err != nil { return nil, err @@ -334,7 +343,7 @@ func (f *filter) readFromKVDB(ctx context.Context, name string, blknum uint64) ( return data, err } -func (f *filter) readFromFRDB(name string, blknum uint64) ([]byte, error) { +func (f *live) readFromFRDB(name string, blknum uint64) ([]byte, error) { table := toTraceTable(name) var data []byte err := f.frdb.ReadAncients(func(reader ethdb.AncientReaderOp) error { @@ -348,7 +357,7 @@ func (f *filter) readFromFRDB(name string, blknum uint64) ([]byte, error) { return data, nil } -func (f *filter) Close() { +func (f *live) Close() { close(f.stopCh) if err := f.kvdb.Close(); err != nil { diff --git a/eth/tracers/live/live_api_eth.go b/eth/tracers/live/live_api_eth.go new file mode 100644 index 0000000000..c3edbc6a4b --- /dev/null +++ b/eth/tracers/live/live_api_eth.go @@ -0,0 +1,40 @@ +package live + +import ( + "context" + "errors" + + "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/internal/ethapi" +) + +type ethAPI struct { + backend tracing.Backend + live *live +} + +func (n *ethAPI) GetTransactionBySenderAndNonce(ctx context.Context, sender common.Address, nonce hexutil.Uint) (*ethapi.RPCTransaction, error) { + // TODO: + // 1. return nil if sender is a contract + // 2. check with txpool first + txHash, err := n.live.kvdb.Get(append(sender.Bytes(), encodeNumber(uint64(nonce))...)) + if err != nil { + return nil, nil + } + + found, tx, blockHash, blockNumber, index, err := n.backend.GetTransaction(ctx, common.BytesToHash(txHash)) + if err != nil { + return nil, err + } + if !found { + return nil, errors.New("transaction not found") + } + + header, err := n.backend.HeaderByHash(ctx, blockHash) + if err != nil { + return nil, err + } + return ethapi.NewRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, n.backend.ChainConfig()), nil +} diff --git a/eth/tracers/live/filter_api.go b/eth/tracers/live/live_api_trace.go similarity index 66% rename from eth/tracers/live/filter_api.go rename to eth/tracers/live/live_api_trace.go index 1935e33197..91290eaeb5 100644 --- a/eth/tracers/live/filter_api.go +++ b/eth/tracers/live/live_api_trace.go @@ -14,9 +14,9 @@ import ( var errTxNotFound = errors.New("transaction not found") -type filterAPI struct { +type traceAPI struct { backend tracing.Backend - filter *filter + live *live } type traceConfig struct { @@ -27,12 +27,12 @@ var defaultTraceConfig = &traceConfig{ Tracer: "callTracer", } -func (api *filterAPI) isSupportedTracer(tracer string) bool { - _, ok := api.filter.tracer.Tracers()[tracer] +func (api *traceAPI) isSupportedTracer(tracer string) bool { + _, ok := api.live.tracer.Tracers()[tracer] return ok } -func (api *filterAPI) Block(ctx context.Context, blockNr rpc.BlockNumber, cfg *traceConfig) ([]interface{}, error) { +func (api *traceAPI) Block(ctx context.Context, blockNr rpc.BlockNumber, cfg *traceConfig) ([]interface{}, error) { tracer, err := api.getTracerOrDefault(cfg) if err != nil { return nil, err @@ -40,14 +40,14 @@ func (api *filterAPI) Block(ctx context.Context, blockNr rpc.BlockNumber, cfg *t blknum := uint64(blockNr.Int64()) if blockNr == rpc.LatestBlockNumber { - blknum = api.filter.latest.Load() + blknum = api.live.latest.Load() } return api.readBlockTraces(ctx, tracer, blknum, tracer == "parityTracer") } -func (api *filterAPI) readBlockTraces(ctx context.Context, tracer string, blknum uint64, isParity bool) ([]interface{}, error) { - traces, err := api.filter.readBlockTraces(ctx, tracer, blknum) +func (api *traceAPI) readBlockTraces(ctx context.Context, tracer string, blknum uint64, isParity bool) ([]interface{}, error) { + traces, err := api.live.readBlockTraces(ctx, tracer, blknum) if err != nil { return nil, err } @@ -85,7 +85,7 @@ func (api *filterAPI) readBlockTraces(ctx context.Context, tracer string, blknum return results, nil } -func (api *filterAPI) Transaction(ctx context.Context, hash common.Hash, cfg *traceConfig) (interface{}, error) { +func (api *traceAPI) Transaction(ctx context.Context, hash common.Hash, cfg *traceConfig) (interface{}, error) { tracer, err := api.getTracerOrDefault(cfg) if err != nil { return nil, err @@ -98,7 +98,7 @@ func (api *filterAPI) Transaction(ctx context.Context, hash common.Hash, cfg *tr if !found { return nil, errTxNotFound } - traces, err := api.filter.readBlockTraces(ctx, tracer, blknum) + traces, err := api.live.readBlockTraces(ctx, tracer, blknum) if err != nil { return nil, err } @@ -132,7 +132,7 @@ const ( ) // Filter returns traces for the given filter configuration. -func (api *filterAPI) Filter(ctx context.Context, req traceFilterConfig, cfg *traceConfig) (interface{}, error) { +func (api *traceAPI) Filter(ctx context.Context, req traceFilterConfig, cfg *traceConfig) (interface{}, error) { tracer, err := api.getTracerOrDefault(cfg) if err != nil { return nil, err @@ -158,7 +158,7 @@ func (api *filterAPI) Filter(ctx context.Context, req traceFilterConfig, cfg *tr if req.ToBlock != nil { toBlock = uint64(*req.ToBlock) } else { - toBlock = api.filter.latest.Load() + toBlock = api.live.latest.Load() } if fromBlock > toBlock { @@ -175,7 +175,7 @@ func (api *filterAPI) Filter(ctx context.Context, req traceFilterConfig, cfg *tr return exportLimitedTraces(func(blknum uint64) ([]interface{}, error) { return api.readBlockTraces(ctx, tracer, blknum, isParity) }, fromBlock, toBlock, count, after) } -func (api *filterAPI) getTracerOrDefault(cfg *traceConfig) (string, error) { +func (api *traceAPI) getTracerOrDefault(cfg *traceConfig) (string, error) { if cfg == nil { return defaultTraceConfig.Tracer, nil } @@ -186,45 +186,3 @@ func (api *filterAPI) getTracerOrDefault(cfg *traceConfig) (string, error) { } return tracer, nil } - -func extractAddres(addrs []*common.Address) map[common.Address]struct{} { - result := make(map[common.Address]struct{}, len(addrs)) - for _, addr := range addrs { - if addr != nil { - result[*addr] = struct{}{} - } - } - return result -} - -func exportLimitedTraces(gen func(blknum uint64) ([]interface{}, error), fromBlock, toBlock, count, after uint64) ([]interface{}, error) { - var ( - nExported uint64 // Number of traces exported - nSkipped uint64 // Number of traces skipped - results = make([]interface{}, 0, 1024) // 1024 is the initial capacity - ) - - for blknum := fromBlock; blknum <= toBlock && nExported < count; blknum++ { - traces, err := gen(uint64(blknum)) - if err != nil { - return nil, err - } - - nTraces := uint64(len(traces)) - if after > nSkipped { - skip := min(after-nSkipped, nTraces) - nSkipped += skip - if skip == nTraces { - // Skip if the whole block is skipped - continue - } - traces = traces[skip:] - } - - // Export at most the remaining traces - maxExport := min(count-nExported, uint64(len(traces))) - results = append(results, traces[:maxExport]...) - nExported += maxExport - } - return results, nil -} diff --git a/eth/tracers/live/filter_freeze.go b/eth/tracers/live/live_freeze.go similarity index 92% rename from eth/tracers/live/filter_freeze.go rename to eth/tracers/live/live_freeze.go index 4ca8b2e8ad..08a2187da7 100644 --- a/eth/tracers/live/filter_freeze.go +++ b/eth/tracers/live/live_freeze.go @@ -15,7 +15,7 @@ const ( kvdbTailKey = "FilterFreezerTail" ) -func (f *filter) freeze() { +func (f *live) freeze() { var lastFinalized uint64 for { select { @@ -52,7 +52,7 @@ func (f *filter) freeze() { } } -func (f *filter) getFreezerTail() (tail uint64) { +func (f *live) getFreezerTail() (tail uint64) { tailBytes, _ := f.kvdb.Get([]byte(kvdbTailKey)) if len(tailBytes) > 0 { @@ -64,13 +64,13 @@ func (f *filter) getFreezerTail() (tail uint64) { return } -func (f *filter) updateFreezerTail(tail uint64) error { +func (f *live) updateFreezerTail(tail uint64) error { tailBytes := make([]byte, 8) binary.BigEndian.PutUint64(tailBytes, tail) return f.kvdb.Put([]byte(kvdbTailKey), tailBytes) } -func (f *filter) moveBlockToFreezer(blknum uint64) error { +func (f *live) moveBlockToFreezer(blknum uint64) error { header, err := f.backend.HeaderByNumber(context.Background(), rpc.BlockNumber(blknum)) if err != nil { return err @@ -107,7 +107,7 @@ func (f *filter) moveBlockToFreezer(blknum uint64) error { return nil } -func (f *filter) deleteKVDBEntriesWithPrefix(blknum uint64) error { +func (f *live) deleteKVDBEntriesWithPrefix(blknum uint64) error { prefix := encodeNumber(blknum) batch := f.kvdb.NewBatch() it := f.kvdb.NewIterator(prefix, nil) diff --git a/eth/tracers/live/nonce.go b/eth/tracers/live/nonce.go deleted file mode 100644 index 8734eb0350..0000000000 --- a/eth/tracers/live/nonce.go +++ /dev/null @@ -1,108 +0,0 @@ -package live - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "sync/atomic" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/tracing" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/tracers" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rpc" -) - -func init() { - tracers.LiveDirectory.Register("nonce", newNonce) -} - -type nonce struct { - backend tracing.Backend - kvdb ethdb.Database - latest atomic.Uint64 -} - -type nonceTracerConfig struct { - Path string `json:"path"` // Path to the directory where the tracer logs will be stored -} - -func newNonce(cfg json.RawMessage, stack tracers.LiveApiRegister, backend tracing.Backend) (*tracing.Hooks, error) { - var config nonceTracerConfig - if cfg != nil { - if err := json.Unmarshal(cfg, &config); err != nil { - return nil, fmt.Errorf("failed to parse config: %v", err) - } - } - if config.Path == "" { - return nil, errors.New("nonce tracer output path is required") - } - - kvdb, err := rawdb.NewPebbleDBDatabase(config.Path, 128, 1024, "nonce", false, false) - if err != nil { - return nil, err - } - - n := &nonce{ - backend: backend, - kvdb: kvdb, - } - log.Info("Open nonce tracer", "path", config.Path) - - apis := []rpc.API{{Namespace: "eth", Service: n}} - stack.RegisterAPIs(apis) - - return &tracing.Hooks{ - OnBlockStart: n.onBlockStart, - OnTxStart: n.onTxStart, - }, nil -} - -func (n *nonce) onBlockStart(ev tracing.BlockEvent) { - blknum := ev.Block.NumberU64() - n.latest.Store(blknum) -} - -func (n *nonce) onTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { - key := append(from.Bytes(), encodeNumber(tx.Nonce())...) - val := tx.Hash().Bytes() - if err := n.kvdb.Put(key, val); err != nil { - log.Warn("Failed to put nonce kvdb", "err", err) - } -} - -func (n *nonce) GetTransactionBySenderAndNonce(ctx context.Context, sender common.Address, nonce hexutil.Uint) (*ethapi.RPCTransaction, error) { - // TODO: - // 1. return nil if sender is a contract - // 2. check with txpool first - txHash, err := n.kvdb.Get(append(sender.Bytes(), encodeNumber(uint64(nonce))...)) - if err != nil { - return nil, nil - } - - found, tx, blockHash, blockNumber, index, err := n.backend.GetTransaction(ctx, common.BytesToHash(txHash)) - if err != nil { - return nil, err - } - if !found { - return nil, errors.New("transaction not found") - } - - header, err := n.backend.HeaderByHash(ctx, blockHash) - if err != nil { - return nil, err - } - return ethapi.NewRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, n.backend.ChainConfig()), nil -} - -func (n *nonce) Close() { - if err := n.kvdb.Close(); err != nil { - log.Error("Close kvdb failed", "err", err) - } -} diff --git a/eth/tracers/live/utils.go b/eth/tracers/live/utils.go new file mode 100644 index 0000000000..0e936892f1 --- /dev/null +++ b/eth/tracers/live/utils.go @@ -0,0 +1,45 @@ +package live + +import "github.com/ethereum/go-ethereum/common" + +func extractAddres(addrs []*common.Address) map[common.Address]struct{} { + result := make(map[common.Address]struct{}, len(addrs)) + for _, addr := range addrs { + if addr != nil { + result[*addr] = struct{}{} + } + } + return result +} + +func exportLimitedTraces(gen func(blknum uint64) ([]interface{}, error), fromBlock, toBlock, count, after uint64) ([]interface{}, error) { + var ( + nExported uint64 // Number of traces exported + nSkipped uint64 // Number of traces skipped + results = make([]interface{}, 0, 1024) // 1024 is the initial capacity + ) + + for blknum := fromBlock; blknum <= toBlock && nExported < count; blknum++ { + traces, err := gen(uint64(blknum)) + if err != nil { + return nil, err + } + + nTraces := uint64(len(traces)) + if after > nSkipped { + skip := min(after-nSkipped, nTraces) + nSkipped += skip + if skip == nTraces { + // Skip if the whole block is skipped + continue + } + traces = traces[skip:] + } + + // Export at most the remaining traces + maxExport := min(count-nExported, uint64(len(traces))) + results = append(results, traces[:maxExport]...) + nExported += maxExport + } + return results, nil +} diff --git a/eth/tracers/live/filter_api_test.go b/eth/tracers/live/utils_test.go similarity index 100% rename from eth/tracers/live/filter_api_test.go rename to eth/tracers/live/utils_test.go