func putSStxRecord(ns walletdb.ReadWriteBucket, record *sstxRecord, dbVersion uint32) error {
	bucket := ns.NestedReadWriteBucket(sstxRecordsBucketName)
	serializedSStxRecord, err := serializeSStxRecord(record, dbVersion)
	if err != nil {
		str := fmt.Sprintf("failed to serialize sstxrecord '%s'", record.tx.Hash())
		return stakeStoreError(apperrors.ErrDatabase, str, err)
	}
	err = bucket.Put(record.tx.Hash()[:], serializedSStxRecord)
	if err != nil {
		str := fmt.Sprintf("failed to store sstxrecord '%s'", record.tx.Hash())
		return stakeStoreError(apperrors.ErrDatabase, str, err)
	}
	return nil
}
putSStxRecord inserts a given SStx record to the SStxrecords bucket.
package udb
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"time"
	"github.com/HcashOrg/hcashd/blockchain/stake"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
	"github.com/HcashOrg/hcashd/wire"
	"github.com/HcashOrg/hcashutil"
	"github.com/HcashOrg/hcashwallet/apperrors"
	"github.com/HcashOrg/hcashwallet/walletdb"
)
const (
	int8Size  = 1
	int16Size = 2
	int32Size = 4
	int64Size = 8
	hashSize  = 32
	ssgenRecordSize = 32 + 4 + 32 + 2 + 8
	ssrtxRecordSize = 32 + 4 + 32 + 8
	stakePoolUserTicketSize = 32 + 4 + 1 + 4 + 32
	stakePoolTicketsPrefixSize = 5
	stakePoolInvalidPrefixSize = 5
	scriptHashSize = 20
)
var (
	sstxTicket2PKHPrefix = []byte{0xba, 0x76, 0xa9}
	sstxTicket2SHPrefix = []byte{0xba, 0xa9, 0x14}
	stakePoolTicketsPrefix = []byte("tickt")
	stakePoolInvalidPrefix = []byte("invld")
)
var (
	sstxRecordsBucketName  = []byte("sstxrecords")
	ssgenRecordsBucketName = []byte("ssgenrecords")
	ssrtxRecordsBucketName = []byte("ssrtxrecords")
	stakeStoreCreateDateName = []byte("stakestorecreated")
)
func deserializeSStxRecord(serializedSStxRecord []byte, dbVersion uint32) (*sstxRecord, error) {
	switch {
	case dbVersion < 3:
		record := new(sstxRecord)
		curPos := 0
		msgTxLen := int(binary.LittleEndian.Uint64(
			serializedSStxRecord[curPos : curPos+int64Size]))
		curPos += int64Size
		curPos += int32Size
		record.voteBitsSet = false
		voteBitsLen := int(serializedSStxRecord[curPos])
		if voteBitsLen != 0 {
			record.voteBitsSet = true
		}
		curPos += int8Size
		record.voteBits = binary.LittleEndian.Uint16(
			serializedSStxRecord[curPos : curPos+int16Size])
		curPos += int16Size
		if voteBitsLen != 0 {
			record.voteBitsExt = make([]byte, voteBitsLen-int16Size)
			copy(record.voteBitsExt, serializedSStxRecord[curPos:curPos+voteBitsLen-int16Size])
		}
		curPos += stake.MaxSingleBytePushLength - int16Size
		buf := bytes.NewBuffer(serializedSStxRecord[curPos : curPos+msgTxLen])
		curPos += msgTxLen
		msgTx := new(wire.MsgTx)
		err := msgTx.Deserialize(buf)
		if err != nil {
			if err == io.EOF {
				err = io.ErrUnexpectedEOF
			}
			return nil, err
		}
		tx := hcashutil.NewTx(msgTx)
		tx.SetIndex(hcashutil.TxIndexUnknown)
		tx.SetTree(wire.TxTreeStake)
		record.tx = tx
		received := int64(binary.LittleEndian.Uint64(
			serializedSStxRecord[curPos : curPos+int64Size]))
		record.ts = time.Unix(received, 0)
		return record, nil
	case dbVersion >= 3:
		serializedSStxRecord = serializedSStxRecord[4:]
		var tx wire.MsgTx
		err := tx.Deserialize(bytes.NewReader(serializedSStxRecord))
		if err != nil {
			return nil, err
		}
		unixTime := int64(binary.LittleEndian.Uint64(serializedSStxRecord[tx.SerializeSize():]))
		return &sstxRecord{tx: hcashutil.NewTx(&tx), ts: time.Unix(unixTime, 0)}, nil
	default:
		panic("unreachable")
	}
}
func deserializeSStxTicketHash160(serializedSStxRecord []byte, dbVersion uint32) (hash160 []byte, p2sh bool, err error) {
	var pkscriptLocOffset int
	var txOffset int
	switch {
	case dbVersion < 3:
		pkscriptLocOffset = 8  
		txOffset = 8 + 4 + 1 + stake.MaxSingleBytePushLength
	case dbVersion >= 3:
		pkscriptLocOffset = 0
		txOffset = 4
	}
	pkscriptLoc := int(binary.LittleEndian.Uint32(serializedSStxRecord[pkscriptLocOffset:])) + txOffset
	prefixBytes := serializedSStxRecord[pkscriptLoc : pkscriptLoc+3]
	scriptHash := make([]byte, 20)
	p2sh = false
	switch {
	case bytes.Equal(prefixBytes, sstxTicket2PKHPrefix):
		scrHashLoc := pkscriptLoc + 4
		if scrHashLoc+20 >= len(serializedSStxRecord) {
			return nil, false, stakeStoreError(apperrors.ErrDatabase,
				"bad serialized sstx record size for pubkey hash", nil)
		}
		copy(scriptHash, serializedSStxRecord[scrHashLoc:scrHashLoc+20])
	case bytes.Equal(prefixBytes, sstxTicket2SHPrefix):
		scrHashLoc := pkscriptLoc + 3
		if scrHashLoc+20 >= len(serializedSStxRecord) {
			return nil, false, stakeStoreError(apperrors.ErrDatabase,
				"bad serialized sstx record size for script hash", nil)
		}
		copy(scriptHash, serializedSStxRecord[scrHashLoc:scrHashLoc+20])
		p2sh = true
	}
	return scriptHash, p2sh, nil
}
func serializeSStxRecord(record *sstxRecord, dbVersion uint32) ([]byte, error) {
	switch {
	case dbVersion < 3:
		msgTx := record.tx.MsgTx()
		msgTxSize := int64(msgTx.SerializeSize())
		size := 0
		size += int64Size
		size += int32Size
		size += int8Size
		size += stake.MaxSingleBytePushLength
		size += int(msgTxSize)
		size += int64Size
		buf := make([]byte, size)
		curPos := 0
		binary.LittleEndian.PutUint64(buf[curPos:curPos+int64Size], uint64(msgTxSize))
		curPos += int64Size
		pkScrLoc := msgTx.PkScriptLocs()
		binary.LittleEndian.PutUint32(buf[curPos:curPos+int32Size], uint32(pkScrLoc[0]))
		curPos += int32Size
		buf[curPos] = byte(int16Size + len(record.voteBitsExt))
		curPos += int8Size
		binary.LittleEndian.PutUint16(buf[curPos:curPos+int16Size], record.voteBits)
		curPos += int16Size
		copy(buf[curPos:], record.voteBitsExt)
		curPos += stake.MaxSingleBytePushLength - 2
		var b bytes.Buffer
		b.Grow(msgTx.SerializeSize())
		err := msgTx.Serialize(&b)
		if err != nil {
			return buf, err
		}
		copy(buf[curPos:curPos+int(msgTxSize)], b.Bytes())
		curPos += int(msgTxSize)
		binary.LittleEndian.PutUint64(buf[curPos:curPos+int64Size], uint64(record.ts.Unix()))
		return buf, nil
	case dbVersion >= 3:
		tx := record.tx.MsgTx()
		txSize := tx.SerializeSize()
		buf := make([]byte, 4+txSize+8)  
		pkScrLoc := tx.PkScriptLocs()
		binary.LittleEndian.PutUint32(buf, uint32(pkScrLoc[0]))
		err := tx.Serialize(bytes.NewBuffer(buf[4:4]))
		if err != nil {
			return nil, err
		}
		binary.LittleEndian.PutUint64(buf[4+txSize:], uint64(record.ts.Unix()))
		return buf, nil
	default:
		panic("unreachable")
	}
}
func deserializeSSGenRecord(serializedSSGenRecord []byte) (*ssgenRecord, error) {
	if len(serializedSSGenRecord)%ssgenRecordSize != 0 {
		str := "serialized SSGen record was wrong size"
		return nil, stakeStoreError(apperrors.ErrDatabase, str, nil)
	}
	record := new(ssgenRecord)
	curPos := 0
	copy(record.blockHash[:], serializedSSGenRecord[curPos:curPos+hashSize])
	curPos += hashSize
	record.blockHeight = binary.LittleEndian.Uint32(
		serializedSSGenRecord[curPos : curPos+int32Size])
	curPos += int32Size
	copy(record.txHash[:], serializedSSGenRecord[curPos:curPos+hashSize])
	curPos += hashSize
	record.voteBits = binary.LittleEndian.Uint16(
		serializedSSGenRecord[curPos : curPos+int16Size])
	curPos += int16Size
	record.ts = time.Unix(
		int64(binary.LittleEndian.Uint64(
			serializedSSGenRecord[curPos:curPos+int64Size])),
		0)
	return record, nil
}
func deserializeSSGenRecords(serializedSSGenRecords []byte) ([]*ssgenRecord, error) {
	if len(serializedSSGenRecords)%ssgenRecordSize != 0 {
		err := io.ErrUnexpectedEOF
		return nil, err
	}
	numRecords := len(serializedSSGenRecords) / ssgenRecordSize
	records := make([]*ssgenRecord, numRecords)
	for i := 0; i < numRecords; i++ {
		record, err := deserializeSSGenRecord(
			serializedSSGenRecords[i*ssgenRecordSize : (i+1)*ssgenRecordSize])
		if err != nil {
			str := "problem serializing ssgen record"
			return nil, stakeStoreError(apperrors.ErrDatabase, str, err)
		}
		records[i] = record
	}
	return records, nil
}
func serializeSSGenRecord(record *ssgenRecord) []byte {
	buf := make([]byte, ssgenRecordSize)
	curPos := 0
	copy(buf[curPos:curPos+hashSize], record.blockHash[:])
	curPos += hashSize
	binary.LittleEndian.PutUint32(buf[curPos:curPos+int32Size], record.blockHeight)
	curPos += int32Size
	copy(buf[curPos:curPos+hashSize], record.txHash[:])
	curPos += hashSize
	binary.LittleEndian.PutUint16(buf[curPos:curPos+int16Size], record.voteBits)
	curPos += int16Size
	binary.LittleEndian.PutUint64(buf[curPos:curPos+int64Size], uint64(record.ts.Unix()))
	return buf
}
func serializeSSGenRecords(records []*ssgenRecord) []byte {
	numRecords := len(records)
	buf := make([]byte, numRecords*ssgenRecordSize)
	for i := 0; i < numRecords; i++ {
		recordBytes := serializeSSGenRecord(records[i])
		copy(buf[i*ssgenRecordSize:(i+1)*ssgenRecordSize],
			recordBytes)
	}
	return buf
}
func deserializeSSRtxRecord(serializedSSRtxRecord []byte) (*ssrtxRecord,
	error) {
	if len(serializedSSRtxRecord)%ssrtxRecordSize != 0 {
		str := "serialized SSRtx record was wrong size"
		return nil, stakeStoreError(apperrors.ErrDatabase, str, nil)
	}
	record := new(ssrtxRecord)
	curPos := 0
	copy(record.blockHash[:], serializedSSRtxRecord[curPos:curPos+hashSize])
	curPos += hashSize
	record.blockHeight = binary.LittleEndian.Uint32(
		serializedSSRtxRecord[curPos : curPos+int32Size])
	curPos += int32Size
	copy(record.txHash[:], serializedSSRtxRecord[curPos:curPos+hashSize])
	curPos += hashSize
	record.ts = time.Unix(
		int64(binary.LittleEndian.Uint64(
			serializedSSRtxRecord[curPos:curPos+int64Size])),
		0)
	return record, nil
}
func deserializeSSRtxRecords(serializedSSRtxRecords []byte) ([]*ssrtxRecord,
	error) {
	if len(serializedSSRtxRecords)%ssrtxRecordSize != 0 {
		err := io.ErrUnexpectedEOF
		return nil, err
	}
	numRecords := len(serializedSSRtxRecords) / ssrtxRecordSize
	records := make([]*ssrtxRecord, numRecords)
	for i := 0; i < numRecords; i++ {
		record, err := deserializeSSRtxRecord(
			serializedSSRtxRecords[i*ssrtxRecordSize : (i+1)*ssrtxRecordSize])
		if err != nil {
			str := "problem serializing ssrtx record"
			return nil, stakeStoreError(apperrors.ErrDatabase, str, err)
		}
		records[i] = record
	}
	return records, nil
}
func serializeSSRtxRecord(record *ssrtxRecord) []byte {
	buf := make([]byte, ssrtxRecordSize)
	curPos := 0
	copy(buf[curPos:curPos+hashSize], record.blockHash[:])
	curPos += hashSize
	binary.LittleEndian.PutUint32(buf[curPos:curPos+int32Size], record.blockHeight)
	curPos += int32Size
	copy(buf[curPos:curPos+hashSize], record.txHash[:])
	curPos += hashSize
	binary.LittleEndian.PutUint64(buf[curPos:curPos+int64Size], uint64(record.ts.Unix()))
	return buf
}
func serializeSSRtxRecords(records []*ssrtxRecord) []byte {
	numRecords := len(records)
	buf := make([]byte, numRecords*ssrtxRecordSize)
	for i := 0; i < numRecords; i++ {
		recordBytes := serializeSSRtxRecord(records[i])
		copy(buf[i*ssrtxRecordSize:(i+1)*ssrtxRecordSize],
			recordBytes)
	}
	return buf
}
func stakeStoreExists(ns walletdb.ReadBucket) bool {
	mainBucket := ns.NestedReadBucket(mainBucketName)
	return mainBucket != nil
}
func fetchSStxRecord(ns walletdb.ReadBucket, hash *chainhash.Hash, dbVersion uint32) (*sstxRecord, error) {
	bucket := ns.NestedReadBucket(sstxRecordsBucketName)
	key := hash[:]
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
		return nil, stakeStoreError(apperrors.ErrSStxNotFound, str, nil)
	}
	return deserializeSStxRecord(val, dbVersion)
}
func fetchSStxRecordSStxTicketHash160(ns walletdb.ReadBucket, hash *chainhash.Hash,
	dbVersion uint32) (hash160 []byte, p2sh bool, err error) {
	bucket := ns.NestedReadBucket(sstxRecordsBucketName)
	key := hash[:]
	val := bucket.Get(key)
	if val == nil {
		str := fmt.Sprintf("missing sstx record for hash '%s'", hash.String())
		return nil, false, stakeStoreError(apperrors.ErrSStxNotFound, str, nil)
	}
	return deserializeSStxTicketHash160(val, dbVersion)
}