From 5bb32c5a6f5071766630fc45864166e643b395e6 Mon Sep 17 00:00:00 2001 From: jsvisa Date: Fri, 27 Sep 2024 12:34:59 +0000 Subject: [PATCH] add nonce live tracer Signed-off-by: jsvisa --- core/tracing/hooks.go | 1 + eth/tracers/live/nonce.go | 108 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 eth/tracers/live/nonce.go diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index abab70864c..7915a9ca54 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -78,6 +78,7 @@ type Backend interface { 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) + ChainConfig() *params.ChainConfig } type ( diff --git a/eth/tracers/live/nonce.go b/eth/tracers/live/nonce.go new file mode 100644 index 0000000000..8734eb0350 --- /dev/null +++ b/eth/tracers/live/nonce.go @@ -0,0 +1,108 @@ +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) + } +}