func buildFilterBlocksWatchList(req *FilterBlocksRequest) ([][]byte, error) {
	watchListSize := len(req.ExternalAddrs) +
		len(req.InternalAddrs) +
		(len(req.WatchedOutPoints) * 2)
	watchList := make([][]byte, 0, watchListSize)
	for _, addr := range req.ExternalAddrs {
		p2shAddr, err := txscript.PayToAddrScript(addr)
		if err != nil {
			return nil, err
		}
		watchList = append(watchList, p2shAddr)
	}
	for _, addr := range req.InternalAddrs {
		p2shAddr, err := txscript.PayToAddrScript(addr)
		if err != nil {
			return nil, err
		}
		watchList = append(watchList, p2shAddr)
	}
	for op, addr := range req.WatchedOutPoints {
		addr, err := txscript.PayToAddrScript(addr)
		if err != nil {
			return nil, err
		}
		watchList = append(watchList, addr)
		var buf bytes.Buffer
		if err := op.Serialize(&buf); err != nil {
			return nil, err
		}
		watchList = append(watchList, buf.Bytes())
	}
	return watchList, nil
}
buildFilterBlocksWatchList constructs a watchlist used for matching against a
cfilter from a FilterBlocksRequest. The watchlist will be populated with all
external addresses, internal addresses, and outpoints contained in the
request.
package chain
import (
	"bytes"
	"errors"
	"fmt"
	"sync"
	"time"
	"github.com/gcash/bchd/btcjson"
	"github.com/gcash/bchd/chaincfg"
	"github.com/gcash/bchd/chaincfg/chainhash"
	"github.com/gcash/bchd/rpcclient"
	"github.com/gcash/bchd/txscript"
	"github.com/gcash/bchd/wire"
	"github.com/gcash/bchutil"
	"github.com/gcash/bchutil/gcs"
	"github.com/gcash/bchutil/gcs/builder"
	"github.com/gcash/bchwallet/waddrmgr"
	"github.com/gcash/bchwallet/wtxmgr"
	"github.com/gcash/neutrino"
)
type NeutrinoClient struct {
	CS *neutrino.ChainService
	chainParams *chaincfg.Params
	rescan *neutrino.Rescan
	enqueueNotification     chan interface{}
	dequeueNotification     chan interface{}
	startTime               time.Time
	lastProgressSent        bool
	lastFilteredBlockHeader *wire.BlockHeader
	currentBlock            chan *waddrmgr.BlockStamp
	quit       chan struct{}
	rescanQuit chan struct{}
	rescanErr  <-chan error
	wg         sync.WaitGroup
	started    bool
	scanning   bool
	finished   bool
	isRescan   bool
	clientMtx sync.Mutex
}
func NewNeutrinoClient(chainParams *chaincfg.Params,
	chainService *neutrino.ChainService) *NeutrinoClient {
	return &NeutrinoClient{
		CS:          chainService,
		chainParams: chainParams,
	}
}
func (s *NeutrinoClient) BackEnd() string {
	return "neutrino"
}
func (s *NeutrinoClient) Start() error {
	s.CS.RegisterMempoolCallback(s.onRecvTx)
	s.CS.Start()
	s.clientMtx.Lock()
	defer s.clientMtx.Unlock()
	if !s.started {
		s.enqueueNotification = make(chan interface{})
		s.dequeueNotification = make(chan interface{})
		s.currentBlock = make(chan *waddrmgr.BlockStamp)
		s.quit = make(chan struct{})
		s.started = true
		s.wg.Add(1)
		go func() {
			select {
			case s.enqueueNotification <- ClientConnected{}:
			case <-s.quit:
			}
		}()
		go s.notificationHandler()
	}
	return nil
}
func (s *NeutrinoClient) Stop() {
	s.clientMtx.Lock()
	defer s.clientMtx.Unlock()
	if !s.started {
		return
	}
	close(s.quit)
	s.started = false
}
func (s *NeutrinoClient) WaitForShutdown() {
	s.wg.Wait()
}
func (s *NeutrinoClient) GetBlock(hash *chainhash.Hash) (*wire.MsgBlock, error) {
	block, err := s.CS.GetBlock(*hash)
	if err != nil {
		return nil, err
	}
	return block.MsgBlock(), nil
}
func (s *NeutrinoClient) GetBlockHeight(hash *chainhash.Hash) (int32, error) {
	return s.CS.GetBlockHeight(hash)
}
func (s *NeutrinoClient) GetBestBlock() (*chainhash.Hash, int32, error) {
	chainTip, err := s.CS.BestBlock()
	if err != nil {
		return nil, 0, err
	}
	return &chainTip.Hash, chainTip.Height, nil
}
func (s *NeutrinoClient) BlockStamp() (*waddrmgr.BlockStamp, error) {
	select {
	case bs := <-s.currentBlock:
		return bs, nil
	case <-s.quit:
		return nil, errors.New("disconnected")
	}
}
func (s *NeutrinoClient) GetBlockHash(height int64) (*chainhash.Hash, error) {
	return s.CS.GetBlockHash(height)
}
func (s *NeutrinoClient) GetBlockHeader(
	blockHash *chainhash.Hash) (*wire.BlockHeader, error) {
	return s.CS.GetBlockHeader(blockHash)
}
func (s *NeutrinoClient) IsCurrent() bool {
	return s.CS.IsCurrent()
}
func (s *NeutrinoClient) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (
	*chainhash.Hash, error) {
	err := s.CS.SendTransaction(tx)
	if err != nil {
		return nil, err
	}
	hash := tx.TxHash()
	return &hash, nil
}
func (s *NeutrinoClient) FilterBlocks(
	req *FilterBlocksRequest) (*FilterBlocksResponse, error) {
	blockFilterer := NewBlockFilterer(s.chainParams, req)
	watchList, err := buildFilterBlocksWatchList(req)
	if err != nil {
		return nil, err
	}
	for i, blk := range req.Blocks {
		filter, err := s.pollCFilter(&blk.Hash)
		if err != nil {
			return nil, err
		}
		if filter == nil || filter.N() == 0 {
			continue
		}
		key := builder.DeriveKey(&blk.Hash)
		matched, err := filter.MatchAny(key, watchList)
		if err != nil {
			return nil, err
		} else if !matched {
			continue
		}
		log.Infof("Fetching block height=%d hash=%v",
			blk.Height, blk.Hash)
		rawBlock, err := s.GetBlock(&blk.Hash)
		if err != nil {
			return nil, err
		}
		if !blockFilterer.FilterBlock(rawBlock) {
			continue
		}
		resp := &FilterBlocksResponse{
			BatchIndex:         uint32(i),
			BlockMeta:          blk,
			FoundExternalAddrs: blockFilterer.FoundExternal,
			FoundInternalAddrs: blockFilterer.FoundInternal,
			FoundOutPoints:     blockFilterer.FoundOutPoints,
			RelevantTxns:       blockFilterer.RelevantTxns,
		}
		return resp, nil
	}
	return nil, nil
}