mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 19:42:23 +00:00
cli: add db dump/restore commands
Dump given number of blocks (from the given offset) to file and restore them from it. Fixes #436.
This commit is contained in:
parent
b533dfceba
commit
c885a69edb
1 changed files with 192 additions and 22 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/config"
|
"github.com/CityOfZion/neo-go/config"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
"github.com/CityOfZion/neo-go/pkg/network"
|
"github.com/CityOfZion/neo-go/pkg/network"
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -18,18 +19,63 @@ import (
|
||||||
|
|
||||||
// NewCommands returns 'node' command.
|
// NewCommands returns 'node' command.
|
||||||
func NewCommands() []cli.Command {
|
func NewCommands() []cli.Command {
|
||||||
return []cli.Command{{
|
var cfgFlags = []cli.Flag{
|
||||||
Name: "node",
|
cli.StringFlag{Name: "config-path"},
|
||||||
Usage: "start a NEO node",
|
cli.BoolFlag{Name: "privnet, p"},
|
||||||
Action: startServer,
|
cli.BoolFlag{Name: "mainnet, m"},
|
||||||
Flags: []cli.Flag{
|
cli.BoolFlag{Name: "testnet, t"},
|
||||||
cli.StringFlag{Name: "config-path"},
|
cli.BoolFlag{Name: "debug, d"},
|
||||||
cli.BoolFlag{Name: "privnet, p"},
|
}
|
||||||
cli.BoolFlag{Name: "mainnet, m"},
|
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
|
||||||
cli.BoolFlag{Name: "testnet, t"},
|
copy(cfgWithCountFlags, cfgFlags)
|
||||||
cli.BoolFlag{Name: "debug, d"},
|
cfgWithCountFlags = append(cfgWithCountFlags,
|
||||||
|
cli.UintFlag{
|
||||||
|
Name: "count, c",
|
||||||
|
Usage: "number of blocks to be processed (default or 0: all chain)",
|
||||||
},
|
},
|
||||||
}}
|
cli.UintFlag{
|
||||||
|
Name: "skip, s",
|
||||||
|
Usage: "number of blocks to skip (default: 0)",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
var cfgCountOutFlags = make([]cli.Flag, len(cfgWithCountFlags))
|
||||||
|
copy(cfgCountOutFlags, cfgWithCountFlags)
|
||||||
|
cfgCountOutFlags = append(cfgCountOutFlags, cli.StringFlag{
|
||||||
|
Name: "out, o",
|
||||||
|
Usage: "Output file (stdout if not given)",
|
||||||
|
})
|
||||||
|
var cfgCountInFlags = make([]cli.Flag, len(cfgWithCountFlags))
|
||||||
|
copy(cfgCountInFlags, cfgWithCountFlags)
|
||||||
|
cfgCountInFlags = append(cfgCountInFlags, cli.StringFlag{
|
||||||
|
Name: "in, i",
|
||||||
|
Usage: "Input file (stdin if not given)",
|
||||||
|
})
|
||||||
|
return []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "node",
|
||||||
|
Usage: "start a NEO node",
|
||||||
|
Action: startServer,
|
||||||
|
Flags: cfgFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "db",
|
||||||
|
Usage: "database manipulations",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "dump",
|
||||||
|
Usage: "dump blocks (starting with block #1) to the file",
|
||||||
|
Action: dumpDB,
|
||||||
|
Flags: cfgCountOutFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "restore",
|
||||||
|
Usage: "restore blocks from the file",
|
||||||
|
Action: restoreDB,
|
||||||
|
Flags: cfgCountInFlags,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newGraceContext() context.Context {
|
func newGraceContext() context.Context {
|
||||||
|
@ -43,26 +89,154 @@ func newGraceContext() context.Context {
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer(ctx *cli.Context) error {
|
// getConfigFromContext looks at path and mode flags in the given config and
|
||||||
net := config.ModePrivNet
|
// returns appropriate config.
|
||||||
|
func getConfigFromContext(ctx *cli.Context) (config.Config, error) {
|
||||||
|
var net = config.ModePrivNet
|
||||||
if ctx.Bool("testnet") {
|
if ctx.Bool("testnet") {
|
||||||
net = config.ModeTestNet
|
net = config.ModeTestNet
|
||||||
}
|
}
|
||||||
if ctx.Bool("mainnet") {
|
if ctx.Bool("mainnet") {
|
||||||
net = config.ModeMainNet
|
net = config.ModeMainNet
|
||||||
}
|
}
|
||||||
|
|
||||||
grace, cancel := context.WithCancel(newGraceContext())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
configPath := "./config"
|
configPath := "./config"
|
||||||
if argCp := ctx.String("config-path"); argCp != "" {
|
if argCp := ctx.String("config-path"); argCp != "" {
|
||||||
configPath = argCp
|
configPath = argCp
|
||||||
}
|
}
|
||||||
cfg, err := config.Load(configPath, net)
|
return config.Load(configPath, net)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleLoggingParams enables debugging output is that's requested by the user.
|
||||||
|
func handleLoggingParams(ctx *cli.Context) {
|
||||||
|
if ctx.Bool("debug") {
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCountAndSkipFromContext(ctx *cli.Context) (uint32, uint32) {
|
||||||
|
count := uint32(ctx.Uint("count"))
|
||||||
|
skip := uint32(ctx.Uint("skip"))
|
||||||
|
return count, skip
|
||||||
|
}
|
||||||
|
|
||||||
|
func dumpDB(ctx *cli.Context) error {
|
||||||
|
cfg, err := getConfigFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
handleLoggingParams(ctx)
|
||||||
|
count, skip := getCountAndSkipFromContext(ctx)
|
||||||
|
|
||||||
|
var outStream = os.Stdout
|
||||||
|
if out := ctx.String("out"); out != "" {
|
||||||
|
outStream, err = os.Create(out)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer outStream.Close()
|
||||||
|
writer := io.NewBinWriterFromIO(outStream)
|
||||||
|
|
||||||
|
grace, cancel := context.WithCancel(newGraceContext())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
chain, err := initBlockChain(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
go chain.Run(grace)
|
||||||
|
|
||||||
|
chainHeight := chain.BlockHeight()
|
||||||
|
if skip+count > chainHeight {
|
||||||
|
return cli.NewExitError(fmt.Errorf("chain is not that high (%d) to dump %d blocks starting from %d", chainHeight, count, skip), 1)
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
count = chainHeight - skip
|
||||||
|
}
|
||||||
|
writer.WriteLE(count)
|
||||||
|
for i := skip + 1; i <= count; i++ {
|
||||||
|
bh := chain.GetHeaderHash(int(i))
|
||||||
|
b, err := chain.GetBlock(bh)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("failed to get block %d: %s", i, err), 1)
|
||||||
|
}
|
||||||
|
b.EncodeBinary(writer)
|
||||||
|
if writer.Err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func restoreDB(ctx *cli.Context) error {
|
||||||
|
cfg, err := getConfigFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
handleLoggingParams(ctx)
|
||||||
|
count, skip := getCountAndSkipFromContext(ctx)
|
||||||
|
|
||||||
|
var inStream = os.Stdin
|
||||||
|
if in := ctx.String("in"); in != "" {
|
||||||
|
inStream, err = os.Open(in)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer inStream.Close()
|
||||||
|
reader := io.NewBinReaderFromIO(inStream)
|
||||||
|
|
||||||
|
grace, cancel := context.WithCancel(newGraceContext())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
chain, err := initBlockChain(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go chain.Run(grace)
|
||||||
|
|
||||||
|
var allBlocks uint32
|
||||||
|
reader.ReadLE(&allBlocks)
|
||||||
|
if reader.Err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
if skip+count > allBlocks {
|
||||||
|
return cli.NewExitError(fmt.Errorf("input file has only %d blocks, can't read %d starting from %d", allBlocks, count, skip), 1)
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
count = allBlocks
|
||||||
|
}
|
||||||
|
i := uint32(0)
|
||||||
|
for ; i < skip; i++ {
|
||||||
|
b := &core.Block{}
|
||||||
|
b.DecodeBinary(reader)
|
||||||
|
if reader.Err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ; i < count; i++ {
|
||||||
|
b := &core.Block{}
|
||||||
|
b.DecodeBinary(reader)
|
||||||
|
if reader.Err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
err := chain.AddBlock(b)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("failed to add block %d: %s", i, err), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startServer(ctx *cli.Context) error {
|
||||||
|
cfg, err := getConfigFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
handleLoggingParams(ctx)
|
||||||
|
|
||||||
|
grace, cancel := context.WithCancel(newGraceContext())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
serverConfig := network.NewServerConfig(cfg)
|
serverConfig := network.NewServerConfig(cfg)
|
||||||
|
|
||||||
|
@ -71,10 +245,6 @@ func startServer(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Bool("debug") {
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
server := network.NewServer(serverConfig, chain)
|
server := network.NewServer(serverConfig, chain)
|
||||||
rpcServer := rpc.NewServer(chain, cfg.ApplicationConfiguration.RPCPort, server)
|
rpcServer := rpc.NewServer(chain, cfg.ApplicationConfiguration.RPCPort, server)
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
|
|
Loading…
Reference in a new issue