func deserializeUserTicket(serializedTicket []byte) (*PoolTicket, error) {
	if len(serializedTicket)%stakePoolUserTicketSize != 0 {
		return nil, errors.E(errors.IO, "invalid pool ticket record size")
	}
	record := new(PoolTicket)
	curPos := 0
	copy(record.Ticket[:], serializedTicket[curPos:curPos+hashSize])
	curPos += hashSize
	record.HeightTicket = binary.LittleEndian.Uint32(
		serializedTicket[curPos : curPos+int32Size])
	curPos += int32Size
	record.Status = TicketStatus(serializedTicket[curPos])
	curPos += int8Size
	record.HeightSpent = binary.LittleEndian.Uint32(
		serializedTicket[curPos : curPos+int32Size])
	curPos += int32Size
	copy(record.SpentBy[:], serializedTicket[curPos:curPos+hashSize])
	return record, nil
}
deserializeUserTicket deserializes the passed serialized user
ticket information.
package udb
import (
	"bytes"
	"encoding/binary"
	"io"
	"time"
	"decred.org/dcrwallet/v2/errors"
	"decred.org/dcrwallet/v2/wallet/walletdb"
	"github.com/decred/dcrd/blockchain/stake/v4"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/dcrutil/v4"
	"github.com/decred/dcrd/wire"
)
const (
	int8Size  = 1
	int16Size = 2
	int32Size = 4
	int64Size = 8
	hashSize  = 32
	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 errors.Is(err, io.EOF) {
				err = io.ErrUnexpectedEOF
			}
			return nil, err
		}
		tx := dcrutil.NewTx(msgTx)
		tx.SetIndex(dcrutil.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: dcrutil.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, errors.E(errors.IO, "bad sstx record size")
		}
		copy(scriptHash, serializedSStxRecord[scrHashLoc:scrHashLoc+20])
	case bytes.Equal(prefixBytes, sstxTicket2SHPrefix):
		scrHashLoc := pkscriptLoc + 3
		if scrHashLoc+20 >= len(serializedSStxRecord) {
			return nil, false, errors.E(errors.IO, "bad sstx record size")
		}
		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 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 {
		return nil, errors.E(errors.NotExist, errors.Errorf("no ticket purchase %v", hash))
	}
	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 {
		return nil, false, errors.E(errors.NotExist, errors.Errorf("no ticket purchase %v", hash))
	}
	return deserializeSStxTicketHash160(val, dbVersion)
}
func putSStxRecord(ns walletdb.ReadWriteBucket, record *sstxRecord, dbVersion uint32) error {
	bucket := ns.NestedReadWriteBucket(sstxRecordsBucketName)
	serializedSStxRecord, err := serializeSStxRecord(record, dbVersion)
	if err != nil {
		return errors.E(errors.IO, err)
	}
	err = bucket.Put(record.tx.Hash()[:], serializedSStxRecord)
	if err != nil {
		return errors.E(errors.IO, err)
	}
	return nil
}