package main import ( "crypto/rand" "fmt" "io/ioutil" "net" "net/http" _ "net/http/pprof" "os" "runtime" "strconv" "time" "gopkg.in/macaroon-bakery.v1/bakery" "gopkg.in/macaroon-bakery.v1/bakery/checkers" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" flags "github.com/btcsuite/go-flags" proxy "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcutil" ) const ( autogenCertValidity = 10 * 365 * 24 * time.Hour ) var ( cfg *config shutdownChannel = make(chan struct{}) registeredChains = newChainRegistry() ) // lndMain is the true entry point for lnd. This function is required since // defers created in the top-level scope of a main method aren't executed if // os.Exit() is called. func lndMain() error { // Load the configuration, and parse any command line options. This // function will also set up logging properly. loadedConfig, err := loadConfig() if err != nil { return err } cfg = loadedConfig defer func() { if logRotator != nil { logRotator.Close() } }() // Show version at startup. ltndLog.Infof("Version %s", version()) // Enable http profiling server if requested. if cfg.Profile != "" { go func() { listenAddr := net.JoinHostPort("", cfg.Profile) profileRedirect := http.RedirectHandler("/debug/pprof", http.StatusSeeOther) http.Handle("/", profileRedirect) fmt.Println(http.ListenAndServe(listenAddr, nil)) }() } // Open the channeldb, which is dedicated to storing channel, and // network related metadata. chanDB, err := channeldb.Open(cfg.DataDir) if err != nil { ltndLog.Errorf("unable to open channeldb: %v", err) return err } defer chanDB.Close() // Only process macaroons if --no-macaroons isn't set. var macaroonService *bakery.Service if !cfg.NoMacaroons { // Create the macaroon authentication/authorization service. macaroonService, err = macaroons.NewService(cfg.DataDir) if err != nil { srvrLog.Errorf("unable to create macaroon service: %v", err) return err } // Create macaroon files for lncli to use if they don't exist. if !fileExists(cfg.AdminMacPath) && !fileExists(cfg.ReadMacPath) { err = genMacaroons(macaroonService, cfg.AdminMacPath, cfg.ReadMacPath) if err != nil { ltndLog.Errorf("unable to create macaroon "+ "files: %v", err) return err } } } // With the information parsed from the configuration, create valid // instances of the paertinent interfaces required to operate the // Lightning Network Daemon. activeChainControl, chainCleanUp, err := newChainControlFromConfig(cfg, chanDB) if err != nil { fmt.Printf("unable to create chain control: %v\n", err) return err } if chainCleanUp != nil { defer chainCleanUp() } // Finally before we start the server, we'll register the "holy // trinity" of interface for our current "home chain" with the active // chainRegistry interface. primaryChain := registeredChains.PrimaryChain() registeredChains.RegisterChain(primaryChain, activeChainControl) idPrivKey, err := activeChainControl.wallet.GetIdentitykey() if err != nil { return err } idPrivKey.Curve = btcec.S256() // Set up the core server which will listen for incoming peer // connections. defaultListenAddrs := []string{ net.JoinHostPort("", strconv.Itoa(cfg.PeerPort)), } server, err := newServer(defaultListenAddrs, chanDB, activeChainControl, idPrivKey) if err != nil { srvrLog.Errorf("unable to create server: %v\n", err) return err } // Next, we'll initialize the funding manager itself so it can answer // queries while the wallet+chain are still syncing. nodeSigner := newNodeSigner(idPrivKey) var chanIDSeed [32]byte if _, err := rand.Read(chanIDSeed[:]); err != nil { return err } fundingMgr, err := newFundingManager(fundingConfig{ IDKey: idPrivKey.PubKey(), Wallet: activeChainControl.wallet, Notifier: activeChainControl.chainNotifier, FeeEstimator: activeChainControl.feeEstimator, SignMessage: func(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error) { if pubKey.IsEqual(idPrivKey.PubKey()) { return nodeSigner.SignMessage(pubKey, msg) } return activeChainControl.msgSigner.SignMessage( pubKey, msg, ) }, CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement, error) { return server.genNodeAnnouncement(true) }, SendAnnouncement: func(msg lnwire.Message) error { errChan := server.discoverSrv.ProcessLocalAnnouncement(msg, idPrivKey.PubKey()) return <-errChan }, ArbiterChan: server.breachArbiter.newContracts, SendToPeer: server.SendToPeer, FindPeer: server.FindPeer, TempChanIDSeed: chanIDSeed, FindChannel: func(chanID lnwire.ChannelID) (*lnwallet.LightningChannel, error) { dbChannels, err := chanDB.FetchAllChannels() if err != nil { return nil, err } for _, channel := range dbChannels { if chanID.IsChanPoint(&channel.FundingOutpoint) { return lnwallet.NewLightningChannel( activeChainControl.signer, activeChainControl.chainNotifier, activeChainControl.feeEstimator, channel) } } return nil, fmt.Errorf("unable to find channel") }, DefaultRoutingPolicy: activeChainControl.routingPolicy, NumRequiredConfs: func(chanAmt btcutil.Amount, pushAmt btcutil.Amount) uint16 { // TODO(roasbeef): add configurable mapping // * simple switch initially // * assign coefficient, etc return uint16(cfg.DefaultNumChanConfs) }, RequiredRemoteDelay: func(chanAmt btcutil.Amount) uint16 { // TODO(roasbeef): add additional hooks for // configuration return 4 }, }) if err != nil { return err } if err := fundingMgr.Start(); err != nil { return err } server.fundingMgr = fundingMgr // Ensure we create TLS key and certificate if they don't exist if !fileExists(cfg.TLSCertPath) && !fileExists(cfg.TLSKeyPath) { if err := genCertPair(cfg.TLSCertPath, cfg.TLSKeyPath); err != nil { return err } } // Initialize, and register our implementation of the gRPC interface // exported by the rpcServer. rpcServer := newRPCServer(server, macaroonService) if err := rpcServer.Start(); err != nil { return err } sCreds, err := credentials.NewServerTLSFromFile(cfg.TLSCertPath, cfg.TLSKeyPath) if err != nil { return err } opts := []grpc.ServerOption{grpc.Creds(sCreds)} grpcServer := grpc.NewServer(opts...) lnrpc.RegisterLightningServer(grpcServer, rpcServer) // Next, Start the gRPC server listening for HTTP/2 connections. grpcEndpoint := fmt.Sprintf("localhost:%d", loadedConfig.RPCPort) lis, err := net.Listen("tcp", grpcEndpoint) if err != nil { fmt.Printf("failed to listen: %v", err) return err } defer lis.Close() go func() { rpcsLog.Infof("RPC server listening on %s", lis.Addr()) grpcServer.Serve(lis) }() cCreds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "") if err != nil { return err } // Finally, start the REST proxy for our gRPC server above. ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() mux := proxy.NewServeMux() proxyOpts := []grpc.DialOption{grpc.WithTransportCredentials(cCreds)} err = lnrpc.RegisterLightningHandlerFromEndpoint(ctx, mux, grpcEndpoint, proxyOpts) if err != nil { return err } go func() { restEndpoint := fmt.Sprintf(":%d", loadedConfig.RESTPort) rpcsLog.Infof("gRPC proxy started at localhost%s", restEndpoint) http.ListenAndServeTLS(restEndpoint, cfg.TLSCertPath, cfg.TLSKeyPath, mux) }() // If we're not in simnet mode, We'll wait until we're fully synced to // continue the start up of the remainder of the daemon. This ensures // that we don't accept any possibly invalid state transitions, or // accept channels with spent funds. if !(cfg.Bitcoin.SimNet || cfg.Litecoin.SimNet) { _, bestHeight, err := activeChainControl.chainIO.GetBestBlock() if err != nil { return err } ltndLog.Infof("Waiting for chain backend to finish sync, "+ "start_height=%v", bestHeight) for { synced, err := activeChainControl.wallet.IsSynced() if err != nil { return err } if synced { break } time.Sleep(time.Second * 1) } _, bestHeight, err = activeChainControl.chainIO.GetBestBlock() if err != nil { return err } ltndLog.Infof("Chain backend is fully synced (end_height=%v)!", bestHeight) } // With all the relevant chains initialized, we can finally start the // server itself. if err := server.Start(); err != nil { srvrLog.Errorf("unable to create to start server: %v\n", err) return err } // Now that the server has started, if the autopilot mode is currently // active, then we'll initialize a fresh instance of it and start it. var pilot *autopilot.Agent if cfg.Autopilot.Active { pilot, err := initAutoPilot(server, cfg.Autopilot) if err != nil { ltndLog.Errorf("unable to create autopilot agent: %v", err) return err } if err := pilot.Start(); err != nil { ltndLog.Errorf("unable to start autopilot agent: %v", err) return err } } addInterruptHandler(func() { ltndLog.Infof("Gracefully shutting down the server...") rpcServer.Stop() fundingMgr.Stop() server.Stop() if pilot != nil { pilot.Stop() } server.WaitForShutdown() }) // Wait for shutdown signal from either a graceful server stop or from // the interrupt handler. <-shutdownChannel ltndLog.Info("Shutdown complete") return nil } func main() { // Use all processor cores. // TODO(roasbeef): remove this if required version # is > 1.6? runtime.GOMAXPROCS(runtime.NumCPU()) // Call the "real" main in a nested manner so the defers will properly // be executed in the case of a graceful shutdown. if err := lndMain(); err != nil { if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { } else { fmt.Fprintln(os.Stderr, err) } os.Exit(1) } } // fileExists reports whether the named file or directory exists. // This function is taken from https://github.com/btcsuite/btcd func fileExists(name string) bool { if _, err := os.Stat(name); err != nil { if os.IsNotExist(err) { return false } } return true } // genCertPair generates a key/cert pair to the paths provided. // This function is adapted from https://github.com/btcsuite/btcd func genCertPair(certFile, keyFile string) error { rpcsLog.Infof("Generating TLS certificates...") org := "lnd autogenerated cert" validUntil := time.Now().Add(autogenCertValidity) cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil) if err != nil { return err } // Write cert and key files. if err = ioutil.WriteFile(certFile, cert, 0644); err != nil { return err } if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { os.Remove(certFile) return err } rpcsLog.Infof("Done generating TLS certificates") return nil } // genMacaroons generates a pair of macaroon files; one admin-level and one // read-only. These can also be used to generate more granular macaroons. func genMacaroons(svc *bakery.Service, admFile, roFile string) error { // Generate the admin macaroon and write it to a file. admMacaroon, err := svc.NewMacaroon("", nil, nil) if err != nil { return err } admBytes, err := admMacaroon.MarshalBinary() if err != nil { return err } if err = ioutil.WriteFile(admFile, admBytes, 0600); err != nil { return err } // Generate the read-only macaroon and write it to a file. caveat := checkers.AllowCaveat(roPermissions...) roMacaroon := admMacaroon.Clone() if err = svc.AddCaveat(roMacaroon, caveat); err != nil { return err } roBytes, err := roMacaroon.MarshalBinary() if err != nil { return err } if err = ioutil.WriteFile(roFile, roBytes, 0644); err != nil { os.Remove(admFile) return err } return nil }