func (s *Store) WriteTo(w io.Writer) (n int64, err error) {
	s.mtx.RLock()
	defer s.mtx.RUnlock()
	return s.writeTo(w)
}
WriteTo serializes a key store and writes it to a io.Writer,
returning the number of bytes written and any errors encountered.
package keystore
import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha512"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/big"
	"os"
	"path/filepath"
	"sync"
	"time"
	"golang.org/x/crypto/ripemd160"
	"github.com/gcash/bchd/bchec"
	"github.com/gcash/bchd/chaincfg"
	"github.com/gcash/bchd/chaincfg/chainhash"
	"github.com/gcash/bchd/txscript"
	"github.com/gcash/bchd/wire"
	"github.com/gcash/bchutil"
	"github.com/gcash/bchwallet/internal/legacy/rename"
)
const (
	Filename = "wallet.bin"
	kdfOutputBytes = 32
	maxCommentLen = (1 << 16) - 1
)
const (
	defaultKdfComputeTime = 0.25
	defaultKdfMaxMem      = 32 * 1024 * 1024
)
var (
	ErrAddressNotFound  = errors.New("address not found")
	ErrAlreadyEncrypted = errors.New("private key is already encrypted")
	ErrChecksumMismatch = errors.New("checksum mismatch")
	ErrDuplicate        = errors.New("duplicate key or address")
	ErrMalformedEntry   = errors.New("malformed entry")
	ErrWatchingOnly     = errors.New("keystore is watching-only")
	ErrLocked           = errors.New("keystore is locked")
	ErrWrongPassphrase  = errors.New("wrong passphrase")
)
var fileID = [8]byte{0xba, 'W', 'A', 'L', 'L', 'E', 'T', 0x00}
type entryHeader byte
const (
	addrCommentHeader entryHeader = 1 << iota
	txCommentHeader
	deletedHeader
	scriptHeader
	addrHeader entryHeader = 0
)
func binaryRead(r io.Reader, order binary.ByteOrder, data interface{}) (n int64, err error) {
	var read int
	buf := make([]byte, binary.Size(data))
	if read, err = io.ReadFull(r, buf); err != nil {
		return int64(read), err
	}
	return int64(read), binary.Read(bytes.NewBuffer(buf), order, data)
}
func binaryWrite(w io.Writer, order binary.ByteOrder, data interface{}) (n int64, err error) {
	buf := bytes.Buffer{}
	if err = binary.Write(&buf, order, data); err != nil {
		return 0, err
	}
	written, err := w.Write(buf.Bytes())
	return int64(written), err
}
func pubkeyFromPrivkey(privkey []byte, compress bool) (pubkey []byte) {
	_, pk := bchec.PrivKeyFromBytes(bchec.S256(), privkey)
	if compress {
		return pk.SerializeCompressed()
	}
	return pk.SerializeUncompressed()
}
func keyOneIter(passphrase, salt []byte, memReqts uint64) []byte {
	saltedpass := append(passphrase, salt...)
	lutbl := make([]byte, memReqts)
	seed := sha512.Sum512(saltedpass)
	copy(lutbl[:sha512.Size], seed[:])
	for nByte := 0; nByte < (int(memReqts) - sha512.Size); nByte += sha512.Size {
		hash := sha512.Sum512(lutbl[nByte : nByte+sha512.Size])
		copy(lutbl[nByte+sha512.Size:nByte+2*sha512.Size], hash[:])
	}
	x := lutbl[cap(lutbl)-sha512.Size:]
	seqCt := uint32(memReqts / sha512.Size)
	nLookups := seqCt / 2
	for i := uint32(0); i < nLookups; i++ {
		newIdx := binary.LittleEndian.Uint32(x[cap(x)-4:]) % seqCt
		vIdx := newIdx * sha512.Size
		v := lutbl[vIdx : vIdx+sha512.Size]
		for j := 0; j < sha512.Size; j++ {
			x[j] ^= v[j]
		}
		hash := sha512.Sum512(x)
		copy(x, hash[:])
	}
	return x[:kdfOutputBytes]
}
func kdf(passphrase []byte, params *kdfParameters) []byte {
	masterKey := passphrase
	for i := uint32(0); i < params.nIter; i++ {
		masterKey = keyOneIter(masterKey, params.salt[:], params.mem)
	}
	return masterKey
}
func pad(size int, b []byte) []byte {
	if len(b) > size {
		size = len(b)
	}
	p := make([]byte, size)
	copy(p[size-len(b):], b)
	return p
}
func chainedPrivKey(privkey, pubkey, chaincode []byte) ([]byte, error) {
	if len(privkey) != 32 {
		return nil, fmt.Errorf("invalid privkey length %d (must be 32)",
			len(privkey))
	}
	if len(chaincode) != 32 {
		return nil, fmt.Errorf("invalid chaincode length %d (must be 32)",
			len(chaincode))
	}
	switch n := len(pubkey); n {
	case bchec.PubKeyBytesLenUncompressed, bchec.PubKeyBytesLenCompressed:
	default:
		return nil, fmt.Errorf("invalid pubkey length %d", n)
	}
	xorbytes := make([]byte, 32)
	chainMod := chainhash.DoubleHashB(pubkey)
	for i := range xorbytes {
		xorbytes[i] = chainMod[i] ^ chaincode[i]
	}
	chainXor := new(big.Int).SetBytes(xorbytes)
	privint := new(big.Int).SetBytes(privkey)
	t := new(big.Int).Mul(chainXor, privint)
	b := t.Mod(t, bchec.S256().N).Bytes()
	return pad(32, b), nil
}
func chainedPubKey(pubkey, chaincode []byte) ([]byte, error) {
	var compressed bool
	switch n := len(pubkey); n {
	case bchec.PubKeyBytesLenUncompressed:
		compressed = false
	case bchec.PubKeyBytesLenCompressed:
		compressed = true
	default:
		return nil, fmt.Errorf("invalid pubkey length %d", n)
	}
	if len(chaincode) != 32 {
		return nil, fmt.Errorf("invalid chaincode length %d (must be 32)",
			len(chaincode))
	}
	xorbytes := make([]byte, 32)
	chainMod := chainhash.DoubleHashB(pubkey)
	for i := range xorbytes {
		xorbytes[i] = chainMod[i] ^ chaincode[i]
	}
	oldPk, err := bchec.ParsePubKey(pubkey, bchec.S256())
	if err != nil {
		return nil, err
	}
	newX, newY := bchec.S256().ScalarMult(oldPk.X, oldPk.Y, xorbytes)
	newPk := &bchec.PublicKey{
		Curve: bchec.S256(),
		X:     newX,
		Y:     newY,
	}
	if compressed {
		return newPk.SerializeCompressed(), nil
	}
	return newPk.SerializeUncompressed(), nil
}
type version struct {
	major         byte
	minor         byte
	bugfix        byte
	autoincrement byte
}
var _ io.ReaderFrom = &version{}
var _ io.WriterTo = &version{}
type readerFromVersion interface {
	readFromVersion(version, io.Reader) (int64, error)
	io.WriterTo
}
func (v version) String() string {
	str := fmt.Sprintf("%d.%d", v.major, v.minor)
	if v.bugfix != 0x00 || v.autoincrement != 0x00 {
		str += fmt.Sprintf(".%d", v.bugfix)
	}
	if v.autoincrement != 0x00 {
		str += fmt.Sprintf(".%d", v.autoincrement)
	}
	return str
}
func (v version) Uint32() uint32 {
	return uint32(v.major)<<6 | uint32(v.minor)<<4 | uint32(v.bugfix)<<2 | uint32(v.autoincrement)
}
func (v *version) ReadFrom(r io.Reader) (int64, error) {
	var versBytes [4]byte
	n, err := io.ReadFull(r, versBytes[:])
	if err != nil {
		return int64(n), err
	}
	v.major = versBytes[0]
	v.minor = versBytes[1]
	v.bugfix = versBytes[2]
	v.autoincrement = versBytes[3]
	return int64(n), nil
}
func (v *version) WriteTo(w io.Writer) (int64, error) {
	versBytes := []byte{
		v.major,
		v.minor,
		v.bugfix,
		v.autoincrement,
	}
	n, err := w.Write(versBytes)
	return int64(n), err
}
func (v version) LT(v2 version) bool {
	switch {
	case v.major < v2.major:
		return true
	case v.minor < v2.minor:
		return true
	case v.bugfix < v2.bugfix:
		return true
	case v.autoincrement < v2.autoincrement:
		return true
	default:
		return false
	}
}
func (v version) EQ(v2 version) bool {
	switch {
	case v.major != v2.major:
		return false
	case v.minor != v2.minor:
		return false
	case v.bugfix != v2.bugfix:
		return false
	case v.autoincrement != v2.autoincrement:
		return false
	default:
		return true
	}
}
func (v version) GT(v2 version) bool {
	switch {
	case v.major > v2.major:
		return true
	case v.minor > v2.minor:
		return true
	case v.bugfix > v2.bugfix:
		return true
	case v.autoincrement > v2.autoincrement:
		return true
	default:
		return false
	}
}
var (
	VersArmory = version{1, 35, 0, 0}
	Vers20LastBlocks = version{1, 36, 0, 0}
	VersUnsetNeedsPrivkeyFlag = version{1, 36, 1, 0}
	VersCurrent = VersUnsetNeedsPrivkeyFlag
)
type varEntries struct {
	store   *Store
	entries []io.WriterTo
}
func (v *varEntries) WriteTo(w io.Writer) (n int64, err error) {
	ss := v.entries
	var written int64
	for _, s := range ss {
		var err error
		if written, err = s.WriteTo(w); err != nil {
			return n + written, err
		}
		n += written
	}
	return n, nil
}
func (v *varEntries) ReadFrom(r io.Reader) (n int64, err error) {
	var read int64
	v.entries = nil
	wts := v.entries
	for {
		var header entryHeader
		if read, err = binaryRead(r, binary.LittleEndian, &header); err != nil {
			if err == io.EOF {
				return n + read, nil
			}
			return n + read, err
		}
		n += read
		var wt io.WriterTo
		switch header {
		case addrHeader:
			var entry addrEntry
			entry.addr.store = v.store
			if read, err = entry.ReadFrom(r); err != nil {
				return n + read, err
			}
			n += read
			wt = &entry
		case scriptHeader:
			var entry scriptEntry
			entry.script.store = v.store
			if read, err = entry.ReadFrom(r); err != nil {
				return n + read, err
			}
			n += read
			wt = &entry
		default:
			return n, fmt.Errorf("unknown entry header: %d", uint8(header))
		}
		if wt != nil {
			wts = append(wts, wt)
			v.entries = wts
		}
	}
}
type netParams chaincfg.Params
func (net *netParams) ReadFrom(r io.Reader) (int64, error) {
	var buf [4]byte
	uint32Bytes := buf[:4]
	n, err := io.ReadFull(r, uint32Bytes)
	n64 := int64(n)
	if err != nil {
		return n64, err
	}
	switch wire.BitcoinNet(binary.LittleEndian.Uint32(uint32Bytes)) {
	case wire.MainNet:
		*net = (netParams)(chaincfg.MainNetParams)
	case wire.TestNet3:
		*net = (netParams)(chaincfg.TestNet3Params)
	case wire.SimNet:
		*net = (netParams)(chaincfg.SimNetParams)
	default:
		return n64, errors.New("unknown network")
	}
	return n64, nil
}
func (net *netParams) WriteTo(w io.Writer) (int64, error) {
	var buf [4]byte
	uint32Bytes := buf[:4]
	binary.LittleEndian.PutUint32(uint32Bytes, uint32(net.Net))
	n, err := w.Write(uint32Bytes)
	n64 := int64(n)
	return n64, err
}
type addressKey string
type transactionHashKey string
type comment []byte
func getAddressKey(addr bchutil.Address) addressKey {
	return addressKey(addr.ScriptAddress())
}
type Store struct {
	dirty bool
	path  string
	dir   string
	file  string
	mtx          sync.RWMutex
	vers         version
	net          *netParams
	flags        walletFlags
	createDate   int64
	name         [32]byte
	desc         [256]byte
	highestUsed  int64
	kdfParams    kdfParameters
	keyGenerator bchAddress
	recent recentBlocks
	addrMap map[addressKey]walletAddress
	passphrase       []byte
	secret           []byte
	chainIdxMap      map[int64]bchutil.Address
	importedAddrs    []walletAddress
	lastChainIdx     int64
	missingKeysStart int64
}
func New(dir string, desc string, passphrase []byte, net *chaincfg.Params,
	createdAt *BlockStamp) (*Store, error) {
	if len(desc) > 256 {
		return nil, errors.New("desc exceeds 256 byte maximum size")
	}
	rootkey := make([]byte, 32)
	if _, err := rand.Read(rootkey); err != nil {
		return nil, err
	}
	chaincode := make([]byte, 32)
	if _, err := rand.Read(chaincode); err != nil {
		return nil, err
	}
	kdfp, err := computeKdfParameters(defaultKdfComputeTime, defaultKdfMaxMem)
	if err != nil {
		return nil, err
	}
	aeskey := kdf(passphrase, kdfp)
	s := &Store{
		path: filepath.Join(dir, Filename),
		dir:  dir,
		file: Filename,
		vers: VersCurrent,
		net:  (*netParams)(net),
		flags: walletFlags{
			useEncryption: true,
			watchingOnly:  false,
		},
		createDate:  time.Now().Unix(),
		highestUsed: rootKeyChainIdx,
		kdfParams:   *kdfp,
		recent: recentBlocks{
			lastHeight: createdAt.Height,
			hashes: []*chainhash.Hash{
				createdAt.Hash,
			},
		},
		addrMap:          make(map[addressKey]walletAddress),
		chainIdxMap:      make(map[int64]bchutil.Address),
		lastChainIdx:     rootKeyChainIdx,
		missingKeysStart: rootKeyChainIdx,
		secret:           aeskey,
	}
	copy(s.desc[:], []byte(desc))
	root, err := newRootBchAddress(s, rootkey, nil, chaincode,
		createdAt)
	if err != nil {
		return nil, err
	}
	if err := root.verifyKeypairs(); err != nil {
		return nil, err
	}
	if err := root.encrypt(aeskey); err != nil {
		return nil, err
	}
	s.keyGenerator = *root
	rootAddr := s.keyGenerator.Address()
	s.addrMap[getAddressKey(rootAddr)] = &s.keyGenerator
	s.chainIdxMap[rootKeyChainIdx] = rootAddr
	if err := s.Lock(); err != nil {
		return nil, err
	}
	return s, nil
}
func (s *Store) ReadFrom(r io.Reader) (n int64, err error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	var read int64
	s.net = &netParams{}
	s.addrMap = make(map[addressKey]walletAddress)
	s.chainIdxMap = make(map[int64]bchutil.Address)
	var id [8]byte
	appendedEntries := varEntries{store: s}
	s.keyGenerator.store = s
	datas := []interface{}{
		&id,
		&s.vers,
		s.net,
		&s.flags,
		make([]byte, 6),  
		&s.createDate,
		&s.name,
		&s.desc,
		&s.highestUsed,
		&s.kdfParams,
		make([]byte, 256),
		&s.keyGenerator,
		newUnusedSpace(1024, &s.recent),
		&appendedEntries,
	}
	for _, data := range datas {
		var err error
		switch d := data.(type) {
		case readerFromVersion:
			read, err = d.readFromVersion(s.vers, r)
		case io.ReaderFrom:
			read, err = d.ReadFrom(r)
		default:
			read, err = binaryRead(r, binary.LittleEndian, d)
		}
		n += read
		if err != nil {
			return n, err
		}
	}
	if id != fileID {
		return n, errors.New("unknown file ID")
	}
	rootAddr := s.keyGenerator.Address()
	s.addrMap[getAddressKey(rootAddr)] = &s.keyGenerator
	s.chainIdxMap[rootKeyChainIdx] = rootAddr
	s.lastChainIdx = rootKeyChainIdx
	wts := appendedEntries.entries
	for _, wt := range wts {
		switch e := wt.(type) {
		case *addrEntry:
			addr := e.addr.Address()
			s.addrMap[getAddressKey(addr)] = &e.addr
			if e.addr.Imported() {
				s.importedAddrs = append(s.importedAddrs, &e.addr)
			} else {
				s.chainIdxMap[e.addr.chainIndex] = addr
				if s.lastChainIdx < e.addr.chainIndex {
					s.lastChainIdx = e.addr.chainIndex
				}
			}
			if e.addr.flags.createPrivKeyNextUnlock {
				switch {
				case s.missingKeysStart == rootKeyChainIdx:
					fallthrough
				case e.addr.chainIndex < s.missingKeysStart:
					s.missingKeysStart = e.addr.chainIndex
				}
			}
		case *scriptEntry:
			addr := e.script.Address()
			s.addrMap[getAddressKey(addr)] = &e.script
			s.importedAddrs = append(s.importedAddrs, &e.script)
		default:
			return n, errors.New("unknown appended entry")
		}
	}
	return n, nil
}