Fall back to aezeed prompt if root key is missing

pull/3/head
Oliver Gugger 4 years ago
parent de4f5256ef
commit 26e1986cef
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -70,7 +70,7 @@ Usage:
chantools [OPTIONS] derivekey [derivekey-OPTIONS] chantools [OPTIONS] derivekey [derivekey-OPTIONS]
[derivekey command options] [derivekey command options]
--rootkey= BIP32 HD root key to derive the key from. --rootkey= BIP32 HD root key to derive the key from. Leave empty to prompt for lnd 24 word aezeed.
--path= The BIP32 derivation path to derive. Must start with "m/". --path= The BIP32 derivation path to derive. Must start with "m/".
--neuter Do not output the private key, just the public key. --neuter Do not output the private key, just the public key.
``` ```
@ -93,7 +93,7 @@ Usage:
chantools [OPTIONS] dumpbackup [dumpbackup-OPTIONS] chantools [OPTIONS] dumpbackup [dumpbackup-OPTIONS]
[dumpbackup command options] [dumpbackup command options]
--rootkey= BIP32 HD root key of the wallet that was used to create the backup. --rootkey= BIP32 HD root key of the wallet that was used to create the backup. Leave empty to prompt for lnd 24 word aezeed.
--multi_file= The lnd channel.backup file to dump. --multi_file= The lnd channel.backup file to dump.
``` ```
@ -133,7 +133,7 @@ Usage:
chantools [OPTIONS] forceclose [forceclose-OPTIONS] chantools [OPTIONS] forceclose [forceclose-OPTIONS]
[forceclose command options] [forceclose command options]
--rootkey= BIP32 HD root key to use. --rootkey= BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed.
--channeldb= The lnd channel.db file to use for force-closing channels. --channeldb= The lnd channel.db file to use for force-closing channels.
--publish Should the force-closing TX be published to the chain API? --publish Should the force-closing TX be published to the chain API?
``` ```
@ -168,7 +168,7 @@ Usage:
chantools [OPTIONS] rescueclosed [rescueclosed-OPTIONS] chantools [OPTIONS] rescueclosed [rescueclosed-OPTIONS]
[rescueclosed command options] [rescueclosed command options]
--rootkey= BIP32 HD root key to use. --rootkey= BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed.
--channeldb= The lnd channel.db file to use for rescuing force-closed channels. --channeldb= The lnd channel.db file to use for rescuing force-closed channels.
``` ```
@ -230,7 +230,7 @@ Usage:
chantools [OPTIONS] sweeptimelock [sweeptimelock-OPTIONS] chantools [OPTIONS] sweeptimelock [sweeptimelock-OPTIONS]
[sweeptimelock command options] [sweeptimelock command options]
--rootkey= BIP32 HD root key to use. --rootkey= BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed.
--publish Should the sweep TX be published to the chain API? --publish Should the sweep TX be published to the chain API?
--sweepaddr= The address the funds should be sweeped to --sweepaddr= The address the funds should be sweeped to
--maxcsvlimit= Maximum CSV limit to use. (default 2000) --maxcsvlimit= Maximum CSV limit to use. (default 2000)

@ -17,13 +17,21 @@ type deriveKeyCommand struct {
func (c *deriveKeyCommand) Execute(_ []string) error { func (c *deriveKeyCommand) Execute(_ []string) error {
setupChainParams(cfg) setupChainParams(cfg)
// Check that root key is valid. var (
if c.RootKey == "" { extendedKey *hdkeychain.ExtendedKey
return fmt.Errorf("root key is required") 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()
} }
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil { if err != nil {
return fmt.Errorf("error parsing root key: %v", err) return fmt.Errorf("error reading root key: %v", err)
} }
return deriveKey(extendedKey, c.Path, c.Neuter) return deriveKey(extendedKey, c.Path, c.Neuter)

@ -11,20 +11,28 @@ import (
) )
type dumpBackupCommand struct { type dumpBackupCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key of the wallet that was used to create the backup."` RootKey string `long:"rootkey" description:"BIP32 HD root key of the wallet that was used to create the backup. Leave empty to prompt for lnd 24 word aezeed."`
MultiFile string `long:"multi_file" description:"The lnd channel.backup file to dump."` MultiFile string `long:"multi_file" description:"The lnd channel.backup file to dump."`
} }
func (c *dumpBackupCommand) Execute(_ []string) error { func (c *dumpBackupCommand) Execute(_ []string) error {
setupChainParams(cfg) setupChainParams(cfg)
// Check that root key is valid. var (
if c.RootKey == "" { extendedKey *hdkeychain.ExtendedKey
return fmt.Errorf("root key is required") 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()
} }
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil { if err != nil {
return fmt.Errorf("error parsing root key: %v", err) return fmt.Errorf("error reading root key: %v", err)
} }
// Check that we have a backup file. // Check that we have a backup file.

@ -19,20 +19,29 @@ import (
) )
type forceCloseCommand struct { type forceCloseCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."` RootKey string `long:"rootkey" description:"BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed."`
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to use for force-closing channels."` 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?"` Publish bool `long:"publish" description:"Should the force-closing TX be published to the chain API?"`
} }
func (c *forceCloseCommand) Execute(_ []string) error { func (c *forceCloseCommand) Execute(_ []string) error {
// Check that root key is valid. var (
if c.RootKey == "" { extendedKey *hdkeychain.ExtendedKey
return fmt.Errorf("root key is required") 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()
} }
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil { if err != nil {
return fmt.Errorf("error parsing root key: %v", err) return fmt.Errorf("error reading root key: %v", err)
} }
// Check that we have a channel DB. // Check that we have a channel DB.
if c.ChannelDB == "" { if c.ChannelDB == "" {
return fmt.Errorf("rescue DB is required") return fmt.Errorf("rescue DB is required")
@ -43,7 +52,7 @@ func (c *forceCloseCommand) Execute(_ []string) error {
} }
// Parse channel entries from any of the possible input files. // Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg) entries, err := parseInputType(cfg)
if err != nil { if err != nil {
return err return err
} }

@ -1,12 +1,17 @@
package main package main
import ( import (
"bufio"
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/aezeed"
"golang.org/x/crypto/ssh/terminal"
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
"syscall"
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/guggero/chantools/dataformat" "github.com/guggero/chantools/dataformat"
@ -38,7 +43,7 @@ var (
) )
func main() { func main() {
err := Main() err := runCommandParser()
if err == nil { if err == nil {
return return
} }
@ -50,7 +55,7 @@ func main() {
os.Exit(0) os.Exit(0)
} }
func Main() error { func runCommandParser() error {
setupLogging() setupLogging()
// Parse command line. // Parse command line.
@ -93,7 +98,7 @@ func Main() error {
return err return err
} }
func parseInput(cfg *config) ([]*dataformat.SummaryEntry, error) { func parseInputType(cfg *config) ([]*dataformat.SummaryEntry, error) {
var ( var (
content []byte content []byte
err error err error
@ -144,6 +149,57 @@ func readInput(input string) ([]byte, error) {
return ioutil.ReadFile(input) return ioutil.ReadFile(input)
} }
func rootKeyFromConsole() (*hdkeychain.ExtendedKey, 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 nil, 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 nil, 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 nil, 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 nil, fmt.Errorf("failed to decrypt seed with passphrase"+
": %v", err)
}
rootKey, err := hdkeychain.NewMaster(cipherSeed.Entropy[:], chainParams)
if err != nil {
return nil, fmt.Errorf("failed to derive master extended key")
}
return rootKey, nil
}
func setupChainParams(cfg *config) { func setupChainParams(cfg *config) {
if cfg.Testnet { if cfg.Testnet {
chainParams = &chaincfg.TestNet3Params chainParams = &chaincfg.TestNet3Params

@ -32,18 +32,26 @@ type cacheEntry struct {
} }
type rescueClosedCommand struct { type rescueClosedCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."` RootKey string `long:"rootkey" description:"BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed."`
ChannelDB string `long:"channeldb" description:"The lnd channel.db file to use for rescuing force-closed channels."` ChannelDB string `long:"channeldb" description:"The lnd channel.db file to use for rescuing force-closed channels."`
} }
func (c *rescueClosedCommand) Execute(_ []string) error { func (c *rescueClosedCommand) Execute(_ []string) error {
// Check that root key is valid. var (
if c.RootKey == "" { extendedKey *hdkeychain.ExtendedKey
return fmt.Errorf("root key is required") 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()
} }
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil { if err != nil {
return fmt.Errorf("error parsing root key: %v", err) return fmt.Errorf("error reading root key: %v", err)
} }
// Check that we have a channel DB. // Check that we have a channel DB.
@ -56,7 +64,7 @@ func (c *rescueClosedCommand) Execute(_ []string) error {
} }
// Parse channel entries from any of the possible input files. // Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg) entries, err := parseInputType(cfg)
if err != nil { if err != nil {
return err return err
} }

@ -1,65 +1,16 @@
package main package main
import ( import (
"bufio"
"fmt" "fmt"
"os"
"strings"
"syscall"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/aezeed"
"golang.org/x/crypto/ssh/terminal"
) )
type showRootKeyCommand struct{} type showRootKeyCommand struct{}
func (c *showRootKeyCommand) Execute(_ []string) error { func (c *showRootKeyCommand) Execute(_ []string) error {
// We'll now prompt the user to enter in their 24-word mnemonic. rootKey, err := rootKeyFromConsole()
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 { if err != nil {
return fmt.Errorf("failed to derive master extended key") return fmt.Errorf("failed to read root key from console: %v",
err)
} }
fmt.Printf("\nYour BIP32 HD root key is: %s\n", rootKey.String()) fmt.Printf("\nYour BIP32 HD root key is: %s\n", rootKey.String())
return nil return nil

@ -14,7 +14,7 @@ type summaryCommand struct{}
func (c *summaryCommand) Execute(_ []string) error { func (c *summaryCommand) Execute(_ []string) error {
// Parse channel entries from any of the possible input files. // Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg) entries, err := parseInputType(cfg)
if err != nil { if err != nil {
return err return err
} }

@ -20,20 +20,28 @@ const (
) )
type sweepTimeLockCommand struct { type sweepTimeLockCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to use."` RootKey string `long:"rootkey" description:"BIP32 HD root key to use. Leave empty to prompt for lnd 24 word aezeed."`
Publish bool `long:"publish" description:"Should the sweep TX be published to the chain API?"` 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"` 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)"` MaxCsvLimit int `long:"maxcsvlimit" description:"Maximum CSV limit to use. (default 2000)"`
} }
func (c *sweepTimeLockCommand) Execute(_ []string) error { func (c *sweepTimeLockCommand) Execute(_ []string) error {
// Check that root key is valid. var (
if c.RootKey == "" { extendedKey *hdkeychain.ExtendedKey
return fmt.Errorf("root key is required") 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()
} }
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil { if err != nil {
return fmt.Errorf("error parsing root key: %v", err) return fmt.Errorf("error reading root key: %v", err)
} }
// Make sure sweep addr is set. // Make sure sweep addr is set.
@ -42,7 +50,7 @@ func (c *sweepTimeLockCommand) Execute(_ []string) error {
} }
// Parse channel entries from any of the possible input files. // Parse channel entries from any of the possible input files.
entries, err := parseInput(cfg) entries, err := parseInputType(cfg)
if err != nil { if err != nil {
return err return err
} }

Loading…
Cancel
Save