package main import ( "encoding/hex" "fmt" "io/ioutil" "net" "net/http" _ "net/http/pprof" "os" "runtime" "strconv" "strings" "golang.org/x/net/context" "google.golang.org/grpc" flags "github.com/btcsuite/go-flags" proxy "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/lightningnetwork/lnd/chainntnfs/btcdnotify" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/roasbeef/btcrpcclient" ) 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 backendLog.Flush() // 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() // Set the RPC config from the "home" chain. Multi-chain isn't yet // active, so we'll restrict usage to a particular chain for now. homeChainConfig := cfg.Bitcoin if registeredChains.PrimaryChain() == litecoinChain { homeChainConfig = cfg.Litecoin } ltndLog.Infof("Primary chain is set to: %v", registeredChains.PrimaryChain()) // Next load btcd's TLS cert for the RPC connection. If a raw cert was // specified in the config, then we'll set that directly. Otherwise, we // attempt to read the cert from the path specified in the config. var rpcCert []byte if homeChainConfig.RawRPCCert != "" { rpcCert, err = hex.DecodeString(homeChainConfig.RawRPCCert) if err != nil { return err } } else { certFile, err := os.Open(homeChainConfig.RPCCert) if err != nil { return err } rpcCert, err = ioutil.ReadAll(certFile) if err != nil { return err } if err := certFile.Close(); err != nil { return err } } // If the specified host for the btcd RPC server already has a port // specified, then we use that directly. Otherwise, we assume the // default port according to the selected chain parameters. var btcdHost string if strings.Contains(homeChainConfig.RPCHost, ":") { btcdHost = homeChainConfig.RPCHost } else { btcdHost = fmt.Sprintf("%v:%v", homeChainConfig.RPCHost, activeNetParams.rpcPort) } btcdUser := homeChainConfig.RPCUser btcdPass := homeChainConfig.RPCPass // TODO(roasbeef): parse config here and select chosen notifier instead rpcConfig := &btcrpcclient.ConnConfig{ Host: btcdHost, Endpoint: "ws", User: btcdUser, Pass: btcdPass, Certificates: rpcCert, DisableTLS: false, DisableConnectOnNew: true, DisableAutoReconnect: false, } notifier, err := btcdnotify.New(rpcConfig) if err != nil { return err } // TODO(roasbeef): parse config here select chosen WalletController walletConfig := &btcwallet.Config{ PrivatePass: []byte("hello"), DataDir: homeChainConfig.ChainDir, RPCHost: btcdHost, RPCUser: homeChainConfig.RPCUser, RPCPass: homeChainConfig.RPCPass, CACert: rpcCert, NetParams: activeNetParams.Params, } wc, err := btcwallet.New(*walletConfig) if err != nil { fmt.Printf("unable to create wallet controller: %v\n", err) return err } signer := wc bio := wc fundingSigner := wc // Create, and start the lnwallet, which handles the core payment // channel logic, and exposes control via proxy state machines. wallet, err := lnwallet.NewLightningWallet(chanDB, notifier, wc, signer, bio, activeNetParams.Params) if err != nil { fmt.Printf("unable to create wallet: %v\n", err) return err } if err := wallet.Startup(); err != nil { fmt.Printf("unable to start wallet: %v\n", err) return err } ltndLog.Info("LightningWallet opened") // Set up the core server which will listen for incoming peer // connections. defaultListenAddrs := []string{ net.JoinHostPort("", strconv.Itoa(cfg.PeerPort)), } // 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, &chainControl{ chainIO: bio, chainNotifier: notifier, wallet: wallet, }) // With all the relevant chains initialized, we can finally start the // server itself. server, err := newServer(defaultListenAddrs, notifier, bio, fundingSigner, wallet, chanDB) if err != nil { srvrLog.Errorf("unable to create server: %v\n", err) return err } if err := server.Start(); err != nil { srvrLog.Errorf("unable to create to start server: %v\n", err) return err } addInterruptHandler(func() { ltndLog.Infof("Gracefully shutting down the server...") server.Stop() server.WaitForShutdown() }) // Initialize, and register our implementation of the gRPC server. var opts []grpc.ServerOption grpcServer := grpc.NewServer(opts...) lnrpc.RegisterLightningServer(grpcServer, server.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) }() // 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.WithInsecure()} err = lnrpc.RegisterLightningHandlerFromEndpoint(ctx, mux, grpcEndpoint, proxyOpts) if err != nil { return err } go func() { rpcsLog.Infof("gRPC proxy started") http.ListenAndServe(":8080", mux) }() // 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) } }