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

     1  package dcrlnd
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"decred.org/dcrwallet/v4/wallet"
    13  	"github.com/decred/dcrd/chaincfg/v3"
    14  	"github.com/decred/dcrlnd/blockcache"
    15  	"github.com/decred/dcrlnd/btcwalletcompat"
    16  	"github.com/decred/dcrlnd/chainreg"
    17  	"github.com/decred/dcrlnd/channeldb"
    18  	"github.com/decred/dcrlnd/keychain"
    19  	"github.com/decred/dcrlnd/kvdb"
    20  	"github.com/decred/dcrlnd/lncfg"
    21  	"github.com/decred/dcrlnd/lnrpc"
    22  	"github.com/decred/dcrlnd/lnwallet"
    23  	"github.com/decred/dcrlnd/lnwallet/dcrwallet"
    24  	walletloader "github.com/decred/dcrlnd/lnwallet/dcrwallet/loader"
    25  	"github.com/decred/dcrlnd/macaroons"
    26  	"github.com/decred/dcrlnd/rpcperms"
    27  	"github.com/decred/dcrlnd/signal"
    28  	"github.com/decred/dcrlnd/walletunlocker"
    29  	"github.com/decred/dcrlnd/watchtower"
    30  	"github.com/decred/dcrlnd/watchtower/wtclient"
    31  	"github.com/decred/dcrlnd/watchtower/wtdb"
    32  	"github.com/decred/slog"
    33  	proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    34  	"google.golang.org/grpc"
    35  	"gopkg.in/macaroon-bakery.v2/bakery"
    36  )
    37  
    38  // GrpcRegistrar is an interface that must be satisfied by an external subserver
    39  // that wants to be able to register its own gRPC server onto lnd's main
    40  // grpc.Server instance.
    41  type GrpcRegistrar interface {
    42  	// RegisterGrpcSubserver is called for each net.Listener on which lnd
    43  	// creates a grpc.Server instance. External subservers implementing this
    44  	// method can then register their own gRPC server structs to the main
    45  	// server instance.
    46  	RegisterGrpcSubserver(*grpc.Server) error
    47  }
    48  
    49  // RestRegistrar is an interface that must be satisfied by an external subserver
    50  // that wants to be able to register its own REST mux onto lnd's main
    51  // proxy.ServeMux instance.
    52  type RestRegistrar interface {
    53  	// RegisterRestSubserver is called after lnd creates the main
    54  	// proxy.ServeMux instance. External subservers implementing this method
    55  	// can then register their own REST proxy stubs to the main server
    56  	// instance.
    57  	RegisterRestSubserver(context.Context, *proxy.ServeMux, string,
    58  		[]grpc.DialOption) error
    59  }
    60  
    61  // ExternalValidator is an interface that must be satisfied by an external
    62  // macaroon validator.
    63  type ExternalValidator interface {
    64  	macaroons.MacaroonValidator
    65  
    66  	// Permissions returns the permissions that the external validator is
    67  	// validating. It is a map between the full HTTP URI of each RPC and its
    68  	// required macaroon permissions. If multiple action/entity tuples are
    69  	// specified per URI, they are all required. See rpcserver.go for a list
    70  	// of valid action and entity values.
    71  	Permissions() map[string][]bakery.Op
    72  }
    73  
    74  // DatabaseBuilder is an interface that must be satisfied by the implementation
    75  // that provides lnd's main database backend instances.
    76  type DatabaseBuilder interface {
    77  	// BuildDatabase extracts the current databases that we'll use for
    78  	// normal operation in the daemon. A function closure that closes all
    79  	// opened databases is also returned.
    80  	BuildDatabase(ctx context.Context) (*DatabaseInstances, func(), error)
    81  }
    82  
    83  // WalletConfigBuilder is an interface that must be satisfied by a custom wallet
    84  // implementation.
    85  type WalletConfigBuilder interface {
    86  	// BuildWalletConfig is responsible for creating or unlocking and then
    87  	// fully initializing a wallet.
    88  	BuildWalletConfig(context.Context, *DatabaseInstances,
    89  		*rpcperms.InterceptorChain,
    90  		[]*ListenerWithSignal) (*chainreg.PartialChainControl,
    91  		*chainreg.WalletConfig, func(), error)
    92  }
    93  
    94  // ChainControlBuilder is an interface that must be satisfied by a custom wallet
    95  // implementation.
    96  type ChainControlBuilder interface {
    97  	// BuildChainControl is responsible for creating a fully populated chain
    98  	// control instance from a wallet.
    99  	BuildChainControl(*chainreg.PartialChainControl,
   100  		*chainreg.WalletConfig) (*chainreg.ChainControl, func(), error)
   101  }
   102  
   103  // ImplementationCfg is a struct that holds all configuration items for
   104  // components that can be implemented outside lnd itself.
   105  type ImplementationCfg struct {
   106  	// GrpcRegistrar is a type that can register additional gRPC subservers
   107  	// before the main gRPC server is started.
   108  	GrpcRegistrar
   109  
   110  	// RestRegistrar is a type that can register additional REST subservers
   111  	// before the main REST proxy is started.
   112  	RestRegistrar
   113  
   114  	// ExternalValidator is a type that can provide external macaroon
   115  	// validation.
   116  	ExternalValidator
   117  
   118  	// DatabaseBuilder is a type that can provide lnd's main database
   119  	// backend instances.
   120  	DatabaseBuilder
   121  
   122  	// WalletConfigBuilder is a type that can provide a wallet configuration
   123  	// with a fully loaded and unlocked wallet.
   124  	WalletConfigBuilder
   125  
   126  	// ChainControlBuilder is a type that can provide a custom wallet
   127  	// implementation.
   128  	ChainControlBuilder
   129  }
   130  
   131  // DefaultWalletImpl is the default implementation of our normal, btcwallet
   132  // backed configuration.
   133  type DefaultWalletImpl struct {
   134  	cfg         *Config
   135  	logger      slog.Logger
   136  	interceptor signal.Interceptor
   137  
   138  	pwService *walletunlocker.UnlockerService
   139  }
   140  
   141  // NewDefaultWalletImpl creates a new default wallet implementation.
   142  func NewDefaultWalletImpl(cfg *Config, logger slog.Logger,
   143  	interceptor signal.Interceptor) *DefaultWalletImpl {
   144  
   145  	return &DefaultWalletImpl{
   146  		cfg:         cfg,
   147  		logger:      logger,
   148  		interceptor: interceptor,
   149  		pwService:   createWalletUnlockerService(cfg),
   150  	}
   151  }
   152  
   153  // RegisterRestSubserver is called after lnd creates the main proxy.ServeMux
   154  // instance. External subservers implementing this method can then register
   155  // their own REST proxy stubs to the main server instance.
   156  //
   157  // NOTE: This is part of the GrpcRegistrar interface.
   158  func (d *DefaultWalletImpl) RegisterRestSubserver(ctx context.Context,
   159  	mux *proxy.ServeMux, restProxyDest string,
   160  	restDialOpts []grpc.DialOption) error {
   161  
   162  	return lnrpc.RegisterWalletUnlockerHandlerFromEndpoint(
   163  		ctx, mux, restProxyDest, restDialOpts,
   164  	)
   165  }
   166  
   167  // RegisterGrpcSubserver is called for each net.Listener on which lnd creates a
   168  // grpc.Server instance. External subservers implementing this method can then
   169  // register their own gRPC server structs to the main server instance.
   170  //
   171  // NOTE: This is part of the GrpcRegistrar interface.
   172  func (d *DefaultWalletImpl) RegisterGrpcSubserver(s *grpc.Server) error {
   173  	lnrpc.RegisterWalletUnlockerServer(s, d.pwService)
   174  
   175  	return nil
   176  }
   177  
   178  // ValidateMacaroon extracts the macaroon from the context's gRPC metadata,
   179  // checks its signature, makes sure all specified permissions for the called
   180  // method are contained within and finally ensures all caveat conditions are
   181  // met. A non-nil error is returned if any of the checks fail.
   182  //
   183  // NOTE: This is part of the ExternalValidator interface.
   184  func (d *DefaultWalletImpl) ValidateMacaroon(ctx context.Context,
   185  	requiredPermissions []bakery.Op, fullMethod string) error {
   186  
   187  	// Because the default implementation does not return any permissions,
   188  	// we shouldn't be registered as an external validator at all and this
   189  	// should never be invoked.
   190  	return fmt.Errorf("default implementation does not support external " +
   191  		"macaroon validation")
   192  }
   193  
   194  // Permissions returns the permissions that the external validator is
   195  // validating. It is a map between the full HTTP URI of each RPC and its
   196  // required macaroon permissions. If multiple action/entity tuples are specified
   197  // per URI, they are all required. See rpcserver.go for a list of valid action
   198  // and entity values.
   199  //
   200  // NOTE: This is part of the ExternalValidator interface.
   201  func (d *DefaultWalletImpl) Permissions() map[string][]bakery.Op {
   202  	return nil
   203  }
   204  
   205  // BuildWalletConfig is responsible for creating or unlocking and then
   206  // fully initializing a wallet.
   207  //
   208  // NOTE: This is part of the WalletConfigBuilder interface.
   209  func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
   210  	dbs *DatabaseInstances, interceptorChain *rpcperms.InterceptorChain,
   211  	grpcListeners []*ListenerWithSignal) (*chainreg.PartialChainControl,
   212  	*chainreg.WalletConfig, func(), error) {
   213  
   214  	// Keep track of our various cleanup functions. We use a defer function
   215  	// as well to not repeat ourselves with every return statement.
   216  	var (
   217  		cleanUpTasks []func()
   218  		earlyExit    = true
   219  		cleanUp      = func() {
   220  			for _, fn := range cleanUpTasks {
   221  				if fn == nil {
   222  					continue
   223  				}
   224  
   225  				fn()
   226  			}
   227  		}
   228  	)
   229  	defer func() {
   230  		if earlyExit {
   231  			cleanUp()
   232  		}
   233  	}()
   234  
   235  	// Initialize a new block cache.
   236  	blockCache := blockcache.NewBlockCache(d.cfg.BlockCacheSize)
   237  
   238  	var (
   239  		walletInitParams = walletunlocker.WalletUnlockParams{
   240  			// In case we do auto-unlock, we need to be able to send
   241  			// into the channel without blocking so we buffer it.
   242  			MacResponseChan: make(chan []byte, 1),
   243  		}
   244  		privateWalletPw = lnwallet.DefaultPrivatePassphrase
   245  		publicWalletPw  = lnwallet.DefaultPublicPassphrase
   246  	)
   247  
   248  	// If the user didn't request a seed, then we'll manually assume a
   249  	// wallet birthday of now, as otherwise the seed would've specified
   250  	// this information.
   251  	walletInitParams.Birthday = time.Now()
   252  
   253  	// Remote wallet init has some differences.
   254  	isRemoteWallet := d.cfg.Dcrwallet.GRPCHost != "" && d.cfg.Dcrwallet.CertPath != ""
   255  
   256  	d.pwService.SetDB(dbs.ChanStateDB)
   257  	d.pwService.SetLoaderOpts([]walletloader.LoaderOption{dbs.WalletDB})
   258  	d.pwService.SetMacaroonDB(dbs.MacaroonDB)
   259  	walletExists, err := d.pwService.WalletExists()
   260  	if err != nil {
   261  		return nil, nil, nil, err
   262  	}
   263  
   264  	if !walletExists {
   265  		interceptorChain.SetWalletNotCreated()
   266  	} else {
   267  		interceptorChain.SetWalletLocked()
   268  	}
   269  
   270  	// If we've started in auto unlock mode, then a wallet should already
   271  	// exist because we don't want to enable the RPC unlocker in that case
   272  	// for security reasons (an attacker could inject their seed since the
   273  	// RPC is unauthenticated). Only if the user explicitly wants to allow
   274  	// wallet creation we don't error out here.
   275  	if d.cfg.WalletUnlockPasswordFile != "" && !walletExists &&
   276  		!d.cfg.WalletUnlockAllowCreate && !isRemoteWallet {
   277  
   278  		return nil, nil, nil, fmt.Errorf("wallet unlock password file " +
   279  			"was specified but wallet does not exist; initialize " +
   280  			"the wallet before using auto unlocking")
   281  	}
   282  
   283  	// What wallet mode are we running in? We've already made sure the no
   284  	// seed backup and auto unlock aren't both set during config parsing.
   285  	switch {
   286  	// No seed backup means we're also using the default password.
   287  	case d.cfg.NoSeedBackup && !isRemoteWallet:
   288  		// We continue normally, the default password has already been
   289  		// set above.
   290  		d.logger.Warnf("RUNNING IN NO SEED BACKUP MODE")
   291  		wallet, err := noSeedBackupWalletInit(ctx, d.cfg, privateWalletPw, publicWalletPw)
   292  		if err != nil {
   293  			return nil, nil, nil, err
   294  		}
   295  		walletInitParams.Wallet = wallet
   296  		walletInitParams.Password = privateWalletPw
   297  
   298  	// A password for unlocking is provided in a file.
   299  	case d.cfg.WalletUnlockPasswordFile != "" && walletExists:
   300  		d.logger.Infof("Attempting automatic wallet unlock with " +
   301  			"password provided in file")
   302  		pwBytes, err := ioutil.ReadFile(d.cfg.WalletUnlockPasswordFile)
   303  		if err != nil {
   304  			return nil, nil, nil, fmt.Errorf("error reading "+
   305  				"password from file %s: %v",
   306  				d.cfg.WalletUnlockPasswordFile, err)
   307  		}
   308  
   309  		// Remove any newlines at the end of the file. The lndinit tool
   310  		// won't ever write a newline but maybe the file was provisioned
   311  		// by another process or user.
   312  		pwBytes = bytes.TrimRight(pwBytes, "\r\n")
   313  
   314  		// We have the password now, we can ask the unlocker service to
   315  		// do the unlock for us.
   316  		unlockedWallet, unloadWalletFn, err := d.pwService.LoadAndUnlock(
   317  			ctx, pwBytes, 0,
   318  		)
   319  		if err != nil {
   320  			return nil, nil, nil, fmt.Errorf("error unlocking "+
   321  				"wallet with password from file: %v", err)
   322  		}
   323  
   324  		cleanUpTasks = append(cleanUpTasks, func() {
   325  			if err := unloadWalletFn(); err != nil {
   326  				d.logger.Errorf("Could not unload wallet: %v",
   327  					err)
   328  			}
   329  		})
   330  
   331  		privateWalletPw = pwBytes
   332  		publicWalletPw = pwBytes
   333  		walletInitParams.Wallet = unlockedWallet
   334  		walletInitParams.UnloadWallet = unloadWalletFn
   335  
   336  	// If none of the automatic startup options are selected, we fall back
   337  	// to the default behavior of waiting for the wallet creation/unlocking
   338  	// over RPC.
   339  	default:
   340  		if err := d.interceptor.Notifier.NotifyReady(false); err != nil {
   341  			return nil, nil, nil, err
   342  		}
   343  
   344  		params, err := waitForWalletPassword(
   345  			d.cfg, d.pwService, []walletloader.LoaderOption{dbs.WalletDB},
   346  			d.interceptor.ShutdownChannel(),
   347  		)
   348  		if err != nil {
   349  			err := fmt.Errorf("unable to set up wallet password "+
   350  				"listeners: %v", err)
   351  			d.logger.Error(err)
   352  			return nil, nil, nil, err
   353  		}
   354  
   355  		walletInitParams = *params
   356  		privateWalletPw = walletInitParams.Password
   357  		publicWalletPw = walletInitParams.Password
   358  		cleanUpTasks = append(cleanUpTasks, func() {
   359  			if err := walletInitParams.UnloadWallet(); err != nil {
   360  				d.logger.Errorf("Could not unload wallet: %v",
   361  					err)
   362  			}
   363  		})
   364  
   365  		if walletInitParams.RecoveryWindow > 0 {
   366  			d.logger.Infof("Wallet recovery mode enabled with "+
   367  				"address lookahead of %d addresses",
   368  				walletInitParams.RecoveryWindow)
   369  		}
   370  	}
   371  
   372  	var macaroonService *macaroons.Service
   373  	if !d.cfg.NoMacaroons {
   374  		// Create the macaroon authentication/authorization service.
   375  		macaroonService, err = macaroons.NewService(
   376  			dbs.MacaroonDB, "lnd", walletInitParams.StatelessInit,
   377  			macaroons.IPLockChecker,
   378  			macaroons.CustomChecker(interceptorChain),
   379  		)
   380  		if err != nil {
   381  			err := fmt.Errorf("unable to set up macaroon "+
   382  				"authentication: %v", err)
   383  			d.logger.Error(err)
   384  			return nil, nil, nil, err
   385  		}
   386  		cleanUpTasks = append(cleanUpTasks, func() {
   387  			if err := macaroonService.Close(); err != nil {
   388  				d.logger.Errorf("Could not close macaroon "+
   389  					"service: %v", err)
   390  			}
   391  		})
   392  
   393  		// Try to unlock the macaroon store with the private password.
   394  		// Ignore ErrAlreadyUnlocked since it could be unlocked by the
   395  		// wallet unlocker.
   396  		err = macaroonService.CreateUnlock(&privateWalletPw)
   397  		if err != nil && err != macaroons.ErrAlreadyUnlocked {
   398  			err := fmt.Errorf("unable to unlock macaroons: %v", err)
   399  			d.logger.Error(err)
   400  			return nil, nil, nil, err
   401  		}
   402  
   403  		// Send an admin macaroon to all our listeners that requested
   404  		// one by setting a non-nil macaroon channel.
   405  		adminMacBytes, err := bakeMacaroon(
   406  			ctx, macaroonService, adminPermissions(),
   407  		)
   408  		if err != nil {
   409  			return nil, nil, nil, err
   410  		}
   411  		for _, lis := range grpcListeners {
   412  			if lis.MacChan != nil {
   413  				lis.MacChan <- adminMacBytes
   414  			}
   415  		}
   416  
   417  		// In case we actually needed to unlock the wallet, we now need
   418  		// to create an instance of the admin macaroon and send it to
   419  		// the unlocker so it can forward it to the user. In no seed
   420  		// backup mode, there's nobody listening on the channel and we'd
   421  		// block here forever.
   422  		if !d.cfg.NoSeedBackup || isRemoteWallet {
   423  			// The channel is buffered by one element so writing
   424  			// should not block here.
   425  			walletInitParams.MacResponseChan <- adminMacBytes
   426  		}
   427  
   428  		// If the user requested a stateless initialization, no macaroon
   429  		// files should be created.
   430  		if !walletInitParams.StatelessInit &&
   431  			!fileExists(d.cfg.AdminMacPath) &&
   432  			!fileExists(d.cfg.ReadMacPath) &&
   433  			!fileExists(d.cfg.InvoiceMacPath) {
   434  
   435  			// Create macaroon files for lncli to use if they don't
   436  			// exist.
   437  			err = genMacaroons(
   438  				ctx, macaroonService, d.cfg.AdminMacPath,
   439  				d.cfg.ReadMacPath, d.cfg.InvoiceMacPath,
   440  			)
   441  			if err != nil {
   442  				err := fmt.Errorf("unable to create macaroons "+
   443  					"%v", err)
   444  				d.logger.Error(err)
   445  				return nil, nil, nil, err
   446  			}
   447  		}
   448  
   449  		// As a security service to the user, if they requested
   450  		// stateless initialization and there are macaroon files on disk
   451  		// we log a warning.
   452  		if walletInitParams.StatelessInit {
   453  			msg := "Found %s macaroon on disk (%s) even though " +
   454  				"--stateless_init was requested. Unencrypted " +
   455  				"state is accessible by the host system. You " +
   456  				"should change the password and use " +
   457  				"--new_mac_root_key with --stateless_init to " +
   458  				"clean up and invalidate old macaroons."
   459  
   460  			if fileExists(d.cfg.AdminMacPath) {
   461  				d.logger.Warnf(msg, "admin", d.cfg.AdminMacPath)
   462  			}
   463  			if fileExists(d.cfg.ReadMacPath) {
   464  				d.logger.Warnf(msg, "readonly", d.cfg.ReadMacPath)
   465  			}
   466  			if fileExists(d.cfg.InvoiceMacPath) {
   467  				d.logger.Warnf(msg, "invoice", d.cfg.InvoiceMacPath)
   468  			}
   469  		}
   470  
   471  		// We add the macaroon service to our RPC interceptor. This
   472  		// will start checking macaroons against permissions on every
   473  		// RPC invocation.
   474  		interceptorChain.AddMacaroonService(macaroonService)
   475  	}
   476  
   477  	// Now that the wallet password has been provided, transition the RPC
   478  	// state into Unlocked.
   479  	interceptorChain.SetWalletUnlocked()
   480  
   481  	// Since calls to the WalletUnlocker service wait for a response on the
   482  	// macaroon channel, we close it here to make sure they return in case
   483  	// we did not return the admin macaroon above. This will be the case if
   484  	// --no-macaroons is used.
   485  	close(walletInitParams.MacResponseChan)
   486  
   487  	// We'll also close all the macaroon channels since lnd is done sending
   488  	// macaroon data over it.
   489  	for _, lis := range grpcListeners {
   490  		if lis.MacChan != nil {
   491  			close(lis.MacChan)
   492  		}
   493  	}
   494  
   495  	// With the information parsed from the configuration, create valid
   496  	// instances of the pertinent interfaces required to operate the
   497  	// Lightning Network Daemon.
   498  	//
   499  	// When we create the chain control, we need storage for the height
   500  	// hints and also the wallet itself, for these two we want them to be
   501  	// replicated, so we'll pass in the remote channel DB instance.
   502  	chainControlCfg := &chainreg.Config{
   503  		Decred:                      d.cfg.Decred,
   504  		PrimaryChain:                d.cfg.registeredChains.PrimaryChain,
   505  		HeightHintCacheQueryDisable: d.cfg.HeightHintCacheQueryDisable,
   506  		DcrdMode:                    d.cfg.DcrdMode,
   507  		DcrwMode:                    d.cfg.Dcrwallet,
   508  		FullDB:                      dbs.ChanStateDB,
   509  		HeightHintDB:                dbs.HeightHintDB,
   510  		ChanStateDB:                 dbs.ChanStateDB.ChannelStateDB(),
   511  		ActiveNetParams:             d.cfg.ActiveNetParams,
   512  		FeeURL:                      d.cfg.FeeURL,
   513  		Dialer: func(addr string) (net.Conn, error) {
   514  			return d.cfg.net.Dial(
   515  				"tcp", addr, d.cfg.ConnectionTimeout,
   516  			)
   517  		},
   518  		BlockCache:         blockCache,
   519  		WalletUnlockParams: &walletInitParams,
   520  	}
   521  
   522  	// Let's go ahead and create the partial chain control now that is only
   523  	// dependent on our configuration and doesn't require any wallet
   524  	// specific information.
   525  	partialChainControl, pccCleanup, err := chainreg.NewPartialChainControl(
   526  		chainControlCfg,
   527  	)
   528  	cleanUpTasks = append(cleanUpTasks, pccCleanup)
   529  	if err != nil {
   530  		err := fmt.Errorf("unable to create partial chain control: %v",
   531  			err)
   532  		d.logger.Error(err)
   533  		return nil, nil, nil, err
   534  	}
   535  
   536  	walletConfig := &chainreg.WalletConfig{
   537  		PrivatePass:    privateWalletPw,
   538  		PublicPass:     publicWalletPw,
   539  		Birthday:       walletInitParams.Birthday,
   540  		RecoveryWindow: walletInitParams.RecoveryWindow,
   541  		AccountNb:      d.cfg.Dcrwallet.AccountNumber,
   542  		/*
   543  			NetParams:      d.cfg.ActiveNetParams.Params,
   544  			CoinType:       d.cfg.ActiveNetParams.CoinType,
   545  			Wallet:         walletInitParams.Wallet,
   546  			LoaderOptions:  []btcwallet.LoaderOption{dbs.WalletDB},
   547  			ChainSource:    partialChainControl.ChainSource,
   548  		*/
   549  	}
   550  
   551  	// Parse coin selection strategy.
   552  	switch d.cfg.CoinSelectionStrategy {
   553  	case "largest":
   554  		walletConfig.CoinSelectionStrategy = btcwalletcompat.CoinSelectionLargest
   555  
   556  	case "random":
   557  		walletConfig.CoinSelectionStrategy = btcwalletcompat.CoinSelectionRandom
   558  
   559  	default:
   560  		return nil, nil, nil, fmt.Errorf("unknown coin selection "+
   561  			"strategy %v", d.cfg.CoinSelectionStrategy)
   562  	}
   563  
   564  	// Initialize the appropriate syncer.
   565  	switch {
   566  	case !isRemoteWallet && !d.cfg.Dcrwallet.SPV:
   567  		walletConfig.Syncer, err = dcrwallet.NewRPCSyncer(*partialChainControl.RPCConfig,
   568  			d.cfg.ActiveNetParams.Params)
   569  	case !isRemoteWallet && d.cfg.Dcrwallet.SPV:
   570  		spvCfg := &dcrwallet.SPVSyncerConfig{
   571  			Peers:          d.cfg.Dcrwallet.SPVConnect,
   572  			Net:            d.cfg.ActiveNetParams.Params,
   573  			AppDataDir:     filepath.Join(d.cfg.Decred.ChainDir),
   574  			DialFunc:       d.cfg.Dcrwallet.DialFunc,
   575  			DisableRelayTx: d.cfg.Dcrwallet.DisableRelayTx,
   576  		}
   577  		walletConfig.Syncer, err = dcrwallet.NewSPVSyncer(spvCfg)
   578  	}
   579  
   580  	if err != nil {
   581  		return nil, nil, nil, err
   582  	}
   583  
   584  	earlyExit = false
   585  	return partialChainControl, walletConfig, cleanUp, nil
   586  }
   587  
   588  // BuildChainControl is responsible for creating a fully populated chain
   589  // control instance from a wallet.
   590  //
   591  // NOTE: This is part of the ChainControlBuilder interface.
   592  func (d *DefaultWalletImpl) BuildChainControl(
   593  	partialChainControl *chainreg.PartialChainControl,
   594  	walletConfig *chainreg.WalletConfig) (*chainreg.ChainControl, func(), error) {
   595  
   596  	cfg := partialChainControl.Cfg
   597  
   598  	var chainIO lnwallet.BlockChainIO
   599  	if partialChainControl.Cfg.Decred.Node == "dcrd" {
   600  		var err error
   601  		chainIO, err = dcrwallet.NewRPCChainIO(*partialChainControl.RPCConfig,
   602  			cfg.ActiveNetParams.Params, cfg.BlockCache)
   603  		if err != nil {
   604  			return nil, nil, err
   605  		}
   606  	}
   607  
   608  	dcrwConfig := &dcrwallet.Config{
   609  		Syncer:         walletConfig.Syncer,
   610  		ChainIO:        chainIO,
   611  		PrivatePass:    walletConfig.PrivatePass,
   612  		PublicPass:     walletConfig.PublicPass,
   613  		Birthday:       walletConfig.Birthday,
   614  		RecoveryWindow: walletConfig.RecoveryWindow,
   615  		DataDir:        cfg.Decred.ChainDir,
   616  		NetParams:      cfg.ActiveNetParams.Params,
   617  		Wallet:         partialChainControl.Cfg.WalletUnlockParams.Wallet,
   618  		Loader:         partialChainControl.Cfg.WalletUnlockParams.Loader,
   619  		BlockCache:     cfg.BlockCache,
   620  		DB:             cfg.FullDB,
   621  	}
   622  
   623  	walletController, err := dcrwallet.New(*dcrwConfig)
   624  	if err != nil {
   625  		err := fmt.Errorf("unable to create wallet controller: %v", err)
   626  		d.logger.Error(err)
   627  		return nil, nil, err
   628  	}
   629  
   630  	// When using dcrwallet mode, set the the chainIO to be the wallet
   631  	// itself.
   632  	if chainIO == nil {
   633  		chainIO = walletController
   634  	}
   635  
   636  	// Create, and start the lnwallet, which handles the core payment
   637  	// channel logic, and exposes control via proxy state machines.
   638  	lnWalletConfig := lnwallet.Config{
   639  		Database:           partialChainControl.Cfg.ChanStateDB,
   640  		Notifier:           partialChainControl.ChainNotifier,
   641  		WalletController:   walletController,
   642  		Signer:             walletController,
   643  		FeeEstimator:       partialChainControl.FeeEstimator,
   644  		SecretKeyRing:      walletController,
   645  		ChainIO:            chainIO,
   646  		DefaultConstraints: partialChainControl.ChannelConstraints,
   647  		NetParams:          *partialChainControl.Cfg.ActiveNetParams.Params,
   648  	}
   649  
   650  	// We've created the wallet configuration now, so we can finish
   651  	// initializing the main chain control.
   652  	activeChainControl, cleanUp, err := chainreg.NewChainControl(
   653  		lnWalletConfig, walletController, partialChainControl,
   654  	)
   655  	if err != nil {
   656  		err := fmt.Errorf("unable to create chain control: %v", err)
   657  		d.logger.Error(err)
   658  		return nil, nil, err
   659  	}
   660  
   661  	return activeChainControl, cleanUp, nil
   662  }
   663  
   664  // DatabaseInstances is a struct that holds all instances to the actual
   665  // databases that are used in lnd.
   666  type DatabaseInstances struct {
   667  	// GraphDB is the database that stores the channel graph used for path
   668  	// finding.
   669  	//
   670  	// NOTE/TODO: This currently _needs_ to be the same instance as the
   671  	// ChanStateDB below until the separation of the two databases is fully
   672  	// complete!
   673  	GraphDB *channeldb.DB
   674  
   675  	// ChanStateDB is the database that stores all of our node's channel
   676  	// state.
   677  	//
   678  	// NOTE/TODO: This currently _needs_ to be the same instance as the
   679  	// GraphDB above until the separation of the two databases is fully
   680  	// complete!
   681  	ChanStateDB *channeldb.DB
   682  
   683  	// HeightHintDB is the database that stores height hints for spends.
   684  	HeightHintDB kvdb.Backend
   685  
   686  	// MacaroonDB is the database that stores macaroon root keys.
   687  	MacaroonDB kvdb.Backend
   688  
   689  	// DecayedLogDB is the database that stores p2p related encryption
   690  	// information.
   691  	DecayedLogDB kvdb.Backend
   692  
   693  	// TowerClientDB is the database that stores the watchtower client's
   694  	// configuration.
   695  	TowerClientDB wtclient.DB
   696  
   697  	// TowerServerDB is the database that stores the watchtower server's
   698  	// configuration.
   699  	TowerServerDB watchtower.DB
   700  
   701  	// WalletDB is the configuration for loading the wallet database using
   702  	// the btcwallet's loader.
   703  	WalletDB walletloader.LoaderOption
   704  }
   705  
   706  // DefaultDatabaseBuilder is a type that builds the default database backends
   707  // for lnd, using the given configuration to decide what actual implementation
   708  // to use.
   709  type DefaultDatabaseBuilder struct {
   710  	cfg    *Config
   711  	logger slog.Logger
   712  }
   713  
   714  // NewDefaultDatabaseBuilder returns a new instance of the default database
   715  // builder.
   716  func NewDefaultDatabaseBuilder(cfg *Config,
   717  	logger slog.Logger) *DefaultDatabaseBuilder {
   718  
   719  	return &DefaultDatabaseBuilder{
   720  		cfg:    cfg,
   721  		logger: logger,
   722  	}
   723  }
   724  
   725  // BuildDatabase extracts the current databases that we'll use for normal
   726  // operation in the daemon. A function closure that closes all opened databases
   727  // is also returned.
   728  func (d *DefaultDatabaseBuilder) BuildDatabase(
   729  	ctx context.Context) (*DatabaseInstances, func(), error) {
   730  
   731  	d.logger.Infof("Opening the main database, this might take a few " +
   732  		"minutes...")
   733  
   734  	cfg := d.cfg
   735  	if cfg.DB.Backend == lncfg.BoltBackend {
   736  		d.logger.Infof("Opening bbolt database, sync_freelist=%v, "+
   737  			"auto_compact=%v", !cfg.DB.Bolt.NoFreelistSync,
   738  			cfg.DB.Bolt.AutoCompact)
   739  	}
   740  
   741  	startOpenTime := time.Now()
   742  
   743  	databaseBackends, err := cfg.DB.GetBackends(
   744  		ctx, cfg.graphDatabaseDir(), cfg.networkDir, filepath.Join(
   745  			cfg.Watchtower.TowerDir,
   746  			cfg.registeredChains.PrimaryChain().String(),
   747  			lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
   748  		), cfg.WtClient.Active, cfg.Watchtower.Active,
   749  	)
   750  	if err != nil {
   751  		return nil, nil, fmt.Errorf("unable to obtain database "+
   752  			"backends: %v", err)
   753  	}
   754  
   755  	// With the full remote mode we made sure both the graph and channel
   756  	// state DB point to the same local or remote DB and the same namespace
   757  	// within that DB.
   758  	dbs := &DatabaseInstances{
   759  		HeightHintDB: databaseBackends.HeightHintDB,
   760  		MacaroonDB:   databaseBackends.MacaroonDB,
   761  		DecayedLogDB: databaseBackends.DecayedLogDB,
   762  		WalletDB:     databaseBackends.WalletDB,
   763  	}
   764  	cleanUp := func() {
   765  		// We can just close the returned close functions directly. Even
   766  		// if we decorate the channel DB with an additional struct, its
   767  		// close function still just points to the kvdb backend.
   768  		for name, closeFunc := range databaseBackends.CloseFuncs {
   769  			if err := closeFunc(); err != nil {
   770  				d.logger.Errorf("Error closing %s "+
   771  					"database: %v", name, err)
   772  			}
   773  		}
   774  	}
   775  	if databaseBackends.Remote {
   776  		d.logger.Infof("Using remote %v database! Creating "+
   777  			"graph and channel state DB instances", cfg.DB.Backend)
   778  	} else {
   779  		d.logger.Infof("Creating local graph and channel state DB " +
   780  			"instances")
   781  	}
   782  
   783  	dbOptions := []channeldb.OptionModifier{
   784  		channeldb.OptionSetRejectCacheSize(cfg.Caches.RejectCacheSize),
   785  		channeldb.OptionSetChannelCacheSize(cfg.Caches.ChannelCacheSize),
   786  		channeldb.OptionSetBatchCommitInterval(cfg.DB.BatchCommitInterval),
   787  		channeldb.OptionDryRunMigration(cfg.DryRunMigration),
   788  		channeldb.OptionSetUseGraphCache(!cfg.DB.NoGraphCache),
   789  	}
   790  
   791  	// We want to pre-allocate the channel graph cache according to what we
   792  	// expect for mainnet to speed up memory allocation.
   793  	if cfg.ActiveNetParams.Name == chaincfg.MainNetParams().Name {
   794  		dbOptions = append(
   795  			dbOptions, channeldb.OptionSetPreAllocCacheNumNodes(
   796  				channeldb.DefaultPreAllocCacheNumNodes,
   797  			),
   798  		)
   799  	}
   800  
   801  	// Otherwise, we'll open two instances, one for the state we only need
   802  	// locally, and the other for things we want to ensure are replicated.
   803  	dbs.GraphDB, err = channeldb.CreateWithBackend(
   804  		databaseBackends.GraphDB, dbOptions...,
   805  	)
   806  	switch {
   807  	// Give the DB a chance to dry run the migration. Since we know that
   808  	// both the channel state and graph DBs are still always behind the same
   809  	// backend, we know this would be applied to both of those DBs.
   810  	case err == channeldb.ErrDryRunMigrationOK:
   811  		d.logger.Infof("Graph DB dry run migration successful")
   812  		return nil, nil, err
   813  
   814  	case err != nil:
   815  		cleanUp()
   816  
   817  		err := fmt.Errorf("unable to open graph DB: %v", err)
   818  		d.logger.Error(err)
   819  		return nil, nil, err
   820  	}
   821  
   822  	// For now, we don't _actually_ split the graph and channel state DBs on
   823  	// the code level. Since they both are based upon the *channeldb.DB
   824  	// struct it will require more refactoring to fully separate them. With
   825  	// the full remote mode we at least know for now that they both point to
   826  	// the same DB backend (and also namespace within that) so we only need
   827  	// to apply any migration once.
   828  	//
   829  	// TODO(guggero): Once the full separation of anything graph related
   830  	// from the channeldb.DB is complete, the decorated instance of the
   831  	// channel state DB should be created here individually instead of just
   832  	// using the same struct (and DB backend) instance.
   833  	dbs.ChanStateDB = dbs.GraphDB
   834  
   835  	// Wrap the watchtower client DB and make sure we clean up.
   836  	if cfg.WtClient.Active {
   837  		dbs.TowerClientDB, err = wtdb.OpenClientDB(
   838  			databaseBackends.TowerClientDB,
   839  		)
   840  		if err != nil {
   841  			cleanUp()
   842  
   843  			err := fmt.Errorf("unable to open %s database: %v",
   844  				lncfg.NSTowerClientDB, err)
   845  			d.logger.Error(err)
   846  			return nil, nil, err
   847  		}
   848  	}
   849  
   850  	// Wrap the watchtower server DB and make sure we clean up.
   851  	if cfg.Watchtower.Active {
   852  		dbs.TowerServerDB, err = wtdb.OpenTowerDB(
   853  			databaseBackends.TowerServerDB,
   854  		)
   855  		if err != nil {
   856  			cleanUp()
   857  
   858  			err := fmt.Errorf("unable to open %s database: %v",
   859  				lncfg.NSTowerServerDB, err)
   860  			d.logger.Error(err)
   861  			return nil, nil, err
   862  		}
   863  	}
   864  
   865  	openTime := time.Since(startOpenTime)
   866  	d.logger.Infof("Database(s) now open (time_to_open=%v)!", openTime)
   867  
   868  	return dbs, cleanUp, nil
   869  }
   870  
   871  // waitForWalletPassword blocks until a password is provided by the user to
   872  // this RPC server.
   873  func waitForWalletPassword(cfg *Config,
   874  	pwService *walletunlocker.UnlockerService,
   875  	loaderOpts []walletloader.LoaderOption, shutdownChan <-chan struct{}) (
   876  	*walletunlocker.WalletUnlockParams, error) {
   877  
   878  	ctx := context.TODO()
   879  
   880  	// Wait for user to provide the password.
   881  	ltndLog.Infof("Waiting for wallet encryption password. Use `lncli " +
   882  		"create` to create a wallet, `lncli unlock` to unlock an " +
   883  		"existing wallet, or `lncli changepassword` to change the " +
   884  		"password of an existing wallet and unlock it.")
   885  
   886  	// We currently don't distinguish between getting a password to be used
   887  	// for creation or unlocking, as a new wallet db will be created if
   888  	// none exists when creating the chain control.
   889  	select {
   890  
   891  	// The wallet is being created for the first time, we'll check to see
   892  	// if the user provided any entropy for seed creation. If so, then
   893  	// we'll create the wallet early to load the seed.
   894  	case initMsg := <-pwService.InitMsgs:
   895  		password := initMsg.Passphrase
   896  		cipherSeed := initMsg.WalletSeed
   897  		extendedKey := initMsg.WalletExtendedKey
   898  		recoveryWindow := initMsg.RecoveryWindow
   899  
   900  		// Before we proceed, we'll check the internal version of the
   901  		// seed. If it's greater than the current key derivation
   902  		// version, then we'll return an error as we don't understand
   903  		// this.
   904  		const latestVersion = keychain.KeyDerivationVersion
   905  		if cipherSeed != nil &&
   906  			cipherSeed.InternalVersion != latestVersion {
   907  
   908  			return nil, fmt.Errorf("invalid internal "+
   909  				"seed version %v, current version is %v",
   910  				cipherSeed.InternalVersion,
   911  				keychain.KeyDerivationVersion)
   912  		}
   913  
   914  		netDir := dcrwallet.NetworkDir(
   915  			cfg.Decred.ChainDir, cfg.ActiveNetParams.Params,
   916  		)
   917  		loader := walletloader.NewLoader(
   918  			cfg.ActiveNetParams.Params, netDir, recoveryWindow,
   919  			// loaderOpts...,
   920  		)
   921  
   922  		// With the seed, we can now use the wallet loader to create
   923  		// the wallet, then pass it back to avoid unlocking it again.
   924  		var (
   925  			birthday  time.Time
   926  			newWallet *wallet.Wallet
   927  			err       error
   928  		)
   929  		switch {
   930  		// A normal cipher seed was given, use the birthday encoded in
   931  		// it and create the wallet from that.
   932  		case cipherSeed != nil:
   933  			birthday = cipherSeed.BirthdayTime()
   934  			newWallet, err = loader.CreateNewWallet(
   935  				ctx, password, password, cipherSeed.Entropy[:],
   936  				birthday,
   937  			)
   938  
   939  		// No seed was given, we're importing a wallet from its extended
   940  		// private key.
   941  		case extendedKey != nil:
   942  			birthday = initMsg.ExtendedKeyBirthday
   943  			newWallet, err = loader.CreateNewWalletExtendedKey(
   944  				ctx, password, password, extendedKey,
   945  			)
   946  
   947  		default:
   948  			// The unlocker service made sure either the cipher seed
   949  			// or the extended key is set so, we shouldn't get here.
   950  			// The default case is just here for readability and
   951  			// completeness.
   952  			err = fmt.Errorf("cannot create wallet, neither seed " +
   953  				"nor extended key was given")
   954  		}
   955  		if err != nil {
   956  			// Don't leave the file open in case the new wallet
   957  			// could not be created for whatever reason.
   958  			if err := loader.UnloadWallet(); err != nil {
   959  				ltndLog.Errorf("Could not unload new "+
   960  					"wallet: %v", err)
   961  			}
   962  			return nil, err
   963  		}
   964  
   965  		// For new wallets, the ResetWalletTransactions flag is a no-op.
   966  		if cfg.resetWalletTransactions {
   967  			ltndLog.Warnf("Ignoring reset-wallet-transactions " +
   968  				"flag for new wallet as it has no effect")
   969  		}
   970  
   971  		return &walletunlocker.WalletUnlockParams{
   972  			Password:        password,
   973  			Birthday:        birthday,
   974  			RecoveryWindow:  recoveryWindow,
   975  			Wallet:          newWallet,
   976  			Loader:          loader,
   977  			ChansToRestore:  initMsg.ChanBackups,
   978  			UnloadWallet:    loader.UnloadWallet,
   979  			StatelessInit:   initMsg.StatelessInit,
   980  			MacResponseChan: pwService.MacResponseChan,
   981  		}, nil
   982  
   983  	// The wallet has already been created in the past, and is simply being
   984  	// unlocked. So we'll just return these passphrases.
   985  	case unlockMsg := <-pwService.UnlockMsgs:
   986  		// Resetting the transactions is something the user likely only
   987  		// wants to do once so we add a prominent warning to the log to
   988  		// remind the user to turn off the setting again after
   989  		// successful completion.
   990  		if cfg.resetWalletTransactions {
   991  			ltndLog.Warnf("Dropped all transaction history from " +
   992  				"on-chain wallet. Remember to disable " +
   993  				"reset-wallet-transactions flag for next " +
   994  				"start of lnd")
   995  		}
   996  
   997  		return &walletunlocker.WalletUnlockParams{
   998  			Password:        unlockMsg.Passphrase,
   999  			RecoveryWindow:  unlockMsg.RecoveryWindow,
  1000  			Wallet:          unlockMsg.Wallet,
  1001  			Loader:          unlockMsg.Loader,
  1002  			Conn:            unlockMsg.Conn,
  1003  			ChansToRestore:  unlockMsg.ChanBackups,
  1004  			UnloadWallet:    unlockMsg.UnloadWallet,
  1005  			StatelessInit:   unlockMsg.StatelessInit,
  1006  			MacResponseChan: pwService.MacResponseChan,
  1007  		}, nil
  1008  
  1009  	// If we got a shutdown signal we just return with an error immediately
  1010  	case <-shutdownChan:
  1011  		return nil, fmt.Errorf("shutting down")
  1012  	}
  1013  }