From f780b0f0a8549c0bdd4d0581767ebab55d03e4cc Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 3 Mar 2020 22:13:20 +0100 Subject: [PATCH] Add chanbackup command --- README.md | 24 +++++++++++ cmd/chantools/chanbackup.go | 83 +++++++++++++++++++++++++++++++++++++ cmd/chantools/main.go | 30 +++++++++++++- go.mod | 1 + 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 cmd/chantools/chanbackup.go diff --git a/README.md b/README.md index 2d894cc..14ce91c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ * [Installation](#installation) * [Overview](#overview) * [Commands](#commands) + + [chanbackup](#chanbackup) + [derivekey](#derivekey) + [dumpbackup](#dumpbackup) + [dumpchannels](#dumpchannels) @@ -57,6 +58,7 @@ Help Options: -h, --help Show this help message Available commands: + chanbackup Create a channel.backup file from a channel database. derivekey Derive a key with a specific derivation path from the BIP32 HD root key. dumpbackup Dump the content of a channel.backup file. dumpchannels Dump all channel information from lnd's channel database. @@ -73,6 +75,28 @@ Available commands: ## Commands +### chanbackup + +```text +Usage: + chantools [OPTIONS] chanbackup [chanbackup-OPTIONS] + +[chanbackup command options] + --rootkey= BIP32 HD root key of the wallet that should be used to create the backup. Leave empty to prompt for lnd 24 word aezeed. + --channeldb= The lnd channel.db file to create the backup from. + --multi_file= The lnd channel.backup file to create. +``` + +This command creates a new channel.backup from a channel.db file. + +Example command: + +```bash +chantools chanbackup --rootkey xprvxxxxxxxxxx \ + --channeldb ~/.lnd/data/graph/mainnet/channel.db \ + --multi_file new_channel_backup.backup +``` + ### derivekey ```text diff --git a/cmd/chantools/chanbackup.go b/cmd/chantools/chanbackup.go new file mode 100644 index 0000000..e3d60ae --- /dev/null +++ b/cmd/chantools/chanbackup.go @@ -0,0 +1,83 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/guggero/chantools/btc" + "github.com/lightningnetwork/lnd/chanbackup" + "github.com/lightningnetwork/lnd/keychain" + "path" + + "github.com/btcsuite/btcutil/hdkeychain" + "github.com/lightningnetwork/lnd/channeldb" +) + +type chanBackupCommand struct { + RootKey string `long:"rootkey" description:"BIP32 HD root key of the wallet that should be used to create the backup. Leave empty to prompt for lnd 24 word aezeed."` + ChannelDB string `long:"channeldb" description:"The lnd channel.db file to create the backup from."` + MultiFile string `long:"multi_file" description:"The lnd channel.backup file to create."` +} + +func (c *chanBackupCommand) Execute(_ []string) error { + setupChainParams(cfg) + + var ( + extendedKey *hdkeychain.ExtendedKey + err error + ) + + // Check that root key is valid or fall back to console input. + switch { + case c.RootKey != "": + extendedKey, err = hdkeychain.NewKeyFromString(c.RootKey) + + default: + extendedKey, _, err = rootKeyFromConsole() + } + if err != nil { + return fmt.Errorf("error reading root key: %v", err) + } + + // Check that we have a backup file. + if c.MultiFile == "" { + return fmt.Errorf("backup file is required") + } + + // Check that we have a channel DB. + if c.ChannelDB == "" { + return fmt.Errorf("channel DB is required") + } + db, err := channeldb.Open(path.Dir(c.ChannelDB)) + if err != nil { + return fmt.Errorf("error opening rescue DB: %v", err) + } + multiFile := chanbackup.NewMultiFile(c.MultiFile) + keyRing := &btc.HDKeyRing{ + ExtendedKey: extendedKey, + ChainParams: chainParams, + } + return createChannelBackup(db, multiFile, keyRing) +} + +func createChannelBackup(db *channeldb.DB, multiFile *chanbackup.MultiFile, + ring keychain.KeyRing) error { + + singles, err := chanbackup.FetchStaticChanBackups(db) + if err != nil { + return fmt.Errorf("error extracting channel backup: %v", err) + } + multi := &chanbackup.Multi{ + Version: chanbackup.DefaultMultiVersion, + StaticBackups: singles, + } + var b bytes.Buffer + err = multi.PackToWriter(&b, ring) + if err != nil { + return fmt.Errorf("unable to pack backup: %v", err) + } + err = multiFile.UpdateAndSwap(b.Bytes()) + if err != nil { + return fmt.Errorf("unable to write backup file: %v", err) + } + return nil +} diff --git a/cmd/chantools/main.go b/cmd/chantools/main.go index ef6607a..e61ff92 100644 --- a/cmd/chantools/main.go +++ b/cmd/chantools/main.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/lightningnetwork/lnd/chanbackup" "io/ioutil" "os" "strings" @@ -12,6 +13,7 @@ import ( "time" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btclog" "github.com/btcsuite/btcutil/hdkeychain" "github.com/guggero/chantools/dataformat" "github.com/jessevdk/go-flags" @@ -115,6 +117,10 @@ func runCommandParser() error { "wallet.db file and optionally extracts the BIP32 HD "+ "root key.", "", &walletInfoCommand{}, ) + _, _ = parser.AddCommand( + "chanbackup", "Create a channel.backup file from a channel "+ + "database.", "", &chanBackupCommand{}, + ) _, err := parser.Parse() return err @@ -258,7 +264,9 @@ func setupChainParams(cfg *config) { } func setupLogging() { - logWriter.RegisterSubLogger("CHAN", log) + setSubLogger("CHAN", log) + addSubLogger("CHDB", channeldb.UseLogger) + addSubLogger("BCKP", chanbackup.UseLogger) err := logWriter.InitLogRotator("./results/chantools.log", 10, 3) if err != nil { panic(err) @@ -269,6 +277,26 @@ func setupLogging() { } } +// addSubLogger is a helper method to conveniently create and register the +// logger of one or more sub systems. +func addSubLogger(subsystem string, useLoggers ...func(btclog.Logger)) { + // Create and register just a single logger to prevent them from + // overwriting each other internally. + logger := build.NewSubLogger(subsystem, logWriter.GenSubLogger) + setSubLogger(subsystem, logger, useLoggers...) +} + +// setSubLogger is a helper method to conveniently register the logger of a sub +// system. +func setSubLogger(subsystem string, logger btclog.Logger, + useLoggers ...func(btclog.Logger)) { + + logWriter.RegisterSubLogger(subsystem, logger) + for _, useLogger := range useLoggers { + useLogger(logger) + } +} + func noConsole() ([]byte, error) { return nil, fmt.Errorf("wallet db requires console access") } diff --git a/go.mod b/go.mod index 38e9cb6..5f823f1 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ require ( git.schwanenlied.me/yawning/bsaes.git v0.0.0-20190320102049-26d1add596b6 // indirect github.com/Yawning/aez v0.0.0-20180408160647-ec7426b44926 // indirect github.com/btcsuite/btcd v0.20.1-beta + github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422 github.com/btcsuite/btcwallet v0.11.0 github.com/btcsuite/btcwallet/walletdb v1.1.0