// TestInterfaces tests all registered interfaces with a unified set of tests // which excersie each of the required methods found within the WalletController // interface. // // NOTE: In the future, when additional implementations of the WalletController // interface have been implemented, in order to ensure the new concrete // implementation is automatically tested, two steps must be undertaken. First, // one needs add a "non-captured" (_) import from the new sub-package. This // import should trigger an init() method within the package which registeres // the interface. Second, an additional case in the switch within the main loop // below needs to be added which properly initializes the interface. // // TODO(roasbeef): purge bobNode in favor of dual lnwallet's func TestLightningWallet(t *testing.T) { netParams := &chaincfg.SimNetParams // Initialize the harness around a btcd node which will serve as our // dedicated miner to generate blocks, cause re-orgs, etc. We'll set // up this node with a chain length of 125, so we have plentyyy of BTC // to play around with. miningNode, err := rpctest.New(netParams, nil, nil) defer miningNode.TearDown() if err != nil { t.Fatalf("unable to create mining node: %v", err) } if err := miningNode.SetUp(true, 25); err != nil { t.Fatalf("unable to set up mining node: %v", err) } rpcConfig := miningNode.RPCConfig() chainNotifier, err := btcdnotify.New(&rpcConfig) if err != nil { t.Fatalf("unable to create notifier: %v", err) } if err := chainNotifier.Start(); err != nil { t.Fatalf("unable to start notifier: %v", err) } var bio lnwallet.BlockChainIO var signer lnwallet.Signer var wc lnwallet.WalletController for _, walletDriver := range lnwallet.RegisteredWallets() { tempTestDir, err := ioutil.TempDir("", "lnwallet") if err != nil { t.Fatalf("unable to create temp directory: %v", err) } defer os.RemoveAll(tempTestDir) walletType := walletDriver.WalletType switch walletType { case "btcwallet": btcwalletConfig := &btcwallet.Config{ PrivatePass: privPass, HdSeed: testHdSeed[:], DataDir: tempTestDir, NetParams: netParams, RpcHost: rpcConfig.Host, RpcUser: rpcConfig.User, RpcPass: rpcConfig.Pass, CACert: rpcConfig.Certificates, } wc, err = walletDriver.New(btcwalletConfig) if err != nil { t.Fatalf("unable to create btcwallet: %v", err) } signer = wc.(*btcwallet.BtcWallet) bio = wc.(*btcwallet.BtcWallet) default: t.Fatalf("unknown wallet driver: %v", walletType) } // Funding via 20 outputs with 4BTC each. lnwallet, err := createTestWallet(tempTestDir, miningNode, netParams, chainNotifier, wc, signer, bio) if err != nil { t.Fatalf("unable to create test ln wallet: %v", err) } // The wallet should now have 80BTC available for spending. assertProperBalance(t, lnwallet, 1, 80) // Execute every test, clearing possibly mutated wallet state after // each step. for _, walletTest := range walletTests { walletTest(miningNode, lnwallet, t) // TODO(roasbeef): possible reset mining node's chainstate to // initial level, cleanly wipe buckets if err := clearWalletState(lnwallet); err != nil && err != bolt.ErrBucketNotFound { t.Fatalf("unable to wipe wallet state: %v", err) } } lnwallet.Shutdown() } }
// 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 meta-data. chanDB, err := channeldb.Open(cfg.DataDir, activeNetParams.Params) if err != nil { fmt.Println("unable to open channeldb: ", err) return err } defer chanDB.Close() // Next load btcd's TLS cert for the RPC connection. If a raw cert was // specified in the config, then we'll se that directly. Otherwise, we // attempt to read the cert from the path specified in the config. var rpcCert []byte if cfg.RawRPCCert != "" { rpcCert, err = hex.DecodeString(cfg.RawRPCCert) if err != nil { return err } } else { certFile, err := os.Open(cfg.RPCCert) if err != nil { return err } rpcCert, err = ioutil.ReadAll(certFile) if err != nil { return err } if err := certFile.Close(); err != nil { return err } } rpcIP, err := net.LookupHost(cfg.RPCHost) if err != nil { fmt.Printf("unable to resolve rpc host: %v", err) return err } btcdHost := fmt.Sprintf("%v:%v", cfg.RPCHost, activeNetParams.rpcPort) btcdUser := cfg.RPCUser btcdPass := cfg.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: filepath.Join(cfg.DataDir, "lnwallet"), RpcHost: fmt.Sprintf("%v:%v", rpcIP[0], activeNetParams.rpcPort), RpcUser: cfg.RPCUser, RpcPass: cfg.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 // 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)), } server, err := newServer(defaultListenAddrs, notifier, bio, 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: %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 } 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() swaggerPattern := proxy.MustPattern(proxy.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "swagger"}, "")) // TODO(roasbeef): accept path to swagger file as command-line option mux.Handle("GET", swaggerPattern, func(w http.ResponseWriter, r *http.Request, p map[string]string) { http.ServeFile(w, r, "lnrpc/rpc.swagger.json") }) 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 }