Skip to content

Commit dd708c1

Browse files
authored
Merge pull request #16319 from rjl493456442/dump_preimages
cmd: implement preimage dump and import cmds
2 parents 7c131f4 + 495bdb0 commit dd708c1

File tree

4 files changed

+160
-4
lines changed

4 files changed

+160
-4
lines changed

cmd/geth/chaincmd.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,34 @@ Requires a first argument of the file to write to.
9595
Optional second and third arguments control the first and
9696
last block to write. In this mode, the file will be appended
9797
if already existing.`,
98+
}
99+
importPreimagesCommand = cli.Command{
100+
Action: utils.MigrateFlags(importPreimages),
101+
Name: "import-preimages",
102+
Usage: "Import the preimage database from an RLP stream",
103+
ArgsUsage: "<datafile>",
104+
Flags: []cli.Flag{
105+
utils.DataDirFlag,
106+
utils.CacheFlag,
107+
utils.LightModeFlag,
108+
},
109+
Category: "BLOCKCHAIN COMMANDS",
110+
Description: `
111+
The import-preimages command imports hash preimages from an RLP encoded stream.`,
112+
}
113+
exportPreimagesCommand = cli.Command{
114+
Action: utils.MigrateFlags(exportPreimages),
115+
Name: "export-preimages",
116+
Usage: "Export the preimage database into an RLP stream",
117+
ArgsUsage: "<dumpfile>",
118+
Flags: []cli.Flag{
119+
utils.DataDirFlag,
120+
utils.CacheFlag,
121+
utils.LightModeFlag,
122+
},
123+
Category: "BLOCKCHAIN COMMANDS",
124+
Description: `
125+
The export-preimages command export hash preimages to an RLP encoded stream`,
98126
}
99127
copydbCommand = cli.Command{
100128
Action: utils.MigrateFlags(copyDb),
@@ -299,7 +327,39 @@ func exportChain(ctx *cli.Context) error {
299327
if err != nil {
300328
utils.Fatalf("Export error: %v\n", err)
301329
}
302-
fmt.Printf("Export done in %v", time.Since(start))
330+
fmt.Printf("Export done in %v\n", time.Since(start))
331+
return nil
332+
}
333+
334+
// importPreimages imports preimage data from the specified file.
335+
func importPreimages(ctx *cli.Context) error {
336+
if len(ctx.Args()) < 1 {
337+
utils.Fatalf("This command requires an argument.")
338+
}
339+
stack := makeFullNode(ctx)
340+
diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase)
341+
342+
start := time.Now()
343+
if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil {
344+
utils.Fatalf("Export error: %v\n", err)
345+
}
346+
fmt.Printf("Export done in %v\n", time.Since(start))
347+
return nil
348+
}
349+
350+
// exportPreimages dumps the preimage data to specified json file in streaming way.
351+
func exportPreimages(ctx *cli.Context) error {
352+
if len(ctx.Args()) < 1 {
353+
utils.Fatalf("This command requires an argument.")
354+
}
355+
stack := makeFullNode(ctx)
356+
diskdb := utils.MakeChainDatabase(ctx, stack).(*ethdb.LDBDatabase)
357+
358+
start := time.Now()
359+
if err := utils.ExportPreimages(diskdb, ctx.Args().First()); err != nil {
360+
utils.Fatalf("Export error: %v\n", err)
361+
}
362+
fmt.Printf("Export done in %v\n", time.Since(start))
303363
return nil
304364
}
305365

cmd/geth/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ func init() {
155155
initCommand,
156156
importCommand,
157157
exportCommand,
158+
importPreimagesCommand,
159+
exportPreimagesCommand,
158160
copydbCommand,
159161
removedbCommand,
160162
dumpCommand,

cmd/utils/cmd.go

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ import (
2727
"strings"
2828
"syscall"
2929

30+
"github.com/ethereum/go-ethereum/common"
3031
"github.com/ethereum/go-ethereum/core"
3132
"github.com/ethereum/go-ethereum/core/types"
33+
"github.com/ethereum/go-ethereum/crypto"
34+
"github.com/ethereum/go-ethereum/ethdb"
3235
"github.com/ethereum/go-ethereum/internal/debug"
3336
"github.com/ethereum/go-ethereum/log"
3437
"github.com/ethereum/go-ethereum/node"
@@ -105,6 +108,8 @@ func ImportChain(chain *core.BlockChain, fn string) error {
105108
}
106109

107110
log.Info("Importing blockchain", "file", fn)
111+
112+
// Open the file handle and potentially unwrap the gzip stream
108113
fh, err := os.Open(fn)
109114
if err != nil {
110115
return err
@@ -180,8 +185,12 @@ func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block
180185
return nil
181186
}
182187

188+
// ExportChain exports a blockchain into the specified file, truncating any data
189+
// already present in the file.
183190
func ExportChain(blockchain *core.BlockChain, fn string) error {
184191
log.Info("Exporting blockchain", "file", fn)
192+
193+
// Open the file handle and potentially wrap with a gzip stream
185194
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
186195
if err != nil {
187196
return err
@@ -193,7 +202,7 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
193202
writer = gzip.NewWriter(writer)
194203
defer writer.(*gzip.Writer).Close()
195204
}
196-
205+
// Iterate over the blocks and export them
197206
if err := blockchain.Export(writer); err != nil {
198207
return err
199208
}
@@ -202,9 +211,12 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
202211
return nil
203212
}
204213

214+
// ExportAppendChain exports a blockchain into the specified file, appending to
215+
// the file if data already exists in it.
205216
func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
206217
log.Info("Exporting blockchain", "file", fn)
207-
// TODO verify mode perms
218+
219+
// Open the file handle and potentially wrap with a gzip stream
208220
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
209221
if err != nil {
210222
return err
@@ -216,10 +228,86 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
216228
writer = gzip.NewWriter(writer)
217229
defer writer.(*gzip.Writer).Close()
218230
}
219-
231+
// Iterate over the blocks and export them
220232
if err := blockchain.ExportN(writer, first, last); err != nil {
221233
return err
222234
}
223235
log.Info("Exported blockchain to", "file", fn)
224236
return nil
225237
}
238+
239+
// ImportPreimages imports a batch of exported hash preimages into the database.
240+
func ImportPreimages(db *ethdb.LDBDatabase, fn string) error {
241+
log.Info("Importing preimages", "file", fn)
242+
243+
// Open the file handle and potentially unwrap the gzip stream
244+
fh, err := os.Open(fn)
245+
if err != nil {
246+
return err
247+
}
248+
defer fh.Close()
249+
250+
var reader io.Reader = fh
251+
if strings.HasSuffix(fn, ".gz") {
252+
if reader, err = gzip.NewReader(reader); err != nil {
253+
return err
254+
}
255+
}
256+
stream := rlp.NewStream(reader, 0)
257+
258+
// Import the preimages in batches to prevent disk trashing
259+
preimages := make(map[common.Hash][]byte)
260+
261+
for {
262+
// Read the next entry and ensure it's not junk
263+
var blob []byte
264+
265+
if err := stream.Decode(&blob); err != nil {
266+
if err == io.EOF {
267+
break
268+
}
269+
return err
270+
}
271+
// Accumulate the preimages and flush when enough ws gathered
272+
preimages[crypto.Keccak256Hash(blob)] = common.CopyBytes(blob)
273+
if len(preimages) > 1024 {
274+
if err := core.WritePreimages(db, 0, preimages); err != nil {
275+
return err
276+
}
277+
preimages = make(map[common.Hash][]byte)
278+
}
279+
}
280+
// Flush the last batch preimage data
281+
if len(preimages) > 0 {
282+
return core.WritePreimages(db, 0, preimages)
283+
}
284+
return nil
285+
}
286+
287+
// ExportPreimages exports all known hash preimages into the specified file,
288+
// truncating any data already present in the file.
289+
func ExportPreimages(db *ethdb.LDBDatabase, fn string) error {
290+
log.Info("Exporting preimages", "file", fn)
291+
292+
// Open the file handle and potentially wrap with a gzip stream
293+
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
294+
if err != nil {
295+
return err
296+
}
297+
defer fh.Close()
298+
299+
var writer io.Writer = fh
300+
if strings.HasSuffix(fn, ".gz") {
301+
writer = gzip.NewWriter(writer)
302+
defer writer.(*gzip.Writer).Close()
303+
}
304+
// Iterate over the preimages and export them
305+
it := db.NewIteratorWithPrefix([]byte("secure-key-"))
306+
for it.Next() {
307+
if err := rlp.Encode(writer, it.Value()); err != nil {
308+
return err
309+
}
310+
}
311+
log.Info("Exported preimages", "file", fn)
312+
return nil
313+
}

ethdb/database.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/syndtr/goleveldb/leveldb/filter"
3030
"github.com/syndtr/goleveldb/leveldb/iterator"
3131
"github.com/syndtr/goleveldb/leveldb/opt"
32+
"github.com/syndtr/goleveldb/leveldb/util"
3233
)
3334

3435
var OpenFileLimit = 64
@@ -121,6 +122,11 @@ func (db *LDBDatabase) NewIterator() iterator.Iterator {
121122
return db.db.NewIterator(nil, nil)
122123
}
123124

125+
// NewIteratorWithPrefix returns a iterator to iterate over subset of database content with a particular prefix.
126+
func (db *LDBDatabase) NewIteratorWithPrefix(prefix []byte) iterator.Iterator {
127+
return db.db.NewIterator(util.BytesPrefix(prefix), nil)
128+
}
129+
124130
func (db *LDBDatabase) Close() {
125131
// Stop the metrics collection to avoid internal database races
126132
db.quitLock.Lock()

0 commit comments

Comments
 (0)