github.com/ZuluSpl0it/Sia@v1.3.7/modules/wallet/transactions.go (about) 1 package wallet 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "sort" 8 9 "github.com/NebulousLabs/Sia/build" 10 "github.com/NebulousLabs/Sia/encoding" 11 "github.com/NebulousLabs/Sia/modules" 12 "github.com/NebulousLabs/Sia/types" 13 ) 14 15 var ( 16 errOutOfBounds = errors.New("requesting transactions at unknown confirmation heights") 17 ) 18 19 // AddressTransactions returns all of the wallet transactions associated with a 20 // single unlock hash. 21 func (w *Wallet) AddressTransactions(uh types.UnlockHash) (pts []modules.ProcessedTransaction, err error) { 22 if err := w.tg.Add(); err != nil { 23 return []modules.ProcessedTransaction{}, err 24 } 25 defer w.tg.Done() 26 // ensure durability of reported transactions 27 w.mu.Lock() 28 defer w.mu.Unlock() 29 if err = w.syncDB(); err != nil { 30 return 31 } 32 33 txnIndices, _ := dbGetAddrTransactions(w.dbTx, uh) 34 for _, i := range txnIndices { 35 pt, err := dbGetProcessedTransaction(w.dbTx, i) 36 if err != nil { 37 continue 38 } 39 pts = append(pts, pt) 40 } 41 return pts, nil 42 } 43 44 // AddressUnconfirmedTransactions returns all of the unconfirmed wallet transactions 45 // related to a specific address. 46 func (w *Wallet) AddressUnconfirmedTransactions(uh types.UnlockHash) (pts []modules.ProcessedTransaction, err error) { 47 if err := w.tg.Add(); err != nil { 48 return []modules.ProcessedTransaction{}, err 49 } 50 defer w.tg.Done() 51 // ensure durability of reported transactions 52 w.mu.Lock() 53 defer w.mu.Unlock() 54 if err = w.syncDB(); err != nil { 55 return 56 } 57 58 // Scan the full list of unconfirmed transactions to see if there are any 59 // related transactions. 60 for _, pt := range w.unconfirmedProcessedTransactions { 61 relevant := false 62 for _, input := range pt.Inputs { 63 if input.RelatedAddress == uh { 64 relevant = true 65 break 66 } 67 } 68 for _, output := range pt.Outputs { 69 if output.RelatedAddress == uh { 70 relevant = true 71 break 72 } 73 } 74 if relevant { 75 pts = append(pts, pt) 76 } 77 } 78 return pts, err 79 } 80 81 // Transaction returns the transaction with the given id. 'False' is returned 82 // if the transaction does not exist. 83 func (w *Wallet) Transaction(txid types.TransactionID) (pt modules.ProcessedTransaction, found bool, err error) { 84 if err := w.tg.Add(); err != nil { 85 return modules.ProcessedTransaction{}, false, err 86 } 87 defer w.tg.Done() 88 // ensure durability of reported transaction 89 w.mu.Lock() 90 defer w.mu.Unlock() 91 if err = w.syncDB(); err != nil { 92 return 93 } 94 95 // Get the keyBytes for the given txid 96 keyBytes, err := dbGetTransactionIndex(w.dbTx, txid) 97 if err != nil { 98 return modules.ProcessedTransaction{}, false, nil 99 } 100 101 // Retrieve the transaction 102 found = encoding.Unmarshal(w.dbTx.Bucket(bucketProcessedTransactions).Get(keyBytes), &pt) == nil 103 return 104 } 105 106 // Transactions returns all transactions relevant to the wallet that were 107 // confirmed in the range [startHeight, endHeight]. 108 func (w *Wallet) Transactions(startHeight, endHeight types.BlockHeight) (pts []modules.ProcessedTransaction, err error) { 109 if err := w.tg.Add(); err != nil { 110 return nil, err 111 } 112 defer w.tg.Done() 113 // ensure durability of reported transactions 114 w.mu.Lock() 115 defer w.mu.Unlock() 116 if err = w.syncDB(); err != nil { 117 return 118 } 119 120 height, err := dbGetConsensusHeight(w.dbTx) 121 if err != nil { 122 return 123 } else if startHeight > height || startHeight > endHeight { 124 return nil, errOutOfBounds 125 } 126 127 // Get the bucket, the largest key in it and the cursor 128 bucket := w.dbTx.Bucket(bucketProcessedTransactions) 129 cursor := bucket.Cursor() 130 nextKey := bucket.Sequence() + 1 131 132 // Database is empty 133 if nextKey == 1 { 134 return 135 } 136 137 var pt modules.ProcessedTransaction 138 keyBytes := make([]byte, 8) 139 var result int 140 func() { 141 // Recover from possible panic during binary search 142 defer func() { 143 r := recover() 144 if r != nil { 145 err = fmt.Errorf("%v", r) 146 } 147 }() 148 149 // Start binary searching 150 result = sort.Search(int(nextKey), func(i int) bool { 151 // Create the key for the index 152 binary.BigEndian.PutUint64(keyBytes, uint64(i)) 153 154 // Retrieve the processed transaction 155 key, ptBytes := cursor.Seek(keyBytes) 156 if build.DEBUG && key == nil { 157 panic("Failed to retrieve processed Transaction by key") 158 } 159 160 // Decode the transaction 161 if err = decodeProcessedTransaction(ptBytes, &pt); build.DEBUG && err != nil { 162 panic(err) 163 } 164 165 return pt.ConfirmationHeight >= startHeight 166 }) 167 }() 168 if err != nil { 169 return 170 } 171 172 if uint64(result) == nextKey { 173 // No transaction was found 174 return 175 } 176 177 // Create the key that corresponds to the result of the search 178 binary.BigEndian.PutUint64(keyBytes, uint64(result)) 179 180 // Get the processed transaction and decode it 181 key, ptBytes := cursor.Seek(keyBytes) 182 if build.DEBUG && key == nil { 183 build.Critical("Couldn't find the processed transaction from the search.") 184 } 185 if err = decodeProcessedTransaction(ptBytes, &pt); build.DEBUG && err != nil { 186 build.Critical(err) 187 } 188 189 // Gather all transactions until endHeight is reached 190 for pt.ConfirmationHeight <= endHeight { 191 if build.DEBUG && pt.ConfirmationHeight < startHeight { 192 build.Critical("wallet processed transactions are not sorted") 193 } 194 pts = append(pts, pt) 195 196 // Get next processed transaction 197 key, ptBytes := cursor.Next() 198 if key == nil { 199 break 200 } 201 202 // Decode the transaction 203 if err := decodeProcessedTransaction(ptBytes, &pt); build.DEBUG && err != nil { 204 panic("Failed to decode the processed transaction") 205 } 206 } 207 return 208 } 209 210 // UnconfirmedTransactions returns the set of unconfirmed transactions that are 211 // relevant to the wallet. 212 func (w *Wallet) UnconfirmedTransactions() ([]modules.ProcessedTransaction, error) { 213 if err := w.tg.Add(); err != nil { 214 return nil, err 215 } 216 defer w.tg.Done() 217 w.mu.RLock() 218 defer w.mu.RUnlock() 219 return w.unconfirmedProcessedTransactions, nil 220 }