package pool import ( "crypto/tls" "crypto/x509" "fmt" "net" "os" "path/filepath" "time" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/cert" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) var ( // DefaultBaseDir is the default root data directory where pool will // store all its data. On UNIX like systems this will resolve to // ~/.pool. Below this directory the logs and network directory will be // created. DefaultBaseDir = btcutil.AppDataDir("pool", false) // DefaultNetwork is the default bitcoin network pool runs on. DefaultNetwork = "mainnet" // DefaultLogFilename is the default name that is given to the pool log // file. DefaultLogFilename = "poold.log" defaultLogLevel = "info" defaultLogDirname = "logs" defaultLogDir = filepath.Join(DefaultBaseDir, defaultLogDirname) defaultMaxLogFiles = 3 defaultMaxLogFileSize = 10 defaultMinBackoff = 5 * time.Second defaultMaxBackoff = 1 * time.Minute // DefaultTLSCertFilename is the default file name for the autogenerated // TLS certificate. DefaultTLSCertFilename = "tls.cert" // DefaultTLSKeyFilename is the default file name for the autogenerated // TLS key. DefaultTLSKeyFilename = "tls.key" defaultSelfSignedOrganization = "pool autogenerated cert" // DefaultTLSCertPath is the default full path of the autogenerated TLS // certificate. DefaultTLSCertPath = filepath.Join( DefaultBaseDir, DefaultNetwork, DefaultTLSCertFilename, ) // DefaultTLSKeyPath is the default full path of the autogenerated TLS // key. DefaultTLSKeyPath = filepath.Join( DefaultBaseDir, DefaultNetwork, DefaultTLSKeyFilename, ) // DefaultMacaroonFilename is the default file name for the // autogenerated pool macaroon. DefaultMacaroonFilename = "pool.macaroon" // DefaultMacaroonPath is the default full path of the base pool // macaroon. DefaultMacaroonPath = filepath.Join( DefaultBaseDir, DefaultNetwork, DefaultMacaroonFilename, ) ) type LndConfig struct { Host string `long:"host" description:"lnd instance rpc address"` MacaroonDir string `long:"macaroondir" description:"Path to the directory containing all the required lnd macaroons"` TLSPath string `long:"tlspath" description:"Path to lnd tls certificate"` } type Config struct { ShowVersion bool `long:"version" description:"Display version information and exit"` Insecure bool `long:"insecure" description:"disable tls"` Network string `long:"network" description:"network to run on" choice:"regtest" choice:"testnet" choice:"mainnet" choice:"simnet"` AuctionServer string `long:"auctionserver" description:"auction server address host:port"` TLSPathAuctSrv string `long:"tlspathauctserver" description:"Path to auction server tls certificate"` RPCListen string `long:"rpclisten" description:"Address to listen on for gRPC clients"` RESTListen string `long:"restlisten" description:"Address to listen on for REST clients"` BaseDir string `long:"basedir" description:"The base directory where pool stores all its data. If set, this option overwrites --logdir, --macaroonpath, --tlscertpath and --tlskeypath."` LogDir string `long:"logdir" description:"Directory to log output."` MaxLogFiles int `long:"maxlogfiles" description:"Maximum logfiles to keep (0 for no rotation)"` MaxLogFileSize int `long:"maxlogfilesize" description:"Maximum logfile size in MB"` MinBackoff time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to the server. Valid time units are {s, m, h}."` MaxBackoff time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to the server. Valid time units are {s, m, h}."` DebugLevel string `long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` TLSCertPath string `long:"tlscertpath" description:"Path to write the TLS certificate for pool's RPC and REST services."` TLSKeyPath string `long:"tlskeypath" description:"Path to write the TLS private key for pool's RPC and REST services."` TLSExtraIPs []string `long:"tlsextraip" description:"Adds an extra IP to the generated certificate."` TLSExtraDomains []string `long:"tlsextradomain" description:"Adds an extra domain to the generated certificate."` TLSAutoRefresh bool `long:"tlsautorefresh" description:"Re-generate TLS certificate and key if the IPs or domains are changed."` TLSDisableAutofill bool `long:"tlsdisableautofill" description:"Do not include the interface IPs or the system hostname in TLS certificate, use first --tlsextradomain as Common Name instead, if set."` MacaroonPath string `long:"macaroonpath" description:"Path to write the macaroon for pool's RPC and REST services if it doesn't exist."` NewNodesOnly bool `long:"newnodesonly" description:"Only accept channels from nodes that the connected lnd node doesn't already have open or pending channels with."` Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65535"` FakeAuth bool `long:"fakeauth" description:"Disable LSAT authentication and instead use a fake LSAT ID to identify. For testing only, cannot be set on mainnet."` TxLabelPrefix string `long:"txlabelprefix" description:"If set, then every transaction poold makes will be created with a label that has this string as a prefix."` Lnd *LndConfig `group:"lnd" namespace:"lnd"` // RPCListener is a network listener that can be set if poold should be // used as a library and listen on the given listener instead of what is // configured in the --rpclisten parameter. Setting this will also // disable REST. RPCListener net.Listener // AuctioneerDialOpts is a list of dial options that should be used when // dialing the auctioneer server. AuctioneerDialOpts []grpc.DialOption } const ( MainnetServer = "pool.lightning.finance:12010" TestnetServer = "test.pool.lightning.finance:12010" // defaultRPCTimeout is the default number of seconds an unary RPC call // is allowed to take to complete. defaultRPCTimeout = 30 * time.Second defaultLsatMaxCost = btcutil.Amount(1000) defaultLsatMaxFee = btcutil.Amount(10) ) // DefaultConfig returns the default value for the Config struct. func DefaultConfig() Config { return Config{ Network: DefaultNetwork, RPCListen: "localhost:12010", RESTListen: "localhost:8281", Insecure: false, BaseDir: DefaultBaseDir, LogDir: defaultLogDir, MaxLogFiles: defaultMaxLogFiles, MaxLogFileSize: defaultMaxLogFileSize, MinBackoff: defaultMinBackoff, MaxBackoff: defaultMaxBackoff, DebugLevel: defaultLogLevel, TLSCertPath: DefaultTLSCertPath, TLSKeyPath: DefaultTLSKeyPath, MacaroonPath: DefaultMacaroonPath, Lnd: &LndConfig{ Host: "localhost:10009", }, } } // Validate cleans up paths in the config provided and validates it. func Validate(cfg *Config) error { // Cleanup any paths before we use them. cfg.BaseDir = lncfg.CleanAndExpandPath(cfg.BaseDir) cfg.LogDir = lncfg.CleanAndExpandPath(cfg.LogDir) cfg.TLSCertPath = lncfg.CleanAndExpandPath(cfg.TLSCertPath) cfg.TLSKeyPath = lncfg.CleanAndExpandPath(cfg.TLSKeyPath) cfg.MacaroonPath = lncfg.CleanAndExpandPath(cfg.MacaroonPath) // Since our pool directory overrides our log and TLS dir values, make // sure that they are not set when base dir is set. We hard here rather // than overwriting and potentially confusing the user. baseDirSet := cfg.BaseDir != DefaultBaseDir if baseDirSet { logDirSet := cfg.LogDir != defaultLogDir tlsCertPathSet := cfg.TLSCertPath != DefaultTLSCertPath tlsKeyPathSet := cfg.TLSKeyPath != DefaultTLSKeyPath macaroonPathSet := cfg.MacaroonPath != DefaultMacaroonPath if logDirSet { return fmt.Errorf("basedir overwrites logdir, please " + "only set one value") } if tlsCertPathSet { return fmt.Errorf("basedir overwrites tlscertpath, " + "please only set one value") } if tlsKeyPathSet { return fmt.Errorf("basedir overwrites tlskeypath, " + "please only set one value") } if macaroonPathSet { return fmt.Errorf("basedir overwrites macaroonpath, " + "please only set one value") } // Once we are satisfied that no other config value was set, we // replace them with our pool dir. cfg.LogDir = filepath.Join(cfg.BaseDir, defaultLogDirname) } // Append the network type to the log and base directory so it is // "namespaced" per network in the same fashion as the data directory. cfg.LogDir = filepath.Join(cfg.LogDir, cfg.Network) cfg.BaseDir = filepath.Join(cfg.BaseDir, cfg.Network) // We want the TLS and macaroon files to also be in the "namespaced" sub // directory. Replace the default values with actual values in case the // user specified basedir. if cfg.TLSCertPath == DefaultTLSCertPath { cfg.TLSCertPath = filepath.Join( cfg.BaseDir, DefaultTLSCertFilename, ) } if cfg.TLSKeyPath == DefaultTLSKeyPath { cfg.TLSKeyPath = filepath.Join( cfg.BaseDir, DefaultTLSKeyFilename, ) } if cfg.MacaroonPath == DefaultMacaroonPath { cfg.MacaroonPath = filepath.Join( cfg.BaseDir, DefaultMacaroonFilename, ) } // If either of these directories do not exist, create them. if err := os.MkdirAll(cfg.BaseDir, os.ModePerm); err != nil { return err } if err := os.MkdirAll(cfg.LogDir, os.ModePerm); err != nil { return err } return nil } // getTLSConfig generates a new self signed certificate or refreshes an existing // one if necessary, then returns the full TLS configuration for initializing // a secure server interface. func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials, error) { // Let's load our certificate first or create then load if it doesn't // yet exist. certData, parsedCert, err := loadCertWithCreate(cfg) if err != nil { return nil, nil, err } // If the certificate expired or it was outdated, delete it and the TLS // key and generate a new pair. if time.Now().After(parsedCert.NotAfter) { log.Info("TLS certificate is expired or outdated, " + "removing old file then generating a new one") err := os.Remove(cfg.TLSCertPath) if err != nil { return nil, nil, err } err = os.Remove(cfg.TLSKeyPath) if err != nil { return nil, nil, err } certData, _, err = loadCertWithCreate(cfg) if err != nil { return nil, nil, err } } tlsCfg := cert.TLSConfFromCert(certData) restCreds, err := credentials.NewClientTLSFromFile( cfg.TLSCertPath, "", ) if err != nil { return nil, nil, err } return tlsCfg, &restCreds, nil } // loadCertWithCreate tries to load the TLS certificate from disk. If the // specified cert and key files don't exist, the certificate/key pair is created // first. func loadCertWithCreate(cfg *Config) (tls.Certificate, *x509.Certificate, error) { // Ensure we create TLS key and certificate if they don't exist. if !lnrpc.FileExists(cfg.TLSCertPath) && !lnrpc.FileExists(cfg.TLSKeyPath) { log.Infof("Generating TLS certificates...") err := cert.GenCertPair( defaultSelfSignedOrganization, cfg.TLSCertPath, cfg.TLSKeyPath, cfg.TLSExtraIPs, cfg.TLSExtraDomains, cfg.TLSDisableAutofill, cert.DefaultAutogenValidity, ) if err != nil { return tls.Certificate{}, nil, err } log.Infof("Done generating TLS certificates") } return cert.LoadCert(cfg.TLSCertPath, cfg.TLSKeyPath) }