From a3a00d410a9977bb2786f07989f000c15b31fb09 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 09:51:24 +0100 Subject: [PATCH 1/8] lnd: extract ReadPassphrase into own function --- lnd/aezeed.go | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/lnd/aezeed.go b/lnd/aezeed.go index d9b46e7..589ef76 100644 --- a/lnd/aezeed.go +++ b/lnd/aezeed.go @@ -25,6 +25,7 @@ var ( multipleSpaces = regexp.MustCompile(" [ ]+") ) +// ReadAezeed reads an aezeed from the console or the environment variable. func ReadAezeed(params *chaincfg.Params) (*hdkeychain.ExtendedKey, time.Time, error) { @@ -67,6 +68,32 @@ func ReadAezeed(params *chaincfg.Params) (*hdkeychain.ExtendedKey, time.Time, len(cipherSeedMnemonic), 24) } + passphraseBytes, err := ReadPassphrase("doesn't have") + if err != nil { + return nil, time.Unix(0, 0), 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(passphraseBytes) + if err != nil { + return nil, time.Unix(0, 0), fmt.Errorf("failed to decrypt "+ + "seed with passphrase: %w", err) + } + rootKey, err := hdkeychain.NewMaster(cipherSeed.Entropy[:], params) + if err != nil { + return nil, time.Unix(0, 0), fmt.Errorf("failed to derive " + + "master extended key") + } + return rootKey, cipherSeed.BirthdayTime(), nil +} + +// ReadPassphrase reads a cipher seed passphrase from the console or the +// environment variable. +func ReadPassphrase(verb string) ([]byte, error) { // Additionally, the user may have a passphrase, that will also need to // be provided so the daemon can properly decipher the cipher seed. // Try the environment variable first. @@ -85,14 +112,14 @@ func ReadAezeed(params *chaincfg.Params) (*hdkeychain.ExtendedKey, time.Time, // The environment variable didn't contain anything, we'll read the // passphrase from the terminal. case passphrase == "": - fmt.Printf("Input your cipher seed passphrase (press enter " + - "if your seed doesn't have a passphrase): ") + fmt.Printf("Input your cipher seed passphrase (press enter "+ + "if your seed %s a passphrase): ", verb) var err error passphraseBytes, err = terminal.ReadPassword( int(syscall.Stdin), //nolint ) if err != nil { - return nil, time.Unix(0, 0), err + return nil, err } fmt.Println() @@ -101,20 +128,5 @@ func ReadAezeed(params *chaincfg.Params) (*hdkeychain.ExtendedKey, time.Time, passphraseBytes = []byte(passphrase) } - 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(passphraseBytes) - if err != nil { - return nil, time.Unix(0, 0), fmt.Errorf("failed to decrypt "+ - "seed with passphrase: %w", err) - } - rootKey, err := hdkeychain.NewMaster(cipherSeed.Entropy[:], params) - if err != nil { - return nil, time.Unix(0, 0), fmt.Errorf("failed to derive " + - "master extended key") - } - return rootKey, cipherSeed.BirthdayTime(), nil + return passphraseBytes, nil } From b169634d85dd87c419fb9602e161a64c5c4c4554 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 09:51:31 +0100 Subject: [PATCH 2/8] cmd/chantools: add new createwallet subcommand This commit adds a new subcommand for creating a new lnd compatible wallet.db file from an existing aezeed, master root key (xprv) or by generating a new aezeed. --- cmd/chantools/createwallet.go | 230 ++++++++++++++++++++++++++++++++++ cmd/chantools/root.go | 1 + 2 files changed, 231 insertions(+) create mode 100644 cmd/chantools/createwallet.go diff --git a/cmd/chantools/createwallet.go b/cmd/chantools/createwallet.go new file mode 100644 index 0000000..9a1d666 --- /dev/null +++ b/cmd/chantools/createwallet.go @@ -0,0 +1,230 @@ +package main + +import ( + "bytes" + "fmt" + "os" + "strings" + "time" + + "github.com/btcsuite/btcd/btcutil/hdkeychain" + _ "github.com/btcsuite/btcwallet/walletdb/bdb" + "github.com/lightninglabs/chantools/lnd" + "github.com/lightningnetwork/lnd/aezeed" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/btcwallet" + "github.com/spf13/cobra" +) + +type createWalletCommand struct { + WalletDBDir string + GenerateSeed bool + + rootKey *rootKey + cmd *cobra.Command +} + +func newCreateWalletCommand() *cobra.Command { + cc := &createWalletCommand{} + cc.cmd = &cobra.Command{ + Use: "createwallet", + Short: "Create a new lnd compatible wallet.db file from an " + + "existing seed or by generating a new one", + Long: `Creates a new wallet that can be used with lnd or with +chantools. The wallet can be created from an existing seed or a new one can be +generated (use --generateseed).`, + Example: `chantools createwallet \ + --walletdbdir ~/.lnd/data/chain/bitcoin/mainnet`, + RunE: cc.Execute, + } + cc.cmd.Flags().StringVar( + &cc.WalletDBDir, "walletdbdir", "", "the folder to create the "+ + "new wallet.db file in", + ) + cc.cmd.Flags().BoolVar( + &cc.GenerateSeed, "generateseed", false, "generate a new "+ + "seed instead of using an existing one", + ) + + cc.rootKey = newRootKey(cc.cmd, "creating the new wallet") + + return cc.cmd +} + +func (c *createWalletCommand) Execute(_ *cobra.Command, _ []string) error { + var ( + publicWalletPw = lnwallet.DefaultPublicPassphrase + privateWalletPw = lnwallet.DefaultPrivatePassphrase + masterRootKey *hdkeychain.ExtendedKey + birthday time.Time + err error + ) + + // Check that we have a wallet DB. + if c.WalletDBDir == "" { + return fmt.Errorf("wallet DB directory is required") + } + + // Make sure the directory (and parents) exists. + if err := os.MkdirAll(c.WalletDBDir, 0700); err != nil { + return fmt.Errorf("error creating wallet DB directory '%s': %w", + c.WalletDBDir, err) + } + + // Check if we should create a new seed or read if from the console or + // environment. + if c.GenerateSeed { + fmt.Printf("Generating new lnd compatible aezeed...\n") + seed, err := aezeed.New( + keychain.KeyDerivationVersionTaproot, nil, time.Now(), + ) + if err != nil { + return fmt.Errorf("error creating new seed: %w", err) + } + birthday = seed.BirthdayTime() + + // Derive the master extended key from the seed. + masterRootKey, err = hdkeychain.NewMaster( + seed.Entropy[:], chainParams, + ) + if err != nil { + return fmt.Errorf("failed to derive master extended "+ + "key: %w", err) + } + + passphrase, err := lnd.ReadPassphrase("shouldn't use") + if err != nil { + return fmt.Errorf("error reading passphrase: %w", err) + } + + mnemonic, err := seed.ToMnemonic(passphrase) + if err != nil { + return fmt.Errorf("error converting seed to "+ + "mnemonic: %w", err) + } + + fmt.Println("Generated new seed") + printCipherSeedWords(mnemonic[:]) + } else { + masterRootKey, birthday, err = c.rootKey.readWithBirthday() + if err != nil { + return err + } + } + + // To automate things with chantools, we also offer reading the wallet + // password from environment variables. + pw := []byte(strings.TrimSpace(os.Getenv(passwordEnvName))) + + // Because we cannot differentiate between an empty and a non-existent + // environment variable, we need a special character that indicates that + // no password should be used. We use a single dash (-) for that as that + // would be too short for an explicit password anyway. + switch { + // The user indicated in the environment variable that no passphrase + // should be used. We don't set any value. + case string(pw) == "-": + + // The environment variable didn't contain anything, we'll read the + // passphrase from the terminal. + case len(pw) == 0: + fmt.Printf("\n\nThe wallet password is used to encrypt the " + + "wallet.db file itself and is unrelated to the seed.\n") + pw, err = passwordFromConsole("Input new wallet password: ") + if err != nil { + return err + } + pw2, err := passwordFromConsole("Confirm new wallet password: ") + if err != nil { + return err + } + + if !bytes.Equal(pw, pw2) { + return fmt.Errorf("passwords don't match") + } + + if len(pw) > 0 { + publicWalletPw = pw + privateWalletPw = pw + } + + // There was a password in the environment, just use it directly. + default: + publicWalletPw = pw + privateWalletPw = pw + } + + // Try to create the wallet. + loader, err := btcwallet.NewWalletLoader( + chainParams, 0, btcwallet.LoaderWithLocalWalletDB( + c.WalletDBDir, true, 0, + ), + ) + if err != nil { + return fmt.Errorf("error creating wallet loader: %w", err) + } + + _, err = loader.CreateNewWalletExtendedKey( + publicWalletPw, privateWalletPw, masterRootKey, birthday, + ) + if err != nil { + return fmt.Errorf("error creating new wallet: %w", err) + } + + if err := loader.UnloadWallet(); err != nil { + return fmt.Errorf("error unloading wallet: %w", err) + } + + fmt.Printf("Wallet created successfully at %v\n", c.WalletDBDir) + + return nil +} + +func printCipherSeedWords(mnemonicWords []string) { + fmt.Println("!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " + + "RESTORE THE WALLET!!!") + fmt.Println() + + fmt.Println("---------------BEGIN LND CIPHER SEED---------------") + + numCols := 4 + colWords := monoWidthColumns(mnemonicWords, numCols) + for i := 0; i < len(colWords); i += numCols { + fmt.Printf("%2d. %3s %2d. %3s %2d. %3s %2d. %3s\n", + i+1, colWords[i], i+2, colWords[i+1], i+3, + colWords[i+2], i+4, colWords[i+3]) + } + + fmt.Println("---------------END LND CIPHER SEED-----------------") + + fmt.Println("\n!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " + + "RESTORE THE WALLET!!!") +} + +// monoWidthColumns takes a set of words, and the number of desired columns, +// and returns a new set of words that have had white space appended to the +// word in order to create a mono-width column. +func monoWidthColumns(words []string, ncols int) []string { + // Determine max size of words in each column. + colWidths := make([]int, ncols) + for i, word := range words { + col := i % ncols + curWidth := colWidths[col] + if len(word) > curWidth { + colWidths[col] = len(word) + } + } + + // Append whitespace to each word to make columns mono-width. + finalWords := make([]string, len(words)) + for i, word := range words { + col := i % ncols + width := colWidths[col] + + diff := width - len(word) + finalWords[i] = word + strings.Repeat(" ", diff) + } + + return finalWords +} diff --git a/cmd/chantools/root.go b/cmd/chantools/root.go index 93a35b1..1ce9ea3 100644 --- a/cmd/chantools/root.go +++ b/cmd/chantools/root.go @@ -101,6 +101,7 @@ func main() { rootCmd.AddCommand( newChanBackupCommand(), newClosePoolAccountCommand(), + newCreateWalletCommand(), newCompactDBCommand(), newDeletePaymentsCommand(), newDeriveKeyCommand(), From 37179e5215278d79028540e1fa1cda62db267dca Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 11:16:19 +0100 Subject: [PATCH 3/8] lnd+cmd/chantools: extract functions from walletinfo --- cmd/chantools/createwallet.go | 8 +- cmd/chantools/root.go | 28 ----- cmd/chantools/walletinfo.go | 159 +++---------------------- cmd/chantools/walletinfo_test.go | 3 +- lnd/aezeed.go | 198 +++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+), 177 deletions(-) diff --git a/cmd/chantools/createwallet.go b/cmd/chantools/createwallet.go index 9a1d666..2ee162b 100644 --- a/cmd/chantools/createwallet.go +++ b/cmd/chantools/createwallet.go @@ -115,7 +115,7 @@ func (c *createWalletCommand) Execute(_ *cobra.Command, _ []string) error { // To automate things with chantools, we also offer reading the wallet // password from environment variables. - pw := []byte(strings.TrimSpace(os.Getenv(passwordEnvName))) + pw := []byte(strings.TrimSpace(os.Getenv(lnd.PasswordEnvName))) // Because we cannot differentiate between an empty and a non-existent // environment variable, we need a special character that indicates that @@ -131,11 +131,13 @@ func (c *createWalletCommand) Execute(_ *cobra.Command, _ []string) error { case len(pw) == 0: fmt.Printf("\n\nThe wallet password is used to encrypt the " + "wallet.db file itself and is unrelated to the seed.\n") - pw, err = passwordFromConsole("Input new wallet password: ") + pw, err = lnd.PasswordFromConsole("Input new wallet password: ") if err != nil { return err } - pw2, err := passwordFromConsole("Confirm new wallet password: ") + pw2, err := lnd.PasswordFromConsole( + "Confirm new wallet password: ", + ) if err != nil { return err } diff --git a/cmd/chantools/root.go b/cmd/chantools/root.go index 1ce9ea3..5dfae0e 100644 --- a/cmd/chantools/root.go +++ b/cmd/chantools/root.go @@ -1,14 +1,12 @@ package main import ( - "bufio" "bytes" "encoding/json" "fmt" "io/ioutil" "os" "strings" - "syscall" "time" "github.com/btcsuite/btcd/btcutil/hdkeychain" @@ -22,7 +20,6 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/peer" "github.com/spf13/cobra" - "golang.org/x/crypto/ssh/terminal" ) const ( @@ -265,27 +262,6 @@ func readInput(input string) ([]byte, error) { return ioutil.ReadFile(input) } -func passwordFromConsole(userQuery string) ([]byte, error) { - // Read from terminal (if there is one). - if terminal.IsTerminal(int(syscall.Stdin)) { //nolint - fmt.Print(userQuery) - pw, err := terminal.ReadPassword(int(syscall.Stdin)) //nolint - if err != nil { - return nil, err - } - fmt.Println() - return pw, nil - } - - // Read from stdin as a fallback. - reader := bufio.NewReader(os.Stdin) - pw, err := reader.ReadBytes('\n') - if err != nil { - return nil, err - } - return pw, nil -} - func setupLogging() { setSubLogger("CHAN", log) addSubLogger("CHDB", channeldb.UseLogger) @@ -328,10 +304,6 @@ func setSubLogger(subsystem string, logger btclog.Logger, } } -func noConsole() ([]byte, error) { - return nil, fmt.Errorf("wallet db requires console access") -} - func newExplorerAPI(apiURL string) *btc.ExplorerAPI { // Override for testnet if default is used. if apiURL == defaultAPIURL && diff --git a/cmd/chantools/walletinfo.go b/cmd/chantools/walletinfo.go index 0d2c519..7715136 100644 --- a/cmd/chantools/walletinfo.go +++ b/cmd/chantools/walletinfo.go @@ -1,28 +1,19 @@ package main import ( - "errors" "fmt" - "os" - "strings" "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcwallet/snacl" "github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/walletdb" _ "github.com/btcsuite/btcwallet/walletdb/bdb" "github.com/lightninglabs/chantools/lnd" "github.com/lightningnetwork/lnd/keychain" - "github.com/lightningnetwork/lnd/lncfg" - "github.com/lightningnetwork/lnd/lnwallet" "github.com/spf13/cobra" - "go.etcd.io/bbolt" ) const ( - passwordEnvName = "WALLET_PASSWORD" - walletInfoFormat = ` Identity Pubkey: %x BIP32 HD extended root key: %s @@ -38,19 +29,7 @@ Scope: m/%d'/%d' ) var ( - // Namespace from github.com/btcsuite/btcwallet/wallet/wallet.go. - waddrmgrNamespaceKey = []byte("waddrmgr") - - // Bucket names from github.com/btcsuite/btcwallet/waddrmgr/db.go. - mainBucketName = []byte("main") - masterPrivKeyName = []byte("mpriv") - cryptoPrivKeyName = []byte("cpriv") - masterHDPrivName = []byte("mhdpriv") - defaultAccount = uint32(waddrmgr.DefaultAccountNum) - openCallbacks = &waddrmgr.OpenCallbacks{ - ObtainSeed: noConsole, - ObtainPrivatePass: noConsole, - } + defaultAccount = uint32(waddrmgr.DefaultAccountNum) ) type walletInfoCommand struct { @@ -98,75 +77,24 @@ or simply press without entering a password when being prompted.`, } func (c *walletInfoCommand) Execute(_ *cobra.Command, _ []string) error { - var ( - publicWalletPw = lnwallet.DefaultPublicPassphrase - privateWalletPw = lnwallet.DefaultPrivatePassphrase - err error - ) - // Check that we have a wallet DB. if c.WalletDB == "" { return fmt.Errorf("wallet DB is required") } - // To automate things with chantools, we also offer reading the wallet - // password from environment variables. - pw := []byte(strings.TrimSpace(os.Getenv(passwordEnvName))) - - // Because we cannot differentiate between an empty and a non-existent - // environment variable, we need a special character that indicates that - // no password should be used. We use a single dash (-) for that as that - // would be too short for an explicit password anyway. - switch { - // The user indicated in the environment variable that no passphrase - // should be used. We don't set any value. - case string(pw) == "-": - - // The environment variable didn't contain anything, we'll read the - // passphrase from the terminal. - case len(pw) == 0: - pw, err = passwordFromConsole("Input wallet password: ") - if err != nil { - return err - } - if len(pw) > 0 { - publicWalletPw = pw - privateWalletPw = pw - } - - // There was a password in the environment, just use it directly. - default: - publicWalletPw = pw - privateWalletPw = pw - } - - // Try to load and open the wallet. - db, err := walletdb.Open( - "bdb", lncfg.CleanAndExpandPath(c.WalletDB), false, - lnd.DefaultOpenTimeout, + w, privateWalletPw, cleanup, err := lnd.OpenWallet( + c.WalletDB, chainParams, ) - if errors.Is(err, bbolt.ErrTimeout) { - return fmt.Errorf("error opening wallet database, make sure " + - "lnd is not running and holding the exclusive lock " + - "on the wallet") - } if err != nil { - return fmt.Errorf("error opening wallet database: %w", err) + return fmt.Errorf("error opening wallet file '%s': %w", + c.WalletDB, err) } - defer func() { _ = db.Close() }() - w, err := wallet.Open(db, publicWalletPw, openCallbacks, chainParams, 0) - if err != nil { - return err - } - - // Start and unlock the wallet. - w.Start() - defer w.Stop() - err = w.Unlock(privateWalletPw, nil) - if err != nil { - return err - } + defer func() { + if err := cleanup(); err != nil { + log.Errorf("error closing wallet: %v", err) + } + }() // Print the wallet info and if requested the root key. identityKey, scopeInfo, err := walletInfo(w, c.DumpAddrs) @@ -175,7 +103,9 @@ func (c *walletInfoCommand) Execute(_ *cobra.Command, _ []string) error { } rootKey := na if c.WithRootKey { - masterHDPrivKey, err := decryptRootKey(db, privateWalletPw) + masterHDPrivKey, err := lnd.DecryptWalletRootKey( + w.Database(), privateWalletPw, + ) if err != nil { return err } @@ -259,7 +189,7 @@ func walletInfo(w *wallet.Wallet, dumpAddrs bool) (*btcec.PublicKey, string, err = walletdb.View( w.Database(), func(tx walletdb.ReadTx) error { waddrmgrNs := tx.ReadBucket( - waddrmgrNamespaceKey, + lnd.WaddrmgrNamespaceKey, ) return mgr.ForEachAccountAddress( @@ -304,64 +234,3 @@ func printScopeInfo(name string, w *wallet.Wallet, return scopeInfo, nil } - -func decryptRootKey(db walletdb.DB, privPassphrase []byte) ([]byte, error) { - // Step 1: Load the encryption parameters and encrypted keys from the - // database. - var masterKeyPrivParams []byte - var cryptoKeyPrivEnc []byte - var masterHDPrivEnc []byte - err := walletdb.View(db, func(tx walletdb.ReadTx) error { - ns := tx.ReadBucket(waddrmgrNamespaceKey) - if ns == nil { - return fmt.Errorf("namespace '%s' does not exist", - waddrmgrNamespaceKey) - } - - mainBucket := ns.NestedReadBucket(mainBucketName) - if mainBucket == nil { - return fmt.Errorf("bucket '%s' does not exist", - mainBucketName) - } - - val := mainBucket.Get(masterPrivKeyName) - if val != nil { - masterKeyPrivParams = make([]byte, len(val)) - copy(masterKeyPrivParams, val) - } - val = mainBucket.Get(cryptoPrivKeyName) - if val != nil { - cryptoKeyPrivEnc = make([]byte, len(val)) - copy(cryptoKeyPrivEnc, val) - } - val = mainBucket.Get(masterHDPrivName) - if val != nil { - masterHDPrivEnc = make([]byte, len(val)) - copy(masterHDPrivEnc, val) - } - - return nil - }) - if err != nil { - return nil, err - } - - // Step 2: Unmarshal the master private key parameters and derive - // key from passphrase. - var masterKeyPriv snacl.SecretKey - if err := masterKeyPriv.Unmarshal(masterKeyPrivParams); err != nil { - return nil, err - } - if err := masterKeyPriv.DeriveKey(&privPassphrase); err != nil { - return nil, err - } - - // Step 3: Decrypt the keys in the correct order. - cryptoKeyPriv := &snacl.CryptoKey{} - cryptoKeyPrivBytes, err := masterKeyPriv.Decrypt(cryptoKeyPrivEnc) - if err != nil { - return nil, err - } - copy(cryptoKeyPriv[:], cryptoKeyPrivBytes) - return cryptoKeyPriv.Decrypt(masterHDPrivEnc) -} diff --git a/cmd/chantools/walletinfo_test.go b/cmd/chantools/walletinfo_test.go index ab4c180..a5a841d 100644 --- a/cmd/chantools/walletinfo_test.go +++ b/cmd/chantools/walletinfo_test.go @@ -3,6 +3,7 @@ package main import ( "testing" + "github.com/lightninglabs/chantools/lnd" "github.com/stretchr/testify/require" ) @@ -20,7 +21,7 @@ func TestWalletInfo(t *testing.T) { WithRootKey: true, } - t.Setenv(passwordEnvName, testPassPhrase) + t.Setenv(lnd.PasswordEnvName, testPassPhrase) err := info.Execute(nil, nil) require.NoError(t, err) diff --git a/lnd/aezeed.go b/lnd/aezeed.go index 589ef76..5c45266 100644 --- a/lnd/aezeed.go +++ b/lnd/aezeed.go @@ -2,6 +2,7 @@ package lnd import ( "bufio" + "errors" "fmt" "os" "regexp" @@ -11,20 +12,46 @@ import ( "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcwallet/snacl" + "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/btcsuite/btcwallet/wallet" + "github.com/btcsuite/btcwallet/walletdb" "github.com/lightningnetwork/lnd/aezeed" + "github.com/lightningnetwork/lnd/lncfg" + "github.com/lightningnetwork/lnd/lnwallet" + "go.etcd.io/bbolt" "golang.org/x/crypto/ssh/terminal" ) const ( MnemonicEnvName = "AEZEED_MNEMONIC" PassphraseEnvName = "AEZEED_PASSPHRASE" + PasswordEnvName = "WALLET_PASSWORD" ) var ( numberDotsRegex = regexp.MustCompile(`[\d.\-\n\r\t]*`) multipleSpaces = regexp.MustCompile(" [ ]+") + + openCallbacks = &waddrmgr.OpenCallbacks{ + ObtainSeed: noConsole, + ObtainPrivatePass: noConsole, + } + + // Namespace from github.com/btcsuite/btcwallet/wallet/wallet.go. + WaddrmgrNamespaceKey = []byte("waddrmgr") + + // Bucket names from github.com/btcsuite/btcwallet/waddrmgr/db.go. + mainBucketName = []byte("main") + masterPrivKeyName = []byte("mpriv") + cryptoPrivKeyName = []byte("cpriv") + masterHDPrivName = []byte("mhdpriv") ) +func noConsole() ([]byte, error) { + return nil, fmt.Errorf("wallet db requires console access") +} + // ReadAezeed reads an aezeed from the console or the environment variable. func ReadAezeed(params *chaincfg.Params) (*hdkeychain.ExtendedKey, time.Time, error) { @@ -130,3 +157,174 @@ func ReadPassphrase(verb string) ([]byte, error) { return passphraseBytes, nil } + +// PasswordFromConsole reads a password from the console or stdin. +func PasswordFromConsole(userQuery string) ([]byte, error) { + // Read from terminal (if there is one). + if terminal.IsTerminal(int(syscall.Stdin)) { //nolint + fmt.Print(userQuery) + pw, err := terminal.ReadPassword(int(syscall.Stdin)) //nolint + if err != nil { + return nil, err + } + fmt.Println() + return pw, nil + } + + // Read from stdin as a fallback. + reader := bufio.NewReader(os.Stdin) + pw, err := reader.ReadBytes('\n') + if err != nil { + return nil, err + } + return pw, nil +} + +// OpenWallet opens a lnd compatible wallet and returns it, along with the +// private wallet password. +func OpenWallet(walletDbPath string, + chainParams *chaincfg.Params) (*wallet.Wallet, []byte, func() error, + error) { + + var ( + publicWalletPw = lnwallet.DefaultPublicPassphrase + privateWalletPw = lnwallet.DefaultPrivatePassphrase + err error + ) + + // To automate things with chantools, we also offer reading the wallet + // password from environment variables. + pw := []byte(strings.TrimSpace(os.Getenv(PasswordEnvName))) + + // Because we cannot differentiate between an empty and a non-existent + // environment variable, we need a special character that indicates that + // no password should be used. We use a single dash (-) for that as that + // would be too short for an explicit password anyway. + switch { + // The user indicated in the environment variable that no passphrase + // should be used. We don't set any value. + case string(pw) == "-": + + // The environment variable didn't contain anything, we'll read the + // passphrase from the terminal. + case len(pw) == 0: + pw, err = PasswordFromConsole("Input wallet password: ") + if err != nil { + return nil, nil, nil, err + } + if len(pw) > 0 { + publicWalletPw = pw + privateWalletPw = pw + } + + // There was a password in the environment, just use it directly. + default: + publicWalletPw = pw + privateWalletPw = pw + } + + // Try to load and open the wallet. + db, err := walletdb.Open( + "bdb", lncfg.CleanAndExpandPath(walletDbPath), false, + DefaultOpenTimeout, + ) + if errors.Is(err, bbolt.ErrTimeout) { + return nil, nil, nil, fmt.Errorf("error opening wallet " + + "database, make sure lnd is not running and holding " + + "the exclusive lock on the wallet") + } + if err != nil { + return nil, nil, nil, fmt.Errorf("error opening wallet "+ + "database: %w", err) + } + + w, err := wallet.Open(db, publicWalletPw, openCallbacks, chainParams, 0) + if err != nil { + _ = db.Close() + return nil, nil, nil, fmt.Errorf("error opening wallet %w", err) + } + + // Start and unlock the wallet. + w.Start() + err = w.Unlock(privateWalletPw, nil) + if err != nil { + w.Stop() + _ = db.Close() + return nil, nil, nil, err + } + + cleanup := func() error { + w.Stop() + if err := db.Close(); err != nil { + return err + } + + return nil + } + + return w, privateWalletPw, cleanup, nil +} + +// DecryptWalletRootKey decrypts a lnd compatible wallet's root key. +func DecryptWalletRootKey(db walletdb.DB, + privatePassphrase []byte) ([]byte, error) { + + // Step 1: Load the encryption parameters and encrypted keys from the + // database. + var masterKeyPrivParams []byte + var cryptoKeyPrivEnc []byte + var masterHDPrivEnc []byte + err := walletdb.View(db, func(tx walletdb.ReadTx) error { + ns := tx.ReadBucket(WaddrmgrNamespaceKey) + if ns == nil { + return fmt.Errorf("namespace '%s' does not exist", + WaddrmgrNamespaceKey) + } + + mainBucket := ns.NestedReadBucket(mainBucketName) + if mainBucket == nil { + return fmt.Errorf("bucket '%s' does not exist", + mainBucketName) + } + + val := mainBucket.Get(masterPrivKeyName) + if val != nil { + masterKeyPrivParams = make([]byte, len(val)) + copy(masterKeyPrivParams, val) + } + val = mainBucket.Get(cryptoPrivKeyName) + if val != nil { + cryptoKeyPrivEnc = make([]byte, len(val)) + copy(cryptoKeyPrivEnc, val) + } + val = mainBucket.Get(masterHDPrivName) + if val != nil { + masterHDPrivEnc = make([]byte, len(val)) + copy(masterHDPrivEnc, val) + } + + return nil + }) + if err != nil { + return nil, err + } + + // Step 2: Unmarshal the master private key parameters and derive + // key from passphrase. + var masterKeyPriv snacl.SecretKey + if err := masterKeyPriv.Unmarshal(masterKeyPrivParams); err != nil { + return nil, err + } + if err := masterKeyPriv.DeriveKey(&privatePassphrase); err != nil { + return nil, err + } + + // Step 3: Decrypt the keys in the correct order. + cryptoKeyPriv := &snacl.CryptoKey{} + cryptoKeyPrivBytes, err := masterKeyPriv.Decrypt(cryptoKeyPrivEnc) + if err != nil { + return nil, err + } + copy(cryptoKeyPriv[:], cryptoKeyPrivBytes) + return cryptoKeyPriv.Decrypt(masterHDPrivEnc) +} From 5c39df02d30e3a275d8c083392506a7400256abf Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 11:29:55 +0100 Subject: [PATCH 4/8] cmd/chantools: allow root key to be read from wallet DB With this commit we allow a third option for reading the master root key for any command that requires access to it: Reading and decrypting it directly from an lnd wallet password. --- cmd/chantools/root.go | 44 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/cmd/chantools/root.go b/cmd/chantools/root.go index 5dfae0e..1d70faa 100644 --- a/cmd/chantools/root.go +++ b/cmd/chantools/root.go @@ -140,8 +140,9 @@ func main() { } type rootKey struct { - RootKey string - BIP39 bool + RootKey string + BIP39 bool + WalletDB string } func newRootKey(cmd *cobra.Command, desc string) *rootKey { @@ -156,6 +157,12 @@ func newRootKey(cmd *cobra.Command, desc string) *rootKey { "passphrase from the terminal instead of asking for "+ "lnd seed format or providing the --rootkey flag", ) + cmd.Flags().StringVar( + &r.WalletDB, "walletdb", "", "read the seed/master root key "+ + "to use fro "+desc+" from an lnd wallet.db file "+ + "instead of asking for a seed or providing the "+ + "--rootkey flag", + ) return r } @@ -178,6 +185,39 @@ func (r *rootKey) readWithBirthday() (*hdkeychain.ExtendedKey, time.Time, extendedKey, err := btc.ReadMnemonicFromTerminal(chainParams) return extendedKey, time.Unix(0, 0), err + case r.WalletDB != "": + wallet, pw, cleanup, err := lnd.OpenWallet( + r.WalletDB, chainParams, + ) + if err != nil { + return nil, time.Unix(0, 0), fmt.Errorf("error "+ + "opening wallet '%s': %w", r.WalletDB, err) + } + + defer func() { + if err := cleanup(); err != nil { + log.Errorf("error closing wallet: %v", err) + } + }() + + extendedKeyBytes, err := lnd.DecryptWalletRootKey( + wallet.Database(), pw, + ) + if err != nil { + return nil, time.Unix(0, 0), fmt.Errorf("error "+ + "decrypting wallet root key: %w", err) + } + + extendedKey, err := hdkeychain.NewKeyFromString( + string(extendedKeyBytes), + ) + if err != nil { + return nil, time.Unix(0, 0), fmt.Errorf("error "+ + "parsing master key: %w", err) + } + + return extendedKey, wallet.Manager.Birthday(), nil + default: return lnd.ReadAezeed(chainParams) } From e80dcbfb6764ce37462f2f0ccc6c08a0e1217dce Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 12:51:29 +0100 Subject: [PATCH 5/8] lnd+cmd/chantools: add AddPartialSignatureForPrivateKey to signer --- cmd/chantools/rescuefunding.go | 2 +- cmd/chantools/signmessage.go | 2 +- lnd/signer.go | 52 +++++++++++++++++++++++++++++++--- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/cmd/chantools/rescuefunding.go b/cmd/chantools/rescuefunding.go index 3fbea6e..cc062a8 100644 --- a/cmd/chantools/rescuefunding.go +++ b/cmd/chantools/rescuefunding.go @@ -209,7 +209,7 @@ func (c *rescueFundingCommand) Execute(_ *cobra.Command, _ []string) error { Index: c.LocalKeyIndex, }, } - privKey, err := signer.FetchPrivKey(localKeyDesc) + privKey, err := signer.FetchPrivateKey(localKeyDesc) if err != nil { return fmt.Errorf("error deriving local key: %w", err) } diff --git a/cmd/chantools/signmessage.go b/cmd/chantools/signmessage.go index 52de1c6..06c3ba5 100644 --- a/cmd/chantools/signmessage.go +++ b/cmd/chantools/signmessage.go @@ -61,7 +61,7 @@ func (c *signMessageCommand) Execute(_ *cobra.Command, _ []string) error { } // Fetch the private key for node key. - privKey, err := signer.FetchPrivKey(&keychain.KeyDescriptor{ + privKey, err := signer.FetchPrivateKey(&keychain.KeyDescriptor{ KeyLocator: keyLocator, }) if err != nil { diff --git a/lnd/signer.go b/lnd/signer.go index 19fcb68..da51e1c 100644 --- a/lnd/signer.go +++ b/lnd/signer.go @@ -30,15 +30,15 @@ func (s *Signer) SignOutputRaw(tx *wire.MsgTx, // First attempt to fetch the private key which corresponds to the // specified public key. - privKey, err := s.FetchPrivKey(&signDesc.KeyDesc) + privKey, err := s.FetchPrivateKey(&signDesc.KeyDesc) if err != nil { return nil, err } - return s.SignOutputRawWithPrivkey(tx, signDesc, privKey) + return s.SignOutputRawWithPrivateKey(tx, signDesc, privKey) } -func (s *Signer) SignOutputRawWithPrivkey(tx *wire.MsgTx, +func (s *Signer) SignOutputRawWithPrivateKey(tx *wire.MsgTx, signDesc *input.SignDescriptor, privKey *secp256k1.PrivateKey) (input.Signature, error) { @@ -122,7 +122,7 @@ func (s *Signer) ComputeInputScript(_ *wire.MsgTx, _ *input.SignDescriptor) ( return nil, fmt.Errorf("unimplemented") } -func (s *Signer) FetchPrivKey(descriptor *keychain.KeyDescriptor) ( +func (s *Signer) FetchPrivateKey(descriptor *keychain.KeyDescriptor) ( *btcec.PrivateKey, error) { key, err := DeriveChildren(s.ExtendedKey, []uint32{ @@ -181,6 +181,50 @@ func (s *Signer) AddPartialSignature(packet *psbt.Packet, return nil } +func (s *Signer) AddPartialSignatureForPrivateKey(packet *psbt.Packet, + privateKey *btcec.PrivateKey, utxo *wire.TxOut, witnessScript []byte, + inputIndex int) error { + + // Now we add our partial signature. + prevOutFetcher := wallet.PsbtPrevOutputFetcher(packet) + signDesc := &input.SignDescriptor{ + WitnessScript: witnessScript, + Output: utxo, + InputIndex: inputIndex, + HashType: txscript.SigHashAll, + PrevOutputFetcher: prevOutFetcher, + SigHashes: txscript.NewTxSigHashes( + packet.UnsignedTx, prevOutFetcher, + ), + } + ourSigRaw, err := s.SignOutputRawWithPrivateKey( + packet.UnsignedTx, signDesc, privateKey, + ) + if err != nil { + return fmt.Errorf("error signing with our key: %w", err) + } + ourSig := append(ourSigRaw.Serialize(), byte(txscript.SigHashAll)) + + // Great, we were able to create our sig, let's add it to the PSBT. + updater, err := psbt.NewUpdater(packet) + if err != nil { + return fmt.Errorf("error creating PSBT updater: %w", err) + } + status, err := updater.Sign( + inputIndex, ourSig, privateKey.PubKey().SerializeCompressed(), + nil, witnessScript, + ) + if err != nil { + return fmt.Errorf("error adding signature to PSBT: %w", err) + } + if status != 0 { + return fmt.Errorf("unexpected status for signature update, "+ + "got %d wanted 0", status) + } + + return nil +} + // maybeTweakPrivKey examines the single tweak parameters on the passed sign // descriptor and may perform a mapping on the passed private key in order to // utilize the tweaks, if populated. From 9f8484bb892203597f7d2400c81f1bc38fa68c16 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 12:52:15 +0100 Subject: [PATCH 6/8] cmd/chantools: add signpsbt subcommand --- cmd/chantools/root.go | 1 + cmd/chantools/signpsbt.go | 217 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 cmd/chantools/signpsbt.go diff --git a/cmd/chantools/root.go b/cmd/chantools/root.go index 1d70faa..b968a3e 100644 --- a/cmd/chantools/root.go +++ b/cmd/chantools/root.go @@ -123,6 +123,7 @@ func main() { newShowRootKeyCommand(), newSignMessageCommand(), newSignRescueFundingCommand(), + newSignPSBTCommand(), newSummaryCommand(), newSweepTimeLockCommand(), newSweepTimeLockManualCommand(), diff --git a/cmd/chantools/signpsbt.go b/cmd/chantools/signpsbt.go new file mode 100644 index 0000000..877b1d6 --- /dev/null +++ b/cmd/chantools/signpsbt.go @@ -0,0 +1,217 @@ +package main + +import ( + "bytes" + "encoding/base64" + "encoding/binary" + "fmt" + "os" + + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcutil/psbt" + "github.com/lightninglabs/chantools/lnd" + "github.com/spf13/cobra" +) + +type signPSBTCommand struct { + Psbt string + FromRawPsbtFile string + ToRawPsbtFile string + + rootKey *rootKey + cmd *cobra.Command +} + +func newSignPSBTCommand() *cobra.Command { + cc := &signPSBTCommand{} + cc.cmd = &cobra.Command{ + Use: "signpsbt", + Short: "Sign a Partially Signed Bitcoin Transaction (PSBT)", + Long: `Sign a PSBT with a master root key. The PSBT must contain +an input that is owned by the master root key.`, + Example: `chantools signpsbt \ + --psbt + +chantools signpsbt --fromrawpsbtfile `, + RunE: cc.Execute, + } + cc.cmd.Flags().StringVar( + &cc.Psbt, "psbt", "", "Partially Signed Bitcoin Transaction "+ + "to sign", + ) + cc.cmd.Flags().StringVar( + &cc.FromRawPsbtFile, "fromrawpsbtfile", "", "the file containing "+ + "the raw, binary encoded PSBT packet to sign", + ) + cc.cmd.Flags().StringVar( + &cc.ToRawPsbtFile, "torawpsbtfile", "", "the file to write "+ + "the resulting signed raw, binary encoded PSBT packet "+ + "to", + ) + + cc.rootKey = newRootKey(cc.cmd, "signing the PSBT") + + return cc.cmd +} + +func (c *signPSBTCommand) Execute(_ *cobra.Command, _ []string) error { + extendedKey, err := c.rootKey.read() + if err != nil { + return fmt.Errorf("error reading root key: %w", err) + } + + signer := &lnd.Signer{ + ExtendedKey: extendedKey, + ChainParams: chainParams, + } + + var packet *psbt.Packet + + // Decode the PSBT, either from the command line or the binary file. + switch { + case c.Psbt != "": + packet, err = psbt.NewFromRawBytes( + bytes.NewReader([]byte(c.Psbt)), true, + ) + if err != nil { + return fmt.Errorf("error decoding PSBT: %w", err) + } + + case c.FromRawPsbtFile != "": + f, err := os.Open(c.FromRawPsbtFile) + if err != nil { + return fmt.Errorf("error opening PSBT file '%s': %w", + c.FromRawPsbtFile, err) + } + + packet, err = psbt.NewFromRawBytes(f, false) + if err != nil { + return fmt.Errorf("error decoding PSBT from file "+ + "'%s': %w", c.FromRawPsbtFile, err) + } + + default: + return fmt.Errorf("either the PSBT or the raw PSBT file " + + "must be set") + } + + err = signPsbt(extendedKey, packet, signer) + if err != nil { + return fmt.Errorf("error signing PSBT: %w", err) + } + + switch { + case c.ToRawPsbtFile != "": + f, err := os.Create(c.ToRawPsbtFile) + if err != nil { + return fmt.Errorf("error creating PSBT file '%s': %w", + c.ToRawPsbtFile, err) + } + + if err := packet.Serialize(f); err != nil { + return fmt.Errorf("error serializing PSBT to file "+ + "'%s': %w", c.ToRawPsbtFile, err) + } + + fmt.Printf("Successfully signed PSBT and wrote it to file "+ + "'%s'\n", c.ToRawPsbtFile) + + default: + var buf bytes.Buffer + if err := packet.Serialize(&buf); err != nil { + return fmt.Errorf("error serializing PSBT: %w", err) + } + + fmt.Printf("Successfully signed PSBT:\n\n%s\n", + base64.StdEncoding.EncodeToString(buf.Bytes())) + } + + return nil +} + +func signPsbt(rootKey *hdkeychain.ExtendedKey, + packet *psbt.Packet, signer *lnd.Signer) error { + + // Check that we have an input with a derivation path that belongs to + // the root key. + derivationPath, inputIndex, err := findMatchingDerivationPath( + rootKey, packet, + ) + if err != nil { + return fmt.Errorf("could not find matching derivation path: %w", + err) + } + + if len(derivationPath) < 5 { + return fmt.Errorf("invalid derivation path, expected at least "+ + "5 elements, got %d", len(derivationPath)) + } + + localKey, err := lnd.DeriveChildren(rootKey, derivationPath) + if err != nil { + return fmt.Errorf("could not derive local key: %w", err) + } + + if len(packet.Inputs[inputIndex].WitnessScript) == 0 { + return fmt.Errorf("invalid PSBT, input %d is missing witness "+ + "script", inputIndex) + } + witnessScript := packet.Inputs[inputIndex].WitnessScript + if packet.Inputs[inputIndex].WitnessUtxo == nil { + return fmt.Errorf("invalid PSBT, input %d is missing witness "+ + "UTXO", inputIndex) + } + utxo := packet.Inputs[inputIndex].WitnessUtxo + + localPrivateKey, err := localKey.ECPrivKey() + if err != nil { + return fmt.Errorf("error getting private key: %w", err) + } + err = signer.AddPartialSignatureForPrivateKey( + packet, localPrivateKey, utxo, witnessScript, inputIndex, + ) + if err != nil { + return fmt.Errorf("error adding partial signature: %w", err) + } + + return nil +} + +func findMatchingDerivationPath(rootKey *hdkeychain.ExtendedKey, + packet *psbt.Packet) ([]uint32, int, error) { + + pubKey, err := rootKey.ECPubKey() + if err != nil { + return nil, 0, fmt.Errorf("error getting public key: %w", err) + } + + pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed()) + fingerprint := binary.LittleEndian.Uint32(pubKeyHash[:4]) + + for idx, input := range packet.Inputs { + if len(input.Bip32Derivation) == 0 { + continue + } + + for _, derivation := range input.Bip32Derivation { + // A special case where there is only a single + // derivation path and the master key fingerprint is not + // set, we assume we are the correct signer... This + // might not be correct, but we have no way of knowing. + if derivation.MasterKeyFingerprint == 0 && + len(input.Bip32Derivation) == 1 { + + return derivation.Bip32Path, idx, nil + } + + // The normal case, where a derivation path has the + // master fingerprint set. + if derivation.MasterKeyFingerprint == fingerprint { + return derivation.Bip32Path, idx, nil + } + } + } + + return nil, 0, fmt.Errorf("no matching derivation path found") +} From 1a46f9099f026b350b64ab4e13c7b0fe64ce668e Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 13:01:02 +0100 Subject: [PATCH 7/8] root: bump version to v0.13.0 --- cmd/chantools/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/chantools/root.go b/cmd/chantools/root.go index b968a3e..1ca5121 100644 --- a/cmd/chantools/root.go +++ b/cmd/chantools/root.go @@ -30,7 +30,7 @@ const ( // version is the current version of the tool. It is set during build. // NOTE: When changing this, please also update the version in the // download link shown in the README. - version = "0.12.2" + version = "0.13.0" na = "n/a" // lndVersion is the current version of lnd that we support. This is From 71b824e105ec67e33ecabafb87b8f165fa7b27a1 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 12 Mar 2024 13:06:32 +0100 Subject: [PATCH 8/8] README+doc: update all docs --- README.md | 6 ++- doc/chantools.md | 3 ++ doc/chantools_chanbackup.md | 1 + doc/chantools_closepoolaccount.md | 1 + doc/chantools_createwallet.md | 44 ++++++++++++++++++++ doc/chantools_derivekey.md | 13 +++--- doc/chantools_doublespendinputs.md | 1 + doc/chantools_dumpbackup.md | 1 + doc/chantools_fakechanbackup.md | 1 + doc/chantools_filterbackup.md | 1 + doc/chantools_fixoldbackup.md | 1 + doc/chantools_forceclose.md | 1 + doc/chantools_genimportscript.md | 1 + doc/chantools_pullanchor.md | 1 + doc/chantools_recoverloopin.md | 1 + doc/chantools_rescueclosed.md | 1 + doc/chantools_rescuefunding.md | 1 + doc/chantools_rescuetweakedkey.md | 1 + doc/chantools_showrootkey.md | 7 ++-- doc/chantools_signmessage.md | 31 ++++++++++---- doc/chantools_signpsbt.md | 46 +++++++++++++++++++++ doc/chantools_signrescuefunding.md | 9 ++-- doc/chantools_sweepremoteclosed.md | 1 + doc/chantools_sweeptimelock.md | 1 + doc/chantools_sweeptimelockmanual.md | 1 + doc/chantools_triggerforceclose.md | 1 + doc/chantools_zombierecovery_makeoffer.md | 1 + doc/chantools_zombierecovery_preparekeys.md | 1 + doc/chantools_zombierecovery_signoffer.md | 9 ++-- 29 files changed, 162 insertions(+), 26 deletions(-) create mode 100644 doc/chantools_createwallet.md create mode 100644 doc/chantools_signpsbt.md diff --git a/README.md b/README.md index 4553b29..042e20c 100644 --- a/README.md +++ b/README.md @@ -410,6 +410,7 @@ Available Commands: chanbackup Create a channel.backup file from a channel database closepoolaccount Tries to close a Pool account that has expired compactdb Create a copy of a channel.db file in safe/read-only mode + createwallet Create a new lnd compatible wallet.db file from an existing seed or by generating a new one deletepayments Remove all (failed) payments from a channel DB derivekey Derive a key with a specific derivation path doublespendinputs Tries to double spend the given inputs by deriving the private for the address and sweeping the funds to the given address. This can only be used with inputs that belong to an lnd wallet. @@ -430,6 +431,7 @@ Available Commands: rescuetweakedkey Attempt to rescue funds locked in an address with a key that was affected by a specific bug in lnd showrootkey Extract and show the BIP32 HD root key from the 24 word lnd aezeed signmessage Sign a message with the nodes identity pubkey. + signpsbt Sign a Partially Signed Bitcoin Transaction (PSBT) signrescuefunding Rescue funds locked in a funding multisig output that never resulted in a proper channel; this is the command the remote node (the non-initiator) of the channel needs to run summary Compile a summary about the current state of channels sweeptimelock Sweep the force-closed state after the time lock has expired @@ -470,6 +472,7 @@ Legend: | [chanbackup](doc/chantools_chanbackup.md) | :pencil: Extract a `channel.backup` file from a `channel.db` file | | [closepoolaccount](doc/chantools_closepoolaccount.md) | :pencil: Manually close an expired Lightning Pool account | | [compactdb](doc/chantools_compactdb.md) | Run database compaction manually to reclaim space | +| [createwallet](doc/chantools_createwallet.md) | :pencil: Create a new lnd compatible wallet.db file from an existing seed or by generating a new one | | [deletepayments](doc/chantools_deletepayments.md) | Remove ALL payments from a `channel.db` file to reduce size | | [derivekey](doc/chantools_derivekey.md) | :pencil: Derive a single private/public key from `lnd`'s seed, use to test seed | | [doublespendinputs](doc/chantools_doublespendinputs.md) | :pencil: Tries to double spend the given inputs by deriving the private for the address and sweeping the funds to the given address | @@ -483,13 +486,14 @@ Legend: | [forceclose](doc/chantools_forceclose.md) | :pencil: (:skull: :warning:) Publish an old channel state from a `channel.db` file | | [genimportscript](doc/chantools_genimportscript.md) | :pencil: Create a script/text file that can be used to import `lnd` keys into other software | | [migratedb](doc/chantools_migratedb.md) | Upgrade the `channel.db` file to the latest version | -| [pullanchor](doc/chantools_pullanchor.md) | :pencil: Attempt to CPFP an anchor output of a channel | +| [pullanchor](doc/chantools_pullanchor.md) | :pencil: Attempt to CPFP an anchor output of a channel | | [recoverloopin](doc/chantools_recoverloopin.md) | :pencil: Recover funds from a failed Lightning Loop inbound swap | | [removechannel](doc/chantools_removechannel.md) | (:skull: :warning:) Remove a single channel from a `channel.db` file | | [rescueclosed](doc/chantools_rescueclosed.md) | :pencil: (:pushpin:) Rescue funds in a legacy (pre `STATIC_REMOTE_KEY`) channel output | | [rescuefunding](doc/chantools_rescuefunding.md) | :pencil: (:pushpin:) Rescue funds from a funding transaction. Deprecated, use [zombierecovery](doc/chantools_zombierecovery.md) instead | | [showrootkey](doc/chantools_showrootkey.md) | :pencil: Display the master root key (`xprv`) from your seed (DO NOT SHARE WITH ANYONE) | | [signmessage](doc/chantools_signmessage.md) | :pencil: Sign a message with the nodes identity pubkey. | +| [signpsbt](doc/chantools_signpsbt.md) | :pencil: Sign a Partially Signed Bitcoin Transaction (PSBT) | | [signrescuefunding](doc/chantools_signrescuefunding.md) | :pencil: (:pushpin:) Sign to funds from a funding transaction. Deprecated, use [zombierecovery](doc/chantools_zombierecovery.md) instead | | [summary](doc/chantools_summary.md) | Create a summary of channel funds from a `channel.db` file | | [sweepremoteclosed](doc/chantools_sweepremoteclosed.md) | :pencil: Find channel funds from remotely force closed channels and sweep them | diff --git a/doc/chantools.md b/doc/chantools.md index cca764f..544c8e2 100644 --- a/doc/chantools.md +++ b/doc/chantools.md @@ -23,6 +23,7 @@ https://github.com/lightninglabs/chantools/. * [chantools chanbackup](chantools_chanbackup.md) - Create a channel.backup file from a channel database * [chantools closepoolaccount](chantools_closepoolaccount.md) - Tries to close a Pool account that has expired * [chantools compactdb](chantools_compactdb.md) - Create a copy of a channel.db file in safe/read-only mode +* [chantools createwallet](chantools_createwallet.md) - Create a new lnd compatible wallet.db file from an existing seed or by generating a new one * [chantools deletepayments](chantools_deletepayments.md) - Remove all (failed) payments from a channel DB * [chantools derivekey](chantools_derivekey.md) - Derive a key with a specific derivation path * [chantools doublespendinputs](chantools_doublespendinputs.md) - Replace a transaction by double spending its input @@ -43,6 +44,8 @@ https://github.com/lightninglabs/chantools/. * [chantools rescuefunding](chantools_rescuefunding.md) - Rescue funds locked in a funding multisig output that never resulted in a proper channel; this is the command the initiator of the channel needs to run * [chantools rescuetweakedkey](chantools_rescuetweakedkey.md) - Attempt to rescue funds locked in an address with a key that was affected by a specific bug in lnd * [chantools showrootkey](chantools_showrootkey.md) - Extract and show the BIP32 HD root key from the 24 word lnd aezeed +* [chantools signmessage](chantools_signmessage.md) - Sign a message with the node's private key. +* [chantools signpsbt](chantools_signpsbt.md) - Sign a Partially Signed Bitcoin Transaction (PSBT) * [chantools signrescuefunding](chantools_signrescuefunding.md) - Rescue funds locked in a funding multisig output that never resulted in a proper channel; this is the command the remote node (the non-initiator) of the channel needs to run * [chantools summary](chantools_summary.md) - Compile a summary about the current state of channels * [chantools sweepremoteclosed](chantools_sweepremoteclosed.md) - Go through all the addresses that could have funds of channels that were force-closed by the remote party. A public block explorer is queried for each address and if any balance is found, all funds are swept to a given address diff --git a/doc/chantools_chanbackup.md b/doc/chantools_chanbackup.md index 52c67ea..b321192 100644 --- a/doc/chantools_chanbackup.md +++ b/doc/chantools_chanbackup.md @@ -27,6 +27,7 @@ chantools chanbackup \ -h, --help help for chanbackup --multi_file string lnd channel.backup file to create --rootkey string BIP32 HD root key of the wallet to use for creating the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro creating the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_closepoolaccount.md b/doc/chantools_closepoolaccount.md index da9d900..718ead9 100644 --- a/doc/chantools_closepoolaccount.md +++ b/doc/chantools_closepoolaccount.md @@ -42,6 +42,7 @@ chantools closepoolaccount \ --publish publish sweep TX to the chain API instead of just printing the TX --rootkey string BIP32 HD root key of the wallet to use for deriving keys; leave empty to prompt for lnd 24 word aezeed --sweepaddr string address to recover the funds to; specify 'fromseed' to derive a new address from the seed automatically + --walletdb string read the seed/master root key to use fro deriving keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_createwallet.md b/doc/chantools_createwallet.md new file mode 100644 index 0000000..c0388cb --- /dev/null +++ b/doc/chantools_createwallet.md @@ -0,0 +1,44 @@ +## chantools createwallet + +Create a new lnd compatible wallet.db file from an existing seed or by generating a new one + +### Synopsis + +Creates a new wallet that can be used with lnd or with +chantools. The wallet can be created from an existing seed or a new one can be +generated (use --generateseed). + +``` +chantools createwallet [flags] +``` + +### Examples + +``` +chantools createwallet \ + --walletdbdir ~/.lnd/data/chain/bitcoin/mainnet +``` + +### Options + +``` + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + --generateseed generate a new seed instead of using an existing one + -h, --help help for createwallet + --rootkey string BIP32 HD root key of the wallet to use for creating the new wallet; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro creating the new wallet from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag + --walletdbdir string the folder to create the new wallet.db file in +``` + +### Options inherited from parent commands + +``` + -r, --regtest Indicates if regtest parameters should be used + -s, --signet Indicates if the public signet parameters should be used + -t, --testnet Indicates if testnet parameters should be used +``` + +### SEE ALSO + +* [chantools](chantools.md) - Chantools helps recover funds from lightning channels + diff --git a/doc/chantools_derivekey.md b/doc/chantools_derivekey.md index 13d4974..31a3e1c 100644 --- a/doc/chantools_derivekey.md +++ b/doc/chantools_derivekey.md @@ -23,12 +23,13 @@ chantools derivekey --identity ### Options ``` - --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag - -h, --help help for derivekey - --identity derive the lnd identity_pubkey - --neuter don't output private key(s), only public key(s) - --path string BIP32 derivation path to derive; must start with "m/" - --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + -h, --help help for derivekey + --identity derive the lnd identity_pubkey + --neuter don't output private key(s), only public key(s) + --path string BIP32 derivation path to derive; must start with "m/" + --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_doublespendinputs.md b/doc/chantools_doublespendinputs.md index fe6b5a6..a4ff97b 100644 --- a/doc/chantools_doublespendinputs.md +++ b/doc/chantools_doublespendinputs.md @@ -34,6 +34,7 @@ chantools doublespendinputs \ --recoverywindow uint32 number of keys to scan per internal/external branch; output will consist of double this amount of keys (default 2500) --rootkey string BIP32 HD root key of the wallet to use for deriving the input keys; leave empty to prompt for lnd 24 word aezeed --sweepaddr string address to recover the funds to; specify 'fromseed' to derive a new address from the seed automatically + --walletdb string read the seed/master root key to use fro deriving the input keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_dumpbackup.md b/doc/chantools_dumpbackup.md index bf6856d..f011962 100644 --- a/doc/chantools_dumpbackup.md +++ b/doc/chantools_dumpbackup.md @@ -25,6 +25,7 @@ chantools dumpbackup \ -h, --help help for dumpbackup --multi_file string lnd channel.backup file to dump --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_fakechanbackup.md b/doc/chantools_fakechanbackup.md index 3cff89e..e5dc94e 100644 --- a/doc/chantools_fakechanbackup.md +++ b/doc/chantools_fakechanbackup.md @@ -65,6 +65,7 @@ chantools fakechanbackup --from_channel_graph lncli_describegraph.json \ --remote_node_addr string the remote node connection information in the format pubkey@host:port --rootkey string BIP32 HD root key of the wallet to use for encrypting the backup; leave empty to prompt for lnd 24 word aezeed --short_channel_id string the short channel ID in the format xx + --walletdb string read the seed/master root key to use fro encrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_filterbackup.md b/doc/chantools_filterbackup.md index 9f03187..475d646 100644 --- a/doc/chantools_filterbackup.md +++ b/doc/chantools_filterbackup.md @@ -27,6 +27,7 @@ chantools filterbackup \ -h, --help help for filterbackup --multi_file string lnd channel.backup file to filter --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_fixoldbackup.md b/doc/chantools_fixoldbackup.md index f0523ba..56f351e 100644 --- a/doc/chantools_fixoldbackup.md +++ b/doc/chantools_fixoldbackup.md @@ -28,6 +28,7 @@ chantools fixoldbackup \ -h, --help help for fixoldbackup --multi_file string lnd channel.backup file to fix --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_forceclose.md b/doc/chantools_forceclose.md index 41b0b7d..a2f0d49 100644 --- a/doc/chantools_forceclose.md +++ b/doc/chantools_forceclose.md @@ -43,6 +43,7 @@ chantools forceclose \ --pendingchannels string channel input is in the format of lncli's pendingchannels format; specify '-' to read from stdin --publish publish force-closing TX to the chain API instead of just printing the TX --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_genimportscript.md b/doc/chantools_genimportscript.md index 5249b34..776f064 100644 --- a/doc/chantools_genimportscript.md +++ b/doc/chantools_genimportscript.md @@ -51,6 +51,7 @@ chantools genimportscript --format bitcoin-cli \ --rescanfrom uint32 block number to rescan from; will be set automatically from the wallet birthday if the lnd 24 word aezeed is entered (default 500000) --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed --stdout write generated import script to standard out instead of writing it to a file + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_pullanchor.md b/doc/chantools_pullanchor.md index 523a6c7..79104e8 100644 --- a/doc/chantools_pullanchor.md +++ b/doc/chantools_pullanchor.md @@ -33,6 +33,7 @@ chantools pullanchor \ -h, --help help for pullanchor --rootkey string BIP32 HD root key of the wallet to use for deriving keys; leave empty to prompt for lnd 24 word aezeed --sponsorinput string the input to use to sponsor the CPFP transaction; must be owned by the lnd node that owns the anchor output + --walletdb string read the seed/master root key to use fro deriving keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_recoverloopin.md b/doc/chantools_recoverloopin.md index 5ae3d4a..a7dabc9 100644 --- a/doc/chantools_recoverloopin.md +++ b/doc/chantools_recoverloopin.md @@ -35,6 +35,7 @@ chantools recoverloopin \ --sweepaddr string address to recover the funds to; specify 'fromseed' to derive a new address from the seed automatically --txid string transaction id of the on-chain transaction that created the HTLC --vout uint32 output index of the on-chain transaction that created the HTLC + --walletdb string read the seed/master root key to use fro deriving starting key from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_rescueclosed.md b/doc/chantools_rescueclosed.md index ccb8202..7420959 100644 --- a/doc/chantools_rescueclosed.md +++ b/doc/chantools_rescueclosed.md @@ -60,6 +60,7 @@ chantools rescueclosed --fromsummary results/summary-xxxxxx.json \ --lnd_log string the lnd log file to read to get the commit_point values when rescuing multiple channels at the same time --pendingchannels string channel input is in the format of lncli's pendingchannels format; specify '-' to read from stdin --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_rescuefunding.md b/doc/chantools_rescuefunding.md index 4a730b2..fb50b39 100644 --- a/doc/chantools_rescuefunding.md +++ b/doc/chantools_rescuefunding.md @@ -50,6 +50,7 @@ chantools rescuefunding \ --remotepubkey string in case a channel DB is not available (but perhaps a channel backup file), the remote multisig public key can be specified manually --rootkey string BIP32 HD root key of the wallet to use for deriving keys; leave empty to prompt for lnd 24 word aezeed --sweepaddr string address to recover the funds to; specify 'fromseed' to derive a new address from the seed automatically + --walletdb string read the seed/master root key to use fro deriving keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_rescuetweakedkey.md b/doc/chantools_rescuetweakedkey.md index 7e16367..63c57a3 100644 --- a/doc/chantools_rescuetweakedkey.md +++ b/doc/chantools_rescuetweakedkey.md @@ -29,6 +29,7 @@ chantools rescuetweakedkey \ --path string BIP32 derivation path to derive the starting key from; must start with "m/" --rootkey string BIP32 HD root key of the wallet to use for deriving starting key; leave empty to prompt for lnd 24 word aezeed --targetaddr string address the funds are locked in + --walletdb string read the seed/master root key to use fro deriving starting key from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_showrootkey.md b/doc/chantools_showrootkey.md index 0fd8e76..c264d04 100644 --- a/doc/chantools_showrootkey.md +++ b/doc/chantools_showrootkey.md @@ -21,9 +21,10 @@ chantools showrootkey ### Options ``` - --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag - -h, --help help for showrootkey - --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + -h, --help help for showrootkey + --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_signmessage.md b/doc/chantools_signmessage.md index 54906e7..56fb812 100644 --- a/doc/chantools_signmessage.md +++ b/doc/chantools_signmessage.md @@ -1,10 +1,12 @@ ## chantools signmessage -Signs a message with the nodes key, results in the same signature as -`lncli signmessage` +Sign a message with the node's private key. ### Synopsis +Sign msg with the resident node's private key. + Returns the signature as a zbase32 string. + ``` chantools signmessage [flags] ``` @@ -18,9 +20,22 @@ chantools signmessage --msg=foobar ### Options ``` - --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag - -h, --help help for signmessage - --msg string the message to sign - --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed - --single_hash single hash the msg instead of double hash (lnd default is false) -``` \ No newline at end of file + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + -h, --help help for signmessage + --msg string the message to sign + --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag +``` + +### Options inherited from parent commands + +``` + -r, --regtest Indicates if regtest parameters should be used + -s, --signet Indicates if the public signet parameters should be used + -t, --testnet Indicates if testnet parameters should be used +``` + +### SEE ALSO + +* [chantools](chantools.md) - Chantools helps recover funds from lightning channels + diff --git a/doc/chantools_signpsbt.md b/doc/chantools_signpsbt.md new file mode 100644 index 0000000..f7fe39e --- /dev/null +++ b/doc/chantools_signpsbt.md @@ -0,0 +1,46 @@ +## chantools signpsbt + +Sign a Partially Signed Bitcoin Transaction (PSBT) + +### Synopsis + +Sign a PSBT with a master root key. The PSBT must contain +an input that is owned by the master root key. + +``` +chantools signpsbt [flags] +``` + +### Examples + +``` +chantools signpsbt \ + --psbt + +chantools signpsbt --fromrawpsbtfile +``` + +### Options + +``` + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + --fromrawpsbtfile string the file containing the raw, binary encoded PSBT packet to sign + -h, --help help for signpsbt + --psbt string Partially Signed Bitcoin Transaction to sign + --rootkey string BIP32 HD root key of the wallet to use for signing the PSBT; leave empty to prompt for lnd 24 word aezeed + --torawpsbtfile string the file to write the resulting signed raw, binary encoded PSBT packet to + --walletdb string read the seed/master root key to use fro signing the PSBT from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag +``` + +### Options inherited from parent commands + +``` + -r, --regtest Indicates if regtest parameters should be used + -s, --signet Indicates if the public signet parameters should be used + -t, --testnet Indicates if testnet parameters should be used +``` + +### SEE ALSO + +* [chantools](chantools.md) - Chantools helps recover funds from lightning channels + diff --git a/doc/chantools_signrescuefunding.md b/doc/chantools_signrescuefunding.md index 7f46d9e..b6373f5 100644 --- a/doc/chantools_signrescuefunding.md +++ b/doc/chantools_signrescuefunding.md @@ -26,10 +26,11 @@ chantools signrescuefunding \ ### Options ``` - --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag - -h, --help help for signrescuefunding - --psbt string Partially Signed Bitcoin Transaction that was provided by the initiator of the channel to rescue - --rootkey string BIP32 HD root key of the wallet to use for deriving keys; leave empty to prompt for lnd 24 word aezeed + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + -h, --help help for signrescuefunding + --psbt string Partially Signed Bitcoin Transaction that was provided by the initiator of the channel to rescue + --rootkey string BIP32 HD root key of the wallet to use for deriving keys; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro deriving keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_sweepremoteclosed.md b/doc/chantools_sweepremoteclosed.md index f254b19..f682a4a 100644 --- a/doc/chantools_sweepremoteclosed.md +++ b/doc/chantools_sweepremoteclosed.md @@ -41,6 +41,7 @@ chantools sweepremoteclosed \ --recoverywindow uint32 number of keys to scan per derivation path (default 200) --rootkey string BIP32 HD root key of the wallet to use for sweeping the wallet; leave empty to prompt for lnd 24 word aezeed --sweepaddr string address to recover the funds to; specify 'fromseed' to derive a new address from the seed automatically + --walletdb string read the seed/master root key to use fro sweeping the wallet from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_sweeptimelock.md b/doc/chantools_sweeptimelock.md index 5284585..a147518 100644 --- a/doc/chantools_sweeptimelock.md +++ b/doc/chantools_sweeptimelock.md @@ -41,6 +41,7 @@ chantools sweeptimelock \ --publish publish sweep TX to the chain API instead of just printing the TX --rootkey string BIP32 HD root key of the wallet to use for deriving keys; leave empty to prompt for lnd 24 word aezeed --sweepaddr string address to recover the funds to; specify 'fromseed' to derive a new address from the seed automatically + --walletdb string read the seed/master root key to use fro deriving keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_sweeptimelockmanual.md b/doc/chantools_sweeptimelockmanual.md index 7c5b9ff..1623193 100644 --- a/doc/chantools_sweeptimelockmanual.md +++ b/doc/chantools_sweeptimelockmanual.md @@ -63,6 +63,7 @@ chantools sweeptimelockmanual \ --rootkey string BIP32 HD root key of the wallet to use for deriving keys; leave empty to prompt for lnd 24 word aezeed --sweepaddr string address to recover the funds to; specify 'fromseed' to derive a new address from the seed automatically --timelockaddr string address of the time locked commitment output where the funds are stuck in + --walletdb string read the seed/master root key to use fro deriving keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_triggerforceclose.md b/doc/chantools_triggerforceclose.md index 6903a8a..c9672a2 100644 --- a/doc/chantools_triggerforceclose.md +++ b/doc/chantools_triggerforceclose.md @@ -31,6 +31,7 @@ chantools triggerforceclose \ -h, --help help for triggerforceclose --peer string remote peer address (@[:]) --rootkey string BIP32 HD root key of the wallet to use for deriving the identity key; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro deriving the identity key from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_zombierecovery_makeoffer.md b/doc/chantools_zombierecovery_makeoffer.md index a8c2009..b9e9c1e 100644 --- a/doc/chantools_zombierecovery_makeoffer.md +++ b/doc/chantools_zombierecovery_makeoffer.md @@ -35,6 +35,7 @@ chantools zombierecovery makeoffer \ --node1_keys string the JSON file generated in theprevious step ('preparekeys') command of node 1 --node2_keys string the JSON file generated in theprevious step ('preparekeys') command of node 2 --rootkey string BIP32 HD root key of the wallet to use for signing the offer; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro signing the offer from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_zombierecovery_preparekeys.md b/doc/chantools_zombierecovery_preparekeys.md index 085ac9c..6429e72 100644 --- a/doc/chantools_zombierecovery_preparekeys.md +++ b/doc/chantools_zombierecovery_preparekeys.md @@ -31,6 +31,7 @@ chantools zombierecovery preparekeys \ --num_keys uint32 the number of multisig keys to derive (default 2500) --payout_addr string the address where this node's rescued funds should be sent to, must be a P2WPKH (native SegWit) address --rootkey string BIP32 HD root key of the wallet to use for deriving the multisig keys; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro deriving the multisig keys from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands diff --git a/doc/chantools_zombierecovery_signoffer.md b/doc/chantools_zombierecovery_signoffer.md index 309ebd7..7e4b1ce 100644 --- a/doc/chantools_zombierecovery_signoffer.md +++ b/doc/chantools_zombierecovery_signoffer.md @@ -21,10 +21,11 @@ chantools zombierecovery signoffer \ ### Options ``` - --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag - -h, --help help for signoffer - --psbt string the base64 encoded PSBT that the other party sent as an offer to rescue funds - --rootkey string BIP32 HD root key of the wallet to use for signing the offer; leave empty to prompt for lnd 24 word aezeed + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + -h, --help help for signoffer + --psbt string the base64 encoded PSBT that the other party sent as an offer to rescue funds + --rootkey string BIP32 HD root key of the wallet to use for signing the offer; leave empty to prompt for lnd 24 word aezeed + --walletdb string read the seed/master root key to use fro signing the offer from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag ``` ### Options inherited from parent commands