func (w *Wallet) CFilterV2(ctx context.Context, blockHash *chainhash.Hash) ([gcs2.KeySize]byte, *gcs2.FilterV2, error) {
	var f *gcs2.FilterV2
	var key [gcs2.KeySize]byte
	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		var err error
		key, f, err = w.txStore.CFilterV2(dbtx, blockHash)
		return err
	})
	return key, f, err
}
CFilterV2 returns the version 2 regular compact filter for a block along
with the key required to query it for matches against committed scripts.
package wallet
import (
	"bytes"
	"context"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"decred.org/dcrwallet/v2/deployments"
	"decred.org/dcrwallet/v2/errors"
	"decred.org/dcrwallet/v2/internal/compat"
	"decred.org/dcrwallet/v2/rpc/client/dcrd"
	"decred.org/dcrwallet/v2/rpc/jsonrpc/types"
	"decred.org/dcrwallet/v2/validate"
	"decred.org/dcrwallet/v2/wallet/txrules"
	"decred.org/dcrwallet/v2/wallet/txsizes"
	"decred.org/dcrwallet/v2/wallet/udb"
	"decred.org/dcrwallet/v2/wallet/walletdb"
	"github.com/decred/dcrd/blockchain/stake/v4"
	blockchain "github.com/decred/dcrd/blockchain/standalone/v2"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrd/crypto/blake256"
	"github.com/decred/dcrd/dcrec"
	"github.com/decred/dcrd/dcrec/secp256k1/v4"
	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
	"github.com/decred/dcrd/dcrutil/v4"
	gcs2 "github.com/decred/dcrd/gcs/v3"
	"github.com/decred/dcrd/hdkeychain/v3"
	dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
	"github.com/decred/dcrd/txscript/v4"
	"github.com/decred/dcrd/txscript/v4/sign"
	"github.com/decred/dcrd/txscript/v4/stdaddr"
	"github.com/decred/dcrd/wire"
	"golang.org/x/sync/errgroup"
)
const (
	InsecurePubPassphrase = "public"
)
var (
	SimulationPassphrase = []byte("password")
)
var (
	waddrmgrNamespaceKey  = []byte("waddrmgr")
	wtxmgrNamespaceKey    = []byte("wtxmgr")
	wstakemgrNamespaceKey = []byte("wstakemgr")
)
type StakeDifficultyInfo struct {
	BlockHash       *chainhash.Hash
	BlockHeight     int64
	StakeDifficulty int64
}
type outpoint struct {
	hash  chainhash.Hash
	index uint32
}
type Wallet struct {
	disapprovePercent uint32
	db       walletdb.DB
	manager  *udb.Manager
	txStore  *udb.Store
	stakeMgr *udb.StakeStore
	stakeSettingsLock  sync.Mutex
	defaultVoteBits    stake.VoteBits
	votingEnabled      bool
	poolAddress        stdaddr.StakeAddress
	poolFees           float64
	manualTickets      bool
	stakePoolEnabled   bool
	stakePoolColdAddrs map[string]struct{}
	subsidyCache       *blockchain.SubsidyCache
	tspends            map[chainhash.Hash]wire.MsgTx
	tspendPolicy       map[chainhash.Hash]stake.TreasuryVoteT
	tspendKeyPolicy    map[string]stake.TreasuryVoteT  
	gapLimit        uint32
	accountGapLimit int
	initialHeight int32
	networkBackend   NetworkBackend
	networkBackendMu sync.Mutex
	lockedOutpoints  map[outpoint]struct{}
	lockedOutpointMu sync.Mutex
	relayFee                dcrutil.Amount
	relayFeeMu              sync.Mutex
	allowHighFees           bool
	disableCoinTypeUpgrades bool
	recentlyPublished       map[chainhash.Hash]struct{}
	recentlyPublishedMu     sync.Mutex
	addressReuse     bool
	ticketAddress    stdaddr.StakeAddress
	addressBuffers   map[uint32]*bip0044AccountData
	addressBuffersMu sync.Mutex
	passphraseUsedMu        sync.RWMutex
	passphraseTimeoutMu     sync.Mutex
	passphraseTimeoutCancel chan struct{}
	NtfnServer *NotificationServer
	chainParams *chaincfg.Params
}
type Config struct {
	DB DB
	PubPassphrase []byte
	VotingEnabled bool
	AddressReuse  bool
	VotingAddress stdaddr.StakeAddress
	PoolAddress   stdaddr.StakeAddress
	PoolFees      float64
	GapLimit                uint32
	AccountGapLimit         int
	DisableCoinTypeUpgrades bool
	StakePoolColdExtKey string
	ManualTickets       bool
	AllowHighFees       bool
	RelayFee            dcrutil.Amount
	Params              *chaincfg.Params
}
func (w *Wallet) DisapprovePercent() uint32 {
	return atomic.LoadUint32(&w.disapprovePercent)
}
func (w *Wallet) SetDisapprovePercent(percent uint32) {
	atomic.StoreUint32(&w.disapprovePercent, percent)
}
func (w *Wallet) FetchOutput(ctx context.Context, outPoint *wire.OutPoint) (*wire.TxOut, error) {
	const op errors.Op = "wallet.FetchOutput"
	var out *wire.TxOut
	err := walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
		txmgrNs := tx.ReadBucket(wtxmgrNamespaceKey)
		outTx, err := w.txStore.Tx(txmgrNs, &outPoint.Hash)
		if err != nil {
			return err
		}
		out = outTx.TxOut[outPoint.Index]
		return err
	})
	if err != nil {
		return nil, errors.E(op, err)
	}
	return out, nil
}
func (w *Wallet) VotingEnabled() bool {
	w.stakeSettingsLock.Lock()
	enabled := w.votingEnabled
	w.stakeSettingsLock.Unlock()
	return enabled
}
func (w *Wallet) AddTSpend(tx wire.MsgTx) error {
	hash := tx.TxHash()
	log.Infof("TSpend arrived: %v", hash)
	w.stakeSettingsLock.Lock()
	defer w.stakeSettingsLock.Unlock()
	if _, ok := w.tspends[hash]; ok {
		return fmt.Errorf("tspend already cached")
	}
	w.tspends[hash] = tx
	return nil
}
func (w *Wallet) GetAllTSpends(ctx context.Context) []*wire.MsgTx {
	_, height := w.MainChainTip(ctx)
	w.stakeSettingsLock.Lock()
	defer w.stakeSettingsLock.Unlock()
	txs := make([]*wire.MsgTx, 0, len(w.tspends))
	for k := range w.tspends {
		v := w.tspends[k]
		if uint32(height) > v.Expiry {
			delete(w.tspends, k)
			continue
		}
		txs = append(txs, &v)
	}
	return txs
}
func voteVersion(params *chaincfg.Params) uint32 {
	switch params.Net {
	case wire.MainNet:
		return 9
	case 0x48e7a065:  
		return 6
	case wire.TestNet3:
		return 10
	case wire.SimNet:
		return 10
	default:
		return 1
	}
}
func CurrentAgendas(params *chaincfg.Params) (version uint32, agendas []chaincfg.ConsensusDeployment) {
	version = voteVersion(params)
	if params.Deployments == nil {
		return version, nil
	}
	return version, params.Deployments[version]
}
func (w *Wallet) readDBVoteBits(dbtx walletdb.ReadTx) stake.VoteBits {
	version, deployments := CurrentAgendas(w.chainParams)
	vb := stake.VoteBits{
		Bits:         0x0001,
		ExtendedBits: make([]byte, 4),
	}
	binary.LittleEndian.PutUint32(vb.ExtendedBits, version)
	if len(deployments) == 0 {
		return vb
	}
	for i := range deployments {
		d := &deployments[i]
		choiceID := udb.DefaultAgendaPreference(dbtx, version, d.Vote.Id)
		if choiceID == "" {
			continue
		}
		for j := range d.Vote.Choices {
			choice := &d.Vote.Choices[j]
			if choiceID == choice.Id {
				vb.Bits |= choice.Bits
				break
			}
		}
	}
	return vb
}
func (w *Wallet) readDBTicketVoteBits(dbtx walletdb.ReadTx, ticketHash *chainhash.Hash) (stake.VoteBits, bool) {
	version, deployments := CurrentAgendas(w.chainParams)
	tvb := stake.VoteBits{
		Bits:         0x0001,
		ExtendedBits: make([]byte, 4),
	}
	binary.LittleEndian.PutUint32(tvb.ExtendedBits, version)
	if len(deployments) == 0 {
		return tvb, false
	}
	var hasSavedPrefs bool
	for i := range deployments {
		d := &deployments[i]
		choiceID := udb.TicketAgendaPreference(dbtx, ticketHash, version, d.Vote.Id)
		if choiceID == "" {
			continue
		}
		hasSavedPrefs = true
		for j := range d.Vote.Choices {
			choice := &d.Vote.Choices[j]
			if choiceID == choice.Id {
				tvb.Bits |= choice.Bits
				break
			}
		}
	}
	return tvb, hasSavedPrefs
}
func (w *Wallet) readDBTreasuryPolicies(dbtx walletdb.ReadTx) (map[chainhash.Hash]stake.TreasuryVoteT, error) {
	return udb.TSpendPolicies(dbtx)
}
func (w *Wallet) readDBTreasuryKeyPolicies(dbtx walletdb.ReadTx) (map[string]stake.TreasuryVoteT, error) {
	return udb.TreasuryKeyPolicies(dbtx)
}
func (w *Wallet) VoteBits() stake.VoteBits {
	w.stakeSettingsLock.Lock()
	vb := w.defaultVoteBits
	w.stakeSettingsLock.Unlock()
	return vb
}
type AgendaChoice struct {
	AgendaID string
	ChoiceID string
}
func (w *Wallet) AgendaChoices(ctx context.Context, ticketHash *chainhash.Hash) (choices []AgendaChoice, voteBits uint16, err error) {
	const op errors.Op = "wallet.AgendaChoices"
	version, deployments := CurrentAgendas(w.chainParams)
	if len(deployments) == 0 {
		return nil, 0, nil
	}
	choices = make([]AgendaChoice, len(deployments))
	for i := range choices {
		choices[i].AgendaID = deployments[i].Vote.Id
		choices[i].ChoiceID = "abstain"
	}
	var ownTicket bool
	var hasSavedPrefs bool
	voteBits = 1
	err = walletdb.View(ctx, w.db, func(tx walletdb.ReadTx) error {
		if ticketHash != nil {
			ownTicket = w.txStore.OwnTicket(tx, ticketHash)
			ownTicket = ownTicket || w.stakeMgr.OwnTicket(ticketHash)
			if !ownTicket {
				return nil
			}
		}
		for i := range deployments {
			agenda := &deployments[i].Vote
			var choice string
			if ticketHash == nil {
				choice = udb.DefaultAgendaPreference(tx, version, agenda.Id)
			} else {
				choice = udb.TicketAgendaPreference(tx, ticketHash, version, agenda.Id)
			}
			if choice == "" {
				continue
			}
			hasSavedPrefs = true
			choices[i].ChoiceID = choice
			for j := range agenda.Choices {
				if agenda.Choices[j].Id == choice {
					voteBits |= agenda.Choices[j].Bits
					break
				}
			}
		}
		return nil
	})
	if err != nil {
		return nil, 0, errors.E(op, err)
	}
	if ticketHash != nil && !ownTicket {
		return nil, 0, errors.E(errors.NotExist, "ticket not found")
	}
	if ticketHash != nil && !hasSavedPrefs {
		return w.AgendaChoices(ctx, nil)
	}
	return choices, voteBits, nil
}
func (w *Wallet) SetAgendaChoices(ctx context.Context, ticketHash *chainhash.Hash, choices ...AgendaChoice) (voteBits uint16, err error) {
	const op errors.Op = "wallet.SetAgendaChoices"
	version, deployments := CurrentAgendas(w.chainParams)
	if len(deployments) == 0 {
		return 0, errors.E("no agendas to set for this network")
	}
	if ticketHash != nil {
		var ownTicket bool
		err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
			ownTicket = w.txStore.OwnTicket(dbtx, ticketHash) || w.stakeMgr.OwnTicket(ticketHash)
			return nil
		})
		if err != nil {
			return 0, errors.E(op, err)
		}
		if !ownTicket {
			return 0, errors.E(errors.NotExist, "ticket not found")
		}
	}
	type maskChoice struct {
		mask uint16
		bits uint16
	}
	var appliedChoices []maskChoice
	err = walletdb.Update(ctx, w.db, func(tx walletdb.ReadWriteTx) error {
		for _, c := range choices {
			var matchingAgenda *chaincfg.Vote
			for i := range deployments {
				if deployments[i].Vote.Id == c.AgendaID {
					matchingAgenda = &deployments[i].Vote
					break
				}
			}
			if matchingAgenda == nil {
				return errors.E(errors.Invalid, errors.Errorf("no agenda with ID %q", c.AgendaID))
			}
			var matchingChoice *chaincfg.Choice
			for i := range matchingAgenda.Choices {
				if matchingAgenda.Choices[i].Id == c.ChoiceID {
					matchingChoice = &matchingAgenda.Choices[i]
					break
				}
			}
			if matchingChoice == nil {
				return errors.E(errors.Invalid, errors.Errorf("agenda %q has no choice ID %q", c.AgendaID, c.ChoiceID))
			}
			var err error
			if ticketHash == nil {
				err = udb.SetDefaultAgendaPreference(tx, version, c.AgendaID, c.ChoiceID)
			} else {
				err = udb.SetTicketAgendaPreference(tx, ticketHash, version, c.AgendaID, c.ChoiceID)
			}
			if err != nil {
				return err
			}
			appliedChoices = append(appliedChoices, maskChoice{
				mask: matchingAgenda.Mask,
				bits: matchingChoice.Bits,
			})
			if ticketHash != nil {
				ticketVoteBits, _ := w.readDBTicketVoteBits(tx, ticketHash)
				voteBits = ticketVoteBits.Bits
			}
		}
		return nil
	})
	if err != nil {
		return 0, errors.E(op, err)
	}
	if ticketHash == nil {
		w.stakeSettingsLock.Lock()
		for _, c := range appliedChoices {
			w.defaultVoteBits.Bits &^= c.mask  
			w.defaultVoteBits.Bits |= c.bits   
		}
		voteBits = w.defaultVoteBits.Bits
		w.stakeSettingsLock.Unlock()
	}
	return voteBits, nil
}
func (w *Wallet) TreasuryKeyPolicy(pikey []byte) stake.TreasuryVoteT {
	w.stakeSettingsLock.Lock()
	defer w.stakeSettingsLock.Unlock()
	return w.tspendKeyPolicy[string(pikey)]
}
func (w *Wallet) TSpendPolicy(hash *chainhash.Hash) stake.TreasuryVoteT {
	w.stakeSettingsLock.Lock()
	defer w.stakeSettingsLock.Unlock()
	if policy, ok := w.tspendPolicy[*hash]; ok {
		return policy
	}
	tspend, ok := w.tspends[*hash]
	if !ok {
		return 0  
	}
	pikey := tspend.TxIn[0].SignatureScript[66 : 66+secp256k1.PubKeyBytesLenCompressed]
	return w.tspendKeyPolicy[string(pikey)]
}
type TreasuryKeyPolicy struct {
	PiKey  []byte
	Policy stake.TreasuryVoteT
}
func (w *Wallet) TreasuryKeyPolicies() []TreasuryKeyPolicy {
	w.stakeSettingsLock.Lock()
	defer w.stakeSettingsLock.Unlock()
	policies := make([]TreasuryKeyPolicy, 0, len(w.tspendKeyPolicy))
	for pikey, policy := range w.tspendKeyPolicy {
		policies = append(policies, TreasuryKeyPolicy{
			PiKey:  []byte(pikey),
			Policy: policy,
		})
	}
	return policies
}
func (w *Wallet) SetTreasuryKeyPolicy(ctx context.Context, pikey []byte,
	policy stake.TreasuryVoteT) error {
	switch policy {
	case stake.TreasuryVoteInvalid, stake.TreasuryVoteNo, stake.TreasuryVoteYes:
	default:
		err := errors.Errorf("invalid treasury vote policy %#x", policy)
		return errors.E(errors.Invalid, err)
	}
	defer w.stakeSettingsLock.Unlock()
	w.stakeSettingsLock.Lock()
	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
		return udb.SetTreasuryKeyPolicy(dbtx, pikey, policy)
	})
	if err != nil {
		return err
	}
	if policy == stake.TreasuryVoteInvalid {
		delete(w.tspendKeyPolicy, string(pikey))
		return nil
	}
	w.tspendKeyPolicy[string(pikey)] = policy
	return nil
}
func (w *Wallet) SetTSpendPolicy(ctx context.Context, hash *chainhash.Hash,
	policy stake.TreasuryVoteT) error {
	switch policy {
	case stake.TreasuryVoteInvalid, stake.TreasuryVoteNo, stake.TreasuryVoteYes:
	default:
		err := errors.Errorf("invalid treasury vote policy %#x", policy)
		return errors.E(errors.Invalid, err)
	}
	defer w.stakeSettingsLock.Unlock()
	w.stakeSettingsLock.Lock()
	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
		return udb.SetTSpendPolicy(dbtx, hash, policy)
	})
	if err != nil {
		return err
	}
	if policy == stake.TreasuryVoteInvalid {
		delete(w.tspendPolicy, *hash)
		return nil
	}
	w.tspendPolicy[*hash] = policy
	return nil
}
func (w *Wallet) RelayFee() dcrutil.Amount {
	w.relayFeeMu.Lock()
	relayFee := w.relayFee
	w.relayFeeMu.Unlock()
	return relayFee
}
func (w *Wallet) SetRelayFee(relayFee dcrutil.Amount) {
	w.relayFeeMu.Lock()
	w.relayFee = relayFee
	w.relayFeeMu.Unlock()
}
func (w *Wallet) InitialHeight() int32 {
	return w.initialHeight
}
func (w *Wallet) MainChainTip(ctx context.Context) (hash chainhash.Hash, height int32) {
	walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		hash, height = w.txStore.MainChainTip(dbtx)
		return nil
	})
	return
}
func (w *Wallet) BlockInMainChain(ctx context.Context, hash *chainhash.Hash) (haveBlock, invalidated bool, err error) {
	const op errors.Op = "wallet.BlockInMainChain"
	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		haveBlock, invalidated = w.txStore.BlockInMainChain(dbtx, hash)
		return nil
	})
	if err != nil {
		return false, false, errors.E(op, err)
	}
	return haveBlock, invalidated, nil
}
func (w *Wallet) BlockHeader(ctx context.Context, blockHash *chainhash.Hash) (*wire.BlockHeader, error) {
	var header *wire.BlockHeader
	err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		var err error
		header, err = w.txStore.GetBlockHeader(dbtx, blockHash)
		return err
	})
	return header, err
}