You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

357 lines
13 KiB

  1. package loopd
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "fmt"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "time"
  10. "github.com/btcsuite/btcutil"
  11. "github.com/lightninglabs/aperture/lsat"
  12. "github.com/lightningnetwork/lnd/cert"
  13. "github.com/lightningnetwork/lnd/lncfg"
  14. "github.com/lightningnetwork/lnd/lnrpc"
  15. "google.golang.org/grpc/credentials"
  16. )
  17. var (
  18. // LoopDirBase is the default main directory where loop stores its data.
  19. LoopDirBase = btcutil.AppDataDir("loop", false)
  20. // DefaultNetwork is the default bitcoin network loop runs on.
  21. DefaultNetwork = "mainnet"
  22. defaultLogLevel = "info"
  23. defaultLogDirname = "logs"
  24. defaultLogFilename = "loopd.log"
  25. defaultLogDir = filepath.Join(LoopDirBase, defaultLogDirname)
  26. defaultConfigFile = filepath.Join(
  27. LoopDirBase, DefaultNetwork, defaultConfigFilename,
  28. )
  29. defaultMaxLogFiles = 3
  30. defaultMaxLogFileSize = 10
  31. defaultLoopOutMaxParts = uint32(5)
  32. // DefaultTLSCertFilename is the default file name for the autogenerated
  33. // TLS certificate.
  34. DefaultTLSCertFilename = "tls.cert"
  35. // DefaultTLSKeyFilename is the default file name for the autogenerated
  36. // TLS key.
  37. DefaultTLSKeyFilename = "tls.key"
  38. defaultSelfSignedOrganization = "loop autogenerated cert"
  39. // defaultLndMacaroon is the default macaroon file we use if the old,
  40. // deprecated --lnd.macaroondir config option is used.
  41. defaultLndMacaroon = "admin.macaroon"
  42. // DefaultTLSCertPath is the default full path of the autogenerated TLS
  43. // certificate.
  44. DefaultTLSCertPath = filepath.Join(
  45. LoopDirBase, DefaultNetwork, DefaultTLSCertFilename,
  46. )
  47. // DefaultTLSKeyPath is the default full path of the autogenerated TLS
  48. // key.
  49. DefaultTLSKeyPath = filepath.Join(
  50. LoopDirBase, DefaultNetwork, DefaultTLSKeyFilename,
  51. )
  52. // DefaultMacaroonFilename is the default file name for the
  53. // autogenerated loop macaroon.
  54. DefaultMacaroonFilename = "loop.macaroon"
  55. // DefaultMacaroonPath is the default full path of the base loop
  56. // macaroon.
  57. DefaultMacaroonPath = filepath.Join(
  58. LoopDirBase, DefaultNetwork, DefaultMacaroonFilename,
  59. )
  60. )
  61. type lndConfig struct {
  62. Host string `long:"host" description:"lnd instance rpc address"`
  63. // MacaroonDir is the directory that contains all the macaroon files
  64. // required for the remote connection.
  65. MacaroonDir string `long:"macaroondir" description:"DEPRECATED: Use macaroonpath."`
  66. // MacaroonPath is the path to the single macaroon that should be used
  67. // instead of needing to specify the macaroon directory that contains
  68. // all of lnd's macaroons. The specified macaroon MUST have all
  69. // permissions that all the subservers use, otherwise permission errors
  70. // will occur.
  71. 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."`
  72. TLSPath string `long:"tlspath" description:"Path to lnd tls certificate"`
  73. }
  74. type loopServerConfig struct {
  75. Host string `long:"host" description:"Loop server address host:port"`
  76. Proxy string `long:"proxy" description:"The host:port of a SOCKS proxy through which all connections to the loop server will be established over"`
  77. NoTLS bool `long:"notls" description:"Disable tls for communication to the loop server [testing only]"`
  78. TLSPath string `long:"tlspath" description:"Path to loop server tls certificate [testing only]"`
  79. }
  80. type viewParameters struct{}
  81. type Config struct {
  82. ShowVersion bool `long:"version" description:"Display version information and exit"`
  83. Network string `long:"network" description:"network to run on" choice:"regtest" choice:"testnet" choice:"mainnet" choice:"simnet"`
  84. RPCListen string `long:"rpclisten" description:"Address to listen on for gRPC clients"`
  85. RESTListen string `long:"restlisten" description:"Address to listen on for REST clients"`
  86. CORSOrigin string `long:"corsorigin" description:"The value to send in the Access-Control-Allow-Origin header. Header will be omitted if empty."`
  87. LoopDir string `long:"loopdir" description:"The directory for all of loop's data. If set, this option overwrites --datadir, --logdir, --tlscertpath, --tlskeypath and --macaroonpath."`
  88. ConfigFile string `long:"configfile" description:"Path to configuration file."`
  89. DataDir string `long:"datadir" description:"Directory for loopdb."`
  90. TLSCertPath string `long:"tlscertpath" description:"Path to write the TLS certificate for loop's RPC and REST services."`
  91. TLSKeyPath string `long:"tlskeypath" description:"Path to write the TLS private key for loop's RPC and REST services."`
  92. TLSExtraIPs []string `long:"tlsextraip" description:"Adds an extra IP to the generated certificate."`
  93. TLSExtraDomains []string `long:"tlsextradomain" description:"Adds an extra domain to the generated certificate."`
  94. TLSAutoRefresh bool `long:"tlsautorefresh" description:"Re-generate TLS certificate and key if the IPs or domains are changed."`
  95. 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."`
  96. MacaroonPath string `long:"macaroonpath" description:"Path to write the macaroon for loop's RPC and REST services if it doesn't exist."`
  97. LogDir string `long:"logdir" description:"Directory to log output."`
  98. MaxLogFiles int `long:"maxlogfiles" description:"Maximum logfiles to keep (0 for no rotation)."`
  99. MaxLogFileSize int `long:"maxlogfilesize" description:"Maximum logfile size in MB."`
  100. 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"`
  101. MaxLSATCost uint32 `long:"maxlsatcost" description:"Maximum cost in satoshis that loopd is going to pay for an LSAT token automatically. Does not include routing fees."`
  102. MaxLSATFee uint32 `long:"maxlsatfee" description:"Maximum routing fee in satoshis that we are willing to pay while paying for an LSAT token."`
  103. LoopOutMaxParts uint32 `long:"loopoutmaxparts" description:"The maximum number of payment parts that may be used for a loop out swap."`
  104. Lnd *lndConfig `group:"lnd" namespace:"lnd"`
  105. Server *loopServerConfig `group:"server" namespace:"server"`
  106. View viewParameters `command:"view" alias:"v" description:"View all swaps in the database. This command can only be executed when loopd is not running."`
  107. }
  108. const (
  109. mainnetServer = "swap.lightning.today:11010"
  110. testnetServer = "test.swap.lightning.today:11010"
  111. )
  112. // DefaultConfig returns all default values for the Config struct.
  113. func DefaultConfig() Config {
  114. return Config{
  115. Network: DefaultNetwork,
  116. RPCListen: "localhost:11010",
  117. RESTListen: "localhost:8081",
  118. Server: &loopServerConfig{
  119. NoTLS: false,
  120. },
  121. LoopDir: LoopDirBase,
  122. ConfigFile: defaultConfigFile,
  123. DataDir: LoopDirBase,
  124. LogDir: defaultLogDir,
  125. MaxLogFiles: defaultMaxLogFiles,
  126. MaxLogFileSize: defaultMaxLogFileSize,
  127. DebugLevel: defaultLogLevel,
  128. TLSCertPath: DefaultTLSCertPath,
  129. TLSKeyPath: DefaultTLSKeyPath,
  130. MacaroonPath: DefaultMacaroonPath,
  131. MaxLSATCost: lsat.DefaultMaxCostSats,
  132. MaxLSATFee: lsat.DefaultMaxRoutingFeeSats,
  133. LoopOutMaxParts: defaultLoopOutMaxParts,
  134. Lnd: &lndConfig{
  135. Host: "localhost:10009",
  136. MacaroonPath: filepath.Join(
  137. btcutil.AppDataDir("lnd", false),
  138. "data", "chain", "bitcoin", DefaultNetwork,
  139. "admin.macaroon",
  140. ),
  141. },
  142. }
  143. }
  144. // Validate cleans up paths in the config provided and validates it.
  145. func Validate(cfg *Config) error {
  146. // Cleanup any paths before we use them.
  147. cfg.LoopDir = lncfg.CleanAndExpandPath(cfg.LoopDir)
  148. cfg.DataDir = lncfg.CleanAndExpandPath(cfg.DataDir)
  149. cfg.LogDir = lncfg.CleanAndExpandPath(cfg.LogDir)
  150. cfg.TLSCertPath = lncfg.CleanAndExpandPath(cfg.TLSCertPath)
  151. cfg.TLSKeyPath = lncfg.CleanAndExpandPath(cfg.TLSKeyPath)
  152. cfg.MacaroonPath = lncfg.CleanAndExpandPath(cfg.MacaroonPath)
  153. // Since our loop directory overrides our log/data dir values, make sure
  154. // that they are not set when loop dir is set. We hard here rather than
  155. // overwriting and potentially confusing the user.
  156. loopDirSet := cfg.LoopDir != LoopDirBase
  157. if loopDirSet {
  158. logDirSet := cfg.LogDir != defaultLogDir
  159. dataDirSet := cfg.DataDir != LoopDirBase
  160. tlsCertPathSet := cfg.TLSCertPath != DefaultTLSCertPath
  161. tlsKeyPathSet := cfg.TLSKeyPath != DefaultTLSKeyPath
  162. if logDirSet {
  163. return fmt.Errorf("loopdir overwrites logdir, please " +
  164. "only set one value")
  165. }
  166. if dataDirSet {
  167. return fmt.Errorf("loopdir overwrites datadir, please " +
  168. "only set one value")
  169. }
  170. if tlsCertPathSet {
  171. return fmt.Errorf("loopdir overwrites tlscertpath, " +
  172. "please only set one value")
  173. }
  174. if tlsKeyPathSet {
  175. return fmt.Errorf("loopdir overwrites tlskeypath, " +
  176. "please only set one value")
  177. }
  178. // Once we are satisfied that no other config value was set, we
  179. // replace them with our loop dir.
  180. cfg.DataDir = cfg.LoopDir
  181. cfg.LogDir = filepath.Join(cfg.LoopDir, defaultLogDirname)
  182. }
  183. // Append the network type to the data and log directory so they are
  184. // "namespaced" per network.
  185. cfg.DataDir = filepath.Join(cfg.DataDir, cfg.Network)
  186. cfg.LogDir = filepath.Join(cfg.LogDir, cfg.Network)
  187. // We want the TLS and macaroon files to also be in the "namespaced" sub
  188. // directory. Replace the default values with actual values in case the
  189. // user specified either loopdir or datadir.
  190. if cfg.TLSCertPath == DefaultTLSCertPath {
  191. cfg.TLSCertPath = filepath.Join(
  192. cfg.DataDir, DefaultTLSCertFilename,
  193. )
  194. }
  195. if cfg.TLSKeyPath == DefaultTLSKeyPath {
  196. cfg.TLSKeyPath = filepath.Join(
  197. cfg.DataDir, DefaultTLSKeyFilename,
  198. )
  199. }
  200. if cfg.MacaroonPath == DefaultMacaroonPath {
  201. cfg.MacaroonPath = filepath.Join(
  202. cfg.DataDir, DefaultMacaroonFilename,
  203. )
  204. }
  205. // If either of these directories do not exist, create them.
  206. if err := os.MkdirAll(cfg.DataDir, os.ModePerm); err != nil {
  207. return err
  208. }
  209. if err := os.MkdirAll(cfg.LogDir, os.ModePerm); err != nil {
  210. return err
  211. }
  212. // Make sure only one of the macaroon options is used.
  213. switch {
  214. case cfg.Lnd.MacaroonPath != "" && cfg.Lnd.MacaroonDir != "":
  215. return fmt.Errorf("use --lnd.macaroonpath only")
  216. case cfg.Lnd.MacaroonDir != "":
  217. // With the new version of lndclient we can only specify a
  218. // single macaroon instead of all of them. If the old
  219. // macaroondir is used, we use the admin macaroon located in
  220. // that directory.
  221. cfg.Lnd.MacaroonPath = path.Join(
  222. lncfg.CleanAndExpandPath(cfg.Lnd.MacaroonDir),
  223. defaultLndMacaroon,
  224. )
  225. case cfg.Lnd.MacaroonPath != "":
  226. cfg.Lnd.MacaroonPath = lncfg.CleanAndExpandPath(
  227. cfg.Lnd.MacaroonPath,
  228. )
  229. default:
  230. return fmt.Errorf("must specify --lnd.macaroonpath")
  231. }
  232. return nil
  233. }
  234. // getTLSConfig generates a new self signed certificate or refreshes an existing
  235. // one if necessary, then returns the full TLS configuration for initializing
  236. // a secure server interface.
  237. func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
  238. error) {
  239. // Let's load our certificate first or create then load if it doesn't
  240. // yet exist.
  241. certData, parsedCert, err := loadCertWithCreate(cfg)
  242. if err != nil {
  243. return nil, nil, err
  244. }
  245. // If the certificate expired or it was outdated, delete it and the TLS
  246. // key and generate a new pair.
  247. if time.Now().After(parsedCert.NotAfter) {
  248. log.Info("TLS certificate is expired or outdated, " +
  249. "removing old file then generating a new one")
  250. err := os.Remove(cfg.TLSCertPath)
  251. if err != nil {
  252. return nil, nil, err
  253. }
  254. err = os.Remove(cfg.TLSKeyPath)
  255. if err != nil {
  256. return nil, nil, err
  257. }
  258. certData, _, err = loadCertWithCreate(cfg)
  259. if err != nil {
  260. return nil, nil, err
  261. }
  262. }
  263. tlsCfg := cert.TLSConfFromCert(certData)
  264. tlsCfg.NextProtos = []string{"h2"}
  265. restCreds, err := credentials.NewClientTLSFromFile(
  266. cfg.TLSCertPath, "",
  267. )
  268. if err != nil {
  269. return nil, nil, err
  270. }
  271. return tlsCfg, &restCreds, nil
  272. }
  273. // loadCertWithCreate tries to load the TLS certificate from disk. If the
  274. // specified cert and key files don't exist, the certificate/key pair is created
  275. // first.
  276. func loadCertWithCreate(cfg *Config) (tls.Certificate, *x509.Certificate,
  277. error) {
  278. // Ensure we create TLS key and certificate if they don't exist.
  279. if !lnrpc.FileExists(cfg.TLSCertPath) &&
  280. !lnrpc.FileExists(cfg.TLSKeyPath) {
  281. log.Infof("Generating TLS certificates...")
  282. err := cert.GenCertPair(
  283. defaultSelfSignedOrganization, cfg.TLSCertPath,
  284. cfg.TLSKeyPath, cfg.TLSExtraIPs,
  285. cfg.TLSExtraDomains, cfg.TLSDisableAutofill,
  286. cert.DefaultAutogenValidity,
  287. )
  288. if err != nil {
  289. return tls.Certificate{}, nil, err
  290. }
  291. log.Infof("Done generating TLS certificates")
  292. }
  293. return cert.LoadCert(cfg.TLSCertPath, cfg.TLSKeyPath)
  294. }