package main import ( "fmt" "os" "time" "github.com/guggero/chantools/btc" "github.com/guggero/chantools/lnd" "github.com/spf13/cobra" ) const ( defaultRecoveryWindow = 2500 defaultRescanFrom = 500000 ) type genImportScriptCommand struct { Format string LndPaths bool DerivationPath string RecoveryWindow uint32 RescanFrom uint32 rootKey *rootKey cmd *cobra.Command } func newGenImportScriptCommand() *cobra.Command { cc := &genImportScriptCommand{} cc.cmd = &cobra.Command{ Use: "genimportscript", Short: "Generate a script containing the on-chain " + "keys of an lnd wallet that can be imported into " + "other software like bitcoind", RunE: cc.Execute, } cc.cmd.Flags().StringVar( &cc.Format, "format", "bitcoin-importwallet", "format of the "+ "generated import script; currently supported are: "+ "bitcoin-importwallet, bitcoin-cli and "+ "bitcoin-cli-watchonly", ) cc.cmd.Flags().BoolVar( &cc.LndPaths, "lndpaths", false, "use all derivation paths "+ "that lnd used; results in a large number of results; "+ "cannot be used in conjunction with --derivationpath", ) cc.cmd.Flags().StringVar( &cc.DerivationPath, "derivationpath", "", "use one specific "+ "derivation path; specify the first levels of the "+ "derivation path before any internal/external branch; "+ "Cannot be used in conjunction with --lndpaths", ) cc.cmd.Flags().Uint32Var( &cc.RecoveryWindow, "recoverywindow", defaultRecoveryWindow, "number of keys to scan per internal/external branch; output "+ "will consist of double this amount of keys", ) cc.cmd.Flags().Uint32Var( &cc.RescanFrom, "rescanfrom", defaultRescanFrom, "block "+ "number to rescan from; will be set automatically "+ "from the wallet birthday if the lnd 24 word aezeed "+ "is entered", ) cc.rootKey = newRootKey(cc.cmd, "decrypting the backup") return cc.cmd } func (c *genImportScriptCommand) Execute(_ *cobra.Command, _ []string) error { var ( strPaths []string paths [][]uint32 ) extendedKey, birthday, err := c.rootKey.readWithBirthday() if err != nil { return fmt.Errorf("error reading root key: %v", err) } // The btcwallet gives the birthday a slack of 48 hours, let's do the // same. if !birthday.IsZero() { c.RescanFrom = btc.SeedBirthdayToBlock( chainParams, birthday.Add(-48*time.Hour), ) } // Set default values. if c.RecoveryWindow == 0 { c.RecoveryWindow = defaultRecoveryWindow } if c.RescanFrom == 0 { c.RescanFrom = defaultRescanFrom } // Decide what derivation path(s) to use. switch { default: c.DerivationPath = lnd.WalletDefaultDerivationPath fallthrough case c.DerivationPath != "": derivationPath, err := lnd.ParsePath(c.DerivationPath) if err != nil { return fmt.Errorf("error parsing path: %v", err) } strPaths = []string{c.DerivationPath} paths = [][]uint32{derivationPath} case c.LndPaths && c.DerivationPath != "": return fmt.Errorf("cannot use --lndpaths and --derivationpath " + "at the same time") case c.LndPaths: strPaths, paths, err = lnd.AllDerivationPaths(chainParams) if err != nil { return fmt.Errorf("error getting lnd paths: %v", err) } } exporter := btc.ParseFormat(c.Format) err = btc.ExportKeys( extendedKey, strPaths, paths, chainParams, c.RecoveryWindow, c.RescanFrom, exporter, os.Stdout, ) if err != nil { return fmt.Errorf("error exporting keys: %v", err) } return nil }