// Startup establishes a connection to the RPC source, and spins up all // goroutines required to handle incoming messages. func (l *LightningWallet) Startup() error { // Already started? if atomic.AddInt32(&l.started, 1) != 1 { return nil } // TODO(roasbeef): config... rpcc, err := chain.NewClient(ActiveNetParams, l.cfg.RpcHost, l.cfg.RpcUser, l.cfg.RpcPass, l.cfg.CACert, false) if err != nil { return err } // Start the goroutines in the underlying wallet. l.rpc = rpcc if err := l.rpc.Start(); err != nil { return err } l.Start(rpcc) l.wg.Add(1) // TODO(roasbeef): multiple request handlers? go l.requestHandler() 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() 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)) }() } // Load the wallet database. It must have been created with the // --create option already or this will return an appropriate error. wallet, db, err := openWallet() if err != nil { log.Errorf("%v", err) return err } defer db.Close() // 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. server, err := newRPCServer(cfg.SvrListeners, cfg.RPCMaxClients, cfg.RPCMaxWebsockets) if err != nil { log.Errorf("Unable to create HTTP server: %v", err) return err } server.Start() server.SetWallet(wallet) // Shutdown the server if an interrupt signal is received. addInterruptHandler(server.Stop) go func() { for { // Read CA certs and create the RPC client. var certs []byte if !cfg.DisableClientTLS { certs, err = ioutil.ReadFile(cfg.CAFile) if err != nil { log.Warnf("Cannot open CA file: %v", err) // If there's an error reading the CA file, continue // with nil certs and without the client connection certs = nil } } else { log.Info("Client TLS is disabled") } rpcc, err := chain.NewClient(activeNet.Params, cfg.RPCConnect, cfg.BtcdUsername, cfg.BtcdPassword, certs, cfg.DisableClientTLS) if err != nil { log.Errorf("Cannot create chain server RPC client: %v", err) return } err = rpcc.Start() if err != nil { log.Warnf("Connection to Bitcoin RPC chain server " + "unsuccessful -- available RPC methods will be limited") } // Even if Start errored, we still add the server disconnected. // All client methods will then error, so it's obvious to a // client that the there was a connection problem. server.SetChainServer(rpcc) // Start wallet goroutines and handle RPC client notifications // if the server is not shutting down. select { case <-server.quit: return default: wallet.Start(rpcc) } // Block goroutine until the client is finished. rpcc.WaitForShutdown() wallet.SetChainSynced(false) wallet.Stop() // Reconnect only if the server is not shutting down. select { case <-server.quit: return default: } } }() // Wait for the server to shutdown either due to a stop RPC request // or an interrupt. server.WaitForShutdown() log.Info("Shutdown complete") return nil }