github.com/decred/dcrlnd@v0.7.6/chainreg/chainregistry.go (about)

     1  package chainreg
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net"
     8  	"os"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/decred/dcrd/chaincfg/chainhash"
    13  	"github.com/decred/dcrd/rpcclient/v8"
    14  	"github.com/decred/dcrlnd/blockcache"
    15  	"github.com/decred/dcrlnd/chainntnfs"
    16  	"github.com/decred/dcrlnd/chainntnfs/dcrdnotify"
    17  	"github.com/decred/dcrlnd/chainntnfs/dcrwnotify"
    18  	"github.com/decred/dcrlnd/chainntnfs/remotedcrwnotify"
    19  	"github.com/decred/dcrlnd/channeldb"
    20  	"github.com/decred/dcrlnd/htlcswitch"
    21  	"github.com/decred/dcrlnd/input"
    22  	"github.com/decred/dcrlnd/keychain"
    23  	"github.com/decred/dcrlnd/kvdb"
    24  	"github.com/decred/dcrlnd/lncfg"
    25  	"github.com/decred/dcrlnd/lnwallet"
    26  	"github.com/decred/dcrlnd/lnwallet/chainfee"
    27  	"github.com/decred/dcrlnd/lnwire"
    28  	"github.com/decred/dcrlnd/routing/chainview"
    29  	"github.com/decred/dcrlnd/walletunlocker"
    30  )
    31  
    32  // Config houses necessary fields that a chainControl instance needs to
    33  // function.
    34  type Config struct {
    35  	// Decred defines the settings for the Decred chain.
    36  	Decred *lncfg.Chain
    37  
    38  	// PrimaryChain is a function that returns our primary chain via its
    39  	// ChainCode.
    40  	PrimaryChain func() ChainCode
    41  
    42  	// HeightHintCacheQueryDisable is a boolean that disables height hint
    43  	// queries if true.
    44  	HeightHintCacheQueryDisable bool
    45  
    46  	// DcrdMode defines settings for connecting to a dcrd instance.
    47  	DcrdMode *lncfg.DcrdConfig
    48  
    49  	// DcrwMode defines settings for connecting to a dcrwallet instance.
    50  	DcrwMode *lncfg.DcrwalletConfig
    51  
    52  	// FullDB is the full DB (the parent of ChanStateDB).
    53  	FullDB *channeldb.DB
    54  
    55  	// HeightHintDB is a pointer to the database that stores the height
    56  	// hints.
    57  	HeightHintDB kvdb.Backend
    58  
    59  	// ChanStateDB is a pointer to the database that stores the channel
    60  	// state.
    61  	ChanStateDB *channeldb.ChannelStateDB
    62  
    63  	// BlockCache is the main cache for storing block information.
    64  	BlockCache *blockcache.BlockCache
    65  
    66  	// WalletUnlockParams are the parameters that were used for unlocking
    67  	// the main wallet.
    68  	WalletUnlockParams *walletunlocker.WalletUnlockParams
    69  
    70  	// ActiveNetParams details the current chain we are on.
    71  	ActiveNetParams DecredNetParams
    72  
    73  	// FeeURL defines the URL for fee estimation we will use. This field is
    74  	// optional.
    75  	FeeURL string
    76  
    77  	// Dialer is a function closure that will be used to establish outbound
    78  	// TCP connections to chain network peers in the event of a pruned block being
    79  	// requested.
    80  	Dialer func(string) (net.Conn, error)
    81  }
    82  
    83  const (
    84  	// DefaultDecredMinHTLCInMAtoms is the default smallest value htlc this
    85  	// node will accept. This value is proposed in the channel open
    86  	// sequence and cannot be changed during the life of the channel.
    87  	//
    88  	// All forwarded payments are subjected to the min htlc constraint of
    89  	// the routing policy of the outgoing channel. This implicitly controls
    90  	// the minimum htlc value on the incoming channel too.
    91  	DefaultDecredMinHTLCInMAtoms = lnwire.MilliAtom(1000)
    92  
    93  	// DefaultDecredMinHTLCOutMAtoms is the default minimum htlc value that
    94  	// we require for sending out htlcs. Our channel peer may have a lower
    95  	// min htlc channel parameter, but we - by default - don't forward
    96  	// anything under the value defined here.
    97  	DefaultDecredMinHTLCOutMAtoms = lnwire.MilliAtom(1000)
    98  
    99  	// DefaultDecredBaseFeeMAtoms is the default forwarding base fee.
   100  	DefaultDecredBaseFeeMAtoms = lnwire.MilliAtom(1000)
   101  
   102  	// DefaultDecredFeeRate is the default forwarding fee rate.
   103  	DefaultDecredFeeRate = lnwire.MilliAtom(1)
   104  
   105  	// DefaultDecredTimeLockDelta is the default forwarding time lock
   106  	// delta.
   107  	DefaultDecredTimeLockDelta = 80
   108  
   109  	// DefaultDecredStaticFeePerKB is the fee rate of 10000 atom/kB
   110  	DefaultDecredStaticFeePerKB = chainfee.AtomPerKByte(1e4)
   111  
   112  	// DefaultDecredStaticMinRelayFeeRate is the min relay fee used for
   113  	// static estimators.
   114  	DefaultDecredStaticMinRelayFeeRate = chainfee.FeePerKBFloor
   115  )
   116  
   117  // PartialChainControl contains all the primary interfaces of the chain control
   118  // that can be purely constructed from the global configuration. No wallet
   119  // instance is required for constructing this partial state.
   120  type PartialChainControl struct {
   121  	// Cfg is the configuration that was used to create the partial chain
   122  	// control.
   123  	Cfg *Config
   124  
   125  	// HealthCheck is a function which can be used to send a low-cost, fast
   126  	// query to the chain backend to ensure we still have access to our
   127  	// node.
   128  	HealthCheck func() error
   129  
   130  	// FeeEstimator is used to estimate an optimal fee for transactions
   131  	// important to us.
   132  	FeeEstimator chainfee.Estimator
   133  
   134  	// ChainNotifier is used to receive blockchain events that we are
   135  	// interested in.
   136  	ChainNotifier chainntnfs.ChainNotifier
   137  
   138  	// ChainView is used in the router for maintaining an up-to-date graph.
   139  	ChainView chainview.FilteredChainView
   140  
   141  	// RoutingPolicy is the routing policy we have decided to use.
   142  	RoutingPolicy htlcswitch.ForwardingPolicy
   143  
   144  	// MinHtlcIn is the minimum HTLC we will accept.
   145  	MinHtlcIn lnwire.MilliAtom
   146  
   147  	// ChannelConstraints is the set of default constraints that will be
   148  	// used for any incoming or outgoing channel reservation requests.
   149  	ChannelConstraints channeldb.ChannelConstraints
   150  
   151  	// RPCConfig is the config to the remote dcrd instance when using a
   152  	// sync mode that requires it.
   153  	RPCConfig *rpcclient.ConnConfig
   154  }
   155  
   156  // ChainControl couples the three primary interfaces lnd utilizes for a
   157  // particular chain together. A single ChainControl instance will exist for all
   158  // the chains lnd is currently active on.
   159  type ChainControl struct {
   160  	// PartialChainControl is the part of the chain control that was
   161  	// initialized purely from the configuration and doesn't contain any
   162  	// wallet related elements.
   163  	*PartialChainControl
   164  
   165  	// ChainIO represents an abstraction over a source that can query the
   166  	// blockchain.
   167  	ChainIO lnwallet.BlockChainIO
   168  
   169  	// Signer is used to provide signatures over things like transactions.
   170  	Signer input.Signer
   171  
   172  	// KeyRing represents a set of keys that we have the private keys to.
   173  	KeyRing keychain.SecretKeyRing
   174  
   175  	// Wc is an abstraction over some basic wallet commands. This base set
   176  	// of commands will be provided to the Wallet *LightningWallet raw
   177  	// pointer below.
   178  	Wc lnwallet.WalletController
   179  
   180  	// MsgSigner is used to sign arbitrary messages.
   181  	MsgSigner lnwallet.MessageSigner
   182  
   183  	// Wallet is our LightningWallet that also contains the abstract Wc
   184  	// above. This wallet handles all of the lightning operations.
   185  	Wallet *lnwallet.LightningWallet
   186  }
   187  
   188  // GenDefaultDcrChannelConstraints generates the default set of channel
   189  // constraints that are to be used when funding a Decred channel.
   190  func GenDefaultDcrConstraints() channeldb.ChannelConstraints {
   191  	dustLimit := lnwallet.DustLimitForSize(input.P2PKHPkScriptSize)
   192  
   193  	return channeldb.ChannelConstraints{
   194  		DustLimit:        dustLimit,
   195  		MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
   196  	}
   197  }
   198  
   199  // NewPartialChainControl creates a new partial chain control that contains all
   200  // the parts that can be purely constructed from the passed in global
   201  // configuration and doesn't need any wallet instance yet.
   202  func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
   203  	// Set the RPC config from the "home" chain. Multi-chain isn't yet
   204  	// active, so we'll restrict usage to a particular chain for now.
   205  	log.Infof("Primary chain is set to: %v", cfg.PrimaryChain())
   206  
   207  	cc := &PartialChainControl{
   208  		Cfg: cfg,
   209  	}
   210  
   211  	switch cfg.PrimaryChain() {
   212  	case DecredChain:
   213  		cc.RoutingPolicy = htlcswitch.ForwardingPolicy{
   214  			MinHTLCOut:    cfg.Decred.MinHTLCOut,
   215  			BaseFee:       cfg.Decred.BaseFee,
   216  			FeeRate:       cfg.Decred.FeeRate,
   217  			TimeLockDelta: cfg.Decred.TimeLockDelta,
   218  		}
   219  		cc.MinHtlcIn = cfg.Decred.MinHTLCIn
   220  		cc.FeeEstimator = chainfee.NewStaticEstimator(
   221  			DefaultDecredStaticFeePerKB,
   222  			DefaultDecredStaticMinRelayFeeRate,
   223  		)
   224  	default:
   225  		return nil, nil, fmt.Errorf("default routing policy for chain "+
   226  			"%v is unknown", cfg.PrimaryChain())
   227  	}
   228  
   229  	var err error
   230  	heightHintCacheConfig := chainntnfs.CacheConfig{
   231  		QueryDisable: cfg.HeightHintCacheQueryDisable,
   232  	}
   233  	if cfg.HeightHintCacheQueryDisable {
   234  		log.Infof("Height Hint Cache Queries disabled")
   235  	}
   236  
   237  	// Initialize the height hint cache within the chain directory.
   238  	hintCache, err := chainntnfs.NewHeightHintCache(
   239  		heightHintCacheConfig, cfg.HeightHintDB,
   240  	)
   241  	if err != nil {
   242  		return nil, nil, fmt.Errorf("unable to initialize height hint "+
   243  			"cache: %v", err)
   244  	}
   245  
   246  	// When running in remote wallet mode, we only support running in dcrw
   247  	// mode (using the wallet for chain operations).
   248  	walletConn := cfg.WalletUnlockParams.Conn
   249  	wallet := cfg.WalletUnlockParams.Wallet
   250  	if walletConn != nil && cfg.Decred.Node != "dcrw" {
   251  		return nil, nil, fmt.Errorf("remote wallet mode only supports " +
   252  			"'node=dcrw' config")
   253  	}
   254  
   255  	// When running in embedded wallet mode with spv on, we only support
   256  	// running in dcrw mode.
   257  	if walletConn == nil && cfg.DcrwMode.SPV && cfg.Decred.Node != "dcrw" {
   258  		return nil, nil, fmt.Errorf("embedded wallet in SPV mode only " +
   259  			"supports 'node=dcrw' config")
   260  	}
   261  
   262  	// We only require a dcrd connection when running in embedded mode and
   263  	// not in SPV mode.
   264  	needsDcrd := walletConn == nil && !cfg.DcrwMode.SPV
   265  	if needsDcrd {
   266  		// Load dcrd's TLS cert for the RPC connection.  If a raw cert
   267  		// was specified in the config, then we'll set that directly.
   268  		// Otherwise, we attempt to read the cert from the path
   269  		// specified in the config.
   270  		dcrdMode := cfg.DcrdMode
   271  		var rpcCert []byte
   272  		if dcrdMode.RawRPCCert != "" {
   273  			rpcCert, err = hex.DecodeString(dcrdMode.RawRPCCert)
   274  			if err != nil {
   275  				return nil, nil, err
   276  			}
   277  		} else {
   278  			certFile, err := os.Open(dcrdMode.RPCCert)
   279  			if err != nil {
   280  				return nil, nil, err
   281  			}
   282  			rpcCert, err = ioutil.ReadAll(certFile)
   283  			if err != nil {
   284  				return nil, nil, err
   285  			}
   286  			if err := certFile.Close(); err != nil {
   287  				return nil, nil, err
   288  			}
   289  		}
   290  
   291  		// If the specified host for the dcrd RPC server already
   292  		// has a port specified, then we use that directly. Otherwise,
   293  		// we assume the default port according to the selected chain
   294  		// parameters.
   295  		var dcrdHost string
   296  		if strings.Contains(dcrdMode.RPCHost, ":") {
   297  			dcrdHost = dcrdMode.RPCHost
   298  		} else {
   299  			dcrdHost = fmt.Sprintf("%v:%v", dcrdMode.RPCHost,
   300  				cfg.ActiveNetParams.RPCPort)
   301  		}
   302  
   303  		dcrdUser := dcrdMode.RPCUser
   304  		dcrdPass := dcrdMode.RPCPass
   305  		cc.RPCConfig = &rpcclient.ConnConfig{
   306  			Host:                 dcrdHost,
   307  			Endpoint:             "ws",
   308  			User:                 dcrdUser,
   309  			Pass:                 dcrdPass,
   310  			Certificates:         rpcCert,
   311  			DisableTLS:           false,
   312  			DisableConnectOnNew:  true,
   313  			DisableAutoReconnect: false,
   314  		}
   315  
   316  		// Verify that the provided dcrd instance exists, is reachable,
   317  		// it's on the correct network and has the features required
   318  		// for dcrlnd to perform its work.
   319  		if err = checkDcrdNode(cfg.ActiveNetParams.Net, *cc.RPCConfig); err != nil {
   320  			log.Errorf("unable to use specified dcrd node: %v",
   321  				err)
   322  			return nil, nil, err
   323  		}
   324  	}
   325  
   326  	// Initialize the appopriate wallet controller (either the embedded
   327  	// dcrwallet or a remote one).
   328  	switch {
   329  	case walletConn != nil:
   330  		log.Info("Using the remote wallet for chain operations")
   331  
   332  		// Remote wallet mode currently always use the wallet for chain
   333  		// notifications and chain IO.
   334  		cc.ChainNotifier, err = remotedcrwnotify.New(
   335  			walletConn, cfg.ActiveNetParams.Params, hintCache,
   336  			hintCache, cfg.BlockCache,
   337  		)
   338  		if err != nil {
   339  			return nil, nil, err
   340  		}
   341  
   342  		cc.ChainView, err = chainview.NewRemoteWalletFilteredChainView(walletConn,
   343  			cfg.BlockCache)
   344  		if err != nil {
   345  			log.Errorf("unable to create chain view: %v", err)
   346  			return nil, nil, err
   347  		}
   348  
   349  	case cfg.Decred.Node == "dcrw":
   350  		// Use the wallet itself for chain IO.
   351  		log.Info("Using the wallet for chain operations")
   352  
   353  		cc.ChainNotifier, err = dcrwnotify.New(
   354  			wallet, cfg.ActiveNetParams.Params, hintCache,
   355  			hintCache, cfg.BlockCache,
   356  		)
   357  		if err != nil {
   358  			return nil, nil, err
   359  		}
   360  
   361  		cc.ChainView, err = chainview.NewDcrwalletFilteredChainView(
   362  			wallet, cfg.BlockCache,
   363  		)
   364  		if err != nil {
   365  			log.Errorf("unable to create chain view: %v", err)
   366  			return nil, nil, err
   367  		}
   368  
   369  	case cfg.Decred.Node == "dcrd":
   370  		// Use the dcrd node for chain IO.
   371  		log.Info("Using dcrd for chain operations")
   372  
   373  		cc.ChainNotifier, err = dcrdnotify.New(
   374  			cc.RPCConfig, cfg.ActiveNetParams.Params, hintCache,
   375  			hintCache, cfg.BlockCache,
   376  		)
   377  		if err != nil {
   378  			return nil, nil, err
   379  		}
   380  
   381  		// Finally, we'll create an instance of the default
   382  		// chain view to be used within the routing layer.
   383  		cc.ChainView, err = chainview.NewDcrdFilteredChainView(*cc.RPCConfig)
   384  		if err != nil {
   385  			log.Errorf("unable to create chain view: %v", err)
   386  			return nil, nil, err
   387  		}
   388  
   389  		// If we're not in simnet or regtest mode, then we'll
   390  		// attempt to use a proper fee estimator.
   391  		if !cfg.Decred.SimNet && !cfg.Decred.RegTest {
   392  			log.Infof("Initializing dcrd backed fee estimator")
   393  
   394  			// Finally, we'll re-initialize the fee estimator, as
   395  			// if we're using dcrd as a backend, then we can use
   396  			// live fee estimates, rather than a statically coded
   397  			// value.
   398  			//
   399  			// TODO(decred) Review if fallbackFeeRate should be higher than
   400  			// the default relay fee.
   401  			fallBackFeeRate := chainfee.AtomPerKByte(1e4)
   402  			cc.FeeEstimator, err = chainfee.NewDcrdEstimator(
   403  				*cc.RPCConfig, fallBackFeeRate,
   404  			)
   405  			if err != nil {
   406  				return nil, nil, err
   407  			}
   408  		}
   409  
   410  	case cfg.Decred.Node == "nochainbackend":
   411  		backend := &NoChainBackend{}
   412  
   413  		cc.ChainNotifier = backend
   414  		cc.ChainView = backend
   415  		cc.FeeEstimator = backend
   416  
   417  		cc.HealthCheck = func() error {
   418  			return nil
   419  		}
   420  
   421  	default:
   422  		return nil, nil, fmt.Errorf("unknown sync mode")
   423  	}
   424  
   425  	// Override default fee estimator if an external service is specified.
   426  	if cfg.FeeURL != "" {
   427  		// Do not cache fees on regtest and simnet to make it easier to
   428  		// execute manual or automated test cases.
   429  		cacheFees := !cfg.Decred.RegTest && !cfg.Decred.SimNet
   430  
   431  		log.Infof("Using external fee estimator %v: cached=%v",
   432  			cfg.FeeURL, cacheFees)
   433  
   434  		cc.FeeEstimator = chainfee.NewWebAPIEstimator(
   435  			chainfee.SparseConfFeeSource{
   436  				URL: cfg.FeeURL,
   437  			},
   438  			!cacheFees,
   439  		)
   440  	}
   441  
   442  	ccCleanup := func() {
   443  		if cc.FeeEstimator != nil {
   444  			if err := cc.FeeEstimator.Stop(); err != nil {
   445  				log.Errorf("Failed to stop feeEstimator: %v",
   446  					err)
   447  			}
   448  		}
   449  	}
   450  
   451  	// Start fee estimator.
   452  	if err := cc.FeeEstimator.Start(); err != nil {
   453  		return nil, nil, err
   454  	}
   455  
   456  	// Select the default channel constraints for the primary chain.
   457  	cc.ChannelConstraints = GenDefaultDcrConstraints()
   458  
   459  	return cc, ccCleanup, nil
   460  }
   461  
   462  // NewChainControl attempts to create a ChainControl instance according
   463  // to the parameters in the passed configuration. Currently three
   464  // branches of ChainControl instances exist: one backed by a running btcd
   465  // full-node, another backed by a running bitcoind full-node, and the other
   466  // backed by a running neutrino light client instance. When running with a
   467  // neutrino light client instance, `neutrinoCS` must be non-nil.
   468  func NewChainControl(walletConfig lnwallet.Config,
   469  	msgSigner lnwallet.MessageSigner,
   470  	pcc *PartialChainControl) (*ChainControl, func(), error) {
   471  
   472  	cc := &ChainControl{
   473  		PartialChainControl: pcc,
   474  		MsgSigner:           msgSigner,
   475  		Signer:              walletConfig.Signer,
   476  		ChainIO:             walletConfig.ChainIO,
   477  		Wc:                  walletConfig.WalletController,
   478  		KeyRing:             walletConfig.SecretKeyRing,
   479  	}
   480  
   481  	ccCleanup := func() {
   482  		if cc.Wallet != nil {
   483  			if err := cc.Wallet.Shutdown(); err != nil {
   484  				log.Errorf("Failed to shutdown wallet: %v", err)
   485  			}
   486  		}
   487  	}
   488  
   489  	// Set the chain IO healthcheck.
   490  	//
   491  	// NOTE: in lnd this is done in NewPartialChainControl().
   492  	pcc.HealthCheck = func() error {
   493  		_, _, err := cc.ChainIO.GetBestBlock()
   494  		return err
   495  	}
   496  
   497  	lnWallet, err := lnwallet.NewLightningWallet(walletConfig)
   498  	if err != nil {
   499  		err := fmt.Errorf("unable to create wallet: %v", err)
   500  		return nil, ccCleanup, err
   501  	}
   502  	if err := lnWallet.Startup(); err != nil {
   503  		err := fmt.Errorf("unable to start wallet: %v", err)
   504  		return nil, ccCleanup, err
   505  	}
   506  
   507  	log.Info("LightningWallet opened")
   508  	cc.Wallet = lnWallet
   509  
   510  	return cc, ccCleanup, nil
   511  }
   512  
   513  var (
   514  	// decredTestnet3Genesis is the genesis hash of Decred's testnet3
   515  	// chain.
   516  	decredTestnet3Genesis = chainhash.Hash([chainhash.HashSize]byte{
   517  		0xac, 0x9b, 0xa4, 0x34, 0xb6, 0xf7, 0x24, 0x9b,
   518  		0x96, 0x98, 0xd1, 0xfc, 0xec, 0x26, 0xd6, 0x08,
   519  		0x7e, 0x83, 0x58, 0xc8, 0x11, 0xc7, 0xe9, 0x22,
   520  		0xf4, 0xca, 0x18, 0x39, 0xe5, 0xdc, 0x49, 0xa6,
   521  	})
   522  
   523  	// decredMainnetGenesis is the genesis hash of Decred's main chain.
   524  	decredMainnetGenesis = chainhash.Hash([chainhash.HashSize]byte{
   525  		0x80, 0xd9, 0x21, 0x2b, 0xf4, 0xce, 0xb0, 0x66,
   526  		0xde, 0xd2, 0x86, 0x6b, 0x39, 0xd4, 0xed, 0x89,
   527  		0xe0, 0xab, 0x60, 0xf3, 0x35, 0xc1, 0x1d, 0xf8,
   528  		0xe7, 0xbf, 0x85, 0xd9, 0xc3, 0x5c, 0x8e, 0x29,
   529  	})
   530  
   531  	// chainMap is a simple index that maps a chain's genesis hash to the
   532  	// ChainCode enum for that chain.
   533  	chainMap = map[chainhash.Hash]ChainCode{
   534  		decredTestnet3Genesis: DecredChain,
   535  
   536  		decredMainnetGenesis: DecredChain,
   537  	}
   538  
   539  	// ChainDNSSeeds is a map of a chain's hash to the set of DNS seeds
   540  	// that will be use to bootstrap peers upon first startup.
   541  	//
   542  	// The first item in the array is the primary host we'll use to attempt
   543  	// the SRV lookup we require. If we're unable to receive a response
   544  	// over UDP, then we'll fall back to manual TCP resolution. The second
   545  	// item in the array is a special A record that we'll query in order to
   546  	// receive the IP address of the current authoritative DNS server for
   547  	// the network seed.
   548  	//
   549  	// TODO(roasbeef): extend and collapse these and chainparams.go into
   550  	// struct like chaincfg.Params
   551  	ChainDNSSeeds = map[chainhash.Hash][][2]string{
   552  		// TODO(decred): Add actual decred DNS seeder addresses once
   553  		// they're up.
   554  		decredMainnetGenesis:  nil,
   555  		decredTestnet3Genesis: nil,
   556  	}
   557  )
   558  
   559  // ChainRegistry keeps track of the current chains
   560  type ChainRegistry struct {
   561  	sync.RWMutex
   562  
   563  	activeChains map[ChainCode]*ChainControl
   564  	netParams    map[ChainCode]*DecredNetParams
   565  
   566  	primaryChain ChainCode
   567  }
   568  
   569  // NewChainRegistry creates a new ChainRegistry.
   570  func NewChainRegistry() *ChainRegistry {
   571  	return &ChainRegistry{
   572  		activeChains: make(map[ChainCode]*ChainControl),
   573  		netParams:    make(map[ChainCode]*DecredNetParams),
   574  	}
   575  }
   576  
   577  // RegisterChain assigns an active ChainControl instance to a target chain
   578  // identified by its ChainCode.
   579  func (c *ChainRegistry) RegisterChain(newChain ChainCode, cc *ChainControl) {
   580  	c.Lock()
   581  	c.activeChains[newChain] = cc
   582  	c.Unlock()
   583  }
   584  
   585  // LookupChain attempts to lookup an active ChainControl instance for the
   586  // target chain.
   587  func (c *ChainRegistry) LookupChain(targetChain ChainCode) (*ChainControl, bool) {
   588  	c.RLock()
   589  	cc, ok := c.activeChains[targetChain]
   590  	c.RUnlock()
   591  	return cc, ok
   592  }
   593  
   594  // LookupChainByHash attempts to look up an active ChainControl which
   595  // corresponds to the passed genesis hash.
   596  func (c *ChainRegistry) LookupChainByHash(
   597  	chainHash chainhash.Hash) (*ChainControl, bool) {
   598  
   599  	c.RLock()
   600  	defer c.RUnlock()
   601  
   602  	targetChain, ok := chainMap[chainHash]
   603  	if !ok {
   604  		return nil, ok
   605  	}
   606  
   607  	cc, ok := c.activeChains[targetChain]
   608  	return cc, ok
   609  }
   610  
   611  // RegisterPrimaryChain sets a target chain as the "home chain" for lnd.
   612  func (c *ChainRegistry) RegisterPrimaryChain(cc ChainCode) {
   613  	c.Lock()
   614  	defer c.Unlock()
   615  
   616  	c.primaryChain = cc
   617  }
   618  
   619  // PrimaryChain returns the primary chain for this running lnd instance. The
   620  // primary chain is considered the "home base" while the other registered
   621  // chains are treated as secondary chains.
   622  func (c *ChainRegistry) PrimaryChain() ChainCode {
   623  	c.RLock()
   624  	defer c.RUnlock()
   625  
   626  	return c.primaryChain
   627  }
   628  
   629  // ActiveChains returns a slice containing the active chains.
   630  func (c *ChainRegistry) ActiveChains() []ChainCode {
   631  	c.RLock()
   632  	defer c.RUnlock()
   633  
   634  	chains := make([]ChainCode, 0, len(c.activeChains))
   635  	for activeChain := range c.activeChains {
   636  		chains = append(chains, activeChain)
   637  	}
   638  
   639  	return chains
   640  }
   641  
   642  // NumActiveChains returns the total number of active chains.
   643  func (c *ChainRegistry) NumActiveChains() uint32 {
   644  	c.RLock()
   645  	defer c.RUnlock()
   646  
   647  	return uint32(len(c.activeChains))
   648  }