diff --git a/cmd/loopd/config.go b/cmd/loopd/config.go new file mode 100644 index 0000000..04e4315 --- /dev/null +++ b/cmd/loopd/config.go @@ -0,0 +1,30 @@ +package main + +type lndConfig struct { + Host string `long:"host" description:"lnd instance rpc address"` + MacaroonPath string `long:"macaroonpath" description:"Path to lnd macaroon"` + TLSPath string `long:"tlspath" description:"Path to lnd tls certificate"` +} + +type viewParameters struct{} + +type config struct { + Insecure bool `long:"insecure" description:"disable tls"` + Network string `long:"network" description:"network to run on" choice:"regtest" choice:"testnet" choice:"mainnet" choice:"simnet"` + SwapServer string `long:"swapserver" description:"swap server address host:port"` + Listen string `long:"listen" description:"address to listen on for rpc lcients"` + + Lnd *lndConfig `group:"lnd" namespace:"lnd"` + + View viewParameters `command:"view" alias:"v" description:"View all swaps in the database. This command can only be executed when loopd is not running."` +} + +var defaultConfig = config{ + Network: "mainnet", + SwapServer: "swap.lightning.today:11009", + Listen: "localhost:11010", + Insecure: false, + Lnd: &lndConfig{ + Host: "localhost:10009", + }, +} diff --git a/cmd/loopd/daemon.go b/cmd/loopd/daemon.go index 8acca5c..9018763 100644 --- a/cmd/loopd/daemon.go +++ b/cmd/loopd/daemon.go @@ -12,20 +12,21 @@ import ( "github.com/lightninglabs/loop" "github.com/lightninglabs/loop/looprpc" - "github.com/urfave/cli" "google.golang.org/grpc" ) -// daemon runs swapd in daemon mode. It will listen for grpc connections, +// daemon runs loopd in daemon mode. It will listen for grpc connections, // execute commands and pass back swap status information. -func daemon(ctx *cli.Context) error { - lnd, err := getLnd(ctx) +func daemon(config *config) error { + lnd, err := getLnd(config.Network, config.Lnd) if err != nil { return err } defer lnd.Close() - swapClient, cleanup, err := getClient(ctx, &lnd.LndServices) + swapClient, cleanup, err := getClient( + config.Network, config.SwapServer, config.Insecure, &lnd.LndServices, + ) if err != nil { return err } @@ -48,7 +49,7 @@ func daemon(ctx *cli.Context) error { } } - // Instantiate the swapd gRPC server. + // Instantiate the loopd gRPC server. server := swapClientServer{ impl: swapClient, lnd: &lnd.LndServices, @@ -60,10 +61,10 @@ func daemon(ctx *cli.Context) error { // Next, Start the gRPC server listening for HTTP/2 connections. logger.Infof("Starting RPC listener") - lis, err := net.Listen("tcp", defaultListenAddr) + lis, err := net.Listen("tcp", config.Listen) if err != nil { return fmt.Errorf("RPC server unable to listen on %s", - defaultListenAddr) + config.Listen) } defer lis.Close() @@ -134,7 +135,7 @@ func daemon(ctx *cli.Context) error { interruptChannel := make(chan os.Signal, 1) signal.Notify(interruptChannel, os.Interrupt) - // Run until the users terminates swapd or an error occurred. + // Run until the users terminates loopd or an error occurred. select { case <-interruptChannel: logger.Infof("Received SIGINT (Ctrl+C).") diff --git a/cmd/loopd/log.go b/cmd/loopd/log.go index 8feea95..5ec7eef 100644 --- a/cmd/loopd/log.go +++ b/cmd/loopd/log.go @@ -11,7 +11,7 @@ import ( // it. var ( backendLog = btclog.NewBackend(logWriter{}) - logger = backendLog.Logger("SWAPD") + logger = backendLog.Logger("LOOPD") ) // logWriter implements an io.Writer that outputs to both standard output and diff --git a/cmd/loopd/main.go b/cmd/loopd/main.go index 49f0e3c..4df2e03 100644 --- a/cmd/loopd/main.go +++ b/cmd/loopd/main.go @@ -3,22 +3,23 @@ package main import ( "fmt" "os" + "path/filepath" "sync" + flags "github.com/jessevdk/go-flags" + "github.com/btcsuite/btcutil" "github.com/lightninglabs/loop" "github.com/lightningnetwork/lnd/lntypes" - "github.com/urfave/cli" ) const ( - defaultListenPort = 11010 defaultConfTarget = int32(2) ) var ( - defaultListenAddr = fmt.Sprintf("localhost:%d", defaultListenPort) - defaultSwapletDir = btcutil.AppDataDir("swaplet", false) + loopDirBase = btcutil.AppDataDir("loop", false) + defaultConfigFilename = "loopd.conf" swaps = make(map[lntypes.Hash]loop.SwapInfo) subscribers = make(map[int]chan<- interface{}) @@ -27,47 +28,58 @@ var ( ) func main() { - app := cli.NewApp() - - app.Flags = []cli.Flag{ - cli.StringFlag{ - Name: "network", - Value: "mainnet", - Usage: "network to run on (regtest, testnet, mainnet)", - }, - cli.StringFlag{ - Name: "lnd", - Value: "localhost:10009", - Usage: "lnd instance rpc address host:port", - }, - cli.StringFlag{ - Name: "swapserver", - Value: "swap.lightning.today:11009", - Usage: "swap server address host:port", - }, - cli.StringFlag{ - Name: "macaroonpath", - Usage: "path to lnd macaroon", - }, - cli.StringFlag{ - Name: "tlspath", - Usage: "path to lnd tls certificate", - }, - cli.BoolFlag{ - Name: "insecure", - Usage: "disable tls", - }, - } - app.Name = "loopd" - app.Version = "0.0.1" - app.Usage = "Lightning Loop Client Daemon" - app.Commands = []cli.Command{ - viewCommand, - } - app.Action = daemon - - err := app.Run(os.Args) + err := start() if err != nil { fmt.Println(err) } } + +func start() error { + config := defaultConfig + + // Parse command line flags. + parser := flags.NewParser(&config, flags.Default) + parser.SubcommandsOptional = true + + _, err := parser.Parse() + if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { + return nil + } + if err != nil { + return err + } + + // Parse ini file. + loopDir := filepath.Join(loopDirBase, config.Network) + if err := os.MkdirAll(loopDir, os.ModePerm); err != nil { + return err + } + + configFile := filepath.Join(loopDir, defaultConfigFilename) + if err := flags.IniParse(configFile, &config); err != nil { + // If it's a parsing related error, then we'll return + // immediately, otherwise we can proceed as possibly the config + // file doesn't exist which is OK. + if _, ok := err.(*flags.IniError); ok { + return err + } + } + + // Parse command line flags again to restore flags overwritten by ini + // parse. + _, err = parser.Parse() + if err != nil { + return err + } + + // Execute command. + if parser.Active == nil { + return daemon(&config) + } + + if parser.Active.Name == "view" { + return view(&config) + } + + return fmt.Errorf("unimplemented command %v", parser.Active.Name) +} diff --git a/cmd/loopd/utils.go b/cmd/loopd/utils.go index 7e7f2f9..787cf23 100644 --- a/cmd/loopd/utils.go +++ b/cmd/loopd/utils.go @@ -6,33 +6,26 @@ import ( "github.com/lightninglabs/loop" "github.com/lightninglabs/loop/lndclient" - "github.com/urfave/cli" ) // getLnd returns an instance of the lnd services proxy. -func getLnd(ctx *cli.Context) (*lndclient.GrpcLndServices, error) { - network := ctx.GlobalString("network") - - return lndclient.NewLndServices(ctx.GlobalString("lnd"), - "client", network, ctx.GlobalString("macaroonpath"), - ctx.GlobalString("tlspath"), +func getLnd(network string, cfg *lndConfig) (*lndclient.GrpcLndServices, error) { + return lndclient.NewLndServices( + cfg.Host, "client", network, cfg.MacaroonPath, cfg.TLSPath, ) } // getClient returns an instance of the swap client. -func getClient(ctx *cli.Context, +func getClient(network, swapServer string, insecure bool, lnd *lndclient.LndServices) (*loop.Client, func(), error) { - network := ctx.GlobalString("network") - storeDir, err := getStoreDir(network) if err != nil { return nil, nil, err } swapClient, cleanUp, err := loop.NewClient( - storeDir, ctx.GlobalString("swapserver"), - ctx.GlobalBool("insecure"), lnd, + storeDir, swapServer, insecure, lnd, ) if err != nil { return nil, nil, err @@ -42,7 +35,7 @@ func getClient(ctx *cli.Context, } func getStoreDir(network string) (string, error) { - dir := filepath.Join(defaultSwapletDir, network) + dir := filepath.Join(loopDirBase, network) if err := os.MkdirAll(dir, os.ModePerm); err != nil { return "", err } diff --git a/cmd/loopd/view.go b/cmd/loopd/view.go index b7373dd..40101c1 100644 --- a/cmd/loopd/view.go +++ b/cmd/loopd/view.go @@ -5,34 +5,24 @@ import ( "strconv" "github.com/lightninglabs/loop/swap" - "github.com/urfave/cli" ) -var viewCommand = cli.Command{ - Name: "view", - Usage: `view all swaps in the database. This command can only be - executed when swapd is not running.`, - Description: ` - Show all pending and completed swaps.`, - Action: view, -} - // view prints all swaps currently in the database. -func view(ctx *cli.Context) error { - network := ctx.GlobalString("network") - - chainParams, err := swap.ChainParamsFromNetwork(network) +func view(config *config) error { + chainParams, err := swap.ChainParamsFromNetwork(config.Network) if err != nil { return err } - lnd, err := getLnd(ctx) + lnd, err := getLnd(config.Network, config.Lnd) if err != nil { return err } defer lnd.Close() - swapClient, cleanup, err := getClient(ctx, &lnd.LndServices) + swapClient, cleanup, err := getClient( + config.Network, config.SwapServer, config.Insecure, &lnd.LndServices, + ) if err != nil { return err } @@ -43,6 +33,10 @@ func view(ctx *cli.Context) error { return err } + if len(swaps) == 0 { + fmt.Printf("No swaps\n") + } + for _, s := range swaps { htlc, err := swap.NewHtlc( s.Contract.CltvExpiry, diff --git a/lndclient/lnd_services.go b/lndclient/lnd_services.go index ab40949..3d3bb54 100644 --- a/lndclient/lnd_services.go +++ b/lndclient/lnd_services.go @@ -45,7 +45,7 @@ func NewLndServices(lndAddress string, application string, *GrpcLndServices, error) { // Setup connection with lnd - logger.Infof("Creating lnd connection") + logger.Infof("Creating lnd connection to %v", lndAddress) conn, err := getClientConn(lndAddress, network, macPath, tlsPath) if err != nil { return nil, err