decred.org/dcrwallet/v3@v3.1.0/spv/rescan.go (about)

     1  // Copyright (c) 2013-2014 The btcsuite developers
     2  // Copyright (c) 2018-2021 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package spv
     7  
     8  import (
     9  	"github.com/decred/dcrd/blockchain/stake/v5"
    10  	"github.com/decred/dcrd/gcs/v4/blockcf2"
    11  	"github.com/decred/dcrd/txscript/v4/stdscript"
    12  	"github.com/decred/dcrd/wire"
    13  )
    14  
    15  // rescanCheckTransaction is a helper function to rescan both stake and regular
    16  // transactions in a block.  It appends transactions that match the filters to
    17  // *matches, while updating the filters to add outpoints for new UTXOs
    18  // controlled by this wallet.  New data added to the Syncer's filters is also
    19  // added to fadded.
    20  //
    21  // This function may only be called with the filter mutex held.
    22  func (s *Syncer) rescanCheckTransactions(matches *[]*wire.MsgTx, fadded *blockcf2.Entries, txs []*wire.MsgTx, tree int8) {
    23  	for i, tx := range txs {
    24  		// Keep track of whether the transaction has already been added
    25  		// to the result.  It shouldn't be added twice.
    26  		added := false
    27  
    28  		txty := stake.TxTypeRegular
    29  		if tree == wire.TxTreeStake {
    30  			txty = stake.DetermineTxType(tx)
    31  		}
    32  
    33  		// Coinbases and stakebases are handled specially: all inputs of a
    34  		// coinbase and the first (stakebase) input of a vote are skipped over
    35  		// as they generate coins and do not reference any previous outputs.
    36  		inputs := tx.TxIn
    37  		if i == 0 && txty == stake.TxTypeRegular {
    38  			goto LoopOutputs
    39  		}
    40  		if txty == stake.TxTypeSSGen {
    41  			inputs = inputs[1:]
    42  		}
    43  
    44  		for _, input := range inputs {
    45  			if !s.rescanFilter.ExistsUnspentOutPoint(&input.PreviousOutPoint) {
    46  				continue
    47  			}
    48  			if !added {
    49  				*matches = append(*matches, tx)
    50  				added = true
    51  			}
    52  		}
    53  
    54  	LoopOutputs:
    55  		for i, output := range tx.TxOut {
    56  			_, addrs := stdscript.ExtractAddrs(output.Version, output.PkScript, s.wallet.ChainParams())
    57  			for _, a := range addrs {
    58  				if !s.rescanFilter.ExistsAddress(a) {
    59  					continue
    60  				}
    61  
    62  				op := wire.OutPoint{
    63  					Hash:  tx.TxHash(),
    64  					Index: uint32(i),
    65  					Tree:  tree,
    66  				}
    67  				if !s.rescanFilter.ExistsUnspentOutPoint(&op) {
    68  					s.rescanFilter.AddUnspentOutPoint(&op)
    69  				}
    70  
    71  				if !added {
    72  					*matches = append(*matches, tx)
    73  					added = true
    74  				}
    75  			}
    76  		}
    77  	}
    78  }
    79  
    80  // rescanBlock rescans a block for any relevant transactions for the passed
    81  // lookup keys.  Returns any discovered transactions and any new data added to
    82  // the filter.
    83  func (s *Syncer) rescanBlock(block *wire.MsgBlock) (matches []*wire.MsgTx, fadded blockcf2.Entries) {
    84  	s.filterMu.Lock()
    85  	s.rescanCheckTransactions(&matches, &fadded, block.STransactions, wire.TxTreeStake)
    86  	s.rescanCheckTransactions(&matches, &fadded, block.Transactions, wire.TxTreeRegular)
    87  	s.filterMu.Unlock()
    88  	return matches, fadded
    89  }
    90  
    91  // filterRelevant filters out all transactions considered irrelevant
    92  // without updating filters.
    93  func (s *Syncer) filterRelevant(txs []*wire.MsgTx) []*wire.MsgTx {
    94  	defer s.filterMu.Unlock()
    95  	s.filterMu.Lock()
    96  
    97  	matches := txs[:0]
    98  Txs:
    99  	for _, tx := range txs {
   100  		for _, in := range tx.TxIn {
   101  			if s.rescanFilter.ExistsUnspentOutPoint(&in.PreviousOutPoint) {
   102  				matches = append(matches, tx)
   103  				continue Txs
   104  			}
   105  		}
   106  		for _, out := range tx.TxOut {
   107  			_, addrs := stdscript.ExtractAddrs(out.Version, out.PkScript, s.wallet.ChainParams())
   108  			for _, a := range addrs {
   109  				if s.rescanFilter.ExistsAddress(a) {
   110  					matches = append(matches, tx)
   111  					continue Txs
   112  				}
   113  			}
   114  		}
   115  	}
   116  
   117  	return matches
   118  }