decred.org/dcrdex@v1.0.5/server/cmd/dcrdex/config.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package main 5 6 import ( 7 "errors" 8 "fmt" 9 "net" 10 "os" 11 "path/filepath" 12 "runtime" 13 "sort" 14 "strconv" 15 "strings" 16 "time" 17 18 "decred.org/dcrdex/dex" 19 "decred.org/dcrdex/dex/wait" 20 "decred.org/dcrdex/server/admin" 21 "decred.org/dcrdex/server/auth" 22 "decred.org/dcrdex/server/book" 23 "decred.org/dcrdex/server/comms" 24 "decred.org/dcrdex/server/db" 25 dexsrv "decred.org/dcrdex/server/dex" 26 "decred.org/dcrdex/server/market" 27 "decred.org/dcrdex/server/matcher" 28 "decred.org/dcrdex/server/swap" 29 "github.com/decred/dcrd/dcrutil/v4" 30 flags "github.com/jessevdk/go-flags" 31 ) 32 33 const ( 34 defaultConfigFilename = "dcrdex.conf" 35 defaultLogFilename = "dcrdex.log" 36 defaultRPCCertFilename = "rpc.cert" 37 defaultRPCKeyFilename = "rpc.key" 38 defaultDataDirname = "data" 39 defaultLogLevel = "debug" 40 defaultLogDirname = "logs" 41 defaultMarketsConfFilename = "markets.json" 42 defaultMaxLogZips = 128 43 defaultPGHost = "127.0.0.1:5432" 44 defaultPGUser = "dcrdex" 45 defaultPGDBName = "dcrdex_{netname}" 46 defaultDEXPrivKeyFilename = "sigkey" 47 defaultRPCHost = "127.0.0.1" 48 defaultRPCPort = "7232" 49 defaultHSHost = defaultRPCHost // should be a loopback address 50 defaultHSPort = "7252" 51 defaultAdminSrvAddr = "127.0.0.1:6542" 52 defaultMaxUserCancels = 2 53 defaultPenaltyThresh = 20 54 55 defaultCancelThresh = 0.95 // 19 cancels : 1 success 56 defaultBroadcastTimeout = 12 * time.Minute // accommodate certain known long block download timeouts 57 defaultTxWaitExpiration = 2 * time.Minute 58 ) 59 60 var ( 61 defaultAppDataDir = dcrutil.AppDataDir("dcrdex", false) 62 ) 63 64 type procOpts struct { 65 HTTPProfile bool 66 CPUProfile string 67 } 68 69 // dexConf is the data that is required to setup the dex. 70 type dexConf struct { 71 DataDir string 72 Network dex.Network 73 DBName string 74 DBUser string 75 DBPass string 76 DBHost string 77 DBPort uint16 78 ShowPGConfig bool 79 MarketsConfPath string 80 CancelThreshold float64 81 FreeCancels bool 82 MaxUserCancels uint32 83 PenaltyThreshold uint32 84 DEXPrivKeyPath string 85 RPCCert string 86 RPCKey string 87 NoTLS bool 88 RPCListen []string 89 HiddenService string 90 BroadcastTimeout time.Duration 91 TxWaitExpiration time.Duration 92 AltDNSNames []string 93 LogMaker *dex.LoggerMaker 94 SigningKeyPW []byte 95 AdminSrvOn bool 96 AdminSrvAddr string 97 AdminSrvPW []byte 98 AdminSrvNoTLS bool 99 NoResumeSwaps bool 100 DisableDataAPI bool 101 NodeRelayAddr string 102 ValidateMarkets bool 103 } 104 105 type flagsData struct { 106 // General application behavior 107 AppDataDir string `short:"A" long:"appdata" description:"Path to application home directory."` 108 ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file."` 109 DataDir string `short:"b" long:"datadir" description:"Directory to store data."` 110 LogDir string `long:"logdir" description:"Directory to log output."` 111 DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}."` 112 LocalLogs bool `long:"loglocal" description:"Use local time zone time stamps in log entries."` 113 MaxLogZips int `long:"maxlogzips" description:"The number of zipped log files created by the log rotator to be retained. Setting to 0 will keep all."` 114 ShowVersion bool `short:"V" long:"version" description:"Display version information and exit."` 115 116 Testnet bool `long:"testnet" description:"Use the test network (default mainnet)."` 117 Simnet bool `long:"simnet" description:"Use the simulation test network (default mainnet)."` 118 119 RPCCert string `long:"rpccert" description:"RPC server TLS certificate file."` 120 RPCKey string `long:"rpckey" description:"RPC server TLS private key file."` 121 RPCListen []string `long:"rpclisten" description:"IP addresses on which the RPC server should listen for incoming connections."` 122 NoTLS bool `long:"notls" description:"Run without TLS encryption."` 123 AltDNSNames []string `long:"altdnsnames" description:"A list of hostnames to include in the RPC certificate (X509v3 Subject Alternative Name)."` 124 HiddenService string `long:"hiddenservice" description:"A host:port on which the RPC server should listen for incoming hidden service connections. No TLS is used for these connections."` 125 126 MarketsConfPath string `long:"marketsconfpath" description:"Path to the markets configuration JSON file."` 127 BroadcastTimeout time.Duration `long:"bcasttimeout" description:"The broadcast timeout specifies how long clients have to broadcast an expected transaction when it is their turn to act. Matches without the expected action by this time are revoked and the actor is penalized (default: 12 minutes)."` 128 TxWaitExpiration time.Duration `long:"txwaitexpiration" description:"How long the server will search for a client-reported transaction before responding to the client with an error indicating that it was not found. This should ideally be less than half of swaps BroadcastTimeout to allow for more than one retry of the client's request (default: 2 minutes)."` 129 DEXPrivKeyPath string `long:"dexprivkeypath" description:"The path to a file containing the DEX private key for message signing."` 130 131 CancelThreshold float64 `long:"cancelthresh" description:"Cancellation rate threshold (cancels/all_completed)."` 132 FreeCancels bool `long:"freecancels" description:"No cancellation rate enforcement (unlimited cancel orders)."` 133 MaxUserCancels uint32 `long:"maxepochcancels" description:"The maximum number of cancel orders allowed for a user in a given epoch."` 134 PenaltyThreshold uint32 `long:"penaltythreshold" description:"The accumulated penalty score at which when a bond is revoked."` 135 136 HTTPProfile bool `long:"httpprof" short:"p" description:"Start HTTP profiler."` 137 CPUProfile string `long:"cpuprofile" description:"File for CPU profiling."` 138 139 PGDBName string `long:"pgdbname" description:"PostgreSQL DB name."` 140 PGUser string `long:"pguser" description:"PostgreSQL DB user."` 141 PGPass string `long:"pgpass" description:"PostgreSQL DB password."` 142 PGHost string `long:"pghost" description:"PostgreSQL server host:port or UNIX socket (e.g. /run/postgresql)."` 143 ShowPGConfig bool `long:"showpgconfig" description:"Logs the PostgreSQL db configuration on system start up."` 144 SigningKeyPassword string `long:"signingkeypass" description:"Password for encrypting/decrypting the dex privkey. INSECURE. Do not set unless absolutely necessary."` 145 AdminSrvOn bool `long:"adminsrvon" description:"Turn on the admin server."` 146 AdminSrvAddr string `long:"adminsrvaddr" description:"Administration HTTPS server address (default: 127.0.0.1:6542)."` 147 AdminSrvPassword string `long:"adminsrvpass" description:"Admin server password. INSECURE. Do not set unless absolutely necessary."` 148 AdminSrvNoTLS bool `long:"adminsrvnotls" description:"Run admin server without TLS. Only use this option if you are using a securely configured reverse proxy."` 149 150 NoResumeSwaps bool `long:"noresumeswaps" description:"Do not attempt to resume swaps that are active in the DB."` 151 152 DisableDataAPI bool `long:"nodata" description:"Disable the HTTP data API."` 153 154 NodeRelayAddr string `long:"noderelayaddr" description:"The public address by which node sources should connect to the node relay"` 155 156 ValidateMarkets bool `long:"validate" description:"Validate the market configuration and quit"` 157 } 158 159 // supportedSubsystems returns a sorted slice of the supported subsystems for 160 // logging purposes. 161 func supportedSubsystems() []string { 162 // Convert the subsystemLoggers map keys to a slice. 163 subsystems := make([]string, 0, len(subsystemLoggers)) 164 for subsysID := range subsystemLoggers { 165 subsystems = append(subsystems, subsysID) 166 } 167 168 // Sort the subsystems for stable display. 169 sort.Strings(subsystems) 170 return subsystems 171 } 172 173 // parseAndSetDebugLevels attempts to parse the specified debug level and set 174 // the levels accordingly. An appropriate error is returned if anything is 175 // invalid. 176 func parseAndSetDebugLevels(debugLevel string, UTC bool) (*dex.LoggerMaker, error) { 177 // Create a LoggerMaker with the level string. 178 lm, err := dex.NewLoggerMaker(logWriter{}, debugLevel, UTC) 179 if err != nil { 180 return nil, err 181 } 182 183 // Create subsystem loggers. 184 for subsysID := range subsystemLoggers { 185 subsystemLoggers[subsysID] = lm.Logger(subsysID) 186 } 187 188 // Set main's Logger. 189 log = subsystemLoggers["MAIN"] 190 191 // Set package-level loggers. TODO: eliminate these by replacing them with 192 // loggers provided to constructors. 193 dexsrv.UseLogger(subsystemLoggers["DEX"]) 194 db.UseLogger(subsystemLoggers["DB"]) 195 comms.UseLogger(subsystemLoggers["COMM"]) 196 auth.UseLogger(subsystemLoggers["AUTH"]) 197 swap.UseLogger(subsystemLoggers["SWAP"]) 198 market.UseLogger(subsystemLoggers["MKT"]) 199 book.UseLogger(subsystemLoggers["BOOK"]) 200 matcher.UseLogger(subsystemLoggers["MTCH"]) 201 wait.UseLogger(subsystemLoggers["WAIT"]) 202 admin.UseLogger(subsystemLoggers["ADMN"]) 203 204 return lm, nil 205 } 206 207 const missingPort = "missing port in address" 208 209 // normalizeNetworkAddress checks for a valid local network address format and 210 // adds default host and port if not present. Invalidates addresses that include 211 // a protocol identifier. 212 func normalizeNetworkAddress(a, defaultHost, defaultPort string) (string, error) { 213 if strings.Contains(a, "://") { 214 return a, fmt.Errorf("address %s contains a protocol identifier, which is not allowed", a) 215 } 216 if a == "" { 217 return net.JoinHostPort(defaultHost, defaultPort), nil 218 } 219 host, port, err := net.SplitHostPort(a) 220 if err != nil { 221 var addrErr *net.AddrError 222 if errors.As(err, &addrErr) && addrErr.Err == missingPort { 223 host = strings.Trim(addrErr.Addr, "[]") // JoinHostPort expects no brackets for ipv6 hosts 224 normalized := net.JoinHostPort(host, defaultPort) 225 host, port, err = net.SplitHostPort(normalized) 226 if err != nil { 227 return a, fmt.Errorf("unable to address %s after port resolution: %w", normalized, err) 228 } 229 } else { 230 return a, fmt.Errorf("unable to normalize address %s: %w", a, err) 231 } 232 } 233 if host == "" { 234 host = defaultHost 235 } 236 if port == "" { 237 port = defaultPort 238 } 239 return net.JoinHostPort(host, port), nil 240 } 241 242 // loadConfig initializes and parses the config using a config file and command 243 // line options. 244 func loadConfig() (*dexConf, *procOpts, error) { 245 loadConfigError := func(err error) (*dexConf, *procOpts, error) { 246 return nil, nil, err 247 } 248 249 // Default config 250 cfg := flagsData{ 251 AppDataDir: defaultAppDataDir, 252 // Defaults for ConfigFile, LogDir, and DataDir are set relative to 253 // AppDataDir. They are not to be set here. 254 MaxLogZips: defaultMaxLogZips, 255 RPCCert: defaultRPCCertFilename, 256 RPCKey: defaultRPCKeyFilename, 257 DebugLevel: defaultLogLevel, 258 PGDBName: defaultPGDBName, 259 PGUser: defaultPGUser, 260 PGHost: defaultPGHost, 261 MarketsConfPath: defaultMarketsConfFilename, 262 DEXPrivKeyPath: defaultDEXPrivKeyFilename, 263 BroadcastTimeout: defaultBroadcastTimeout, 264 TxWaitExpiration: defaultTxWaitExpiration, 265 CancelThreshold: defaultCancelThresh, 266 MaxUserCancels: defaultMaxUserCancels, 267 PenaltyThreshold: defaultPenaltyThresh, 268 } 269 270 // Pre-parse the command line options to see if an alternative config file 271 // or the version flag was specified. Any errors aside from the help message 272 // error can be ignored here since they will be caught by the final parse 273 // below. 274 var preCfg flagsData // zero values as defaults 275 preParser := flags.NewParser(&preCfg, flags.HelpFlag) 276 _, err := preParser.Parse() 277 if err != nil { 278 if e, ok := err.(*flags.Error); ok && e.Type != flags.ErrHelp { 279 fmt.Fprintln(os.Stderr, err) 280 os.Exit(1) 281 } else if ok && e.Type == flags.ErrHelp { 282 fmt.Fprintln(os.Stdout, err) 283 os.Exit(0) 284 } 285 } 286 287 // Show the version and exit if the version flag was specified. 288 if preCfg.ShowVersion { 289 fmt.Printf("%s version %s (Go version %s %s/%s)\n", 290 appName, Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) 291 os.Exit(0) 292 } 293 294 // Special show command to list supported subsystems and exit. 295 if preCfg.DebugLevel == "show" { 296 fmt.Println("Supported subsystems", supportedSubsystems()) 297 os.Exit(0) 298 } 299 300 // If a non-default appdata folder is specified on the command line, it may 301 // be necessary adjust the config file location. If the the config file 302 // location was not specified on the command line, the default location 303 // should be under the non-default appdata directory. However, if the config 304 // file was specified on the command line, it should be used regardless of 305 // the appdata directory. 306 if preCfg.AppDataDir != "" { 307 // appdata was set on the command line. If it is not absolute, make it 308 // relative to cwd. 309 cfg.AppDataDir, err = filepath.Abs(preCfg.AppDataDir) 310 if err != nil { 311 fmt.Fprintf(os.Stderr, "Unable to determine working directory: %v", err) 312 os.Exit(1) 313 } 314 } 315 isDefaultConfigFile := preCfg.ConfigFile == "" 316 if isDefaultConfigFile { 317 preCfg.ConfigFile = filepath.Join(cfg.AppDataDir, defaultConfigFilename) 318 } else if !filepath.IsAbs(preCfg.ConfigFile) { 319 preCfg.ConfigFile = filepath.Join(cfg.AppDataDir, preCfg.ConfigFile) 320 } 321 322 // Config file name for logging. 323 configFile := "NONE (defaults)" 324 325 // Load additional config from file. 326 var configFileError error 327 parser := flags.NewParser(&cfg, flags.Default) 328 // Do not error default config file is missing. 329 if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) { 330 // Non-default config file must exist. 331 if !isDefaultConfigFile { 332 fmt.Fprintln(os.Stderr, err) 333 return loadConfigError(err) 334 } 335 // Warn about missing default config file, but continue. 336 fmt.Printf("Config file (%s) does not exist. Using defaults.\n", 337 preCfg.ConfigFile) 338 } else { 339 // The config file exists, so attempt to parse it. 340 err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) 341 if err != nil { 342 if _, ok := err.(*os.PathError); !ok { 343 fmt.Fprintln(os.Stderr, err) 344 parser.WriteHelp(os.Stderr) 345 return loadConfigError(err) 346 } 347 configFileError = err 348 } 349 configFile = preCfg.ConfigFile 350 } 351 352 // Parse command line options again to ensure they take precedence. 353 _, err = parser.Parse() 354 if err != nil { 355 if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { 356 parser.WriteHelp(os.Stderr) 357 } 358 return loadConfigError(err) 359 } 360 361 // Warn about missing config file after the final command line parse 362 // succeeds. This prevents the warning on help messages and invalid options. 363 if configFileError != nil { 364 fmt.Printf("%v\n", configFileError) 365 return loadConfigError(configFileError) 366 } 367 368 // Select the network. 369 var numNets int 370 network := dex.Mainnet 371 if cfg.Testnet { 372 numNets++ 373 network = dex.Testnet 374 } 375 if cfg.Simnet { 376 numNets++ 377 network = dex.Simnet 378 } 379 if numNets > 1 { 380 err := fmt.Errorf("both testnet and simnet flags specified") 381 fmt.Fprintln(os.Stderr, err) 382 return loadConfigError(err) 383 } 384 385 // Create the app data directory if it doesn't already exist. 386 err = os.MkdirAll(cfg.AppDataDir, 0700) 387 if err != nil { 388 // Show a nicer error message if it's because a symlink is linked to a 389 // directory that does not exist (probably because it's not mounted). 390 if e, ok := err.(*os.PathError); ok && os.IsExist(err) { 391 if link, lerr := os.Readlink(e.Path); lerr == nil { 392 str := "is symlink %s -> %s mounted?" 393 err = fmt.Errorf(str, e.Path, link) 394 } 395 } 396 397 err := fmt.Errorf("failed to create home directory: %v", err) 398 fmt.Fprintln(os.Stderr, err) 399 return loadConfigError(err) 400 } 401 402 // If datadir or logdir are defaults or non-default relative paths, prepend 403 // the appdata directory. 404 if cfg.DataDir == "" { 405 cfg.DataDir = filepath.Join(cfg.AppDataDir, defaultDataDirname) 406 } else if !filepath.IsAbs(cfg.DataDir) { 407 cfg.DataDir = filepath.Join(cfg.AppDataDir, cfg.DataDir) 408 } 409 if cfg.LogDir == "" { 410 cfg.LogDir = filepath.Join(cfg.AppDataDir, defaultLogDirname) 411 } else if !filepath.IsAbs(cfg.LogDir) { 412 cfg.LogDir = filepath.Join(cfg.AppDataDir, cfg.LogDir) 413 } 414 415 // Append the network type to the data directory so it is "namespaced" per 416 // network. In addition to the block database, there are other pieces of 417 // data that are saved to disk such as address manager state. All data is 418 // specific to a network, so namespacing the data directory means each 419 // individual piece of serialized data does not have to worry about changing 420 // names per network and such. 421 // 422 // Make list of old versions of testnet directories here since the network 423 // specific DataDir will be used after this. 424 cfg.DataDir = dex.CleanAndExpandPath(cfg.DataDir) 425 cfg.DataDir = filepath.Join(cfg.DataDir, network.String()) 426 // Create the data folder if it does not exist. 427 err = os.MkdirAll(cfg.DataDir, 0700) 428 if err != nil { 429 return loadConfigError(err) 430 } 431 432 logRotator = nil 433 // Append the network type to the log directory so it is "namespaced" 434 // per network in the same fashion as the data directory. 435 cfg.LogDir = dex.CleanAndExpandPath(cfg.LogDir) 436 cfg.LogDir = filepath.Join(cfg.LogDir, network.String()) 437 438 // Ensure that all specified files are absolute paths, prepending the 439 // appdata path if not. 440 if !filepath.IsAbs(cfg.RPCCert) { 441 cfg.RPCCert = filepath.Join(cfg.AppDataDir, cfg.RPCCert) 442 } 443 if !filepath.IsAbs(cfg.RPCKey) { 444 cfg.RPCKey = filepath.Join(cfg.AppDataDir, cfg.RPCKey) 445 } 446 if !filepath.IsAbs(cfg.MarketsConfPath) { 447 cfg.MarketsConfPath = filepath.Join(cfg.AppDataDir, cfg.MarketsConfPath) 448 } 449 if !filepath.IsAbs(cfg.DEXPrivKeyPath) { 450 cfg.DEXPrivKeyPath = filepath.Join(cfg.AppDataDir, cfg.DEXPrivKeyPath) 451 } 452 453 // Validate each RPC listen host:port. 454 var RPCListen []string 455 if len(cfg.RPCListen) == 0 { 456 RPCListen = []string{defaultRPCHost + ":" + defaultRPCPort} 457 } 458 for i := range cfg.RPCListen { 459 listen, err := normalizeNetworkAddress(cfg.RPCListen[i], defaultRPCHost, defaultRPCPort) 460 if err != nil { 461 return loadConfigError(err) 462 } 463 RPCListen = append(RPCListen, listen) 464 } 465 var HiddenService string 466 if cfg.HiddenService != "" { 467 HiddenService, err = normalizeNetworkAddress(cfg.HiddenService, defaultHSHost, defaultHSPort) 468 if err != nil { 469 return loadConfigError(err) 470 } 471 } 472 473 // Initialize log rotation. This creates the LogDir if needed. 474 if cfg.MaxLogZips < 0 { 475 cfg.MaxLogZips = 0 476 } 477 initLogRotator(filepath.Join(cfg.LogDir, defaultLogFilename), cfg.MaxLogZips) 478 479 // Create the loggers: Parse and validate the debug level string, create the 480 // subsystem loggers, and set package level loggers. The generated 481 // LoggerMaker is used by other subsystems to create new loggers with the 482 // same backend. 483 logMaker, err := parseAndSetDebugLevels(cfg.DebugLevel, !cfg.LocalLogs) 484 if err != nil { 485 fmt.Fprintln(os.Stderr, err) 486 parser.WriteHelp(os.Stderr) 487 return loadConfigError(err) 488 } 489 // Only now can any of the loggers be used. 490 491 log.Infof("App data folder: %s", cfg.AppDataDir) 492 log.Infof("Data folder: %s", cfg.DataDir) 493 log.Infof("Log folder: %s", cfg.LogDir) 494 log.Infof("Config file: %s", configFile) 495 496 if !cfg.LocalLogs { 497 log.Infof("Logging with UTC time stamps. Current local time is %v", time.Now().Local().Format("15:04:05 MST")) 498 } 499 500 var dbPort uint16 501 dbHost := cfg.PGHost 502 // For UNIX sockets, do not attempt to parse out a port. 503 if !strings.HasPrefix(dbHost, "/") { 504 var dbPortStr string 505 dbHost, dbPortStr, err = net.SplitHostPort(cfg.PGHost) 506 if err != nil { 507 return loadConfigError(fmt.Errorf("invalid DB host %q: %v", cfg.PGHost, err)) 508 } 509 port, err := strconv.ParseUint(dbPortStr, 10, 16) 510 if err != nil { 511 return loadConfigError(fmt.Errorf("invalid DB port %q: %v", dbPortStr, err)) 512 } 513 dbPort = uint16(port) 514 } 515 516 adminSrvAddr := defaultAdminSrvAddr 517 if cfg.AdminSrvAddr != "" { 518 _, port, err := net.SplitHostPort(cfg.AdminSrvAddr) 519 if err != nil { 520 return loadConfigError(fmt.Errorf("invalid admin server host %q: %v", cfg.AdminSrvAddr, err)) 521 } 522 _, err = strconv.ParseUint(port, 10, 16) 523 if err != nil { 524 return loadConfigError(fmt.Errorf("invalid admin server port %q: %v", port, err)) 525 } 526 adminSrvAddr = cfg.AdminSrvAddr 527 } 528 529 // If using {netname} then replace it with the network name. 530 cfg.PGDBName = strings.ReplaceAll(cfg.PGDBName, "{netname}", network.String()) 531 532 dexCfg := &dexConf{ 533 DataDir: cfg.DataDir, 534 Network: network, 535 DBName: cfg.PGDBName, 536 DBHost: dbHost, 537 DBPort: dbPort, 538 DBUser: cfg.PGUser, 539 DBPass: cfg.PGPass, 540 ShowPGConfig: cfg.ShowPGConfig, 541 MarketsConfPath: cfg.MarketsConfPath, 542 CancelThreshold: cfg.CancelThreshold, 543 MaxUserCancels: cfg.MaxUserCancels, 544 FreeCancels: cfg.FreeCancels, 545 PenaltyThreshold: cfg.PenaltyThreshold, 546 DEXPrivKeyPath: cfg.DEXPrivKeyPath, 547 RPCCert: cfg.RPCCert, 548 RPCKey: cfg.RPCKey, 549 NoTLS: cfg.NoTLS, 550 RPCListen: RPCListen, 551 HiddenService: HiddenService, 552 BroadcastTimeout: cfg.BroadcastTimeout, 553 TxWaitExpiration: cfg.TxWaitExpiration, 554 AltDNSNames: cfg.AltDNSNames, 555 LogMaker: logMaker, 556 SigningKeyPW: []byte(cfg.SigningKeyPassword), 557 AdminSrvAddr: adminSrvAddr, 558 AdminSrvOn: cfg.AdminSrvOn, 559 AdminSrvPW: []byte(cfg.AdminSrvPassword), 560 AdminSrvNoTLS: cfg.AdminSrvNoTLS, 561 NoResumeSwaps: cfg.NoResumeSwaps, 562 DisableDataAPI: cfg.DisableDataAPI, 563 NodeRelayAddr: cfg.NodeRelayAddr, 564 ValidateMarkets: cfg.ValidateMarkets, 565 } 566 567 opts := &procOpts{ 568 CPUProfile: cfg.CPUProfile, 569 HTTPProfile: cfg.HTTPProfile, 570 } 571 572 return dexCfg, opts, nil 573 }