// createTestWallet creates a test LightningWallet will a total of 20BTC // available for funding channels. func createTestWallet(tempTestDir string, miningNode *rpctest.Harness, netParams *chaincfg.Params, notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController, signer lnwallet.Signer, bio lnwallet.BlockChainIO) (*lnwallet.LightningWallet, error) { dbDir := filepath.Join(tempTestDir, "cdb") cdb, err := channeldb.Open(dbDir, &chaincfg.SegNet4Params) if err != nil { return nil, err } wallet, err := lnwallet.NewLightningWallet(cdb, notifier, wc, signer, bio, netParams) if err != nil { return nil, err } if err := wallet.Startup(); err != nil { return nil, err } // Load our test wallet with 20 outputs each holding 4BTC. if err := loadTestCredits(miningNode, wallet, 20, 4); err != nil { return nil, err } return wallet, nil }
// createTestChannels creates two test channels funded witr 10 BTC, with 5 BTC // allocated to each side. func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChannel, func(), error) { aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), testWalletPrivKey) bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(btcec.S256(), bobsPrivKey) channelCapacity := btcutil.Amount(10 * 1e8) channelBal := channelCapacity / 2 csvTimeoutAlice := uint32(5) csvTimeoutBob := uint32(4) witnessScript, _, err := GenFundingPkScript(aliceKeyPub.SerializeCompressed(), bobKeyPub.SerializeCompressed(), int64(channelCapacity)) if err != nil { return nil, nil, nil, err } prevOut := &wire.OutPoint{ Hash: wire.ShaHash(testHdSeed), Index: 0, } fundingTxIn := wire.NewTxIn(prevOut, nil, nil) bobElkrem := elkrem.NewElkremSender(deriveElkremRoot(bobKeyPriv, bobKeyPub, aliceKeyPub)) bobFirstRevoke, err := bobElkrem.AtIndex(0) if err != nil { return nil, nil, nil, err } bobRevokeKey := DeriveRevocationPubkey(aliceKeyPub, bobFirstRevoke[:]) aliceElkrem := elkrem.NewElkremSender(deriveElkremRoot(aliceKeyPriv, aliceKeyPub, bobKeyPub)) aliceFirstRevoke, err := aliceElkrem.AtIndex(0) if err != nil { return nil, nil, nil, err } aliceRevokeKey := DeriveRevocationPubkey(bobKeyPub, aliceFirstRevoke[:]) aliceCommitTx, err := CreateCommitTx(fundingTxIn, aliceKeyPub, bobKeyPub, aliceRevokeKey, csvTimeoutAlice, channelBal, channelBal) if err != nil { return nil, nil, nil, err } bobCommitTx, err := CreateCommitTx(fundingTxIn, bobKeyPub, aliceKeyPub, bobRevokeKey, csvTimeoutBob, channelBal, channelBal) if err != nil { return nil, nil, nil, err } alicePath, err := ioutil.TempDir("", "alicedb") dbAlice, err := channeldb.Open(alicePath, &chaincfg.TestNet3Params) if err != nil { return nil, nil, nil, err } bobPath, err := ioutil.TempDir("", "bobdb") dbBob, err := channeldb.Open(bobPath, &chaincfg.TestNet3Params) if err != nil { return nil, nil, nil, err } aliceChannelState := &channeldb.OpenChannel{ IdentityPub: aliceKeyPub, ChanID: prevOut, OurCommitKey: aliceKeyPub, TheirCommitKey: bobKeyPub, Capacity: channelCapacity, OurBalance: channelBal, TheirBalance: channelBal, OurCommitTx: aliceCommitTx, FundingOutpoint: prevOut, OurMultiSigKey: aliceKeyPub, TheirMultiSigKey: bobKeyPub, FundingWitnessScript: witnessScript, LocalCsvDelay: csvTimeoutAlice, RemoteCsvDelay: csvTimeoutBob, TheirCurrentRevocation: bobRevokeKey, LocalElkrem: aliceElkrem, RemoteElkrem: &elkrem.ElkremReceiver{}, Db: dbAlice, } bobChannelState := &channeldb.OpenChannel{ IdentityPub: bobKeyPub, ChanID: prevOut, OurCommitKey: bobKeyPub, TheirCommitKey: aliceKeyPub, Capacity: channelCapacity, OurBalance: channelBal, TheirBalance: channelBal, OurCommitTx: bobCommitTx, FundingOutpoint: prevOut, OurMultiSigKey: bobKeyPub, TheirMultiSigKey: aliceKeyPub, FundingWitnessScript: witnessScript, LocalCsvDelay: csvTimeoutBob, RemoteCsvDelay: csvTimeoutAlice, TheirCurrentRevocation: aliceRevokeKey, LocalElkrem: bobElkrem, RemoteElkrem: &elkrem.ElkremReceiver{}, Db: dbBob, } cleanUpFunc := func() { os.RemoveAll(bobPath) os.RemoveAll(alicePath) } aliceSigner := &mockSigner{aliceKeyPriv} bobSigner := &mockSigner{bobKeyPriv} notifier := &mockNotfier{} channelAlice, err := NewLightningChannel(aliceSigner, nil, notifier, aliceChannelState) if err != nil { return nil, nil, nil, err } channelBob, err := NewLightningChannel(bobSigner, nil, notifier, bobChannelState) if err != nil { return nil, nil, nil, err } // Now that the channel are open, simulate the start of a session by // having Alice and Bob extend their revocation windows to each other. err = initRevocationWindows(channelAlice, channelBob, revocationWindow) if err != nil { return nil, nil, nil, err } return channelAlice, channelBob, cleanUpFunc, nil }
// 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 }