Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
18ef57a
P3-1: make InfluxDB reporting interval configurable (upstream #33767)
SegueII Apr 17, 2026
ee0a152
P3-4(A): add BlockTimestamp to RPCTransaction (upstream #33709)
SegueII Apr 17, 2026
5a478dd
P3-4(B): add --rpc.rangelimit alias for --rpc.getlogs.maxrange (upstr…
SegueII Apr 17, 2026
bc9567d
P3-6: fix non-executable tx heartbeat semantics in txpool (upstream #…
SegueII Apr 17, 2026
f9eba28
P3-2: add eth_getStorageValues batched storage RPC (upstream #32591)
SegueII Apr 17, 2026
1f677cf
P3-3: add inspect-trie command with full upstream parity (upstream #2…
SegueII Apr 17, 2026
5cbd999
P3-2: register eth_getStorageValues in web3ext (upstream #32591)
SegueII Apr 17, 2026
ace4a70
Add dbcmd_test.go for snapshot inspection tests and enhance resolveIn…
SegueII Apr 20, 2026
1386679
Implement validation for inspectTrie top-N parameter and enhance Infl…
SegueII Apr 20, 2026
6c5d27b
fix: multi-package audit fixes (zkTrie gate, heartbeat, overflow guar…
SegueII Apr 22, 2026
104b9c6
fix: audit fixes — config preservation, deep-copy, goroutine leak, ct…
SegueII Apr 22, 2026
9cd74eb
refactor: enhance max block range handling and table rendering
SegueII May 9, 2026
33b310a
Merge branch 'main' into eezo-shunt-sync-enhance
SegueII May 11, 2026
f2db966
test: add unit test for internal enqueue behavior in transaction pool
SegueII May 11, 2026
8e0ad29
test: add unit test for demotion of unexecutables in transaction pool
SegueII May 11, 2026
85edba1
test: fix variable naming in transaction pool heartbeat test
SegueII May 11, 2026
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
3 changes: 2 additions & 1 deletion cmd/geth/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ The dumpgenesis command dumps the genesis block configuration in JSON format to
utils.MetricsInfluxDBUsernameFlag,
utils.MetricsInfluxDBPasswordFlag,
utils.MetricsInfluxDBTagsFlag,
utils.MetricsInfluxDBIntervalFlag,
utils.MetricsInfluxDBTokenFlag,
utils.MetricsInfluxDBBucketFlag,
utils.MetricsInfluxDBOrganizationFlag,
Expand Down Expand Up @@ -263,7 +264,7 @@ func importChain(ctx *cli.Context) error {
utils.Fatalf("This command requires an argument.")
}
// Start metrics export if enabled
utils.SetupMetrics(ctx)
utils.SetupMetrics(ctx, makeMetricsConfig(ctx))
// Start system runtime metrics collection
go metrics.CollectProcessMetrics(3 * time.Second)

Expand Down
18 changes: 18 additions & 0 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
if ctx.GlobalIsSet(utils.MetricsInfluxDBTagsFlag.Name) {
cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name)
}
if ctx.GlobalIsSet(utils.MetricsInfluxDBIntervalFlag.Name) {
cfg.Metrics.InfluxDBInterval = ctx.GlobalDuration(utils.MetricsInfluxDBIntervalFlag.Name)
}
if ctx.GlobalIsSet(utils.MetricsEnableInfluxDBV2Flag.Name) {
cfg.Metrics.EnableInfluxDBV2 = ctx.GlobalBool(utils.MetricsEnableInfluxDBV2Flag.Name)
}
Expand All @@ -266,6 +269,21 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
}
}

// makeMetricsConfig returns a metrics.Config populated from the TOML config
// file (when --config is provided) and any metrics-related CLI flags. It is
// used by callsites that need the resolved metrics configuration before the
// full node (makeConfigNode) has been initialised.
func makeMetricsConfig(ctx *cli.Context) metrics.Config {
cfg := gethConfig{Metrics: metrics.DefaultConfig}
if file := ctx.GlobalString(configFileFlag.Name); file != "" {
if err := loadConfig(file, &cfg); err != nil {
utils.Fatalf("%v", err)
}
}
applyMetricConfig(ctx, &cfg)
return cfg.Metrics
}

func deprecated(field string) bool {
switch field {
case "ethconfig.Config.EVMInterpreter":
Expand Down
247 changes: 247 additions & 0 deletions cmd/geth/dbcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,34 @@ import (
"github.com/morph-l2/go-ethereum/trie"
)

var (
inspectTrieTopFlag = cli.IntFlag{
Name: "top",
Usage: "Number of storage tries to include in the top-N ranking (must be > 0)",
Value: 10,
}
inspectTrieExcludeStorageFlag = cli.BoolFlag{
Name: "exclude-storage",
Usage: "Skip walking per-account storage tries",
}
inspectTrieOutputFileFlag = cli.StringFlag{
Name: "output",
Usage: "Write the report as JSON to the given file (default: stdout summary)",
}
inspectTrieDumpPathFlag = cli.StringFlag{
Name: "dump-path",
Usage: "Path for the pass-1 trie dump file (default: <datadir>/trie-dump.bin)",
}
inspectTrieSummarizeFlag = cli.StringFlag{
Name: "summarize",
Usage: "Summarize an existing trie dump file (skips trie traversal)",
}
inspectTrieContractFlag = cli.StringFlag{
Name: "contract",
Usage: "Inspect a single contract's storage footprint by 0x address",
}
)

var (
removedbCommand = cli.Command{
Action: utils.MigrateFlags(removeDB),
Expand All @@ -61,6 +89,7 @@ Remove blockchain and state databases`,
Category: "DATABASE COMMANDS",
Subcommands: []cli.Command{
dbInspectCmd,
dbInspectTrieCmd,
dbStatCmd,
dbCompactCmd,
dbGetCmd,
Expand All @@ -72,6 +101,52 @@ Remove blockchain and state databases`,
dbExportCmd,
},
}
dbInspectTrieCmd = cli.Command{
Action: utils.MigrateFlags(inspectTrie),
Name: "inspect-trie",
ArgsUsage: "[blocknum|latest|snapshot]",
Flags: []cli.Flag{
utils.DataDirFlag,
utils.AncientFlag,
utils.SyncModeFlag,
utils.MainnetFlag,
utils.RopstenFlag,
utils.SepoliaFlag,
utils.RinkebyFlag,
utils.GoerliFlag,
utils.MorphFlag,
utils.MorphHoleskyFlag,
utils.MorphHoodiFlag,
inspectTrieTopFlag,
inspectTrieExcludeStorageFlag,
inspectTrieOutputFileFlag,
inspectTrieDumpPathFlag,
inspectTrieSummarizeFlag,
inspectTrieContractFlag,
},
Usage: "Walk an MPT state trie and report structural metrics",
Description: `This command walks the MPT-encoded account trie for the given target and,
unless --exclude-storage is set, every non-empty storage trie. The walk
is a two-pass operation: pass 1 streams per-storage-trie records into
--dump-path (default <datadir>/trie-dump.bin), pass 2 summarizes the
dump and prints tables to stdout (or writes JSON via --output).

The argument selects the state to inspect:

latest the current head (default if omitted)
<blocknum> the canonical block at the given decimal height
snapshot the state root recorded by the snapshot layer

Alternative modes:

--summarize <path> skip the walk and re-summarize an existing dump
--contract 0xADDR inspect a single contract's storage trie + snap
view, bypassing the top-N account-trie scan

This command only supports MPT mode. It refuses to run against ZKTrie-
encoded history (pre-JadeFork on morph mainnet/Holesky); target a block
after the JadeFork activation time or run on an MPT-native chain.`,
}
dbInspectCmd = cli.Command{
Action: utils.MigrateFlags(inspect),
Name: "inspect",
Expand Down Expand Up @@ -357,6 +432,178 @@ func inspect(ctx *cli.Context) error {
return rawdb.InspectDatabase(db, prefix, start)
}

// inspectTrie is the handler for `geth db inspect-trie`. It dispatches to
// one of three upstream-aligned modes depending on the flags supplied:
//
// - --summarize <path>: skip the trie walk and re-analyze an existing
// pass-1 dump. Useful to regenerate a report cheaply after a long
// walk, or to share dumps across machines for offline inspection.
// - --contract 0xADDR: run trie.InspectContract against the resolved
// state root to compare the trie and snapshot views for one
// contract.
// - otherwise: run the two-pass inspector (pass 1 writes a dump, pass
// 2 produces the summary) via trie.Inspect.
//
// The positional argument selects the state root for the trie-walk and
// contract modes:
//
// latest (default) head block's state root
// <blocknum> canonical block at decimal height
// snapshot state root recorded by the snapshot layer
//
// ZKTrie-encoded morph history is refused via trie.ErrUnsupportedTrieFormat.
// This matches upstream's "MPT only" contract since the inspector does
// not understand morph's zkTrie encoding.
func inspectTrie(ctx *cli.Context) error {
topN := ctx.Int(inspectTrieTopFlag.Name)
if err := validateInspectTrieTopN(topN); err != nil {
return err
}

// Mode 1: summarize an existing dump. The trie database is not
// needed since we only read the binary dump.
if summarizePath := ctx.String(inspectTrieSummarizeFlag.Name); summarizePath != "" {
if ctx.NArg() > 0 {
return fmt.Errorf("block argument is not supported with --%s", inspectTrieSummarizeFlag.Name)
}
cfg := &trie.InspectConfig{
TopN: topN,
Path: ctx.String(inspectTrieOutputFileFlag.Name),
DumpPath: summarizePath,
}
log.Info("Summarizing trie dump", "path", summarizePath, "top", topN)
return trie.Summarize(summarizePath, cfg)
}

stack, _ := makeConfigNode(ctx)
defer stack.Close()

db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()

root, blockNumber, blockTime, blockMetaKnown, err := resolveInspectTarget(ctx, db)
if err != nil {
return err
}

// Detect ZKTrie mode: when the chain uses ZKTrie format we must be
// able to confirm the target block is past JadeFork (where the trie
// switched to MPT). If block metadata is unknown we cannot make that
// determination, so we treat unknown metadata as unsafe and refuse.
chainConfig := rawdb.ReadChainConfig(db, rawdb.ReadCanonicalHash(db, 0))
if chainConfig != nil && chainConfig.Morph.UseZktrie && (!blockMetaKnown || !chainConfig.IsJadeFork(blockTime)) {
return fmt.Errorf("%w (block %d time %d predates or cannot confirm JadeFork)",
trie.ErrUnsupportedTrieFormat, blockNumber, blockTime)
}

triedb := trie.NewDatabase(db)

// Mode 2: single-contract inspection. The result is printed to
// stdout; --output/--dump-path are ignored in this mode because
// InspectContract emits a fixed report directly.
if contractArg := ctx.String(inspectTrieContractFlag.Name); contractArg != "" {
if !common.IsHexAddress(contractArg) {
return fmt.Errorf("invalid --%s value %q: not an address", inspectTrieContractFlag.Name, contractArg)
}
address := common.HexToAddress(contractArg)
log.Info("Inspecting contract", "address", address, "root", root, "block", blockNumber)
return trie.InspectContract(triedb, db, root, address)
}

// Mode 3: full two-pass trie inspection.
dumpPath := ctx.String(inspectTrieDumpPathFlag.Name)
if dumpPath == "" {
dumpPath = stack.ResolvePath("trie-dump.bin")
}
cfg := &trie.InspectConfig{
NoStorage: ctx.Bool(inspectTrieExcludeStorageFlag.Name),
TopN: topN,
Path: ctx.String(inspectTrieOutputFileFlag.Name),
DumpPath: dumpPath,
}
log.Info("Inspecting trie",
"root", root,
"block", blockNumber,
"time", blockTime,
"excludeStorage", cfg.NoStorage,
"top", topN,
"dump", cfg.DumpPath,
)
start := time.Now()
if err := trie.Inspect(triedb, root, cfg); err != nil {
return err
}
log.Info("Trie inspection complete", "elapsed", common.PrettyDuration(time.Since(start)))
return nil
}

// resolveInspectTarget picks a state root and best-effort block metadata
// from the positional argument. For the "snapshot" keyword it first tries to
// recover canonical block metadata from the snapshot root; if no matching
// header is available the block metadata is returned as unknown.
func resolveInspectTarget(ctx *cli.Context, db ethdb.Database) (common.Hash, uint64, uint64, bool, error) {
if ctx.NArg() > 1 {
return common.Hash{}, 0, 0, false, fmt.Errorf("max 1 argument: %v", ctx.Command.ArgsUsage)
}

arg := "latest"
if ctx.NArg() == 1 {
arg = ctx.Args().Get(0)
}

if arg == "snapshot" {
return resolveSnapshotInspectTarget(db)
}

if arg == "latest" {
header := rawdb.ReadHeadHeader(db)
if header == nil {
return common.Hash{}, 0, 0, false, errors.New("head header not found; database may be empty")
}
return header.Root, header.Number.Uint64(), header.Time, true, nil
}

n, err := strconv.ParseUint(arg, 10, 64)
if err != nil {
return common.Hash{}, 0, 0, false, fmt.Errorf("invalid block argument %q: %w", arg, err)
}
hash := rawdb.ReadCanonicalHash(db, n)
if hash == (common.Hash{}) {
return common.Hash{}, 0, 0, false, fmt.Errorf("canonical hash for block %d not found", n)
}
header := rawdb.ReadHeader(db, hash, n)
if header == nil {
return common.Hash{}, 0, 0, false, fmt.Errorf("header for block %d / %x not found", n, hash)
}
return header.Root, header.Number.Uint64(), header.Time, true, nil
}

func resolveSnapshotInspectTarget(db ethdb.Database) (common.Hash, uint64, uint64, bool, error) {
root := rawdb.ReadSnapshotRoot(db)
if root == (common.Hash{}) {
return common.Hash{}, 0, 0, false, errors.New("snapshot root not found in database")
}
if header := rawdb.ReadHeadHeader(db); header != nil && header.Root == root {
return root, header.Number.Uint64(), header.Time, true, nil
}
if number := rawdb.ReadSnapshotRecoveryNumber(db); number != nil {
hash := rawdb.ReadCanonicalHash(db, *number)
if hash != (common.Hash{}) {
if header := rawdb.ReadHeader(db, hash, *number); header != nil && header.Root == root {
return root, header.Number.Uint64(), header.Time, true, nil
}
}
}
return root, 0, 0, false, nil
}

func validateInspectTrieTopN(topN int) error {
if topN <= 0 {
return fmt.Errorf("invalid --%s value %d (must be > 0)", inspectTrieTopFlag.Name, topN)
}
return nil
}

func showLeveldbStats(db ethdb.Stater) {
if stats, err := db.Stat("leveldb.stats"); err != nil {
log.Warn("Failed to read database stats", "error", err)
Expand Down
Loading
Loading