func (w *Wallet) rescan(ctx context.Context, n NetworkBackend,
	startHash *chainhash.Hash, height int32, p chan<- RescanProgress) error {
	blockHashStorage := make([]chainhash.Hash, maxBlocksPerRescan)
	rescanFrom := *startHash
	inclusive := true
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}
		var rescanBlocks []chainhash.Hash
		err := walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
			txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
			var err error
			rescanBlocks, err = w.txStore.GetMainChainBlockHashes(txmgrNs,
				&rescanFrom, inclusive, blockHashStorage)
			return err
		})
		if err != nil {
			return err
		}
		if len(rescanBlocks) == 0 {
			break
		}
		through := height + int32(len(rescanBlocks)) - 1
		if height == 0 {
			rescanBlocks = rescanBlocks[1:]
			height = 1
			if len(rescanBlocks) == 0 {
				break
			}
		}
		log.Infof("Rescanning block range [%v, %v]...", height, through)
		saveRescanned := func(block *chainhash.Hash, txs []*wire.MsgTx) error {
			return w.SaveRescanned(ctx, block, txs)
		}
		err = n.Rescan(ctx, rescanBlocks, saveRescanned)
		if err != nil {
			return err
		}
		err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
			return w.txStore.UpdateProcessedTxsBlockMarker(dbtx, &rescanBlocks[len(rescanBlocks)-1])
		})
		if err != nil {
			return err
		}
		if p != nil {
			p <- RescanProgress{ScannedThrough: through}
		}
		rescanFrom = rescanBlocks[len(rescanBlocks)-1]
		height += int32(len(rescanBlocks))
		inclusive = false
	}
	log.Infof("Rescan complete")
	return nil
}
rescan synchronously scans over all blocks on the main chain starting at
startHash and height up through the recorded main chain tip block.  The
progress channel, if non-nil, is sent non-error progress notifications with
the heights the rescan has completed through, starting with the start height.
package wallet
import (
	"context"
	"time"
	"decred.org/dcrwallet/v2/errors"
	"decred.org/dcrwallet/v2/wallet/udb"
	"decred.org/dcrwallet/v2/wallet/walletdb"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/crypto/ripemd160"
	"github.com/decred/dcrd/txscript/v4/stdaddr"
	"github.com/decred/dcrd/wire"
)
const maxBlocksPerRescan = 2000
type RescanFilter struct {
	pubKeyHashes      map[[ripemd160.Size]byte]struct{}
	scriptHashes      map[[ripemd160.Size]byte]struct{}
	compressedPubKeys map[[33]byte]struct{}
	otherAddresses map[string]struct{}
	unspent map[wire.OutPoint]struct{}
}
func NewRescanFilter(addresses []stdaddr.Address, unspentOutPoints []*wire.OutPoint) *RescanFilter {
	filter := &RescanFilter{
		pubKeyHashes:      map[[ripemd160.Size]byte]struct{}{},
		scriptHashes:      map[[ripemd160.Size]byte]struct{}{},
		compressedPubKeys: map[[33]byte]struct{}{},
		otherAddresses:    map[string]struct{}{},
		unspent:           make(map[wire.OutPoint]struct{}, len(unspentOutPoints)),
	}
	for _, s := range addresses {
		filter.AddAddress(s)
	}
	for _, op := range unspentOutPoints {
		filter.AddUnspentOutPoint(op)
	}
	return filter
}
func (f *RescanFilter) AddAddress(a stdaddr.Address) {
	switch a := a.(type) {
	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
		f.pubKeyHashes[*a.Hash160()] = struct{}{}
	case *stdaddr.AddressScriptHashV0:
		f.scriptHashes[*a.Hash160()] = struct{}{}
	case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
		serializedPubKey := a.SerializedPubKey()
		switch len(serializedPubKey) {
		case 33:  
			var compressedPubKey [33]byte
			copy(compressedPubKey[:], serializedPubKey)
			f.compressedPubKeys[compressedPubKey] = struct{}{}
		default:
			f.otherAddresses[a.String()] = struct{}{}
		}
	default:
		f.otherAddresses[a.String()] = struct{}{}
	}
}
func (f *RescanFilter) ExistsAddress(a stdaddr.Address) (ok bool) {
	switch a := a.(type) {
	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
		_, ok = f.pubKeyHashes[*a.Hash160()]
	case *stdaddr.AddressScriptHashV0:
		_, ok = f.scriptHashes[*a.Hash160()]
	case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
		serializedPubKey := a.SerializedPubKey()
		switch len(serializedPubKey) {
		case 33:  
			var compressedPubKey [33]byte
			copy(compressedPubKey[:], serializedPubKey)
			_, ok = f.compressedPubKeys[compressedPubKey]
			if !ok {
				a := a.AddressPubKeyHash().(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0)
				_, ok = f.pubKeyHashes[*a.Hash160()]
			}
		default:
			_, ok = f.otherAddresses[a.String()]
		}
	default:
		_, ok = f.otherAddresses[a.String()]
	}
	return
}
func (f *RescanFilter) RemoveAddress(a stdaddr.Address) {
	switch a := a.(type) {
	case *stdaddr.AddressPubKeyHashEcdsaSecp256k1V0:
		delete(f.pubKeyHashes, *a.Hash160())
	case *stdaddr.AddressScriptHashV0:
		delete(f.scriptHashes, *a.Hash160())
	case *stdaddr.AddressPubKeyEcdsaSecp256k1V0:
		serializedPubKey := a.SerializedPubKey()
		switch len(serializedPubKey) {
		case 33:  
			var compressedPubKey [33]byte
			copy(compressedPubKey[:], serializedPubKey)
			delete(f.compressedPubKeys, compressedPubKey)
		default:
			delete(f.otherAddresses, a.String())
		}
	default:
		delete(f.otherAddresses, a.String())
	}
}
func (f *RescanFilter) AddUnspentOutPoint(op *wire.OutPoint) {
	f.unspent[*op] = struct{}{}
}
func (f *RescanFilter) ExistsUnspentOutPoint(op *wire.OutPoint) bool {
	_, ok := f.unspent[*op]
	return ok
}
func (f *RescanFilter) RemoveUnspentOutPoint(op *wire.OutPoint) {
	delete(f.unspent, *op)
}
func (w *Wallet) SaveRescanned(ctx context.Context, hash *chainhash.Hash, txs []*wire.MsgTx) error {
	const op errors.Op = "wallet.SaveRescanned"
	defer w.lockedOutpointMu.Unlock()
	w.lockedOutpointMu.Lock()
	err := walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
		txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)
		blockMeta, err := w.txStore.GetBlockMetaForHash(txmgrNs, hash)
		if err != nil {
			return err
		}
		header, err := w.txStore.GetBlockHeader(dbtx, hash)
		if err != nil {
			return err
		}
		for _, tx := range txs {
			rec, err := udb.NewTxRecordFromMsgTx(tx, time.Now())
			if err != nil {
				return err
			}
			_, err = w.processTransactionRecord(context.Background(), dbtx, rec, header, &blockMeta)
			if err != nil {
				return err
			}
		}
		return w.txStore.UpdateProcessedTxsBlockMarker(dbtx, hash)
	})
	if err != nil {
		return errors.E(op, err)
	}
	return nil
}