Refactor commands into cmd package

pull/3/head
Oliver Gugger 4 years ago
parent 9508a0d8e0
commit 6884fa399f
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -34,11 +34,11 @@ unit:
build:
@$(call print, "Building chantools.")
$(GOBUILD) $(PKG)/cmd/chantools
$(GOBUILD) ./...
install:
@$(call print, "Installing chantools.")
$(GOINSTALL) $(PKG)/cmd/chantools
$(GOINSTALL) ./...
fmt:
@$(call print, "Formatting source.")

@ -1,4 +1,4 @@
package chantools
package main
import (
"fmt"
@ -8,6 +8,27 @@ import (
"github.com/guggero/chantools/btc"
)
type deriveKeyCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to derive the key from."`
Path string `long:"path" description:"The BIP32 derivation path to derive. Must start with \"m/\"."`
Neuter bool `long:"neuter" description:"Do not output the private key, just the public key."`
}
func (c *deriveKeyCommand) Execute(_ []string) error {
setupChainParams(cfg)
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
return deriveKey(extendedKey, c.Path, c.Neuter)
}
func deriveKey(extendedKey *hdkeychain.ExtendedKey, path string,
neuter bool) error {

@ -0,0 +1,76 @@
package main
import (
"fmt"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/davecgh/go-spew/spew"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dump"
"github.com/lightningnetwork/lnd/chanbackup"
)
type dumpBackupCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key of the wallet that was used to create the backup."`
MultiFile string `long:"multi_file" description:"The lnd channel.backup file to dump."`
}
func (c *dumpBackupCommand) Execute(_ []string) error {
setupChainParams(cfg)
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Check that we have a backup file.
if c.MultiFile == "" {
return fmt.Errorf("backup file is required")
}
multiFile := chanbackup.NewMultiFile(c.MultiFile)
multi, err := multiFile.ExtractMulti(&btc.ChannelBackupEncryptionRing{
ExtendedKey: extendedKey,
ChainParams: chainParams,
})
if err != nil {
return fmt.Errorf("could not extract multi file: %v", err)
}
return dumpChannelBackup(multi)
}
func dumpChannelBackup(multi *chanbackup.Multi) error {
dumpSingles := make([]dump.BackupSingle, len(multi.StaticBackups))
for idx, single := range multi.StaticBackups {
dumpSingles[idx] = dump.BackupSingle{
Version: single.Version,
IsInitiator: single.IsInitiator,
ChainHash: single.ChainHash.String(),
FundingOutpoint: single.FundingOutpoint.String(),
ShortChannelID: single.ShortChannelID,
RemoteNodePub: dump.PubKeyToString(
single.RemoteNodePub,
),
Addresses: single.Addresses,
Capacity: single.Capacity,
LocalChanCfg: dump.ToChannelConfig(
single.LocalChanCfg,
),
RemoteChanCfg: dump.ToChannelConfig(
single.RemoteChanCfg,
),
ShaChainRootDesc: dump.ToKeyDescriptor(
single.ShaChainRootDesc,
),
}
}
spew.Dump(dump.BackupMulti{
Version: multi.Version,
StaticBackups: dumpSingles,
})
return nil
}

@ -1,8 +1,10 @@
package chantools
package main
import (
"bytes"
"encoding/hex"
"fmt"
"path"
"github.com/davecgh/go-spew/spew"
"github.com/guggero/chantools/dump"
@ -10,6 +12,22 @@ import (
"github.com/lightningnetwork/lnd/input"
)
type dumpChannelsCommand struct {
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to dump the channels from."`
}
func (c *dumpChannelsCommand) Execute(_ []string) error {
// 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)
}
return dumpChannelInfo(db)
}
func dumpChannelInfo(chanDb *channeldb.DB) error {
channels, err := chanDb.FetchAllChannels()
if err != nil {

@ -1,4 +1,4 @@
package chantools
package main
import (
"bytes"
@ -7,6 +7,7 @@ import (
"fmt"
"io"
"io/ioutil"
"path"
"time"
"github.com/btcsuite/btcd/txscript"
@ -17,6 +18,38 @@ import (
"github.com/lightningnetwork/lnd/input"
)
type forceCloseCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."`
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to use for force-closing channels."`
Publish bool `long:"publish" description:"Should the force-closing TX be published to the chain API?"`
}
func (c *forceCloseCommand) Execute(_ []string) error {
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Check that we have a channel DB.
if c.ChannelDB == "" {
return fmt.Errorf("rescue DB is required")
}
db, err := channeldb.Open(path.Dir(c.ChannelDB))
if err != nil {
return fmt.Errorf("error opening rescue DB: %v", err)
}
// Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg)
if err != nil {
return err
}
return forceCloseChannels(extendedKey, entries, db, c.Publish)
}
func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey,
entries []*dataformat.SummaryEntry, chanDb *channeldb.DB,
publish bool) error {

@ -1,16 +1,163 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/guggero/chantools"
"github.com/btcsuite/btcd/chaincfg"
"github.com/guggero/chantools/dataformat"
"github.com/jessevdk/go-flags"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/channeldb"
)
const (
defaultApiUrl = "https://blockstream.info/api"
)
type config struct {
Testnet bool `long:"testnet" description:"Set to true if testnet parameters should be used."`
ApiUrl string `long:"apiurl" description:"API URL to use (must be esplora compatible)."`
ListChannels string `long:"listchannels" description:"The channel input is in the format of lncli's listchannels format. Specify '-' to read from stdin."`
PendingChannels string `long:"pendingchannels" description:"The channel input is in the format of lncli's pendingchannels format. Specify '-' to read from stdin."`
FromSummary string `long:"fromsummary" description:"The channel input is in the format of this tool's channel summary. Specify '-' to read from stdin."`
FromChannelDB string `long:"fromchanneldb" description:"The channel input is in the format of an lnd channel.db file."`
}
var (
logWriter = build.NewRotatingLogWriter()
log = build.NewSubLogger("CHAN", logWriter.GenSubLogger)
cfg = &config{
ApiUrl: defaultApiUrl,
}
chainParams = &chaincfg.MainNetParams
)
func main() {
if err := chantools.Main(); err != nil {
fmt.Printf("Error running chantools: %v\n", err)
err := Main()
if err == nil {
return
}
_, ok := err.(*flags.Error)
if !ok {
fmt.Printf("Error running chantools: %v\n", err)
}
os.Exit(0)
}
func Main() error {
setupLogging()
// Parse command line.
parser := flags.NewParser(cfg, flags.Default)
_, _ = parser.AddCommand(
"summary", "Compile a summary about the current state of "+
"channels.", "", &summaryCommand{},
)
_, _ = parser.AddCommand(
"rescueclosed", "Try finding the private keys for funds that "+
"are in outputs of remotely force-closed channels.", "",
&rescueClosedCommand{},
)
_, _ = parser.AddCommand(
"forceclose", "Force-close the last state that is in the "+
"channel.db provided.", "", &forceCloseCommand{},
)
_, _ = parser.AddCommand(
"sweeptimelock", "Sweep the force-closed state after the time "+
"lock has expired.", "", &sweepTimeLockCommand{},
)
_, _ = parser.AddCommand(
"dumpchannels", "Dump all channel information from lnd's "+
"channel database.", "", &dumpChannelsCommand{},
)
_, _ = parser.AddCommand(
"showrootkey", "Extract and show the BIP32 HD root key from "+
"the 24 word lnd aezeed.", "", &showRootKeyCommand{},
)
_, _ = parser.AddCommand(
"dumpbackup", "Dump the content of a channel.backup file.", "",
&dumpBackupCommand{},
)
_, _ = parser.AddCommand(
"derivekey", "Derive a key with a specific derivation path "+
"from the BIP32 HD root key.", "", &deriveKeyCommand{},
)
_, err := parser.Parse()
return err
}
func parseInput(cfg *config) ([]*dataformat.SummaryEntry, error) {
var (
content []byte
err error
target dataformat.InputFile
)
switch {
case cfg.ListChannels != "":
content, err = readInput(cfg.ListChannels)
target = &dataformat.ListChannelsFile{}
case cfg.PendingChannels != "":
content, err = readInput(cfg.PendingChannels)
target = &dataformat.PendingChannelsFile{}
case cfg.FromSummary != "":
content, err = readInput(cfg.FromSummary)
target = &dataformat.SummaryEntryFile{}
case cfg.FromChannelDB != "":
db, err := channeldb.Open(cfg.FromChannelDB)
if err != nil {
return nil, fmt.Errorf("error opening channel DB: %v",
err)
}
target = &dataformat.ChannelDBFile{DB: db}
return target.AsSummaryEntries()
default:
return nil, fmt.Errorf("an input file must be specified")
}
if err != nil {
return nil, err
}
decoder := json.NewDecoder(bytes.NewReader(content))
err = decoder.Decode(&target)
if err != nil {
return nil, err
}
return target.AsSummaryEntries()
}
func readInput(input string) ([]byte, error) {
if strings.TrimSpace(input) == "-" {
return ioutil.ReadAll(os.Stdin)
}
return ioutil.ReadFile(input)
}
func setupChainParams(cfg *config) {
if cfg.Testnet {
chainParams = &chaincfg.TestNet3Params
}
}
func setupLogging() {
logWriter.RegisterSubLogger("CHAN", log)
err := logWriter.InitLogRotator("./results/chantools.log", 10, 3)
if err != nil {
panic(err)
}
err = build.ParseAndSetDebugLevels("trace", logWriter)
if err != nil {
panic(err)
}
}

@ -1,17 +1,18 @@
package chantools
package main
import (
"crypto/subtle"
"encoding/json"
"errors"
"fmt"
"github.com/guggero/chantools/btc"
"io/ioutil"
"path"
"time"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dataformat"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input"
@ -30,6 +31,38 @@ type cacheEntry struct {
pubKey *btcec.PublicKey
}
type rescueClosedCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."`
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to use for rescuing force-closed channels."`
}
func (c *rescueClosedCommand) Execute(_ []string) error {
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Check that we have a channel DB.
if c.ChannelDB == "" {
return fmt.Errorf("rescue DB is required")
}
db, err := channeldb.Open(path.Dir(c.ChannelDB))
if err != nil {
return fmt.Errorf("error opening rescue DB: %v", err)
}
// Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg)
if err != nil {
return err
}
return rescueClosedChannels(extendedKey, entries, db)
}
func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey,
entries []*dataformat.SummaryEntry, chanDb *channeldb.DB) error {

@ -0,0 +1,66 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
"syscall"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/aezeed"
"golang.org/x/crypto/ssh/terminal"
)
type showRootKeyCommand struct{}
func (c *showRootKeyCommand) Execute(_ []string) error {
// We'll now prompt the user to enter in their 24-word mnemonic.
fmt.Printf("Input your 24-word mnemonic separated by spaces: ")
reader := bufio.NewReader(os.Stdin)
mnemonicStr, err := reader.ReadString('\n')
if err != nil {
return err
}
// We'll trim off extra spaces, and ensure the mnemonic is all
// lower case, then populate our request.
mnemonicStr = strings.TrimSpace(mnemonicStr)
mnemonicStr = strings.ToLower(mnemonicStr)
cipherSeedMnemonic := strings.Split(mnemonicStr, " ")
fmt.Println()
if len(cipherSeedMnemonic) != 24 {
return fmt.Errorf("wrong cipher seed mnemonic "+
"length: got %v words, expecting %v words",
len(cipherSeedMnemonic), 24)
}
// Additionally, the user may have a passphrase, that will also
// need to be provided so the daemon can properly decipher the
// cipher seed.
fmt.Printf("Input your cipher seed passphrase (press enter if " +
"your seed doesn't have a passphrase): ")
passphrase, err := terminal.ReadPassword(syscall.Stdin)
if err != nil {
return err
}
var mnemonic aezeed.Mnemonic
copy(mnemonic[:], cipherSeedMnemonic[:])
// If we're unable to map it back into the ciphertext, then either the
// mnemonic is wrong, or the passphrase is wrong.
cipherSeed, err := mnemonic.ToCipherSeed(passphrase)
if err != nil {
return err
}
rootKey, err := hdkeychain.NewMaster(cipherSeed.Entropy[:], chainParams)
if err != nil {
return fmt.Errorf("failed to derive master extended key")
}
fmt.Printf("\nYour BIP32 HD root key is: %s\n", rootKey.String())
return nil
}

@ -1,4 +1,4 @@
package chantools
package main
import (
"encoding/json"
@ -10,6 +10,17 @@ import (
"github.com/guggero/chantools/dataformat"
)
type summaryCommand struct{}
func (c *summaryCommand) Execute(_ []string) error {
// Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg)
if err != nil {
return err
}
return summarizeChannels(cfg.ApiUrl, entries)
}
func summarizeChannels(apiUrl string,
channels []*dataformat.SummaryEntry) error {

@ -1,4 +1,4 @@
package chantools
package main
import (
"bytes"
@ -19,6 +19,44 @@ const (
feeSatPerByte = 2
)
type sweepTimeLockCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."`
Publish bool `long:"publish" description:"Should the sweep TX be published to the chain API?"`
SweepAddr string `long:"sweepaddr" description:"The address the funds should be sweeped to"`
MaxCsvLimit int `long:"maxcsvlimit" description:"Maximum CSV limit to use. (default 2000)"`
}
func (c *sweepTimeLockCommand) Execute(_ []string) error {
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Make sure sweep addr is set.
if c.SweepAddr == "" {
return fmt.Errorf("sweep addr is required")
}
// Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg)
if err != nil {
return err
}
// Set default value
if c.MaxCsvLimit == 0 {
c.MaxCsvLimit = 2000
}
return sweepTimeLock(
extendedKey, cfg.ApiUrl, entries, c.SweepAddr, c.MaxCsvLimit,
c.Publish,
)
}
func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiUrl string,
entries []*dataformat.SummaryEntry, sweepAddr string, maxCsvTimeout int,
publish bool) error {

@ -1,40 +0,0 @@
package chantools
import (
"github.com/davecgh/go-spew/spew"
"github.com/guggero/chantools/dump"
"github.com/lightningnetwork/lnd/chanbackup"
)
func dumpChannelBackup(multi *chanbackup.Multi) error {
dumpSingles := make([]dump.BackupSingle, len(multi.StaticBackups))
for idx, single := range multi.StaticBackups {
dumpSingles[idx] = dump.BackupSingle{
Version: single.Version,
IsInitiator: single.IsInitiator,
ChainHash: single.ChainHash.String(),
FundingOutpoint: single.FundingOutpoint.String(),
ShortChannelID: single.ShortChannelID,
RemoteNodePub: dump.PubKeyToString(
single.RemoteNodePub,
),
Addresses: single.Addresses,
Capacity: single.Capacity,
LocalChanCfg: dump.ToChannelConfig(
single.LocalChanCfg,
),
RemoteChanCfg: dump.ToChannelConfig(
single.RemoteChanCfg,
),
ShaChainRootDesc: dump.ToKeyDescriptor(
single.ShaChainRootDesc,
),
}
}
spew.Dump(dump.BackupMulti{
Version: multi.Version,
StaticBackups: dumpSingles,
})
return nil
}

@ -1,393 +0,0 @@
package chantools
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"syscall"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dataformat"
"github.com/jessevdk/go-flags"
"github.com/lightningnetwork/lnd/aezeed"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/channeldb"
"golang.org/x/crypto/ssh/terminal"
)
const (
defaultApiUrl = "https://blockstream.info/api"
)
type config struct {
Testnet bool `long:"testnet" description:"Set to true if testnet parameters should be used."`
ApiUrl string `long:"apiurl" description:"API URL to use (must be esplora compatible)."`
ListChannels string `long:"listchannels" description:"The channel input is in the format of lncli's listchannels format. Specify '-' to read from stdin."`
PendingChannels string `long:"pendingchannels" description:"The channel input is in the format of lncli's pendingchannels format. Specify '-' to read from stdin."`
FromSummary string `long:"fromsummary" description:"The channel input is in the format of this tool's channel summary. Specify '-' to read from stdin."`
FromChannelDB string `long:"fromchanneldb" description:"The channel input is in the format of an lnd channel.db file."`
}
var (
logWriter = build.NewRotatingLogWriter()
log = build.NewSubLogger("CHAN", logWriter.GenSubLogger)
cfg = &config{
ApiUrl: defaultApiUrl,
}
chainParams = &chaincfg.MainNetParams
)
func Main() error {
setupLogging()
// Parse command line.
parser := flags.NewParser(cfg, flags.Default)
_, _ = parser.AddCommand(
"summary", "Compile a summary about the current state of "+
"channels.", "", &summaryCommand{},
)
_, _ = parser.AddCommand(
"rescueclosed", "Try finding the private keys for funds that "+
"are in outputs of remotely force-closed channels.", "",
&rescueClosedCommand{},
)
_, _ = parser.AddCommand(
"forceclose", "Force-close the last state that is in the "+
"channel.db provided.", "", &forceCloseCommand{},
)
_, _ = parser.AddCommand(
"sweeptimelock", "Sweep the force-closed state after the time "+
"lock has expired.", "", &sweepTimeLockCommand{},
)
_, _ = parser.AddCommand(
"dumpchannels", "Dump all channel information from lnd's "+
"channel database.", "", &dumpChannelsCommand{},
)
_, _ = parser.AddCommand(
"showrootkey", "Extract and show the BIP32 HD root key from "+
"the 24 word lnd aezeed.", "", &showRootKeyCommand{},
)
_, _ = parser.AddCommand(
"dumpbackup", "Dump the content of a channel.backup file.", "",
&dumpBackupCommand{},
)
_, _ = parser.AddCommand(
"derivekey", "Derive a key with a specific derivation path "+
"from the BIP32 HD root key.", "", &deriveKeyCommand{},
)
_, err := parser.Parse()
return err
}
type summaryCommand struct{}
func (c *summaryCommand) Execute(_ []string) error {
// Parse channel entries from any of the possible input files.
entries, err := ParseInput(cfg)
if err != nil {
return err
}
return summarizeChannels(cfg.ApiUrl, entries)
}
type rescueClosedCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."`
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to use for rescuing force-closed channels."`
}
func (c *rescueClosedCommand) Execute(_ []string) error {
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Check that we have a channel DB.
if c.ChannelDB == "" {
return fmt.Errorf("rescue DB is required")
}
db, err := channeldb.Open(path.Dir(c.ChannelDB))
if err != nil {
return fmt.Errorf("error opening rescue DB: %v", err)
}
// Parse channel entries from any of the possible input files.
entries, err := ParseInput(cfg)
if err != nil {
return err
}
return rescueClosedChannels(extendedKey, entries, db)
}
type forceCloseCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."`
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to use for force-closing channels."`
Publish bool `long:"publish" description:"Should the force-closing TX be published to the chain API?"`
}
func (c *forceCloseCommand) Execute(_ []string) error {
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Check that we have a channel DB.
if c.ChannelDB == "" {
return fmt.Errorf("rescue DB is required")
}
db, err := channeldb.Open(path.Dir(c.ChannelDB))
if err != nil {
return fmt.Errorf("error opening rescue DB: %v", err)
}
// Parse channel entries from any of the possible input files.
entries, err := ParseInput(cfg)
if err != nil {
return err
}
return forceCloseChannels(extendedKey, entries, db, c.Publish)
}
type sweepTimeLockCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."`
Publish bool `long:"publish" description:"Should the sweep TX be published to the chain API?"`
SweepAddr string `long:"sweepaddr" description:"The address the funds should be sweeped to"`
MaxCsvLimit int `long:"maxcsvlimit" description:"Maximum CSV limit to use. (default 2000)"`
}
func (c *sweepTimeLockCommand) Execute(_ []string) error {
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Make sure sweep addr is set.
if c.SweepAddr == "" {
return fmt.Errorf("sweep addr is required")
}
// Parse channel entries from any of the possible input files.
entries, err := ParseInput(cfg)
if err != nil {
return err
}
// Set default value
if c.MaxCsvLimit == 0 {
c.MaxCsvLimit = 2000
}
return sweepTimeLock(
extendedKey, cfg.ApiUrl, entries, c.SweepAddr, c.MaxCsvLimit,
c.Publish,
)
}
type dumpChannelsCommand struct {
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to dump the channels from."`
}
func (c *dumpChannelsCommand) Execute(_ []string) error {
// 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)
}
return dumpChannelInfo(db)
}
type showRootKeyCommand struct{}
func (c *showRootKeyCommand) Execute(_ []string) error {
// We'll now prompt the user to enter in their 24-word mnemonic.
fmt.Printf("Input your 24-word mnemonic separated by spaces: ")
reader := bufio.NewReader(os.Stdin)
mnemonicStr, err := reader.ReadString('\n')
if err != nil {
return err
}
// We'll trim off extra spaces, and ensure the mnemonic is all
// lower case, then populate our request.
mnemonicStr = strings.TrimSpace(mnemonicStr)
mnemonicStr = strings.ToLower(mnemonicStr)
cipherSeedMnemonic := strings.Split(mnemonicStr, " ")
fmt.Println()
if len(cipherSeedMnemonic) != 24 {
return fmt.Errorf("wrong cipher seed mnemonic "+
"length: got %v words, expecting %v words",
len(cipherSeedMnemonic), 24)
}
// Additionally, the user may have a passphrase, that will also
// need to be provided so the daemon can properly decipher the
// cipher seed.
fmt.Printf("Input your cipher seed passphrase (press enter if " +
"your seed doesn't have a passphrase): ")
passphrase, err := terminal.ReadPassword(syscall.Stdin)
if err != nil {
return err
}
var mnemonic aezeed.Mnemonic
copy(mnemonic[:], cipherSeedMnemonic[:])
// If we're unable to map it back into the ciphertext, then either the
// mnemonic is wrong, or the passphrase is wrong.
cipherSeed, err := mnemonic.ToCipherSeed(passphrase)
if err != nil {
return err
}
rootKey, err := hdkeychain.NewMaster(cipherSeed.Entropy[:], chainParams)
if err != nil {
return fmt.Errorf("failed to derive master extended key")
}
fmt.Printf("\nYour BIP32 HD root key is: %s\n", rootKey.String())
return nil
}
type dumpBackupCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key of the wallet that was used to create the backup."`
MultiFile string `long:"multi_file" description:"The lnd channel.backup file to dump."`
}
func (c *dumpBackupCommand) Execute(_ []string) error {
setupChainParams(cfg)
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
// Check that we have a backup file.
if c.MultiFile == "" {
return fmt.Errorf("backup file is required")
}
multiFile := chanbackup.NewMultiFile(c.MultiFile)
multi, err := multiFile.ExtractMulti(&btc.ChannelBackupEncryptionRing{
ExtendedKey: extendedKey,
ChainParams: chainParams,
})
if err != nil {
return fmt.Errorf("could not extract multi file: %v", err)
}
return dumpChannelBackup(multi)
}
type deriveKeyCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to derive the key from."`
Path string `long:"path" description:"The BIP32 derivation path to derive. Must start with \"m/\"."`
Neuter bool `long:"neuter" description:"Do not output the private key, just the public key."`
}
func (c *deriveKeyCommand) Execute(_ []string) error {
setupChainParams(cfg)
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
return deriveKey(extendedKey, c.Path, c.Neuter)
}
func ParseInput(cfg *config) ([]*dataformat.SummaryEntry, error) {
var (
content []byte
err error
target dataformat.InputFile
)
switch {
case cfg.ListChannels != "":
content, err = readInput(cfg.ListChannels)
target = &dataformat.ListChannelsFile{}
case cfg.PendingChannels != "":
content, err = readInput(cfg.PendingChannels)
target = &dataformat.PendingChannelsFile{}
case cfg.FromSummary != "":
content, err = readInput(cfg.FromSummary)
target = &dataformat.SummaryEntryFile{}
case cfg.FromChannelDB != "":
db, err := channeldb.Open(cfg.FromChannelDB)
if err != nil {
return nil, fmt.Errorf("error opening channel DB: %v",
err)
}
target = &dataformat.ChannelDBFile{DB: db}
return target.AsSummaryEntries()
default:
return nil, fmt.Errorf("an input file must be specified")
}
if err != nil {
return nil, err
}
decoder := json.NewDecoder(bytes.NewReader(content))
err = decoder.Decode(&target)
if err != nil {
return nil, err
}
return target.AsSummaryEntries()
}
func readInput(input string) ([]byte, error) {
if strings.TrimSpace(input) == "-" {
return ioutil.ReadAll(os.Stdin)
}
return ioutil.ReadFile(input)
}
func setupChainParams(cfg *config) {
if cfg.Testnet {
chainParams = &chaincfg.TestNet3Params
}
}
func setupLogging() {
logWriter.RegisterSubLogger("CHAN", log)
err := logWriter.InitLogRotator("./results/chantools.log", 10, 3)
if err != nil {
panic(err)
}
err = build.ParseAndSetDebugLevels("trace", logWriter)
if err != nil {
panic(err)
}
}
Loading…
Cancel
Save