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 }