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

cmd: fix dump cli cannot work in path mode #2160

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
126 changes: 95 additions & 31 deletions cmd/geth/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -590,38 +592,10 @@ func exportPreimages(ctx *cli.Context) error {
}

func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
db := utils.MakeChainDatabase(ctx, stack, true, false)
var header *types.Header
if ctx.NArg() > 1 {
return nil, nil, common.Hash{}, fmt.Errorf("expected 1 argument (number or hash), got %d", ctx.NArg())
}
if ctx.NArg() == 1 {
arg := ctx.Args().First()
if hashish(arg) {
hash := common.HexToHash(arg)
if number := rawdb.ReadHeaderNumber(db, hash); number != nil {
header = rawdb.ReadHeader(db, hash, *number)
} else {
return nil, nil, common.Hash{}, fmt.Errorf("block %x not found", hash)
}
} else {
number, err := strconv.ParseUint(arg, 10, 64)
if err != nil {
return nil, nil, common.Hash{}, err
}
if hash := rawdb.ReadCanonicalHash(db, number); hash != (common.Hash{}) {
header = rawdb.ReadHeader(db, hash, number)
} else {
return nil, nil, common.Hash{}, fmt.Errorf("header for block %d not found", number)
}
}
} else {
// Use latest
header = rawdb.ReadHeadHeader(db)
}
if header == nil {
return nil, nil, common.Hash{}, errors.New("no head block found")
}

startArg := common.FromHex(ctx.String(utils.StartKeyFlag.Name))
var start common.Hash
switch len(startArg) {
Expand All @@ -641,9 +615,99 @@ func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, eth
Start: start.Bytes(),
Max: ctx.Uint64(utils.DumpLimitFlag.Name),
}

db := utils.MakeChainDatabase(ctx, stack, true, false)
provided, err := utils.CompareStateSchemeCLIWithConfig(ctx)
if err != nil {
return nil, nil, common.Hash{}, err
}
scheme, err := rawdb.ParseStateScheme(provided, db)
if err != nil {
return nil, nil, common.Hash{}, err
}
var header *types.Header
if scheme == rawdb.PathScheme {
triedb := trie.NewDatabase(db, &trie.Config{PathDB: pathdb.ReadOnly})
if ctx.NArg() == 1 {
arg := ctx.Args().First()
if hashish(arg) {
hash := common.HexToHash(arg)
if number := rawdb.ReadHeaderNumber(db, hash); number != nil {
header = rawdb.ReadHeader(db, hash, *number)
if header == nil {
return nil, nil, common.Hash{}, fmt.Errorf("no head block found")
}
if contain := triedb.ContainRootHash(header.Root); !contain {
return nil, nil, common.Hash{}, fmt.Errorf("PBSS doesn't contain specified MPT root hash for block hash %x", hash)
}
} else {
return nil, nil, common.Hash{}, fmt.Errorf("block hash %x not found", hash)
}
} else {
number, err := strconv.ParseUint(arg, 10, 64)
if err != nil {
return nil, nil, common.Hash{}, err
}
if hash := rawdb.ReadCanonicalHash(db, number); hash != (common.Hash{}) {
header = rawdb.ReadHeader(db, hash, number)
if header == nil {
return nil, nil, common.Hash{}, fmt.Errorf("no head block found")
}
if contain := triedb.ContainRootHash(header.Root); !contain {
sysvm marked this conversation as resolved.
Show resolved Hide resolved
return nil, nil, common.Hash{}, fmt.Errorf("PBSS doesn't contain specified MPT root hash for block number %d", number)
}
} else {
return nil, nil, common.Hash{}, fmt.Errorf("block number %x not found", number)
sysvm marked this conversation as resolved.
Show resolved Hide resolved
}
}
} else {
if stateRoot := triedb.Head(); stateRoot != (common.Hash{}) {
if contain := triedb.ContainRootHash(stateRoot); !contain {
return nil, nil, common.Hash{}, fmt.Errorf("PBSS doesn't contain specified state root %x", stateRoot)
}
conf.StateScheme = rawdb.PathScheme
log.Info("State dump configured", "mpt root hash", stateRoot, "skipcode", conf.SkipCode,
"skipstorage", conf.SkipStorage, "start", hexutil.Encode(conf.Start), "limit", conf.Max,
"state scheme", conf.StateScheme)
return conf, db, stateRoot, nil
} else {
return nil, nil, common.Hash{}, fmt.Errorf("no top state root hash in path db")
}
}
} else {
if ctx.NArg() == 1 {
arg := ctx.Args().First()
if hashish(arg) {
hash := common.HexToHash(arg)
if number := rawdb.ReadHeaderNumber(db, hash); number != nil {
header = rawdb.ReadHeader(db, hash, *number)
} else {
return nil, nil, common.Hash{}, fmt.Errorf("block %x not found", hash)
}
} else {
number, err := strconv.ParseUint(arg, 10, 64)
if err != nil {
return nil, nil, common.Hash{}, err
}
if hash := rawdb.ReadCanonicalHash(db, number); hash != (common.Hash{}) {
header = rawdb.ReadHeader(db, hash, number)
} else {
return nil, nil, common.Hash{}, fmt.Errorf("header for block %d not found", number)
}
}
} else {
// Use latest
header = rawdb.ReadHeadHeader(db)
}
}

if header == nil {
return nil, nil, common.Hash{}, errors.New("no head block found")
}
conf.StateScheme = scheme
sysvm marked this conversation as resolved.
Show resolved Hide resolved
log.Info("State dump configured", "block", header.Number, "hash", header.Hash().Hex(),
"skipcode", conf.SkipCode, "skipstorage", conf.SkipStorage,
"start", hexutil.Encode(conf.Start), "limit", conf.Max)
"skipcode", conf.SkipCode, "skipstorage", conf.SkipStorage, "start", hexutil.Encode(conf.Start),
"limit", conf.Max, "state scheme", conf.StateScheme)
return conf, db, header.Root, nil
}

Expand Down
9 changes: 5 additions & 4 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -1884,7 +1884,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(StateHistoryFlag.Name) {
cfg.StateHistory = ctx.Uint64(StateHistoryFlag.Name)
}
scheme, err := compareCLIWithConfig(ctx)
scheme, err := CompareStateSchemeCLIWithConfig(ctx)
if err != nil {
Fatalf("%v", err)
}
Expand Down Expand Up @@ -2353,7 +2353,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
}
provided, err := compareCLIWithConfig(ctx)
provided, err := CompareStateSchemeCLIWithConfig(ctx)
if err != nil {
Fatalf("%v", err)
}
Expand Down Expand Up @@ -2425,7 +2425,7 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
config := &trie.Config{
Preimages: preimage,
}
provided, err := compareCLIWithConfig(ctx)
provided, err := CompareStateSchemeCLIWithConfig(ctx)
if err != nil {
Fatalf("%v", err)
}
Expand All @@ -2448,7 +2448,8 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
return trie.NewDatabase(disk, config)
}

func compareCLIWithConfig(ctx *cli.Context) (string, error) {
// CompareStateSchemeCLIWithConfig compare state scheme in CLI with config whether are equal.
func CompareStateSchemeCLIWithConfig(ctx *cli.Context) (string, error) {
var (
cfgScheme string
err error
Expand Down
10 changes: 9 additions & 1 deletion core/state/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"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/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
Expand All @@ -37,6 +38,7 @@ type DumpConfig struct {
OnlyWithAddresses bool
Start []byte
Max uint64
StateScheme string
sysvm marked this conversation as resolved.
Show resolved Hide resolved
}

// DumpCollector interface which the state trie calls during iteration
Expand Down Expand Up @@ -177,7 +179,13 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
}
if !conf.SkipStorage {
account.Storage = make(map[common.Hash]string)
tr, err := obj.getTrie()
var tr Trie
if conf.StateScheme == rawdb.PathScheme {
tr, err = trie.NewStateTrie(trie.StorageTrieID(obj.db.originalRoot, common.BytesToHash(it.Key),
obj.data.Root), obj.db.db.TrieDB())
sysvm marked this conversation as resolved.
Show resolved Hide resolved
} else {
tr, err = obj.getTrie()
}
if err != nil {
log.Error("Failed to load storage trie", "err", err)
continue
Expand Down
14 changes: 13 additions & 1 deletion trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func (db *Database) SetBufferSize(size int) error {
}

// Head return the top non-fork difflayer/disklayer root hash for rewinding.
// It's only supported by path-based database and will return an error for
// It's only supported by path-based database and will return empty hash for
// others.
func (db *Database) Head() common.Hash {
pdb, ok := db.backend.(*pathdb.Database)
Expand All @@ -364,3 +364,15 @@ func (db *Database) Head() common.Hash {
}
return pdb.Head()
}

// ContainRootHash returns whether MPT root hash is existent.
// It's only supported by path-based database and will return false for
// others.
func (db *Database) ContainRootHash(root common.Hash) bool {
pdb, ok := db.backend.(*pathdb.Database)
if !ok {
log.Error("not supported")
sysvm marked this conversation as resolved.
Show resolved Hide resolved
return false
}
return pdb.ContainRootHash(root)
}
2 changes: 1 addition & 1 deletion trie/triedb/pathdb/asyncnodebuffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ func (nc *nodecache) node(owner common.Hash, path []byte, hash common.Hash) (*tr
}
if n.Hash != hash {
dirtyFalseMeter.Mark(1)
log.Error("Unexpected trie node in node buffer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
log.Error("Unexpected trie node in async node buffer", "owner", owner, "path", path, "expect", hash, "got", n.Hash)
return nil, newUnexpectedNodeError("dirty", hash, n.Hash, owner, path, n.Blob)
}
return n, nil
Expand Down
23 changes: 23 additions & 0 deletions trie/triedb/pathdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,26 @@ func (db *Database) Head() common.Hash {
defer db.lock.Unlock()
return db.tree.front()
}

// ContainRootHash returns whether MPT root hash is existent.
func (db *Database) ContainRootHash(root common.Hash) bool {
db.lock.Lock()
defer db.lock.Unlock()

bottom := db.tree.bottom()
if l := db.tree.get(root); l != nil {
if l.rootHash() == bottom.rootHash() {
log.Info("root hash is equal to disk layer root")
sysvm marked this conversation as resolved.
Show resolved Hide resolved
} else {
log.Info("root hash locates in diff layer")
}
return true
}
_, err := bottom.Node(common.Hash{}, []byte(""), root)
if err != nil {
log.Error("Failed to retrieve root hash in disk layer node buffer", "hash", root, "error", err)
return false
}
log.Info("root hash locates in disk layer node buffer")
return true
}