decred.org/dcrdex@v1.0.5/server/cmd/dcrdex/main.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  	"context"
     8  	"crypto/sha256"
     9  	"fmt"
    10  	"net/http"
    11  	_ "net/http/pprof"
    12  	"os"
    13  	"runtime"
    14  	"runtime/pprof"
    15  	"strings"
    16  	"sync"
    17  
    18  	"decred.org/dcrdex/dex"
    19  	"decred.org/dcrdex/dex/encode"
    20  	"decred.org/dcrdex/server/admin"
    21  	_ "decred.org/dcrdex/server/asset/importall"
    22  	dexsrv "decred.org/dcrdex/server/dex"
    23  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    24  )
    25  
    26  func mainCore(ctx context.Context) error {
    27  	// Parse the configuration file, and setup logger.
    28  	cfg, opts, err := loadConfig()
    29  	if err != nil {
    30  		fmt.Printf("Failed to load dcrdex config: %s\n", err.Error())
    31  		return err
    32  	}
    33  	defer func() {
    34  		if logRotator != nil {
    35  			logRotator.Close()
    36  		}
    37  	}()
    38  
    39  	if cfg.ValidateMarkets {
    40  		return dexsrv.ValidateConfigFile(cfg.MarketsConfPath, cfg.Network, log.SubLogger("V"))
    41  	}
    42  
    43  	// Request admin server password if admin server is enabled and
    44  	// server password is not set in config.
    45  	var adminSrvAuthSHA [32]byte
    46  	if cfg.AdminSrvOn {
    47  		if len(cfg.AdminSrvPW) == 0 {
    48  			adminSrvAuthSHA, err = admin.PasswordHashPrompt(ctx, "Admin interface password: ")
    49  			if err != nil {
    50  				return fmt.Errorf("cannot use password: %v", err)
    51  			}
    52  		} else {
    53  			adminSrvAuthSHA = sha256.Sum256(cfg.AdminSrvPW)
    54  			encode.ClearBytes(cfg.AdminSrvPW)
    55  		}
    56  	}
    57  
    58  	if opts.CPUProfile != "" {
    59  		var f *os.File
    60  		f, err = os.Create(opts.CPUProfile)
    61  		if err != nil {
    62  			return err
    63  		}
    64  		err = pprof.StartCPUProfile(f)
    65  		if err != nil {
    66  			return err
    67  		}
    68  		defer pprof.StopCPUProfile()
    69  	}
    70  
    71  	// HTTP profiler
    72  	if opts.HTTPProfile {
    73  		log.Warnf("Starting the HTTP profiler on path /debug/pprof/.")
    74  		// http pprof uses http.DefaultServeMux
    75  		http.Handle("/", http.RedirectHandler("/debug/pprof/", http.StatusSeeOther))
    76  		go func() {
    77  			if err := http.ListenAndServe(":9232", nil); err != nil {
    78  				log.Errorf("ListenAndServe failed for http/pprof: %v", err)
    79  			}
    80  		}()
    81  	}
    82  
    83  	// Display app version.
    84  	log.Infof("%s version %v (Go version %s)", appName, Version, runtime.Version())
    85  	log.Infof("dcrdex starting for network: %s", cfg.Network)
    86  	log.Infof("swap locktimes config: maker %s, taker %s",
    87  		dex.LockTimeMaker(cfg.Network), dex.LockTimeTaker(cfg.Network))
    88  
    89  	// Load the market and asset configurations for the given network.
    90  	markets, assets, err := dexsrv.LoadConfig(cfg.Network, cfg.MarketsConfPath)
    91  	if err != nil {
    92  		return fmt.Errorf("failed to load market and asset config %q: %v",
    93  			cfg.MarketsConfPath, err)
    94  	}
    95  	log.Infof("Found %d assets, loaded %d markets, for network %s",
    96  		len(assets), len(markets), strings.ToUpper(cfg.Network.String()))
    97  	// NOTE: If MaxUserCancelsPerEpoch is ultimately a setting we want to keep,
    98  	// bake it into the markets.json file and load it per-market in settings.go.
    99  	// For now, patch it into each dex.MarketInfo.
   100  	for _, mkt := range markets {
   101  		mkt.MaxUserCancelsPerEpoch = cfg.MaxUserCancels
   102  	}
   103  
   104  	// Load, or create and save, the DEX signing key.
   105  	var privKey *secp256k1.PrivateKey
   106  	if len(cfg.SigningKeyPW) == 0 {
   107  		cfg.SigningKeyPW, err = admin.PasswordPrompt(ctx, "Signing key password: ")
   108  		if err != nil {
   109  			return fmt.Errorf("cannot use password: %v", err)
   110  		}
   111  	}
   112  	privKey, err = dexKey(cfg.DEXPrivKeyPath, cfg.SigningKeyPW)
   113  	encode.ClearBytes(cfg.SigningKeyPW)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	// Create the DEX manager.
   119  	dexConf := &dexsrv.DexConf{
   120  		DataDir:    cfg.DataDir,
   121  		LogBackend: cfg.LogMaker,
   122  		Markets:    markets,
   123  		Assets:     assets,
   124  		Network:    cfg.Network,
   125  		DBConf: &dexsrv.DBConf{
   126  			DBName:       cfg.DBName,
   127  			Host:         cfg.DBHost,
   128  			User:         cfg.DBUser,
   129  			Port:         cfg.DBPort,
   130  			Pass:         cfg.DBPass,
   131  			ShowPGConfig: cfg.ShowPGConfig,
   132  		},
   133  		BroadcastTimeout: cfg.BroadcastTimeout,
   134  		TxWaitExpiration: cfg.TxWaitExpiration,
   135  		CancelThreshold:  cfg.CancelThreshold,
   136  		FreeCancels:      cfg.FreeCancels,
   137  		PenaltyThreshold: cfg.PenaltyThreshold,
   138  		DEXPrivKey:       privKey,
   139  		CommsCfg: &dexsrv.RPCConfig{
   140  			RPCCert:           cfg.RPCCert,
   141  			NoTLS:             cfg.NoTLS,
   142  			RPCKey:            cfg.RPCKey,
   143  			ListenAddrs:       cfg.RPCListen,
   144  			AltDNSNames:       cfg.AltDNSNames,
   145  			DisableDataAPI:    cfg.DisableDataAPI,
   146  			HiddenServiceAddr: cfg.HiddenService,
   147  		},
   148  		NoResumeSwaps: cfg.NoResumeSwaps,
   149  		NodeRelayAddr: cfg.NodeRelayAddr,
   150  	}
   151  	dexMan, err := dexsrv.NewDEX(ctx, dexConf) // ctx cancel just aborts setup; Stop does normal shutdown
   152  	if err != nil {
   153  		return err
   154  	}
   155  
   156  	var wg sync.WaitGroup
   157  	if cfg.AdminSrvOn {
   158  		srvCFG := &admin.SrvConfig{
   159  			Core:    dexMan,
   160  			Addr:    cfg.AdminSrvAddr,
   161  			AuthSHA: adminSrvAuthSHA,
   162  			Cert:    cfg.RPCCert,
   163  			Key:     cfg.RPCKey,
   164  			NoTLS:   cfg.AdminSrvNoTLS,
   165  		}
   166  		adminServer, err := admin.NewServer(srvCFG)
   167  		if err != nil {
   168  			return fmt.Errorf("cannot set up admin server: %v", err)
   169  		}
   170  		wg.Add(1)
   171  		go func() {
   172  			adminServer.Run(ctx)
   173  			wg.Done()
   174  		}()
   175  	}
   176  
   177  	log.Info("The DEX is running. Hit CTRL+C to quit...")
   178  	<-ctx.Done()
   179  	// Wait for the admin server to finish.
   180  	wg.Wait()
   181  
   182  	log.Info("Stopping DEX...")
   183  	dexMan.Stop()
   184  	log.Info("Bye!")
   185  
   186  	return nil
   187  }
   188  
   189  func main() {
   190  	// Create a context that is canceled when a shutdown request is received
   191  	// via requestShutdown.
   192  	ctx := withShutdownCancel(context.Background())
   193  	// Listen for both interrupt signals (e.g. CTRL+C) and shutdown requests
   194  	// (requestShutdown calls).
   195  	go shutdownListener()
   196  
   197  	err := mainCore(ctx)
   198  	if err != nil {
   199  		fmt.Fprintln(os.Stderr, err)
   200  		os.Exit(1)
   201  	}
   202  	os.Exit(0)
   203  }