// createWallet prompts the user for information needed to generate a new wallet // and generates the wallet accordingly. The new wallet will reside at the // provided path. func createWallet(cfg *config) error { dbDir := networkDir(cfg.AppDataDir.Value, activeNet.Params) loader := wallet.NewLoader(activeNet.Params, dbDir) // When there is a legacy keystore, open it now to ensure any errors // don't end up exiting the process after the user has spent time // entering a bunch of information. netDir := networkDir(cfg.AppDataDir.Value, activeNet.Params) keystorePath := filepath.Join(netDir, keystore.Filename) var legacyKeyStore *keystore.Store _, err := os.Stat(keystorePath) if err != nil && !os.IsNotExist(err) { // A stat error not due to a non-existant file should be // returned to the caller. return err } else if err == nil { // Keystore file exists. legacyKeyStore, err = keystore.OpenDir(netDir) if err != nil { return err } } // Start by prompting for the private passphrase. When there is an // existing keystore, the user will be promped for that passphrase, // otherwise they will be prompted for a new one. reader := bufio.NewReader(os.Stdin) privPass, err := prompt.PrivatePass(reader, legacyKeyStore) if err != nil { return err } // When there exists a legacy keystore, unlock it now and set up a // callback to import all keystore keys into the new walletdb // wallet if legacyKeyStore != nil { err = legacyKeyStore.Unlock(privPass) if err != nil { return err } // Import the addresses in the legacy keystore to the new wallet if // any exist, locking each wallet again when finished. loader.RunAfterLoad(func(w *wallet.Wallet) { defer legacyKeyStore.Lock() fmt.Println("Importing addresses from existing wallet...") err := w.Manager.Unlock(privPass) if err != nil { fmt.Printf("ERR: Failed to unlock new wallet "+ "during old wallet key import: %v", err) return } defer w.Manager.Lock() err = convertLegacyKeystore(legacyKeyStore, w.Manager) if err != nil { fmt.Printf("ERR: Failed to import keys from old "+ "wallet format: %v", err) return } // Remove the legacy key store. err = os.Remove(keystorePath) if err != nil { fmt.Printf("WARN: Failed to remove legacy wallet "+ "from'%s'\n", keystorePath) } }) } // Ascertain the public passphrase. This will either be a value // specified by the user or the default hard-coded public passphrase if // the user does not want the additional public data encryption. pubPass, err := prompt.PublicPass(reader, privPass, []byte(wallet.InsecurePubPassphrase), []byte(cfg.WalletPass)) if err != nil { return err } // Ascertain the wallet generation seed. This will either be an // automatically generated value the user has already confirmed or a // value the user has entered which has already been validated. seed, err := prompt.Seed(reader) if err != nil { return err } fmt.Println("Creating the wallet...") w, err := loader.CreateNewWallet(pubPass, privPass, seed) if err != nil { return err } w.Manager.Close() fmt.Println("The wallet has been created successfully.") return nil }
// walletMain is a work-around main function that is required since deferred // functions (such as log flushing) are not called with calls to os.Exit. // Instead, main runs this function and checks for a non-nil error, at which // point any defers have already run, and if the error is non-nil, the program // can be exited with an error exit status. func walletMain() error { // Load configuration and parse command line. This function also // initializes logging and configures it accordingly. tcfg, _, err := loadConfig() if err != nil { return err } cfg = tcfg defer backendLog.Flush() // Show version at startup. log.Infof("Version %s", version()) if cfg.Profile != "" { go func() { listenAddr := net.JoinHostPort("", cfg.Profile) log.Infof("Profile server listening on %s", listenAddr) profileRedirect := http.RedirectHandler("/debug/pprof", http.StatusSeeOther) http.Handle("/", profileRedirect) log.Errorf("%v", http.ListenAndServe(listenAddr, nil)) }() } dbDir := networkDir(cfg.AppDataDir.Value, activeNet.Params) loader := wallet.NewLoader(activeNet.Params, dbDir) // Create and start HTTP server to serve wallet client connections. // This will be updated with the wallet and chain server RPC client // created below after each is created. rpcs, legacyRPCServer, err := startRPCServers(loader) if err != nil { log.Errorf("Unable to create RPC servers: %v", err) return err } // Create and start chain RPC client so it's ready to connect to // the wallet when loaded later. if !cfg.NoInitialLoad { go rpcClientConnectLoop(legacyRPCServer, loader) } loader.RunAfterLoad(func(w *wallet.Wallet) { startWalletRPCServices(w, rpcs, legacyRPCServer) }) if !cfg.NoInitialLoad { // Load the wallet database. It must have been created already // or this will return an appropriate error. _, err = loader.OpenExistingWallet([]byte(cfg.WalletPass), true) if err != nil { log.Error(err) return err } } // Add interrupt handlers to shutdown the various process components // before exiting. Interrupt handlers run in LIFO order, so the wallet // (which should be closed last) is added first. addInterruptHandler(func() { err := loader.UnloadWallet() if err != nil && err != wallet.ErrNotLoaded { log.Errorf("Failed to close wallet: %v", err) } }) if rpcs != nil { addInterruptHandler(func() { // TODO: Does this need to wait for the grpc server to // finish up any requests? log.Warn("Stopping RPC server...") rpcs.Stop() log.Info("RPC server shutdown") }) } if legacyRPCServer != nil { addInterruptHandler(func() { log.Warn("Stopping legacy RPC server...") legacyRPCServer.Stop() log.Info("Legacy RPC server shutdown") }) go func() { <-legacyRPCServer.RequestProcessShutdown() simulateInterrupt() }() } <-interruptHandlersDone log.Info("Shutdown complete") return nil }
// NewLightningWallet creates/opens and initializes a LightningWallet instance. // If the wallet has never been created (according to the passed dataDir), first-time // setup is executed. // TODO(roasbeef): fin...add config func NewLightningWallet(config *Config) (*LightningWallet, walletdb.DB, error) { // Ensure the wallet exists or create it when the create flag is set. netDir := networkDir(config.DataDir, config.NetParams) var pubPass []byte if config.PublicPass == nil { pubPass = defaultPubPassphrase } else { pubPass = config.PublicPass } var walletDB walletdb.DB loader := btcwallet.NewLoader(config.NetParams, netDir) loader.RunAfterLoad(func(w *btcwallet.Wallet, db walletdb.DB) { walletDB = db }) walletExists, err := loader.WalletExists() if err != nil { return nil, nil, err } var createID bool var wallet *btcwallet.Wallet if !walletExists { // Wallet has never been created, perform initial set up. wallet, err = loader.CreateNewWallet(pubPass, config.PrivatePass, config.HdSeed) if err != nil { return nil, nil, err } createID = true } else { // Wallet has been created and been initialized at this point, open it // along with all the required DB namepsaces, and the DB itself. wallet, err = loader.OpenExistingWallet(pubPass, false) if err != nil { return nil, nil, err } } // Create a special namespace for our unique payment channel related // meta-data. Subsequently initializing the channeldb around the // created namespace. lnNamespace, err := walletDB.Namespace(lightningNamespaceKey) if err != nil { return nil, nil, err } cdb := channeldb.New(wallet.Manager, lnNamespace) if err := wallet.Manager.Unlock(config.PrivatePass); err != nil { return nil, nil, err } // If we just created the wallet, then reserve, and store a key for // our ID within the Lightning Network. if createID { adrs, err := wallet.Manager.NextInternalAddresses(waddrmgr.DefaultAccountNum, 1) if err != nil { return nil, nil, err } idPubkeyHash := adrs[0].Address().ScriptAddress() if err := cdb.PutIdKey(idPubkeyHash); err != nil { return nil, nil, err } log.Printf("stored identity key pubkey hash in channeldb\n") } chainNotifier, err := btcdnotify.NewBtcdNotifier(wallet) if err != nil { return nil, nil, err } // TODO(roasbeef): logging return &LightningWallet{ db: walletDB, chainNotifier: chainNotifier, Wallet: wallet, ChannelDB: cdb, msgChan: make(chan interface{}, msgBufferSize), // TODO(roasbeef): make this atomic.Uint32 instead? Which is // faster, locks or CAS? I'm guessing CAS because assembly: // * https://golang.org/src/sync/atomic/asm_amd64.s nextFundingID: 0, cfg: config, fundingLimbo: make(map[uint64]*ChannelReservation), quit: make(chan struct{}), }, walletDB, nil }