github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/update.go (about) 1 package host 2 3 // TODO: Need to check that 'RevisionConfirmed' is sensitive to whether or not 4 // it was the *most recent* revision that got confirmed. 5 6 import ( 7 "encoding/binary" 8 "encoding/json" 9 10 "github.com/NebulousLabs/Sia/crypto" 11 "github.com/NebulousLabs/Sia/modules" 12 "github.com/NebulousLabs/Sia/types" 13 14 "github.com/NebulousLabs/bolt" 15 ) 16 17 // initRescan is a helper function of initConsensusSubscribe, and is called when 18 // the host and the consensus set have become desynchronized. Desynchronization 19 // typically happens if the user is replacing or altering the persistent files 20 // in the consensus set or the host. 21 func (h *Host) initRescan() error { 22 // Reset all of the variables that have relevance to the consensus set. 23 var allObligations []*storageObligation 24 err := func() error { 25 h.mu.Lock() 26 defer h.mu.Unlock() 27 28 // Reset all of the consensus-relevant variables in the host. 29 h.blockHeight = 0 30 31 // Reset all of the storage obligations. 32 err := h.db.Update(func(tx *bolt.Tx) error { 33 bsu := tx.Bucket(bucketStorageObligations) 34 c := bsu.Cursor() 35 for k, soBytes := c.First(); soBytes != nil; k, soBytes = c.Next() { 36 var so storageObligation 37 err := json.Unmarshal(soBytes, &so) 38 if err != nil { 39 return err 40 } 41 so.OriginConfirmed = false 42 so.RevisionConfirmed = false 43 so.ProofConfirmed = false 44 allObligations = append(allObligations, &so) 45 soBytes, err = json.Marshal(so) 46 if err != nil { 47 return err 48 } 49 err = bsu.Put(k, soBytes) 50 if err != nil { 51 return err 52 } 53 } 54 return nil 55 }) 56 if err != nil { 57 return err 58 } 59 60 return h.save() 61 }() 62 if err != nil { 63 return err 64 } 65 66 // Subscribe to the consensus set. This is a blocking call that will not 67 // return until the host has fully caught up to the current block. 68 err = h.cs.ConsensusSetSubscribe(h, modules.ConsensusChangeBeginning) 69 if err != nil { 70 return err 71 } 72 73 // Re-queue all of the action items for the storage obligations. 74 for _, so := range allObligations { 75 soid := so.id() 76 err0 := h.tpool.AcceptTransactionSet(so.OriginTransactionSet) 77 err1 := h.queueActionItem(h.blockHeight+resubmissionTimeout, soid) 78 err2 := h.queueActionItem(so.expiration()-revisionSubmissionBuffer, soid) 79 err3 := h.queueActionItem(so.expiration()+resubmissionTimeout, soid) 80 err = composeErrors(err0, err1, err2, err3) 81 if err != nil { 82 return composeErrors(err, h.removeStorageObligation(so, obligationRejected)) 83 } 84 } 85 return nil 86 } 87 88 // initConsensusSubscription subscribes the host to the consensus set. 89 func (h *Host) initConsensusSubscription() error { 90 err := h.cs.ConsensusSetSubscribe(h, h.recentChange) 91 if err == modules.ErrInvalidConsensusChangeID { 92 // Perform a rescan of the consensus set if the change id that the host 93 // has is unrecognized by the consensus set. This will typically only 94 // happen if the user has been replacing files inside the Sia folder 95 // structure. 96 return h.initRescan() 97 } 98 return err 99 } 100 101 // ProcessConsensusChange will be called by the consensus set every time there 102 // is a change to the blockchain. 103 func (h *Host) ProcessConsensusChange(cc modules.ConsensusChange) { 104 h.mu.Lock() 105 defer h.mu.Unlock() 106 107 // Wrap the whole parsing into a single large database tx to keep things 108 // efficient. 109 var actionItems []*storageObligation 110 err := h.db.Update(func(tx *bolt.Tx) error { 111 for _, block := range cc.RevertedBlocks { 112 // Look for transactions relevant to open storage obligations. 113 for _, txn := range block.Transactions { 114 // Check for file contracts. 115 if len(txn.FileContracts) > 0 { 116 for j := range txn.FileContracts { 117 fcid := txn.FileContractID(uint64(j)) 118 so, err := getStorageObligation(tx, fcid) 119 if err != nil { 120 // The storage folder may not exist, or the disk 121 // may be having trouble. Either way, we ignore the 122 // problem. If the disk is having trouble, the user 123 // will have to perform a rescan. 124 continue 125 } 126 so.OriginConfirmed = false 127 err = putStorageObligation(tx, so) 128 if err != nil { 129 continue 130 } 131 } 132 } 133 134 // Check for file contract revisions. 135 if len(txn.FileContractRevisions) > 0 { 136 for _, fcr := range txn.FileContractRevisions { 137 so, err := getStorageObligation(tx, fcr.ParentID) 138 if err != nil { 139 // The storage folder may not exist, or the disk 140 // may be having trouble. Either way, we ignore the 141 // problem. If the disk is having trouble, the user 142 // will have to perform a rescan. 143 continue 144 } 145 so.RevisionConfirmed = false 146 err = putStorageObligation(tx, so) 147 if err != nil { 148 continue 149 } 150 } 151 } 152 153 // Check for storage proofs. 154 if len(txn.StorageProofs) > 0 { 155 for _, sp := range txn.StorageProofs { 156 // Check database for relevant storage proofs. 157 so, err := getStorageObligation(tx, sp.ParentID) 158 if err != nil { 159 // The storage folder may not exist, or the disk 160 // may be having trouble. Either way, we ignore the 161 // problem. If the disk is having trouble, the user 162 // will have to perform a rescan. 163 continue 164 } 165 so.ProofConfirmed = false 166 err = putStorageObligation(tx, so) 167 if err != nil { 168 continue 169 } 170 } 171 } 172 } 173 174 // Height is not adjusted when dealing with the genesis block because 175 // the default height is 0 and the genesis block height is 0. If 176 // removing the genesis block, height will already be at height 0 and 177 // should not update, lest an underflow occur. 178 if block.ID() != types.GenesisID { 179 h.blockHeight-- 180 } 181 } 182 for _, block := range cc.AppliedBlocks { 183 // Look for transactions relevant to open storage obligations. 184 for _, txn := range block.Transactions { 185 // Check for file contracts. 186 if len(txn.FileContracts) > 0 { 187 for i := range txn.FileContracts { 188 fcid := txn.FileContractID(uint64(i)) 189 so, err := getStorageObligation(tx, fcid) 190 if err != nil { 191 // The storage folder may not exist, or the disk 192 // may be having trouble. Either way, we ignore the 193 // problem. If the disk is having trouble, the user 194 // will have to perform a rescan. 195 continue 196 } 197 so.OriginConfirmed = true 198 err = putStorageObligation(tx, so) 199 if err != nil { 200 continue 201 } 202 } 203 } 204 205 // Check for file contract revisions. 206 if len(txn.FileContractRevisions) > 0 { 207 for _, fcr := range txn.FileContractRevisions { 208 so, err := getStorageObligation(tx, fcr.ParentID) 209 if err != nil { 210 // The storage folder may not exist, or the disk 211 // may be having trouble. Either way, we ignore the 212 // problem. If the disk is having trouble, the user 213 // will have to perform a rescan. 214 continue 215 } 216 so.RevisionConfirmed = true 217 err = putStorageObligation(tx, so) 218 if err != nil { 219 continue 220 } 221 } 222 } 223 224 // Check for storage proofs. 225 if len(txn.StorageProofs) > 0 { 226 for _, sp := range txn.StorageProofs { 227 so, err := getStorageObligation(tx, sp.ParentID) 228 if err != nil { 229 // The storage folder may not exist, or the disk 230 // may be having trouble. Either way, we ignore the 231 // problem. If the disk is having trouble, the user 232 // will have to perform a rescan. 233 continue 234 } 235 so.ProofConfirmed = true 236 err = putStorageObligation(tx, so) 237 if err != nil { 238 continue 239 } 240 } 241 } 242 } 243 244 // Height is not adjusted when dealing with the genesis block because 245 // the default height is 0 and the genesis block height is 0. If adding 246 // the genesis block, height will already be at height 0 and should not 247 // update. 248 if block.ID() != types.GenesisID { 249 h.blockHeight++ 250 } 251 252 // Handle any action items relevant to the current height. 253 bai := tx.Bucket(bucketActionItems) 254 heightBytes := make([]byte, 8) 255 binary.BigEndian.PutUint64(heightBytes, uint64(h.blockHeight)) // BigEndian used so bolt will keep things sorted automatically. 256 existingItems := bai.Get(heightBytes) 257 258 // From the existing items, pull out a storage obligation. 259 knownActionItems := make(map[types.FileContractID]struct{}) 260 obligationIDs := make([]types.FileContractID, len(existingItems)/crypto.HashSize) 261 for i := 0; i < len(existingItems); i += crypto.HashSize { 262 copy(obligationIDs[i/crypto.HashSize][:], existingItems[i:i+crypto.HashSize]) 263 } 264 bso := tx.Bucket(bucketStorageObligations) 265 for _, soid := range obligationIDs { 266 soBytes := bso.Get(soid[:]) 267 var so storageObligation 268 err := json.Unmarshal(soBytes, &so) 269 if err != nil { 270 return err 271 } 272 _, exists := knownActionItems[soid] 273 if !exists { 274 actionItems = append(actionItems, &so) 275 knownActionItems[soid] = struct{}{} 276 } 277 } 278 } 279 return nil 280 }) 281 if err != nil { 282 h.log.Println(err) 283 } 284 285 // Handle the list of action items. 286 for _, ai := range actionItems { 287 h.handleActionItem(ai) 288 } 289 290 // Update the host's recent change pointer to point to the most recent 291 // change. 292 h.recentChange = cc.ID 293 294 // Save the host. 295 err = h.save() 296 if err != nil { 297 h.log.Println("ERROR: could not save during ProcessConsensusChange:", err) 298 } 299 }