func (m *Manager) AccountProperties(ns walletdb.ReadBucket, account uint32) (*AccountProperties, error) {
	defer m.mtx.RUnlock()
	m.mtx.RLock()
	props := &AccountProperties{AccountNumber: account}
	if account != ImportedAddrAccount {
		acctInfo, err := m.loadAccountInfo(ns, account)
		if err != nil {
			return nil, err
		}
		props.AccountName = acctInfo.acctName
		props.AccountType = acctInfo.acctType
		row, err := fetchAccountInfo(ns, account, DBVersion)
		if err != nil {
			return nil, err
		}
		props.LastUsedExternalIndex = row.lastUsedExternalIndex
		props.LastUsedInternalIndex = row.lastUsedInternalIndex
		props.LastReturnedExternalIndex = row.lastReturnedExternalIndex
		props.LastReturnedInternalIndex = row.lastReturnedInternalIndex
	} else {
		props.AccountName = ImportedAddrAccountName  
		var importedKeyCount uint32
		count := func(interface{}) error {
			importedKeyCount++
			return nil
		}
		algType,err := forEachAccountAddress(ns, ImportedAddrAccount, count)
		if err != nil {
			return nil, err
		}
		props.ImportedKeyCount = importedKeyCount
		props.AccountType = algType
	}
	return props, nil
}
AccountProperties returns properties associated with the account, such as the
account number, name, and the number of derived and imported keys.

TODO: Instead of opening a second read transaction after making a change, and
then fetching the account properties with a new read tx, this can be made
more performant by simply returning the new account properties during the
change.
package udb
import (
	"crypto/rand"
	"crypto/sha512"
	"fmt"
	"strconv"
	"sync"
	"github.com/HcashOrg/hcashd/chaincfg"
	"github.com/HcashOrg/hcashd/chaincfg/chainec"
	"github.com/HcashOrg/hcashutil"
	"github.com/HcashOrg/hcashutil/hdkeychain"
	"github.com/HcashOrg/hcashwallet/apperrors"
	"github.com/HcashOrg/hcashwallet/snacl"
	"github.com/HcashOrg/hcashwallet/walletdb"
	"github.com/HcashOrg/hcashd/crypto/bliss"
	"golang.org/x/crypto/ripemd160"
	"github.com/HcashOrg/hcashwallet/internal/zero"
)
const (
	MaxAccountNum = hdkeychain.HardenedKeyStart - 2  
	MaxAddressesPerAccount = hdkeychain.HardenedKeyStart - 1
	ImportedAddrAccount = MaxAccountNum + 1  
	ImportedAddrAccountName = "imported"
	DefaultAccountNum = 0
	DefaultBlissAccountNum = 1
	BlissAccountName = "postquantum"
	defaultAccountName = "default"
	maxCoinType = hdkeychain.HardenedKeyStart - 1
	ExternalBranch uint32 = 0
	InternalBranch uint32 = 1
	saltSize = 32
	AcctypeEc 		uint8 = 0
	AcctypeBliss    uint8 = 1
	AcctypeLms      uint8 = 2
)
var (
	errAlreadyExists = "the specified address manager already exists"
	errCoinTypeTooHigh = "coin type may not exceed " +
		strconv.FormatUint(hdkeychain.HardenedKeyStart-1, 10)
	errAcctTooHigh = "account number may not exceed " +
		strconv.FormatUint(hdkeychain.HardenedKeyStart-1, 10)
	errLocked = "address manager is locked"
	errWatchingOnly = "address manager is watching-only"
)
func managerError(code apperrors.Code, str string, err error) error {
	return apperrors.E{ErrorCode: code, Description: str, Err: err}
}
func isReservedAccountName(name string) bool {
	return name == ImportedAddrAccountName
}
func isReservedAccountNum(acct uint32) bool {
	return acct == ImportedAddrAccount
}
func normalizeAddress(addr hcashutil.Address) hcashutil.Address {
	switch addr := addr.(type) {
	case *hcashutil.AddressSecpPubKey:
		return addr.AddressPubKeyHash()
	default:
		return addr
	}
}
type ScryptOptions struct {
	N, R, P int
}
var defaultScryptOptions = ScryptOptions{
	N: 262144,  
	R: 8,
	P: 1,
}
type accountInfo struct {
	acctName string
	acctType uint8
	acctKeyEncrypted []byte
	acctKeyPriv      *hdkeychain.ExtendedKey
	acctKeyPub       *hdkeychain.ExtendedKey
}
type AccountProperties struct {
	AccountNumber             uint32
	AccountName               string
	AccountType               uint8
	LastUsedExternalIndex     uint32
	LastUsedInternalIndex     uint32
	LastReturnedExternalIndex uint32
	LastReturnedInternalIndex uint32
	ImportedKeyCount          uint32
}
func defaultNewSecretKey(passphrase *[]byte, config *ScryptOptions) (*snacl.SecretKey, error) {
	return snacl.NewSecretKey(passphrase, config.N, config.R, config.P)
}
var newSecretKey = defaultNewSecretKey
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
	mtx2 sync.RWMutex
	returnedSecretsMu sync.RWMutex
	returnedPrivKeys  map[[ripemd160.Size]byte]chainec.PrivateKey
	returnedScripts   map[[ripemd160.Size]byte][]byte
	chainParams  *chaincfg.Params
	watchingOnly bool
	locked       bool
	closed       bool
	acctInfo map[uint32]*accountInfo
	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) lock() {
	for _, acctInfo := range m.acctInfo {
		if acctInfo.acctKeyPriv != nil {
			acctInfo.acctKeyPriv.Zero()
		}
		acctInfo.acctKeyPriv = nil
	}
	m.returnedSecretsMu.Lock()
	for _, privKey := range m.returnedPrivKeys {
		zero.BigInt(privKey.GetD())
	}
	for _, script := range m.returnedScripts {
		zero.Bytes(script)
	}
	m.returnedPrivKeys = nil
	m.returnedScripts = nil
	m.returnedSecretsMu.Unlock()
	m.cryptoKeyScript.Zero()
	m.cryptoKeyPriv.Zero()
	m.masterKeyPriv.Zero()
	zero.Bytea64(&m.hashedPrivPassphrase)
	m.locked = true
}
func (m *Manager) zeroSensitivePublicData() {
	for _, acctInfo := range m.acctInfo {
		acctInfo.acctKeyPub.Zero()
		acctInfo.acctKeyPub = nil
	}
	m.cryptoKeyPub.Zero()
	m.masterKeyPub.Zero()
}
func (m *Manager) WatchingOnly() bool {
	return m.watchingOnly
}
func (m *Manager) Close() error {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if !m.watchingOnly && !m.locked {
		m.lock()
	}
	m.zeroSensitivePublicData()
	m.closed = true
	return nil
}
func (m *Manager) keyToManaged(derivedKey *hdkeychain.ExtendedKey, account,
	branch, index uint32) (ManagedAddress, error) {
	ma, err := newManagedAddressFromExtKey(m, account, derivedKey)
	defer derivedKey.Zero()
	if err != nil {
		return nil, err
	}
	if branch == InternalBranch {
		ma.internal = true
	}
	return ma, nil
}
func deriveKey(acctInfo *accountInfo, branch, index uint32, private bool) (*hdkeychain.ExtendedKey, error) {
	acctKey := acctInfo.acctKeyPub
	if private {
		acctKey = acctInfo.acctKeyPriv
	}
	if acctKey.GetAlgType() == AcctypeBliss && !private {
		return nil, fmt.Errorf("failed to derive extended branch key for bliss")
	}
	branchKey, err := acctKey.Child(branch)
	if err != nil {
		str := fmt.Sprintf("failed to derive extended key branch %d",
			branch)
		return nil, managerError(apperrors.ErrKeyChain, str, err)
	}
	addressKey, err := branchKey.Child(index)
	branchKey.Zero()  
	if err != nil {
		str := fmt.Sprintf("failed to derive child extended key -- "+
			"branch %d, child %d",
			branch, index)
		return nil, managerError(apperrors.ErrKeyChain, str, err)
	}
	return addressKey, nil
}
func (m *Manager) GetMasterPubkey(ns walletdb.ReadBucket, account uint32) (string, error) {
	defer m.mtx.Unlock()
	m.mtx.Lock()
	row, err := fetchAccountInfo(ns, account, DBVersion)
	if err != nil {
		return "", maybeConvertDbError(err)
	}
	serializedKeyPub, err := m.cryptoKeyPub.Decrypt(row.pubKeyEncrypted)
	if err != nil {
		str := fmt.Sprintf("failed to decrypt public key for account %d",
			DefaultAccountNum)
		return "", managerError(apperrors.ErrCrypto, str, err)
	}
	return string(serializedKeyPub), nil
}
func (m *Manager) loadAccountInfo(ns walletdb.ReadBucket, account uint32) (*accountInfo, error) {
	m.mtx2.Lock()
	defer m.mtx2.Unlock()
	if acctInfo, ok := m.acctInfo[account]; ok {
		if acctInfo.acctType == AcctypeEc {
			return acctInfo, nil
		}
	}
	row, err := fetchAccountInfo(ns, account, DBVersion)
	if err != nil {
		return nil, maybeConvertDbError(err)
	}
	serializedKeyPub, err := m.cryptoKeyPub.Decrypt(row.pubKeyEncrypted)
	if err != nil {
		str := fmt.Sprintf("failed to decrypt public key for account %d",
			account)
		return nil, managerError(apperrors.ErrCrypto, str, err)
	}
	acctKeyPub, err := hdkeychain.NewKeyFromString(string(serializedKeyPub))
	if err != nil {
		str := fmt.Sprintf("failed to create extended public key for "+
			"account %d", account)
		return nil, managerError(apperrors.ErrKeyChain, str, err)
	}
	acctInfo := &accountInfo{
		acctName:         row.name,
		acctType:         row.acctType,
		acctKeyEncrypted: row.privKeyEncrypted,
		acctKeyPub:       acctKeyPub,
	}
	if !m.locked {
		decrypted, err := m.cryptoKeyPriv.Decrypt(acctInfo.acctKeyEncrypted)
		if err != nil {
			str := fmt.Sprintf("failed to decrypt private key for "+
				"account %d", account)
			return nil, managerError(apperrors.ErrCrypto, str, err)
		}
		acctKeyPriv, err := hdkeychain.NewKeyFromString(string(decrypted))
		if err != nil {
			str := fmt.Sprintf("failed to create extended private "+
				"key for account %d", account)
			return nil, managerError(apperrors.ErrKeyChain, str, err)
		}
		acctInfo.acctKeyPriv = acctKeyPriv
	}
	m.acctInfo[account] = acctInfo
	return acctInfo, nil
}