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/{geth,utils}: add cmd to export preimages in snap enumeration order #28256

Merged
merged 11 commits into from
Nov 22, 2023
38 changes: 38 additions & 0 deletions cmd/geth/chaincmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ It's deprecated, please use "geth db export" instead.
}, utils.DatabaseFlags),
Description: `
This command dumps out the state for a given block (or latest, if none provided).
`,
}
exportSnapshotPreimagesCommand = &cli.Command{
Action: exportSnapshotPreimages,
Name: "export-snapshot-preimages",
Usage: "Export the preimage in snapshot enumeration order",
ArgsUsage: "<dumpfile>",
Flags: flags.Merge([]cli.Flag{utils.TreeRootFlag}, utils.DatabaseFlags),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do something like " [] here, (similar to the the exportCommand), so we don't need to define another flag for the tree root

Description: `
The export-snapshot-preimages command exports hash preimages to a flat file, in exactly
the expected order for the overlay tree migration.
`,
}
)
Expand Down Expand Up @@ -406,6 +417,33 @@ func exportPreimages(ctx *cli.Context) error {
return nil
}

// exportSnapshotPreimages dumps the preimage data to a flat file.
func exportSnapshotPreimages(ctx *cli.Context) error {
if ctx.Args().Len() < 1 {
utils.Fatalf("This command requires an argument.")
}
stack, _ := makeConfigNode(ctx)
defer stack.Close()

chain, _ := utils.MakeChain(ctx, stack, true)

var root common.Hash
if ctx.String(utils.TreeRootFlag.Name) != "" {
rootBytes := common.FromHex(ctx.String(utils.StartKeyFlag.Name))
if len(rootBytes) != common.HashLength {
return fmt.Errorf("invalid root hash length")
}
root = common.BytesToHash(rootBytes)
}

start := time.Now()
if err := utils.ExportSnapshotPreimages(chain, ctx.Args().First(), root); err != nil {
utils.Fatalf("Export error: %v\n", err)
}
fmt.Printf("Export done in %v\n", time.Since(start))
return nil
}

func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
Expand Down
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ func init() {
exportCommand,
importPreimagesCommand,
exportPreimagesCommand,
exportSnapshotPreimagesCommand,
removedbCommand,
dumpCommand,
dumpGenesisCommand,
Expand Down
69 changes: 69 additions & 0 deletions cmd/utils/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,75 @@ func ExportPreimages(db ethdb.Database, fn string) error {
return nil
}

// ExportSnapshotPreimages exports the preimages corresponding to the enumeration of
// the snapshot for a given root.
func ExportSnapshotPreimages(chain *core.BlockChain, fn string, root common.Hash) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func ExportSnapshotPreimages(chain *core.BlockChain, fn string, root common.Hash) error {
func ExportSnapshotPreimages(chain *core.BlockChain, fn string, root common.Hash) error {

Perhaps ExportActivePreimages to signify that it is the preimages that are all active or relevant for the given state

log.Info("Exporting preimages", "file", fn)

fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()

writer := bufio.NewWriter(fh)
defer writer.Flush()

statedb, err := chain.State()
if err != nil {
return fmt.Errorf("failed to open statedb: %w", err)
}

if root == (common.Hash{}) {
root = chain.CurrentBlock().Root
}

accIt, err := chain.Snapshots().AccountIterator(root, common.Hash{})
if err != nil {
return err
}
defer accIt.Release()

count := 0
for accIt.Next() {
acc, err := types.FullAccount(accIt.Account())
if err != nil {
return fmt.Errorf("invalid account encountered during traversal: %s", err)
}
addr := rawdb.ReadPreimage(statedb.Database().DiskDB(), accIt.Hash())
if len(addr) != 20 {
return fmt.Errorf("addr len is zero is not 32: %d", len(addr))
}
if _, err := writer.Write(addr); err != nil {
return fmt.Errorf("failed to write addr preimage: %w", err)
}

if acc.Root != (common.Hash{}) && acc.Root != types.EmptyRootHash {
stIt, err := chain.Snapshots().StorageIterator(root, accIt.Hash(), common.Hash{})
if err != nil {
return fmt.Errorf("failed to create storage iterator: %w", err)
}
for stIt.Next() {
slotnr := rawdb.ReadPreimage(statedb.Database().DiskDB(), stIt.Hash())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feed the stIt.Hash() into a channel instead, and anohter goroutine does the rawdb.ReadPreimage on the other end of that channel

if len(slotnr) != 32 {
return fmt.Errorf("slotnr not 32 len")
}
if _, err := writer.Write(slotnr); err != nil {
return fmt.Errorf("failed to write slotnr preimage: %w", err)
}
}
stIt.Release()
}
count++
if count%100000 == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you do something like
count % 1000 && time.Since(logged) > 8*time.Second here?
That would be in line with the other export commands which try to log every 8 seconds

log.Info("Last exported account", "account", accIt.Hash())
}
}

log.Info("Exported preimages", "file", fn)
return nil
}

// exportHeader is used in the export/import flow. When we do an export,
// the first element we output is the exportHeader.
// Whenever a backwards-incompatible change is made, the Version header
Expand Down
5 changes: 5 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ var (
Usage: "Max number of elements (0 = no limit)",
Value: 0,
}
TreeRootFlag = &cli.StringFlag{
Name: "roothash",
Usage: "Root hash of the tree (if empty, use that of current block)",
Value: "",
}

defaultSyncMode = ethconfig.Defaults.SyncMode
SnapshotFlag = &cli.BoolFlag{
Expand Down