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