Skip to content

Commit eba5366

Browse files
gballetjsign
andcommitted
cmd/{geth,utils}: add cmd to export preimages in snap enumeration order
Co-Authored-By: Ignacio Hagopian <jsign.uy@gmail.com>
1 parent 052355f commit eba5366

File tree

4 files changed

+113
-0
lines changed

4 files changed

+113
-0
lines changed

cmd/geth/chaincmd.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,17 @@ It's deprecated, please use "geth db export" instead.
165165
}, utils.DatabaseFlags),
166166
Description: `
167167
This command dumps out the state for a given block (or latest, if none provided).
168+
`,
169+
}
170+
exportSnapshotPreimagesCommand = &cli.Command{
171+
Action: exportSnapshotPreimages,
172+
Name: "export-snapshot-preimages",
173+
Usage: "Export the preimage in snapshot enumeration order",
174+
ArgsUsage: "<dumpfile>",
175+
Flags: flags.Merge([]cli.Flag{utils.TreeRootFlag}, utils.DatabaseFlags),
176+
Description: `
177+
The export-overlay-preimages command exports hash preimages to a flat file, in exactly
178+
the expected order for the overlay tree migration.
168179
`,
169180
}
170181
)
@@ -406,6 +417,33 @@ func exportPreimages(ctx *cli.Context) error {
406417
return nil
407418
}
408419

420+
// exportSnapshotPreimages dumps the preimage data to a flat file.
421+
func exportSnapshotPreimages(ctx *cli.Context) error {
422+
if ctx.Args().Len() < 1 {
423+
utils.Fatalf("This command requires an argument.")
424+
}
425+
stack, _ := makeConfigNode(ctx)
426+
defer stack.Close()
427+
428+
chain, _ := utils.MakeChain(ctx, stack, true)
429+
430+
var root common.Hash
431+
if ctx.String(utils.TreeRootFlag.Name) != "" {
432+
rootBytes := common.FromHex(ctx.String(utils.StartKeyFlag.Name))
433+
if len(rootBytes) != common.HashLength {
434+
return fmt.Errorf("invalid root hash length")
435+
}
436+
root = common.BytesToHash(rootBytes)
437+
}
438+
439+
start := time.Now()
440+
if err := utils.ExportSnapshotPreimages(chain, ctx.Args().First(), root); err != nil {
441+
utils.Fatalf("Export error: %v\n", err)
442+
}
443+
fmt.Printf("Export done in %v\n", time.Since(start))
444+
return nil
445+
}
446+
409447
func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
410448
db := utils.MakeChainDatabase(ctx, stack, true)
411449
defer db.Close()

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ func init() {
209209
exportCommand,
210210
importPreimagesCommand,
211211
exportPreimagesCommand,
212+
exportSnapshotPreimagesCommand,
212213
removedbCommand,
213214
dumpCommand,
214215
dumpGenesisCommand,

cmd/utils/cmd.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,75 @@ func ExportPreimages(db ethdb.Database, fn string) error {
374374
return nil
375375
}
376376

377+
// ExportSnapshotPreimages exports the preimages corresponding to the enumeration of
378+
// the snapshot for a given root.
379+
func ExportSnapshotPreimages(chain *core.BlockChain, fn string, root common.Hash) error {
380+
log.Info("Exporting preimages", "file", fn)
381+
382+
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
383+
if err != nil {
384+
return err
385+
}
386+
defer fh.Close()
387+
388+
writer := bufio.NewWriter(fh)
389+
defer writer.Flush()
390+
391+
statedb, err := chain.State()
392+
if err != nil {
393+
return fmt.Errorf("failed to open statedb: %w", err)
394+
}
395+
396+
if root == (common.Hash{}) {
397+
root = chain.CurrentBlock().Root
398+
}
399+
400+
accIt, err := chain.Snapshots().AccountIterator(root, common.Hash{})
401+
if err != nil {
402+
return err
403+
}
404+
defer accIt.Release()
405+
406+
count := 0
407+
for accIt.Next() {
408+
acc, err := types.FullAccount(accIt.Account())
409+
if err != nil {
410+
return fmt.Errorf("invalid account encountered during traversal: %s", err)
411+
}
412+
addr := rawdb.ReadPreimage(statedb.Database().DiskDB(), accIt.Hash())
413+
if len(addr) != 20 {
414+
return fmt.Errorf("addr len is zero is not 32: %d", len(addr))
415+
}
416+
if _, err := writer.Write(addr); err != nil {
417+
return fmt.Errorf("failed to write addr preimage: %w", err)
418+
}
419+
420+
if acc.Root != (common.Hash{}) && acc.Root != types.EmptyRootHash {
421+
stIt, err := chain.Snapshots().StorageIterator(root, accIt.Hash(), common.Hash{})
422+
if err != nil {
423+
return fmt.Errorf("failed to create storage iterator: %w", err)
424+
}
425+
for stIt.Next() {
426+
slotnr := rawdb.ReadPreimage(statedb.Database().DiskDB(), stIt.Hash())
427+
if len(slotnr) != 32 {
428+
return fmt.Errorf("slotnr not 32 len")
429+
}
430+
if _, err := writer.Write(slotnr); err != nil {
431+
return fmt.Errorf("failed to write slotnr preimage: %w", err)
432+
}
433+
}
434+
stIt.Release()
435+
}
436+
count++
437+
if count%100000 == 0 {
438+
log.Info("Last exported account", "account", accIt.Hash())
439+
}
440+
}
441+
442+
log.Info("Exported preimages", "file", fn)
443+
return nil
444+
}
445+
377446
// exportHeader is used in the export/import flow. When we do an export,
378447
// the first element we output is the exportHeader.
379448
// Whenever a backwards-incompatible change is made, the Version header

cmd/utils/flags.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ var (
219219
Usage: "Max number of elements (0 = no limit)",
220220
Value: 0,
221221
}
222+
TreeRootFlag = &cli.StringFlag{
223+
Name: "roothash",
224+
Usage: "Root hash of the tree (if empty, use that of current block)",
225+
Value: "",
226+
}
222227

223228
defaultSyncMode = ethconfig.Defaults.SyncMode
224229
SnapshotFlag = &cli.BoolFlag{

0 commit comments

Comments
 (0)