2020-09-09 17:45:32 -07:00
package pool
2019-11-11 14:30:46 +01:00
import (
2020-09-09 16:29:39 +02:00
"crypto/tls"
"crypto/x509"
2020-09-09 16:15:45 +02:00
"fmt"
2020-01-09 14:28:13 +01:00
"net"
2020-09-09 16:15:45 +02:00
"os"
2021-02-02 09:57:21 +01:00
"path"
2019-11-11 14:30:46 +01:00
"path/filepath"
2022-09-23 09:44:46 +02:00
"strconv"
2020-02-19 11:00:07 +01:00
"time"
2019-11-11 14:30:46 +01:00
2022-03-11 14:19:30 -08:00
"github.com/btcsuite/btcd/btcutil"
2020-09-09 16:29:39 +02:00
"github.com/lightningnetwork/lnd/cert"
2020-09-09 16:15:45 +02:00
"github.com/lightningnetwork/lnd/lncfg"
2020-09-09 16:29:39 +02:00
"github.com/lightningnetwork/lnd/lnrpc"
2022-07-05 21:23:06 +02:00
"github.com/lightningnetwork/lnd/signal"
2020-01-09 14:28:13 +01:00
"google.golang.org/grpc"
2020-09-09 16:29:39 +02:00
"google.golang.org/grpc/credentials"
2019-11-11 14:30:46 +01:00
)
var (
2020-09-09 17:45:32 -07:00
// DefaultBaseDir is the default root data directory where pool will
2020-01-09 14:28:13 +01:00
// store all its data. On UNIX like systems this will resolve to
2020-09-09 17:45:32 -07:00
// ~/.pool. Below this directory the logs and network directory will be
2020-01-09 14:28:13 +01:00
// created.
2020-09-09 17:45:32 -07:00
DefaultBaseDir = btcutil . AppDataDir ( "pool" , false )
2019-11-11 14:30:46 +01:00
2020-09-09 16:15:45 +02:00
// DefaultNetwork is the default bitcoin network pool runs on.
DefaultNetwork = "mainnet"
2020-09-09 17:45:32 -07:00
// DefaultLogFilename is the default name that is given to the pool log
2020-01-09 14:28:13 +01:00
// file.
2020-09-09 17:45:32 -07:00
DefaultLogFilename = "poold.log"
2020-01-09 14:28:13 +01:00
defaultLogLevel = "info"
defaultLogDirname = "logs"
defaultLogDir = filepath . Join ( DefaultBaseDir , defaultLogDirname )
2019-11-11 14:30:46 +01:00
defaultMaxLogFiles = 3
defaultMaxLogFileSize = 10
2020-02-19 14:00:31 +01:00
defaultMinBackoff = 5 * time . Second
defaultMaxBackoff = 1 * time . Minute
2020-09-09 16:29:39 +02:00
// 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"
2021-02-02 09:57:21 +01:00
// defaultLndMacaroon is the default macaroon file we use if the old,
// deprecated --lnd.macaroondir config option is used.
defaultLndMacaroon = "admin.macaroon"
2020-09-09 16:29:39 +02:00
// 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 ,
)
2020-09-09 17:13:50 +02:00
// 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 ,
)
2021-02-24 09:49:45 +01:00
// DefaultLndDir is the default location where we look for lnd's tls and
// macaroon files.
DefaultLndDir = btcutil . AppDataDir ( "lnd" , false )
// DefaultLndMacaroonPath is the default location where we look for a
// macaroon to use when connecting to lnd.
DefaultLndMacaroonPath = filepath . Join (
DefaultLndDir , "data" , "chain" , "bitcoin" , DefaultNetwork ,
defaultLndMacaroon ,
)
2021-10-20 19:24:34 -07:00
// DefaultAutogenValidity is the default validity of a self-signed
// certificate. The value corresponds to 14 months
// (14 months * 30 days * 24 hours).
DefaultAutogenValidity = 14 * 30 * 24 * time . Hour
2019-11-11 14:30:46 +01:00
)
2020-01-09 14:28:13 +01:00
type LndConfig struct {
2021-02-02 09:57:21 +01:00
Host string ` long:"host" description:"lnd instance rpc address" `
// MacaroonDir is the directory that contains all the macaroon files
// required for the remote connection.
MacaroonDir string ` long:"macaroondir" description:"DEPRECATED: Use macaroonpath." `
// MacaroonPath is the path to the single macaroon that should be used
// instead of needing to specify the macaroon directory that contains
// all of lnd's macaroons. The specified macaroon MUST have all
// permissions that all the subservers use, otherwise permission errors
// will occur.
MacaroonPath string ` long:"macaroonpath" description:"The full path to the single macaroon to use, either the admin.macaroon or a custom baked one. Cannot be specified at the same time as macaroondir. A custom macaroon must contain ALL permissions required for all subservers to work, otherwise permission errors will occur." `
TLSPath string ` long:"tlspath" description:"Path to lnd tls certificate" `
2019-11-11 14:30:46 +01:00
}
2020-01-09 14:28:13 +01:00
type Config struct {
2020-07-21 09:12:11 +02:00
ShowVersion bool ` long:"version" description:"Display version information and exit" `
2019-11-11 14:30:46 +01:00
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" `
2021-02-04 09:58:52 +01:00
Proxy string ` long:"proxy" description:"The host:port of a SOCKS proxy through which all connections to the pool server will be established over" `
2019-11-11 14:30:46 +01:00
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" `
2020-09-09 17:13:50 +02:00
BaseDir string ` long:"basedir" description:"The base directory where pool stores all its data. If set, this option overwrites --logdir, --macaroonpath, --tlscertpath and --tlskeypath." `
2019-11-11 14:30:46 +01:00
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" `
2020-02-19 14:00:31 +01:00
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}." `
2020-07-21 09:12:11 +02:00
DebugLevel string ` long:"debuglevel" description:"Logging level for all subsystems { trace, debug, info, warn, error, critical} -- You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems" `
2019-11-11 14:30:46 +01:00
2020-09-09 16:29:39 +02:00
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." `
2020-09-09 17:13:50 +02:00
MacaroonPath string ` long:"macaroonpath" description:"Path to write the macaroon for pool's RPC and REST services if it doesn't exist." `
2020-08-14 17:22:09 +02:00
NewNodesOnly bool ` long:"newnodesonly" description:"Only accept channels from nodes that the connected lnd node doesn't already have open or pending channels with." `
2021-07-01 10:31:08 +02:00
LsatMaxRoutingFee btcutil . Amount ` long:"lsatmaxroutingfee" description:"The maximum amount in satoshis we are willing to pay in routing fees when paying for the one-time LSAT auth token that is required to use the Pool service." `
2022-09-23 09:44:46 +02:00
Profile string ` long:"profile" description:"Enable HTTP profiling on given ip:port -- NOTE port must be between 1024 and 65535" `
2020-05-20 13:23:54 +02:00
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." `
2020-05-18 17:06:29 -07:00
2020-10-05 12:46:10 -07:00
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." `
2020-01-09 14:28:13 +01:00
Lnd * LndConfig ` group:"lnd" namespace:"lnd" `
2020-09-09 17:45:32 -07:00
// RPCListener is a network listener that can be set if poold should be
2020-01-09 14:28:13 +01:00
// 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
2021-12-21 14:29:37 +01:00
// DebugConfig is a set of debug options used for development and
// testing only.
DebugConfig * DebugConfig ` group:"debug" namespace:"debug" hidden:"true" `
2022-07-05 21:23:06 +02:00
// ShutdownInterceptor is the custom shutdown signal interceptor that
// the server is going to use to listen for (and issue) shutdown
// commands on.
ShutdownInterceptor signal . Interceptor
2022-12-02 17:37:29 +02:00
// RequestShutdown is a call-back function that can be called in order
// to indicate that pool has received a critical error and needs to shut
// down.
RequestShutdown func ( )
2021-12-21 14:29:37 +01:00
}
// DebugConfig is a set of debug options used for development and testing only.
type DebugConfig struct {
2022-06-30 22:17:31 +02:00
BatchVersion int32 ` long:"batchversion" description:"The batch version to use -- NOTE: for testing purposes only, don't use on mainnet" `
2019-11-11 14:30:46 +01:00
}
const (
2020-09-11 16:51:39 -07:00
MainnetServer = "pool.lightning.finance:12010"
2020-09-16 15:37:07 -07:00
TestnetServer = "test.pool.lightning.finance:12010"
2020-02-19 11:00:07 +01:00
2020-02-19 14:00:31 +01:00
// defaultRPCTimeout is the default number of seconds an unary RPC call
// is allowed to take to complete.
2020-02-19 11:00:07 +01:00
defaultRPCTimeout = 30 * time . Second
defaultLsatMaxCost = btcutil . Amount ( 1000 )
2021-07-01 10:24:36 +02:00
defaultLsatMaxFee = btcutil . Amount ( 50 )
2019-11-11 14:30:46 +01:00
)
2020-07-21 08:25:36 +02:00
// DefaultConfig returns the default value for the Config struct.
func DefaultConfig ( ) Config {
return Config {
2021-07-01 10:31:08 +02:00
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 ,
LsatMaxRoutingFee : defaultLsatMaxFee ,
2020-07-21 08:25:36 +02:00
Lnd : & LndConfig {
2021-02-24 09:49:45 +01:00
Host : "localhost:10009" ,
MacaroonPath : DefaultLndMacaroonPath ,
2020-07-21 08:25:36 +02:00
} ,
2021-12-21 14:29:37 +01:00
DebugConfig : & DebugConfig {
2022-06-30 22:17:31 +02:00
// The default value is dynamic depending on the lnd
// version. So we set an invalid value here to signal
// "no value set".
BatchVersion : - 1 ,
2021-12-21 14:29:37 +01:00
} ,
2022-12-02 17:37:29 +02:00
RequestShutdown : func ( ) { } ,
2020-07-21 08:25:36 +02:00
}
2019-11-11 14:30:46 +01:00
}
2020-09-09 16:15:45 +02:00
// 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 )
2020-09-09 16:29:39 +02:00
cfg . TLSCertPath = lncfg . CleanAndExpandPath ( cfg . TLSCertPath )
cfg . TLSKeyPath = lncfg . CleanAndExpandPath ( cfg . TLSKeyPath )
2020-09-09 17:13:50 +02:00
cfg . MacaroonPath = lncfg . CleanAndExpandPath ( cfg . MacaroonPath )
2020-09-09 16:15:45 +02:00
2020-09-09 16:29:39 +02:00
// 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.
2020-09-09 16:15:45 +02:00
baseDirSet := cfg . BaseDir != DefaultBaseDir
if baseDirSet {
logDirSet := cfg . LogDir != defaultLogDir
2020-09-09 16:29:39 +02:00
tlsCertPathSet := cfg . TLSCertPath != DefaultTLSCertPath
tlsKeyPathSet := cfg . TLSKeyPath != DefaultTLSKeyPath
2020-09-09 17:13:50 +02:00
macaroonPathSet := cfg . MacaroonPath != DefaultMacaroonPath
2020-09-09 16:15:45 +02:00
if logDirSet {
return fmt . Errorf ( "basedir overwrites logdir, please " +
"only set one value" )
}
2020-09-09 16:29:39 +02:00
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" )
}
2020-09-09 17:13:50 +02:00
if macaroonPathSet {
return fmt . Errorf ( "basedir overwrites macaroonpath, " +
"please only set one value" )
}
2020-09-09 16:15:45 +02:00
// 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 )
2020-09-09 17:13:50 +02:00
// 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.
2020-09-09 16:29:39 +02:00
if cfg . TLSCertPath == DefaultTLSCertPath {
cfg . TLSCertPath = filepath . Join (
cfg . BaseDir , DefaultTLSCertFilename ,
)
}
if cfg . TLSKeyPath == DefaultTLSKeyPath {
cfg . TLSKeyPath = filepath . Join (
cfg . BaseDir , DefaultTLSKeyFilename ,
)
}
2020-09-09 17:13:50 +02:00
if cfg . MacaroonPath == DefaultMacaroonPath {
cfg . MacaroonPath = filepath . Join (
cfg . BaseDir , DefaultMacaroonFilename ,
)
}
2020-09-09 16:29:39 +02:00
2020-09-09 16:15:45 +02:00
// 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
}
2021-02-02 09:57:21 +01:00
// Make sure only one of the macaroon options is used.
switch {
2021-02-24 09:49:45 +01:00
case cfg . Lnd . MacaroonPath != DefaultLndMacaroonPath &&
cfg . Lnd . MacaroonDir != "" :
2021-02-02 09:57:21 +01:00
return fmt . Errorf ( "use --lnd.macaroonpath only" )
case cfg . Lnd . MacaroonDir != "" :
// With the new version of lndclient we can only specify a
// single macaroon instead of all of them. If the old
// macaroondir is used, we use the admin macaroon located in
// that directory.
cfg . Lnd . MacaroonPath = path . Join (
lncfg . CleanAndExpandPath ( cfg . Lnd . MacaroonDir ) ,
defaultLndMacaroon ,
)
case cfg . Lnd . MacaroonPath != "" :
cfg . Lnd . MacaroonPath = lncfg . CleanAndExpandPath (
cfg . Lnd . MacaroonPath ,
)
default :
return fmt . Errorf ( "must specify --lnd.macaroonpath" )
}
2021-02-24 09:49:45 +01:00
// Adjust the default lnd macaroon path if only the network is
// specified.
if cfg . Network != DefaultNetwork &&
cfg . Lnd . MacaroonPath == DefaultLndMacaroonPath {
cfg . Lnd . MacaroonPath = path . Join (
DefaultLndDir , "data" , "chain" , "bitcoin" , cfg . Network ,
defaultLndMacaroon ,
)
}
2022-09-23 09:44:46 +02:00
// Enable http profiling and Validate profile port number if requested.
if cfg . Profile != "" {
portErr := fmt . Errorf ( "the profile port must be between 1024 " +
"and 65535" )
inRange := func ( port int ) bool {
return port >= 1024 && port <= 65535
}
// Try to parse Profile as a host:port.
_ , hostPort , err := net . SplitHostPort ( cfg . Profile )
if err == nil {
// Determine if the port is valid.
profilePort , err := strconv . Atoi ( hostPort )
if err != nil || ! inRange ( profilePort ) {
return portErr
}
} else {
// Try to parse Profile as a port.
profilePort , err := strconv . Atoi ( cfg . Profile )
if err != nil || ! inRange ( profilePort ) {
return portErr
}
// Since the user just set a port, we will serve
// debugging information over localhost.
cfg . Profile = net . JoinHostPort ( "127.0.0.1" , cfg . Profile )
}
}
2020-09-09 16:15:45 +02:00
return nil
}
2020-09-09 16:29:39 +02:00
// 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 )
2021-04-23 14:56:48 -05:00
tlsCfg . NextProtos = [ ] string { "h2" }
2020-09-09 16:29:39 +02:00
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..." )
2023-01-30 17:08:23 +01:00
certBytes , keyBytes , err := cert . GenCertPair (
defaultSelfSignedOrganization , cfg . TLSExtraIPs ,
2020-09-09 16:29:39 +02:00
cfg . TLSExtraDomains , cfg . TLSDisableAutofill ,
2021-10-20 19:24:34 -07:00
DefaultAutogenValidity ,
2020-09-09 16:29:39 +02:00
)
if err != nil {
return tls . Certificate { } , nil , err
}
2023-01-30 17:08:23 +01:00
// Now that we have the certificate and key, we'll store them
// to the file system.
err = cert . WriteCertPair (
cfg . TLSCertPath , cfg . TLSKeyPath , certBytes , keyBytes ,
)
if err != nil {
return tls . Certificate { } , nil , err
}
2020-09-09 16:29:39 +02:00
log . Infof ( "Done generating TLS certificates" )
}
return cert . LoadCert ( cfg . TLSCertPath , cfg . TLSKeyPath )
}