func (s *Store) removeDoubleSpends(ns walletdb.ReadWriteBucket, rec *TxRecord) error {
	for _, input := range rec.MsgTx.TxIn {
		prevOut := &input.PreviousOutPoint
		prevOutKey := canonicalOutPoint(&prevOut.Hash, prevOut.Index)
		doubleSpendHash := existsRawUnminedInput(ns, prevOutKey)
		if doubleSpendHash != nil {
			var doubleSpend TxRecord
			doubleSpendVal := existsRawUnmined(ns, doubleSpendHash)
			copy(doubleSpend.Hash[:], doubleSpendHash)  
			err := readRawTxRecord(&doubleSpend.Hash, doubleSpendVal,
				&doubleSpend)
			if err != nil {
				return err
			}
			log.Debugf("Removing double spending transaction %v",
				doubleSpend.Hash)
			err = s.RemoveUnconfirmed(ns, &doubleSpend.MsgTx, &doubleSpend.Hash)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
removeDoubleSpends checks for any unmined transactions which would introduce
a double spend if tx was added to the store (either as a confirmed or unmined
transaction).  Each conflicting transaction and all transactions which spend
it are recursively removed.
package udb
import (
	"bytes"
	"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/wire"
)
func (s *Store) InsertMemPoolTx(dbtx walletdb.ReadWriteTx, rec *TxRecord) error {
	ns := dbtx.ReadWriteBucket(wtxmgrBucketKey)
	_, recVal := latestTxRecord(ns, rec.Hash[:])
	if recVal != nil {
		return errors.E(errors.Exist, "transaction already exists mined")
	}
	v := existsRawUnmined(ns, rec.Hash[:])
	if v != nil {
		return nil
	}
	for _, input := range rec.MsgTx.TxIn {
		prevOut := &input.PreviousOutPoint
		k := canonicalOutPoint(&prevOut.Hash, prevOut.Index)
		if v := existsRawUnminedInput(ns, k); v != nil {
			var spenderHash chainhash.Hash
			readRawUnminedInputSpenderHash(v, &spenderHash)
		DoubleSpendVoteCheck:
			switch rec.TxType {
			case stake.TxTypeSSGen, stake.TxTypeSSRtx:
				spenderVal := existsRawUnmined(ns, spenderHash[:])
				spenderTxBytes := extractRawUnminedTx(spenderVal)
				var spenderTx wire.MsgTx
				err := spenderTx.Deserialize(bytes.NewReader(spenderTxBytes))
				if err != nil {
					return errors.E(errors.IO, err)
				}
				if stake.DetermineTxType(&spenderTx, true, false) != stake.TxTypeSSGen {
					break DoubleSpendVoteCheck
				}
				votedBlock, _ := stake.SSGenBlockVotedOn(&spenderTx)
				tipBlock, _ := s.MainChainTip(dbtx)
				if votedBlock == tipBlock {
					err := errors.Errorf("vote or revocation %v double spends unmined "+
						"vote %v on the tip block", &rec.Hash, &spenderHash)
					return errors.E(errors.DoubleSpend, err)
				}
				err = s.RemoveUnconfirmed(ns, &spenderTx, &spenderHash)
				if err != nil {
					return err
				}
				continue
			}
			err := errors.Errorf("%v conflicts with %v by double spending %v", &rec.Hash, &spenderHash, prevOut)
			return errors.E(errors.DoubleSpend, err)
		}
	}
	log.Infof("Inserting unconfirmed transaction %v", &rec.Hash)
	v, err := valueTxRecord(rec)
	if err != nil {
		return err
	}
	err = putRawUnmined(ns, rec.Hash[:], v)
	if err != nil {
		return err
	}
	if rec.Unpublished {
		err = putUnpublished(ns, rec.Hash[:])
		if err != nil {
			return err
		}
	}
	txType := stake.DetermineTxType(&rec.MsgTx, true, false)
	for i, input := range rec.MsgTx.TxIn {
		if i == 0 && txType == stake.TxTypeSSGen {
			continue
		}
		prevOut := &input.PreviousOutPoint
		k := canonicalOutPoint(&prevOut.Hash, prevOut.Index)
		err = putRawUnminedInput(ns, k, rec.Hash[:])
		if err != nil {
			return err
		}
	}
	if txType == stake.TxTypeSStx {
		tk := rec.Hash[:]
		tv := existsRawTicketRecord(ns, tk)
		if tv == nil {
			tv = valueTicketRecord(-1)
			err := putRawTicketRecord(ns, tk, tv)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
func (s *Store) SetPublished(dbtx walletdb.ReadWriteTx, txHash *chainhash.Hash, published bool) error {
	ns := dbtx.ReadWriteBucket(wtxmgrBucketKey)
	if published {
		return deleteUnpublished(ns, txHash[:])
	}
	return putUnpublished(ns, txHash[:])
}