github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/wallet/update.go (about) 1 package wallet 2 3 import ( 4 "math" 5 6 "github.com/NebulousLabs/Sia/build" 7 "github.com/NebulousLabs/Sia/modules" 8 "github.com/NebulousLabs/Sia/types" 9 ) 10 11 // updateConfirmedSet uses a consensus change to update the confirmed set of 12 // outputs as understood by the wallet. 13 func (w *Wallet) updateConfirmedSet(cc modules.ConsensusChange) { 14 for _, diff := range cc.SiacoinOutputDiffs { 15 // Verify that the diff is relevant to the wallet. 16 _, exists := w.keys[diff.SiacoinOutput.UnlockHash] 17 if !exists { 18 continue 19 } 20 21 _, exists = w.siacoinOutputs[diff.ID] 22 if diff.Direction == modules.DiffApply { 23 if build.DEBUG && exists { 24 panic("adding an existing output to wallet") 25 } 26 w.siacoinOutputs[diff.ID] = diff.SiacoinOutput 27 } else { 28 if build.DEBUG && !exists { 29 panic("deleting nonexisting output from wallet") 30 } 31 delete(w.siacoinOutputs, diff.ID) 32 } 33 } 34 for _, diff := range cc.SiafundOutputDiffs { 35 // Verify that the diff is relevant to the wallet. 36 _, exists := w.keys[diff.SiafundOutput.UnlockHash] 37 if !exists { 38 continue 39 } 40 41 _, exists = w.siafundOutputs[diff.ID] 42 if diff.Direction == modules.DiffApply { 43 if build.DEBUG && exists { 44 panic("adding an existing output to wallet") 45 } 46 w.siafundOutputs[diff.ID] = diff.SiafundOutput 47 } else { 48 if build.DEBUG && !exists { 49 panic("deleting nonexisting output from wallet") 50 } 51 delete(w.siafundOutputs, diff.ID) 52 } 53 } 54 for _, diff := range cc.SiafundPoolDiffs { 55 if diff.Direction == modules.DiffApply { 56 w.siafundPool = diff.Adjusted 57 } else { 58 w.siafundPool = diff.Previous 59 } 60 } 61 } 62 63 // revertHistory reverts any transaction history that was destroyed by reverted 64 // blocks in the consensus change. 65 func (w *Wallet) revertHistory(cc modules.ConsensusChange) { 66 for _, block := range cc.RevertedBlocks { 67 // Remove any transactions that have been reverted. 68 for i := len(block.Transactions) - 1; i >= 0; i-- { 69 // If the transaction is relevant to the wallet, it will be the 70 // most recent transaction appended to w.processedTransactions. 71 // Relevance can be determined just by looking at the last element 72 // of w.processedTransactions. 73 txn := block.Transactions[i] 74 txid := txn.ID() 75 if len(w.processedTransactions) > 0 && txid == w.processedTransactions[len(w.processedTransactions)-1].TransactionID { 76 w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1] 77 delete(w.processedTransactionMap, txid) 78 } 79 } 80 81 // Remove the miner payout transaction if applicable. 82 for _, mp := range block.MinerPayouts { 83 _, exists := w.keys[mp.UnlockHash] 84 if exists { 85 w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1] 86 delete(w.processedTransactionMap, types.TransactionID(block.ID())) 87 break 88 } 89 } 90 w.consensusSetHeight-- 91 } 92 } 93 94 // applyHistory applies any transaction history that was introduced by the 95 // applied blocks. 96 func (w *Wallet) applyHistory(cc modules.ConsensusChange) { 97 for _, block := range cc.AppliedBlocks { 98 w.consensusSetHeight++ 99 // Apply the miner payout transaction if applicable. 100 minerPT := modules.ProcessedTransaction{ 101 Transaction: types.Transaction{}, 102 TransactionID: types.TransactionID(block.ID()), 103 ConfirmationHeight: w.consensusSetHeight, 104 ConfirmationTimestamp: block.Timestamp, 105 } 106 relevant := false 107 for i, mp := range block.MinerPayouts { 108 _, exists := w.keys[mp.UnlockHash] 109 if exists { 110 relevant = true 111 } 112 minerPT.Outputs = append(minerPT.Outputs, modules.ProcessedOutput{ 113 FundType: types.SpecifierMinerPayout, 114 MaturityHeight: w.consensusSetHeight + types.MaturityDelay, 115 WalletAddress: exists, 116 RelatedAddress: mp.UnlockHash, 117 Value: mp.Value, 118 }) 119 w.historicOutputs[types.OutputID(block.MinerPayoutID(uint64(i)))] = mp.Value 120 } 121 if relevant { 122 w.processedTransactions = append(w.processedTransactions, minerPT) 123 w.processedTransactionMap[minerPT.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1] 124 } 125 for _, txn := range block.Transactions { 126 relevant := false 127 pt := modules.ProcessedTransaction{ 128 Transaction: txn, 129 TransactionID: txn.ID(), 130 ConfirmationHeight: w.consensusSetHeight, 131 ConfirmationTimestamp: block.Timestamp, 132 } 133 for _, sci := range txn.SiacoinInputs { 134 _, exists := w.keys[sci.UnlockConditions.UnlockHash()] 135 if exists { 136 relevant = true 137 } 138 pt.Inputs = append(pt.Inputs, modules.ProcessedInput{ 139 FundType: types.SpecifierSiacoinInput, 140 WalletAddress: exists, 141 RelatedAddress: sci.UnlockConditions.UnlockHash(), 142 Value: w.historicOutputs[types.OutputID(sci.ParentID)], 143 }) 144 } 145 for i, sco := range txn.SiacoinOutputs { 146 _, exists := w.keys[sco.UnlockHash] 147 if exists { 148 relevant = true 149 } 150 pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{ 151 FundType: types.SpecifierSiacoinOutput, 152 MaturityHeight: w.consensusSetHeight, 153 WalletAddress: exists, 154 RelatedAddress: sco.UnlockHash, 155 Value: sco.Value, 156 }) 157 w.historicOutputs[types.OutputID(txn.SiacoinOutputID(uint64(i)))] = sco.Value 158 } 159 for _, sfi := range txn.SiafundInputs { 160 _, exists := w.keys[sfi.UnlockConditions.UnlockHash()] 161 if exists { 162 relevant = true 163 } 164 sfiValue := w.historicOutputs[types.OutputID(sfi.ParentID)] 165 pt.Inputs = append(pt.Inputs, modules.ProcessedInput{ 166 FundType: types.SpecifierSiafundInput, 167 WalletAddress: exists, 168 RelatedAddress: sfi.UnlockConditions.UnlockHash(), 169 Value: sfiValue, 170 }) 171 claimValue := w.siafundPool.Sub(w.historicClaimStarts[sfi.ParentID]).Mul(sfiValue) 172 pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{ 173 FundType: types.SpecifierClaimOutput, 174 MaturityHeight: w.consensusSetHeight + types.MaturityDelay, 175 WalletAddress: exists, 176 RelatedAddress: sfi.ClaimUnlockHash, 177 Value: claimValue, 178 }) 179 } 180 for i, sfo := range txn.SiafundOutputs { 181 _, exists := w.keys[sfo.UnlockHash] 182 if exists { 183 relevant = true 184 } 185 pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{ 186 FundType: types.SpecifierSiafundOutput, 187 MaturityHeight: w.consensusSetHeight, 188 WalletAddress: exists, 189 RelatedAddress: sfo.UnlockHash, 190 Value: sfo.Value, 191 }) 192 w.historicOutputs[types.OutputID(txn.SiafundOutputID(uint64(i)))] = sfo.Value 193 w.historicClaimStarts[txn.SiafundOutputID(uint64(i))] = sfo.ClaimStart 194 } 195 for _, fee := range txn.MinerFees { 196 pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{ 197 FundType: types.SpecifierMinerFee, 198 Value: fee, 199 }) 200 } 201 if relevant { 202 w.processedTransactions = append(w.processedTransactions, pt) 203 w.processedTransactionMap[pt.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1] 204 } 205 } 206 } 207 } 208 209 // ProcessConsensusChange parses a consensus change to update the set of 210 // confirmed outputs known to the wallet. 211 func (w *Wallet) ProcessConsensusChange(cc modules.ConsensusChange) { 212 w.mu.Lock() 213 defer w.mu.Unlock() 214 w.updateConfirmedSet(cc) 215 w.revertHistory(cc) 216 w.applyHistory(cc) 217 } 218 219 // ReceiveUpdatedUnconfirmedTransactions updates the wallet's unconfirmed 220 // transaction set. 221 func (w *Wallet) ReceiveUpdatedUnconfirmedTransactions(txns []types.Transaction, _ modules.ConsensusChange) { 222 w.mu.Lock() 223 defer w.mu.Unlock() 224 225 w.unconfirmedProcessedTransactions = nil 226 for _, txn := range txns { 227 // To save on code complexity, relveancy is determined while building 228 // up the wallet transaction. 229 relevant := false 230 pt := modules.ProcessedTransaction{ 231 Transaction: txn, 232 TransactionID: txn.ID(), 233 ConfirmationHeight: types.BlockHeight(math.MaxUint64), 234 ConfirmationTimestamp: types.Timestamp(math.MaxUint64), 235 } 236 for _, sci := range txn.SiacoinInputs { 237 _, exists := w.keys[sci.UnlockConditions.UnlockHash()] 238 if exists { 239 relevant = true 240 } 241 pt.Inputs = append(pt.Inputs, modules.ProcessedInput{ 242 FundType: types.SpecifierSiacoinInput, 243 WalletAddress: exists, 244 RelatedAddress: sci.UnlockConditions.UnlockHash(), 245 Value: w.historicOutputs[types.OutputID(sci.ParentID)], 246 }) 247 } 248 for i, sco := range txn.SiacoinOutputs { 249 _, exists := w.keys[sco.UnlockHash] 250 if exists { 251 relevant = true 252 } 253 pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{ 254 FundType: types.SpecifierSiacoinOutput, 255 MaturityHeight: types.BlockHeight(math.MaxUint64), 256 WalletAddress: exists, 257 RelatedAddress: sco.UnlockHash, 258 Value: sco.Value, 259 }) 260 w.historicOutputs[types.OutputID(txn.SiacoinOutputID(uint64(i)))] = sco.Value 261 } 262 for _, fee := range txn.MinerFees { 263 pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{ 264 FundType: types.SpecifierMinerFee, 265 Value: fee, 266 }) 267 } 268 if relevant { 269 w.unconfirmedProcessedTransactions = append(w.unconfirmedProcessedTransactions, pt) 270 } 271 } 272 }