Skip to content

Commit 3453329

Browse files
Arachnidkaralabe
authored andcommitted
cmd: Added support for copying data to another DB instance
1 parent 3680cd5 commit 3453329

File tree

2 files changed

+196
-1
lines changed

2 files changed

+196
-1
lines changed

cmd/geth/chaincmd.go

Lines changed: 195 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package main
1919
import (
2020
"encoding/json"
2121
"fmt"
22+
"math/big"
2223
"os"
2324
"runtime"
2425
"strconv"
@@ -31,7 +32,9 @@ import (
3132
"github.com/ethereum/go-ethereum/core"
3233
"github.com/ethereum/go-ethereum/core/state"
3334
"github.com/ethereum/go-ethereum/core/types"
35+
"github.com/ethereum/go-ethereum/eth/downloader"
3436
"github.com/ethereum/go-ethereum/ethdb"
37+
"github.com/ethereum/go-ethereum/event"
3538
"github.com/ethereum/go-ethereum/log"
3639
"github.com/ethereum/go-ethereum/trie"
3740
"github.com/syndtr/goleveldb/leveldb/util"
@@ -71,7 +74,7 @@ It expects the genesis file as argument.`,
7174
The import command imports blocks from an RLP-encoded form. The form can be one file
7275
with several RLP-encoded blocks, or several files can be used.
7376
74-
If only one file is used, import error will result in failure. If several files are used,
77+
If only one file is used, import error will result in failure. If several files are used,
7578
processing will proceed even if an individual RLP-file import failure occurs.`,
7679
}
7780
exportCommand = cli.Command{
@@ -90,6 +93,21 @@ Requires a first argument of the file to write to.
9093
Optional second and third arguments control the first and
9194
last block to write. In this mode, the file will be appended
9295
if already existing.`,
96+
}
97+
copydbCommand = cli.Command{
98+
Action: utils.MigrateFlags(copyDb),
99+
Name: "copydb",
100+
Usage: "Copy from one chain DB into another using the downloader",
101+
ArgsUsage: "<filename>",
102+
Flags: []cli.Flag{
103+
utils.DataDirFlag,
104+
utils.CacheFlag,
105+
utils.SyncModeFlag,
106+
utils.FakePoWFlag,
107+
},
108+
Category: "BLOCKCHAIN COMMANDS",
109+
Description: `
110+
The first argument must be the directory containing the blockchain to download from`,
93111
}
94112
removedbCommand = cli.Command{
95113
Action: utils.MigrateFlags(removeDB),
@@ -268,6 +286,182 @@ func exportChain(ctx *cli.Context) error {
268286
return nil
269287
}
270288

289+
type localPeer struct {
290+
chainDb ethdb.Database
291+
hc *core.HeaderChain
292+
dl *downloader.Downloader
293+
}
294+
295+
func (lp *localPeer) Head() (common.Hash, *big.Int) {
296+
header := lp.hc.CurrentHeader()
297+
return header.Hash(), header.Number
298+
}
299+
300+
func (lp *localPeer) RequestHeadersByHash(hash common.Hash, amount int, skip int, reverse bool) error {
301+
var (
302+
headers []*types.Header
303+
unknown bool
304+
)
305+
306+
for !unknown && len(headers) < amount {
307+
origin := lp.hc.GetHeaderByHash(hash)
308+
if origin == nil {
309+
break
310+
}
311+
312+
number := origin.Number.Uint64()
313+
headers = append(headers, origin)
314+
if reverse {
315+
for i := 0; i < int(skip)+1; i++ {
316+
if header := lp.hc.GetHeader(hash, number); header != nil {
317+
hash = header.ParentHash
318+
number--
319+
} else {
320+
unknown = true
321+
break
322+
}
323+
}
324+
} else {
325+
var (
326+
current = origin.Number.Uint64()
327+
next = current + uint64(skip) + 1
328+
)
329+
if header := lp.hc.GetHeaderByNumber(next); header != nil {
330+
if lp.hc.GetBlockHashesFromHash(header.Hash(), uint64(skip+1))[skip] == hash {
331+
hash = header.Hash()
332+
} else {
333+
unknown = true
334+
}
335+
} else {
336+
unknown = true
337+
}
338+
}
339+
}
340+
341+
lp.dl.DeliverHeaders("local", headers)
342+
return nil
343+
}
344+
345+
func (lp *localPeer) RequestHeadersByNumber(num uint64, amount int, skip int, reverse bool) error {
346+
var (
347+
headers []*types.Header
348+
unknown bool
349+
)
350+
351+
for !unknown && len(headers) < amount {
352+
origin := lp.hc.GetHeaderByNumber(num)
353+
if origin == nil {
354+
break
355+
}
356+
357+
if reverse {
358+
if num >= uint64(skip+1) {
359+
num -= uint64(skip + 1)
360+
} else {
361+
unknown = true
362+
}
363+
} else {
364+
num += uint64(skip + 1)
365+
}
366+
headers = append(headers, origin)
367+
}
368+
369+
lp.dl.DeliverHeaders("local", headers)
370+
return nil
371+
}
372+
373+
func (lp *localPeer) RequestBodies(hashes []common.Hash) error {
374+
var (
375+
transactions [][]*types.Transaction
376+
uncles [][]*types.Header
377+
)
378+
379+
for _, hash := range hashes {
380+
block := core.GetBlock(lp.chainDb, hash, lp.hc.GetBlockNumber(hash))
381+
transactions = append(transactions, block.Transactions())
382+
uncles = append(uncles, block.Uncles())
383+
}
384+
385+
lp.dl.DeliverBodies("local", transactions, uncles)
386+
return nil
387+
}
388+
389+
func (lp *localPeer) RequestReceipts(hashes []common.Hash) error {
390+
var receipts [][]*types.Receipt
391+
392+
for _, hash := range hashes {
393+
receipts = append(receipts, core.GetBlockReceipts(lp.chainDb, hash, lp.hc.GetBlockNumber(hash)))
394+
}
395+
396+
lp.dl.DeliverReceipts("local", receipts)
397+
return nil
398+
}
399+
400+
func (lp *localPeer) RequestNodeData(hashes []common.Hash) error {
401+
var data [][]byte
402+
403+
for _, hash := range hashes {
404+
if entry, err := lp.chainDb.Get(hash.Bytes()); err == nil {
405+
data = append(data, entry)
406+
}
407+
}
408+
409+
lp.dl.DeliverNodeData("local", data)
410+
return nil
411+
}
412+
413+
func copyDb(ctx *cli.Context) error {
414+
if len(ctx.Args()) != 1 {
415+
utils.Fatalf("This command requires an argument.")
416+
}
417+
418+
stack := makeFullNode(ctx)
419+
chain, chainDb := utils.MakeChain(ctx, stack)
420+
start := time.Now()
421+
422+
syncmode := *utils.GlobalTextMarshaler(ctx, utils.SyncModeFlag.Name).(*downloader.SyncMode)
423+
mux := new(event.TypeMux)
424+
dl := downloader.New(syncmode, chainDb, mux, chain, nil, nil)
425+
426+
var err error
427+
filename := ctx.Args().First()
428+
cache := ctx.GlobalInt(utils.CacheFlag.Name)
429+
handles := 256
430+
localdb, err := ethdb.NewLDBDatabase(filename, cache, handles)
431+
if err != nil {
432+
return err
433+
}
434+
435+
hc, err := core.NewHeaderChain(localdb, chain.Config(), chain.Engine(), func() bool { return false })
436+
if err != nil {
437+
return err
438+
}
439+
440+
peer := &localPeer{localdb, hc, dl}
441+
if err := dl.RegisterPeer("local", 63, peer); err != nil {
442+
return err
443+
}
444+
445+
currentHeader := hc.CurrentHeader()
446+
if err := dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64()), syncmode); err != nil {
447+
return err
448+
}
449+
for dl.Synchronising() {
450+
time.Sleep(10 * time.Millisecond)
451+
}
452+
453+
fmt.Printf("Database copy done in %v", time.Since(start))
454+
455+
start = time.Now()
456+
fmt.Println("Compacting entire database...")
457+
if err = chainDb.(*ethdb.LDBDatabase).LDB().CompactRange(util.Range{}); err != nil {
458+
utils.Fatalf("Compaction failed: %v", err)
459+
}
460+
fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
461+
462+
return nil
463+
}
464+
271465
func removeDB(ctx *cli.Context) error {
272466
stack, _ := makeConfigNode(ctx)
273467

cmd/geth/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ func init() {
146146
initCommand,
147147
importCommand,
148148
exportCommand,
149+
copydbCommand,
149150
removedbCommand,
150151
dumpCommand,
151152
// See monitorcmd.go:

0 commit comments

Comments
 (0)