func (s *StakeStore) StoreVoteInfo(dbtx walletdb.ReadWriteTx, ticketHash, voteHash, blockHash *chainhash.Hash,
	blockHeight int32, voteBits stake.VoteBits) error {
	ns := dbtx.ReadWriteBucket(wstakemgrBucketKey)
	return insertSSGen(ns, blockHash, int64(blockHeight), voteHash, voteBits.Bits,
		ticketHash)
}
StoreVoteInfo records information about a vote transaction.

TODO: Much of this is redundant and we probably don't want to track it here
anyways.  Would be better to handle this in the transaction manager.
package udb
import (
	"bytes"
	"fmt"
	"sync"
	"time"
	"github.com/HcashOrg/hcashd/blockchain/stake"
	"github.com/HcashOrg/hcashd/chaincfg"
	"github.com/HcashOrg/hcashd/chaincfg/chainec"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
	"github.com/HcashOrg/hcashd/wire"
	"github.com/HcashOrg/hcashutil"
	"github.com/HcashOrg/hcashwallet/apperrors"
	walletchain "github.com/HcashOrg/hcashwallet/chain"
	"github.com/HcashOrg/hcashwallet/walletdb"
)
func stakeStoreError(code apperrors.Code, str string, err error) error {
	return apperrors.E{ErrorCode: code, Description: str, Err: err}
}
type sstxRecord struct {
	tx          *hcashutil.Tx
	ts          time.Time
	voteBitsSet bool    
	voteBits    uint16  
	voteBitsExt []byte  
}
type ssgenRecord struct {
	blockHash   chainhash.Hash
	blockHeight uint32
	txHash      chainhash.Hash
	voteBits    uint16
	ts          time.Time
}
type ssrtxRecord struct {
	blockHash   chainhash.Hash
	blockHeight uint32
	txHash      chainhash.Hash
	ts          time.Time
}
type TicketStatus uint8
const (
	TSImmatureOrLive = iota
	TSVoted
	TSMissed
)
type PoolTicket struct {
	Ticket       chainhash.Hash
	HeightTicket uint32
	Status       TicketStatus
	HeightSpent  uint32
	SpentBy      chainhash.Hash
}
type StakePoolUser struct {
	Tickets        []*PoolTicket
	InvalidTickets []*chainhash.Hash
}
type StakeStore struct {
	Params   *chaincfg.Params
	Manager  *Manager
	chainSvr *walletchain.RPCClient
	ownedSStxs map[chainhash.Hash]struct{}
	mtx        sync.RWMutex  
}
func (s *StakeStore) checkHashInStore(hash *chainhash.Hash) bool {
	_, exists := s.ownedSStxs[*hash]
	return exists
}
func (s *StakeStore) OwnTicket(hash *chainhash.Hash) bool {
	s.mtx.RLock()
	owned := s.checkHashInStore(hash)
	s.mtx.RUnlock()
	return owned
}
func (s *StakeStore) addHashToStore(hash *chainhash.Hash) {
	s.ownedSStxs[*hash] = struct{}{}
}
func (s *StakeStore) insertSStx(ns walletdb.ReadWriteBucket, sstx *hcashutil.Tx) error {
	exists := s.checkHashInStore(sstx.Hash())
	if exists {
		log.Tracef("Attempted to insert SStx %v into the stake store, "+
			"but the SStx already exists.", sstx.Hash())
		return nil
	}
	record := &sstxRecord{
		tx: sstx,
		ts: time.Now(),
	}
	err := putSStxRecord(ns, record, DBVersion)
	if err != nil {
		return err
	}
	s.addHashToStore(sstx.Hash())
	return nil
}
func (s *StakeStore) InsertSStx(ns walletdb.ReadWriteBucket, sstx *hcashutil.Tx) error {
	s.mtx.Lock()
	err := s.insertSStx(ns, sstx)
	s.mtx.Unlock()
	return err
}
func (s *StakeStore) dumpSStxHashes() []chainhash.Hash {
	ownedSStxs := make([]chainhash.Hash, len(s.ownedSStxs))
	itr := 0
	for hash := range s.ownedSStxs {
		ownedSStxs[itr] = hash
		itr++
	}
	return ownedSStxs
}
func (s *StakeStore) DumpSStxHashes() []chainhash.Hash {
	defer s.mtx.RUnlock()
	s.mtx.RLock()
	return s.dumpSStxHashes()
}
func (s *StakeStore) dumpSStxHashesForAddress(ns walletdb.ReadBucket, addr hcashutil.Address) ([]chainhash.Hash, error) {
	hash160 := addr.ScriptAddress()
	if len(hash160) != 20 {
		str := "stake store is closed"
		return nil, stakeStoreError(apperrors.ErrInput, str, nil)
	}
	_, addrIsP2SH := addr.(*hcashutil.AddressScriptHash)
	allTickets := s.dumpSStxHashes()
	var ticketsForAddr []chainhash.Hash
	for _, h := range allTickets {
		thisHash160, p2sh, err := fetchSStxRecordSStxTicketHash160(ns, &h, DBVersion)
		if err != nil {
			str := "failure getting ticket 0th out script hashes from db"
			return nil, stakeStoreError(apperrors.ErrDatabase, str, err)
		}
		if addrIsP2SH != p2sh {
			continue
		}
		if bytes.Equal(hash160, thisHash160) {
			ticketsForAddr = append(ticketsForAddr, h)
		}
	}
	return ticketsForAddr, nil
}
func (s *StakeStore) DumpSStxHashesForAddress(ns walletdb.ReadBucket, addr hcashutil.Address) ([]chainhash.Hash, error) {
	defer s.mtx.RUnlock()
	s.mtx.RLock()
	return s.dumpSStxHashesForAddress(ns, addr)
}
func (s *StakeStore) sstxAddress(ns walletdb.ReadBucket, hash *chainhash.Hash) (hcashutil.Address, error) {
	thisHash160, p2sh, err := fetchSStxRecordSStxTicketHash160(ns, hash, DBVersion)
	if err != nil {
		str := "failure getting ticket 0th out script hashes from db"
		return nil, stakeStoreError(apperrors.ErrDatabase, str, err)
	}
	var addr hcashutil.Address
	if p2sh {
		addr, err = hcashutil.NewAddressScriptHashFromHash(thisHash160, s.Params)
	} else {
		addr, err = hcashutil.NewAddressPubKeyHash(thisHash160, s.Params, chainec.ECTypeSecp256k1)
	}
	if err != nil {
		str := "failure getting ticket 0th out script hashes from db"
		return nil, stakeStoreError(apperrors.ErrDatabase, str, err)
	}
	return addr, nil
}
func (s *StakeStore) SStxAddress(ns walletdb.ReadBucket, hash *chainhash.Hash) (hcashutil.Address, error) {
	return s.sstxAddress(ns, hash)
}
func (s *StakeStore) dumpSSGenHashes(ns walletdb.ReadBucket) ([]chainhash.Hash, error) {
	var voteList []chainhash.Hash
	bucket := ns.NestedReadBucket(ssgenRecordsBucketName)
	err := bucket.ForEach(func(k []byte, v []byte) error {
		recs, errDeser := deserializeSSGenRecords(v)
		if errDeser != nil {
			return errDeser
		}
		for _, rec := range recs {
			voteList = append(voteList, rec.txHash)
		}
		return nil
	})
	return voteList, err
}
func (s *StakeStore) DumpSSGenHashes(ns walletdb.ReadBucket) ([]chainhash.Hash, error) {
	return s.dumpSSGenHashes(ns)
}
func (s *StakeStore) dumpSSRtxTickets(ns walletdb.ReadBucket) ([]chainhash.Hash, error) {
	var ticketList []chainhash.Hash
	bucket := ns.NestedReadBucket(ssrtxRecordsBucketName)
	err := bucket.ForEach(func(k []byte, v []byte) error {
		ticket, errDeser := chainhash.NewHash(k)
		if errDeser != nil {
			return errDeser
		}
		ticketList = append(ticketList, *ticket)
		return nil
	})
	return ticketList, err
}
func (s *StakeStore) DumpSSRtxTickets(ns walletdb.ReadBucket) ([]chainhash.Hash, error) {
	return s.dumpSSRtxTickets(ns)
}
func insertSSGen(ns walletdb.ReadWriteBucket, blockHash *chainhash.Hash, blockHeight int64,
	ssgenHash *chainhash.Hash, voteBits uint16, sstxHash *chainhash.Hash) error {
	if blockHeight <= 0 {
		return fmt.Errorf("invalid SSGen block height")
	}
	record := &ssgenRecord{
		*blockHash,
		uint32(blockHeight),
		*ssgenHash,
		voteBits,
		time.Now(),
	}
	return putSSGenRecord(ns, sstxHash, record)
}
func (s *StakeStore) InsertSSGen(ns walletdb.ReadWriteBucket, blockHash *chainhash.Hash, blockHeight int64, ssgenHash *chainhash.Hash, voteBits uint16, sstxHash *chainhash.Hash) error {
	return insertSSGen(ns, blockHash, blockHeight, ssgenHash, voteBits, sstxHash)
}
func (s *StakeStore) TicketPurchase(dbtx walletdb.ReadTx, hash *chainhash.Hash) (*wire.MsgTx, error) {
	ns := dbtx.ReadBucket(wstakemgrBucketKey)
	ticketRecord, err := fetchSStxRecord(ns, hash, DBVersion)
	if err != nil {
		return nil, err
	}
	return ticketRecord.tx.MsgTx(), nil
}