func (l *Loader) NetworkBackend() (n wallet.NetworkBackend, ok bool) {
	l.mu.Lock()
	if l.wallet != nil {
		n, _ = l.wallet.NetworkBackend()
	}
	l.mu.Unlock()
	return n, n != nil
}
NetworkBackend returns the associated wallet network backend, if any, and a
bool describing whether a non-nil network backend was set.
package loader
import (
	"context"
	"net"
	"os"
	"path/filepath"
	"sync"
	"decred.org/dcrwallet/v2/errors"
	"decred.org/dcrwallet/v2/wallet"
	_ "decred.org/dcrwallet/v2/wallet/drivers/bdb"  
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/dcrutil/v4"
	"github.com/decred/dcrd/txscript/v4/stdaddr"
)
const (
	walletDbName = "wallet.db"
	driver       = "bdb"
)
type Loader struct {
	callbacks   []func(*wallet.Wallet)
	chainParams *chaincfg.Params
	dbDirPath   string
	wallet      *wallet.Wallet
	db          wallet.DB
	stakeOptions            *StakeOptions
	gapLimit                uint32
	accountGapLimit         int
	disableCoinTypeUpgrades bool
	allowHighFees           bool
	manualTickets           bool
	relayFee                dcrutil.Amount
	mu sync.Mutex
	DialCSPPServer DialFunc
}
type StakeOptions struct {
	VotingEnabled       bool
	AddressReuse        bool
	VotingAddress       stdaddr.StakeAddress
	PoolAddress         stdaddr.StakeAddress
	PoolFees            float64
	StakePoolColdExtKey string
}
type DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
func NewLoader(chainParams *chaincfg.Params, dbDirPath string, stakeOptions *StakeOptions, gapLimit uint32,
	allowHighFees bool, relayFee dcrutil.Amount, accountGapLimit int, disableCoinTypeUpgrades bool, manualTickets bool) *Loader {
	return &Loader{
		chainParams:             chainParams,
		dbDirPath:               dbDirPath,
		stakeOptions:            stakeOptions,
		gapLimit:                gapLimit,
		accountGapLimit:         accountGapLimit,
		disableCoinTypeUpgrades: disableCoinTypeUpgrades,
		allowHighFees:           allowHighFees,
		manualTickets:           manualTickets,
		relayFee:                relayFee,
	}
}
func (l *Loader) onLoaded(w *wallet.Wallet, db wallet.DB) {
	for _, fn := range l.callbacks {
		fn(w)
	}
	l.wallet = w
	l.db = db
	l.callbacks = nil  
}
func (l *Loader) RunAfterLoad(fn func(*wallet.Wallet)) {
	l.mu.Lock()
	if l.wallet != nil {
		w := l.wallet
		l.mu.Unlock()
		fn(w)
	} else {
		l.callbacks = append(l.callbacks, fn)
		l.mu.Unlock()
	}
}
func (l *Loader) CreateWatchingOnlyWallet(ctx context.Context, extendedPubKey string, pubPass []byte) (w *wallet.Wallet, err error) {
	const op errors.Op = "loader.CreateWatchingOnlyWallet"
	defer l.mu.Unlock()
	l.mu.Lock()
	if l.wallet != nil {
		return nil, errors.E(op, errors.Exist, "wallet already loaded")
	}
	if fi, err := os.Stat(l.dbDirPath); err != nil {
		if os.IsNotExist(err) {
			if err = os.MkdirAll(l.dbDirPath, 0700); err != nil {
				return nil, errors.E(op, err)
			}
		} else {
			return nil, errors.E(op, err)
		}
	} else {
		if !fi.IsDir() {
			return nil, errors.E(op, errors.Invalid, errors.Errorf("%q is not a directory", l.dbDirPath))
		}
	}
	dbPath := filepath.Join(l.dbDirPath, walletDbName)
	exists, err := fileExists(dbPath)
	if err != nil {
		return nil, errors.E(op, err)
	}
	if exists {
		return nil, errors.E(op, errors.Exist, "wallet already exists")
	}
	defer func() {
		if err != nil {
			_ = os.Remove(dbPath)
		}
	}()
	err = os.MkdirAll(l.dbDirPath, 0700)
	if err != nil {
		return nil, errors.E(op, err)
	}
	db, err := wallet.CreateDB(driver, dbPath)
	if err != nil {
		return nil, errors.E(op, err)
	}
	err = wallet.CreateWatchOnly(ctx, db, extendedPubKey, pubPass, l.chainParams)
	if err != nil {
		return nil, errors.E(op, err)
	}
	so := l.stakeOptions
	cfg := &wallet.Config{
		DB:                      db,
		PubPassphrase:           pubPass,
		VotingEnabled:           so.VotingEnabled,
		AddressReuse:            so.AddressReuse,
		VotingAddress:           so.VotingAddress,
		PoolAddress:             so.PoolAddress,
		PoolFees:                so.PoolFees,
		GapLimit:                l.gapLimit,
		AccountGapLimit:         l.accountGapLimit,
		DisableCoinTypeUpgrades: l.disableCoinTypeUpgrades,
		StakePoolColdExtKey:     so.StakePoolColdExtKey,
		ManualTickets:           l.manualTickets,
		AllowHighFees:           l.allowHighFees,
		RelayFee:                l.relayFee,
		Params:                  l.chainParams,
	}
	w, err = wallet.Open(ctx, cfg)
	if err != nil {
		return nil, errors.E(op, err)
	}
	l.onLoaded(w, db)
	return w, nil
}
func (l *Loader) CreateNewWallet(ctx context.Context, pubPassphrase, privPassphrase, seed []byte) (w *wallet.Wallet, err error) {
	const op errors.Op = "loader.CreateNewWallet"
	defer l.mu.Unlock()
	l.mu.Lock()
	if l.wallet != nil {
		return nil, errors.E(op, errors.Exist, "wallet already opened")
	}
	if fi, err := os.Stat(l.dbDirPath); err != nil {
		if os.IsNotExist(err) {
			if err = os.MkdirAll(l.dbDirPath, 0700); err != nil {
				return nil, errors.E(op, err)
			}
		} else {
			return nil, errors.E(op, err)
		}
	} else {
		if !fi.IsDir() {
			return nil, errors.E(op, errors.Errorf("%q is not a directory", l.dbDirPath))
		}
	}
	dbPath := filepath.Join(l.dbDirPath, walletDbName)
	exists, err := fileExists(dbPath)
	if err != nil {
		return nil, errors.E(op, err)
	}
	if exists {
		return nil, errors.E(op, errors.Exist, "wallet DB exists")
	}
	defer func() {
		if err != nil {
			_ = os.Remove(dbPath)
		}
	}()
	err = os.MkdirAll(l.dbDirPath, 0700)
	if err != nil {
		return nil, errors.E(op, err)
	}
	db, err := wallet.CreateDB(driver, dbPath)
	if err != nil {
		return nil, errors.E(op, err)
	}
	err = wallet.Create(ctx, db, pubPassphrase, privPassphrase, seed, l.chainParams)
	if err != nil {
		return nil, errors.E(op, err)
	}
	so := l.stakeOptions
	cfg := &wallet.Config{
		DB:                      db,
		PubPassphrase:           pubPassphrase,
		VotingEnabled:           so.VotingEnabled,
		AddressReuse:            so.AddressReuse,
		VotingAddress:           so.VotingAddress,
		PoolAddress:             so.PoolAddress,
		PoolFees:                so.PoolFees,
		GapLimit:                l.gapLimit,
		AccountGapLimit:         l.accountGapLimit,
		DisableCoinTypeUpgrades: l.disableCoinTypeUpgrades,
		StakePoolColdExtKey:     so.StakePoolColdExtKey,
		ManualTickets:           l.manualTickets,
		AllowHighFees:           l.allowHighFees,
		RelayFee:                l.relayFee,
		Params:                  l.chainParams,
	}
	w, err = wallet.Open(ctx, cfg)
	if err != nil {
		return nil, errors.E(op, err)
	}
	l.onLoaded(w, db)
	return w, nil
}
func (l *Loader) OpenExistingWallet(ctx context.Context, pubPassphrase []byte) (w *wallet.Wallet, rerr error) {
	const op errors.Op = "loader.OpenExistingWallet"
	defer l.mu.Unlock()
	l.mu.Lock()
	if l.wallet != nil {
		return nil, errors.E(op, errors.Exist, "wallet already opened")
	}
	dbPath := filepath.Join(l.dbDirPath, walletDbName)
	l.mu.Unlock()
	db, err := wallet.OpenDB(driver, dbPath)
	l.mu.Lock()
	if err != nil {
		log.Errorf("Failed to open database: %v", err)
		return nil, errors.E(op, err)
	}
	defer func() {
		if rerr != nil {
			db.Close()
		}
	}()
	so := l.stakeOptions
	cfg := &wallet.Config{
		DB:                      db,
		PubPassphrase:           pubPassphrase,
		VotingEnabled:           so.VotingEnabled,
		AddressReuse:            so.AddressReuse,
		VotingAddress:           so.VotingAddress,
		PoolAddress:             so.PoolAddress,
		PoolFees:                so.PoolFees,
		GapLimit:                l.gapLimit,
		AccountGapLimit:         l.accountGapLimit,
		DisableCoinTypeUpgrades: l.disableCoinTypeUpgrades,
		StakePoolColdExtKey:     so.StakePoolColdExtKey,
		ManualTickets:           l.manualTickets,
		AllowHighFees:           l.allowHighFees,
		RelayFee:                l.relayFee,
		Params:                  l.chainParams,
	}
	w, err = wallet.Open(ctx, cfg)
	if err != nil {
		return nil, errors.E(op, err)
	}
	l.onLoaded(w, db)
	return w, nil
}
func (l *Loader) DbDirPath() string {
	return l.dbDirPath
}
func (l *Loader) WalletExists() (bool, error) {
	const op errors.Op = "loader.WalletExists"
	dbPath := filepath.Join(l.dbDirPath, walletDbName)
	exists, err := fileExists(dbPath)
	if err != nil {
		return false, errors.E(op, err)
	}
	return exists, nil
}
func (l *Loader) LoadedWallet() (*wallet.Wallet, bool) {
	l.mu.Lock()
	w := l.wallet
	l.mu.Unlock()
	return w, w != nil
}
func (l *Loader) UnloadWallet() error {
	const op errors.Op = "loader.UnloadWallet"
	defer l.mu.Unlock()
	l.mu.Lock()
	if l.wallet == nil {
		return errors.E(op, errors.Invalid, "wallet is unopened")
	}
	err := l.db.Close()
	if err != nil {
		return errors.E(op, err)
	}
	l.wallet = nil
	l.db = nil
	return nil
}