Skip to content

Commit c9246f6

Browse files
rjl493456442zzyalbert
authored andcommitted
cmd/geth: implement data import and export (ethereum#22931)
This PR offers two more database sub commands for exporting and importing data. Two exporters are implemented: preimage and snapshot data respectively. The import command is generic, it can take any data export and import into leveldb. The data format has a 'magic' for disambiguation, and a version field for future compatibility.
1 parent 1ad2168 commit c9246f6

File tree

7 files changed

+590
-15
lines changed

7 files changed

+590
-15
lines changed

cmd/geth/chaincmd.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ be gzipped.`,
138138
},
139139
Category: "BLOCKCHAIN COMMANDS",
140140
Description: `
141-
The import-preimages command imports hash preimages from an RLP encoded stream.`,
141+
The import-preimages command imports hash preimages from an RLP encoded stream.
142+
It's deprecated, please use "geth db import" instead.
143+
`,
142144
}
143145
exportPreimagesCommand = cli.Command{
144146
Action: utils.MigrateFlags(exportPreimages),
@@ -152,7 +154,9 @@ be gzipped.`,
152154
},
153155
Category: "BLOCKCHAIN COMMANDS",
154156
Description: `
155-
The export-preimages command export hash preimages to an RLP encoded stream`,
157+
The export-preimages command exports hash preimages to an RLP encoded stream.
158+
It's deprecated, please use "geth db export" instead.
159+
`,
156160
}
157161
dumpCommand = cli.Command{
158162
Action: utils.MigrateFlags(dump),
@@ -366,7 +370,6 @@ func exportPreimages(ctx *cli.Context) error {
366370
if len(ctx.Args()) < 1 {
367371
utils.Fatalf("This command requires an argument.")
368372
}
369-
370373
stack, _ := makeConfigNode(ctx)
371374
defer stack.Close()
372375

cmd/geth/dbcmd.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717
package main
1818

1919
import (
20+
"bytes"
2021
"errors"
2122
"fmt"
2223
"os"
24+
"os/signal"
2325
"path/filepath"
2426
"sort"
2527
"strconv"
28+
"strings"
29+
"syscall"
2630
"time"
2731

2832
"github.com/ethereum/go-ethereum/cmd/utils"
@@ -63,6 +67,8 @@ Remove blockchain and state databases`,
6367
dbPutCmd,
6468
dbGetSlotsCmd,
6569
dbDumpFreezerIndex,
70+
dbImportCmd,
71+
dbExportCmd,
6672
},
6773
}
6874
dbInspectCmd = cli.Command{
@@ -172,6 +178,36 @@ WARNING: This is a low-level operation which may cause database corruption!`,
172178
},
173179
Description: "This command displays information about the freezer index.",
174180
}
181+
dbImportCmd = cli.Command{
182+
Action: utils.MigrateFlags(importLDBdata),
183+
Name: "import",
184+
Usage: "Imports leveldb-data from an exported RLP dump.",
185+
ArgsUsage: "<dumpfile> <start (optional)",
186+
Flags: []cli.Flag{
187+
utils.DataDirFlag,
188+
utils.SyncModeFlag,
189+
utils.MainnetFlag,
190+
utils.RopstenFlag,
191+
utils.RinkebyFlag,
192+
utils.GoerliFlag,
193+
},
194+
Description: "The import command imports the specific chain data from an RLP encoded stream.",
195+
}
196+
dbExportCmd = cli.Command{
197+
Action: utils.MigrateFlags(exportChaindata),
198+
Name: "export",
199+
Usage: "Exports the chain data into an RLP dump. If the <dumpfile> has .gz suffix, gzip compression will be used.",
200+
ArgsUsage: "<type> <dumpfile>",
201+
Flags: []cli.Flag{
202+
utils.DataDirFlag,
203+
utils.SyncModeFlag,
204+
utils.MainnetFlag,
205+
utils.RopstenFlag,
206+
utils.RinkebyFlag,
207+
utils.GoerliFlag,
208+
},
209+
Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.",
210+
}
175211
)
176212

177213
func removeDB(ctx *cli.Context) error {
@@ -494,3 +530,133 @@ func parseHexOrString(str string) ([]byte, error) {
494530
}
495531
return b, err
496532
}
533+
534+
func importLDBdata(ctx *cli.Context) error {
535+
start := 0
536+
switch ctx.NArg() {
537+
case 1:
538+
break
539+
case 2:
540+
s, err := strconv.Atoi(ctx.Args().Get(1))
541+
if err != nil {
542+
return fmt.Errorf("second arg must be an integer: %v", err)
543+
}
544+
start = s
545+
default:
546+
return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
547+
}
548+
var (
549+
fName = ctx.Args().Get(0)
550+
stack, _ = makeConfigNode(ctx)
551+
interrupt = make(chan os.Signal, 1)
552+
stop = make(chan struct{})
553+
)
554+
defer stack.Close()
555+
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
556+
defer signal.Stop(interrupt)
557+
defer close(interrupt)
558+
go func() {
559+
if _, ok := <-interrupt; ok {
560+
log.Info("Interrupted during ldb import, stopping at next batch")
561+
}
562+
close(stop)
563+
}()
564+
db := utils.MakeChainDatabase(ctx, stack, false)
565+
return utils.ImportLDBData(db, fName, int64(start), stop)
566+
}
567+
568+
type preimageIterator struct {
569+
iter ethdb.Iterator
570+
}
571+
572+
func (iter *preimageIterator) Next() (byte, []byte, []byte, bool) {
573+
for iter.iter.Next() {
574+
key := iter.iter.Key()
575+
if bytes.HasPrefix(key, rawdb.PreimagePrefix) && len(key) == (len(rawdb.PreimagePrefix)+common.HashLength) {
576+
return utils.OpBatchAdd, key, iter.iter.Value(), true
577+
}
578+
}
579+
return 0, nil, nil, false
580+
}
581+
582+
func (iter *preimageIterator) Release() {
583+
iter.iter.Release()
584+
}
585+
586+
type snapshotIterator struct {
587+
init bool
588+
account ethdb.Iterator
589+
storage ethdb.Iterator
590+
}
591+
592+
func (iter *snapshotIterator) Next() (byte, []byte, []byte, bool) {
593+
if !iter.init {
594+
iter.init = true
595+
return utils.OpBatchDel, rawdb.SnapshotRootKey, nil, true
596+
}
597+
for iter.account.Next() {
598+
key := iter.account.Key()
599+
if bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength) {
600+
return utils.OpBatchAdd, key, iter.account.Value(), true
601+
}
602+
}
603+
for iter.storage.Next() {
604+
key := iter.storage.Key()
605+
if bytes.HasPrefix(key, rawdb.SnapshotStoragePrefix) && len(key) == (len(rawdb.SnapshotStoragePrefix)+2*common.HashLength) {
606+
return utils.OpBatchAdd, key, iter.storage.Value(), true
607+
}
608+
}
609+
return 0, nil, nil, false
610+
}
611+
612+
func (iter *snapshotIterator) Release() {
613+
iter.account.Release()
614+
iter.storage.Release()
615+
}
616+
617+
// chainExporters defines the export scheme for all exportable chain data.
618+
var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
619+
"preimage": func(db ethdb.Database) utils.ChainDataIterator {
620+
iter := db.NewIterator(rawdb.PreimagePrefix, nil)
621+
return &preimageIterator{iter: iter}
622+
},
623+
"snapshot": func(db ethdb.Database) utils.ChainDataIterator {
624+
account := db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
625+
storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
626+
return &snapshotIterator{account: account, storage: storage}
627+
},
628+
}
629+
630+
func exportChaindata(ctx *cli.Context) error {
631+
if ctx.NArg() < 2 {
632+
return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
633+
}
634+
// Parse the required chain data type, make sure it's supported.
635+
kind := ctx.Args().Get(0)
636+
kind = strings.ToLower(strings.Trim(kind, " "))
637+
exporter, ok := chainExporters[kind]
638+
if !ok {
639+
var kinds []string
640+
for kind := range chainExporters {
641+
kinds = append(kinds, kind)
642+
}
643+
return fmt.Errorf("invalid data type %s, supported types: %s", kind, strings.Join(kinds, ", "))
644+
}
645+
var (
646+
stack, _ = makeConfigNode(ctx)
647+
interrupt = make(chan os.Signal, 1)
648+
stop = make(chan struct{})
649+
)
650+
defer stack.Close()
651+
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
652+
defer signal.Stop(interrupt)
653+
defer close(interrupt)
654+
go func() {
655+
if _, ok := <-interrupt; ok {
656+
log.Info("Interrupted during db export, stopping at next batch")
657+
}
658+
close(stop)
659+
}()
660+
db := utils.MakeChainDatabase(ctx, stack, true)
661+
return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop)
662+
}

0 commit comments

Comments
 (0)