func (m *Manager) AddrAccount(ns walletdb.ReadBucket,
	address btcutil.Address) (*ScopedKeyManager, uint32, error) {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	for _, scopedMgr := range m.scopedManagers {
		if _, err := scopedMgr.Address(ns, address); err != nil {
			continue
		}
		accNo, err := scopedMgr.AddrAccount(ns, address)
		if err != nil {
			return nil, 0, err
		}
		return scopedMgr, accNo, err
	}
	str := fmt.Sprintf("unable to find key for addr %v", address)
	return nil, 0, managerError(ErrAddressNotFound, str, nil)
}
AddrAccount returns the account to which the given address belongs. We also
return the scoped manager that owns the addr+account combo.
package waddrmgr
import (
	"crypto/rand"
	"crypto/sha512"
	"fmt"
	"sync"
	"time"
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcutil/hdkeychain"
	"github.com/btcsuite/btcwallet/internal/zero"
	"github.com/btcsuite/btcwallet/snacl"
	"github.com/btcsuite/btcwallet/walletdb"
	"github.com/lightninglabs/neutrino/cache/lru"
)
const (
	MaxAccountNum = hdkeychain.HardenedKeyStart - 2  
	MaxAddressesPerAccount = hdkeychain.HardenedKeyStart - 1
	ImportedAddrAccount = MaxAccountNum + 1  
	ImportedAddrAccountName = "imported"
	DefaultAccountNum = 0
	defaultAccountName = "default"
	maxCoinType = hdkeychain.HardenedKeyStart - 1
	ExternalBranch uint32 = 0
	InternalBranch uint32 = 1
	saltSize = 32
)
func isReservedAccountName(name string) bool {
	return name == ImportedAddrAccountName
}
func isReservedAccountNum(acct uint32) bool {
	return acct == ImportedAddrAccount
}
type ScryptOptions struct {
	N, R, P int
}
type OpenCallbacks struct {
	ObtainSeed ObtainUserInputFunc
	ObtainPrivatePass ObtainUserInputFunc
}
var DefaultScryptOptions = ScryptOptions{
	N: 262144,  
	R: 8,
	P: 1,
}
var FastScryptOptions = ScryptOptions{
	N: 16,
	R: 8,
	P: 1,
}
type addrKey string
type accountInfo struct {
	acctName string
	acctType accountType
	acctKeyEncrypted []byte
	acctKeyPriv      *hdkeychain.ExtendedKey
	acctKeyPub       *hdkeychain.ExtendedKey
	nextExternalIndex uint32
	lastExternalAddr  ManagedAddress
	nextInternalIndex uint32
	lastInternalAddr  ManagedAddress
	addrSchema *ScopeAddrSchema
	masterKeyFingerprint uint32
}
type AccountProperties struct {
	AccountNumber uint32
	AccountName string
	ExternalKeyCount uint32
	InternalKeyCount uint32
	ImportedKeyCount uint32
	AccountPubKey *hdkeychain.ExtendedKey
	MasterKeyFingerprint uint32
	KeyScope KeyScope
	IsWatchOnly bool
	AddrSchema *ScopeAddrSchema
}
type unlockDeriveInfo struct {
	managedAddr ManagedAddress
	branch      uint32
	index       uint32
}
type SecretKeyGenerator func(
	passphrase *[]byte, config *ScryptOptions) (*snacl.SecretKey, error)
func defaultNewSecretKey(passphrase *[]byte,
	config *ScryptOptions) (*snacl.SecretKey, error) {
	return snacl.NewSecretKey(passphrase, config.N, config.R, config.P)
}
var (
	secretKeyGen = defaultNewSecretKey
	secretKeyGenMtx sync.RWMutex
)
func SetSecretKeyGen(keyGen SecretKeyGenerator) SecretKeyGenerator {
	secretKeyGenMtx.Lock()
	oldKeyGen := secretKeyGen
	secretKeyGen = keyGen
	secretKeyGenMtx.Unlock()
	return oldKeyGen
}
func newSecretKey(passphrase *[]byte,
	config *ScryptOptions) (*snacl.SecretKey, error) {
	secretKeyGenMtx.RLock()
	defer secretKeyGenMtx.RUnlock()
	return secretKeyGen(passphrase, config)
}
type EncryptorDecryptor interface {
	Encrypt(in []byte) ([]byte, error)
	Decrypt(in []byte) ([]byte, error)
	Bytes() []byte
	CopyBytes([]byte)
	Zero()
}
type cryptoKey struct {
	snacl.CryptoKey
}
func (ck *cryptoKey) Bytes() []byte {
	return ck.CryptoKey[:]
}
func (ck *cryptoKey) CopyBytes(from []byte) {
	copy(ck.CryptoKey[:], from)
}
func defaultNewCryptoKey() (EncryptorDecryptor, error) {
	key, err := snacl.GenerateCryptoKey()
	if err != nil {
		return nil, err
	}
	return &cryptoKey{*key}, nil
}
type CryptoKeyType byte
const (
	CKTPrivate CryptoKeyType = iota
	CKTScript
	CKTPublic
)
var newCryptoKey = defaultNewCryptoKey
type Manager struct {
	mtx sync.RWMutex
	scopedManagers map[KeyScope]*ScopedKeyManager
	externalAddrSchemas map[AddressType][]KeyScope
	internalAddrSchemas map[AddressType][]KeyScope
	syncState    syncState
	watchingOnly bool
	birthday     time.Time
	locked       bool
	closed       bool
	chainParams  *chaincfg.Params
	masterKeyPub  *snacl.SecretKey
	masterKeyPriv *snacl.SecretKey
	cryptoKeyPub EncryptorDecryptor
	cryptoKeyPrivEncrypted []byte
	cryptoKeyPriv          EncryptorDecryptor
	cryptoKeyScriptEncrypted []byte
	cryptoKeyScript          EncryptorDecryptor
	privPassphraseSalt   [saltSize]byte
	hashedPrivPassphrase [sha512.Size]byte
}
func (m *Manager) WatchOnly() bool {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	return m.watchOnly()
}
func (m *Manager) watchOnly() bool {
	return m.watchingOnly
}
func (m *Manager) IsWatchOnlyAccount(ns walletdb.ReadBucket, keyScope KeyScope,
	account uint32) (bool, error) {
	if m.WatchOnly() {
		return true, nil
	}
	if account == ImportedAddrAccount {
		return true, nil
	}
	scopedMgr, err := m.FetchScopedKeyManager(keyScope)
	if err != nil {
		return false, err
	}
	return scopedMgr.IsWatchOnlyAccount(ns, account)
}
func (m *Manager) lock() {
	for _, manager := range m.scopedManagers {
		for _, acctInfo := range manager.acctInfo {
			if acctInfo.acctKeyPriv != nil {
				acctInfo.acctKeyPriv.Zero()
			}
			acctInfo.acctKeyPriv = nil
		}
	}
	for _, manager := range m.scopedManagers {
		for _, ma := range manager.addrs {
			switch addr := ma.(type) {
			case *managedAddress:
				addr.lock()
			case *scriptAddress:
				addr.lock()
			}
		}
	}
	m.cryptoKeyScript.Zero()
	m.cryptoKeyPriv.Zero()
	m.masterKeyPriv.Zero()
	zero.Bytea64(&m.hashedPrivPassphrase)
	m.locked = true
}
func (m *Manager) Close() {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if m.closed {
		return
	}
	for _, manager := range m.scopedManagers {
		manager.Close()
	}
	if !m.watchingOnly && !m.locked {
		m.lock()
	}
	m.cryptoKeyPub.Zero()
	m.masterKeyPub.Zero()
	m.closed = true
}
func (m *Manager) NewScopedKeyManager(ns walletdb.ReadWriteBucket,
	scope KeyScope, addrSchema ScopeAddrSchema) (*ScopedKeyManager, error) {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	var rootPriv *hdkeychain.ExtendedKey
	if !m.watchingOnly {
		if m.locked {
			return nil, managerError(ErrLocked, errLocked, nil)
		}
		masterRootPrivEnc, _ := fetchMasterHDKeys(ns)
		if masterRootPrivEnc == nil {
			return nil, managerError(ErrWatchingOnly, "", nil)
		}
		serializedMasterRootPriv, err :=
			m.cryptoKeyPriv.Decrypt(masterRootPrivEnc)
		if err != nil {
			str := fmt.Sprintf("failed to decrypt master root " +
				"serialized private key")
			return nil, managerError(ErrLocked, str, err)
		}
		rootPriv, err = hdkeychain.NewKeyFromString(
			string(serializedMasterRootPriv),
		)
		zero.Bytes(serializedMasterRootPriv)
		if err != nil {
			str := fmt.Sprintf("failed to create master extended " +
				"private key")
			return nil, managerError(ErrKeyChain, str, err)
		}
	}
	scopeBucket := ns.NestedReadWriteBucket(scopeBucketName)
	if err := createScopedManagerNS(scopeBucket, &scope); err != nil {
		return nil, err
	}
	scopeSchemas := ns.NestedReadWriteBucket(scopeSchemaBucketName)
	if scopeSchemas == nil {
		str := "scope schema bucket not found"
		return nil, managerError(ErrDatabase, str, nil)
	}
	scopeKey := scopeToBytes(&scope)
	schemaBytes := scopeSchemaToBytes(&addrSchema)
	err := scopeSchemas.Put(scopeKey[:], schemaBytes)
	if err != nil {
		return nil, err
	}
	if !m.watchingOnly {
		err = createManagerKeyScope(
			ns, scope, rootPriv, m.cryptoKeyPub, m.cryptoKeyPriv,
		)
		if err != nil {
			return nil, err
		}
	}
	m.scopedManagers[scope] = &ScopedKeyManager{
		scope:        scope,
		addrSchema:   addrSchema,
		rootManager:  m,
		addrs:        make(map[addrKey]ManagedAddress),
		acctInfo:     make(map[uint32]*accountInfo),
		privKeyCache: lru.NewCache(defaultPrivKeyCacheSize),
	}
	m.externalAddrSchemas[addrSchema.ExternalAddrType] = append(
		m.externalAddrSchemas[addrSchema.ExternalAddrType], scope,
	)
	m.internalAddrSchemas[addrSchema.InternalAddrType] = append(
		m.internalAddrSchemas[addrSchema.InternalAddrType], scope,
	)
	return m.scopedManagers[scope], nil
}
func (m *Manager) FetchScopedKeyManager(scope KeyScope) (*ScopedKeyManager, error) {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	sm, ok := m.scopedManagers[scope]
	if !ok {
		str := fmt.Sprintf("scope %v not found", scope)
		return nil, managerError(ErrScopeNotFound, str, nil)
	}
	return sm, nil
}
func (m *Manager) ActiveScopedKeyManagers() []*ScopedKeyManager {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	var scopedManagers []*ScopedKeyManager
	for _, smgr := range m.scopedManagers {
		scopedManagers = append(scopedManagers, smgr)
	}
	return scopedManagers
}
func (m *Manager) ScopesForExternalAddrType(addrType AddressType) []KeyScope {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	return m.externalAddrSchemas[addrType]
}
func (m *Manager) ScopesForInternalAddrTypes(addrType AddressType) []KeyScope {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	return m.internalAddrSchemas[addrType]
}
func (m *Manager) NeuterRootKey(ns walletdb.ReadWriteBucket) error {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	masterRootPrivEnc, _ := fetchMasterHDKeys(ns)
	if masterRootPrivEnc == nil {
		return nil
	}
	zero.Bytes(masterRootPrivEnc)
	return ns.NestedReadWriteBucket(mainBucketName).Delete(masterHDPrivName)
}
func (m *Manager) Address(ns walletdb.ReadBucket,
	address btcutil.Address) (ManagedAddress, error) {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	for _, scopedMgr := range m.scopedManagers {
		addr, err := scopedMgr.Address(ns, address)
		if err != nil {
			continue
		}
		return addr, nil
	}
	str := fmt.Sprintf("unable to find key for addr %v", address)
	return nil, managerError(ErrAddressNotFound, str, nil)
}
func (m *Manager) MarkUsed(ns walletdb.ReadWriteBucket, address btcutil.Address) error {
	m.mtx.RLock()
	defer m.mtx.RUnlock()
	for _, scopedMgr := range m.scopedManagers {
		if _, err := scopedMgr.Address(ns, address); err != nil {
			continue
		}
		return scopedMgr.MarkUsed(ns, address)
	}
	str := fmt.Sprintf("unable to find key for addr %v", address)
	return managerError(ErrAddressNotFound, str, nil)
}