gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/proto/merkleroots.go (about) 1 package proto 2 3 // TODO currently the cached trees are not persisted and we build them at 4 // startup. For petabytes of data this might take a long time. 5 6 import ( 7 "fmt" 8 "io" 9 10 "gitlab.com/NebulousLabs/errors" 11 "gitlab.com/SiaPrime/SiaPrime/build" 12 "gitlab.com/SiaPrime/SiaPrime/crypto" 13 ) 14 15 // merkleRootsCacheHeight is the height of the subTrees in cachedSubTrees. A 16 // height of 7 means that 128 sector roots are covered by a single cached 17 // subTree. 18 const merkleRootsCacheHeight = 7 19 20 // merkleRootsPerCache is the number of merkle roots in a cached subTree of 21 // merkleRootsCacheHeight height. 22 const merkleRootsPerCache = 1 << merkleRootsCacheHeight 23 24 type ( 25 // merkleRoots is a helper struct that makes it easier to add/insert/remove 26 // merkleRoots within a SafeContract. 27 // Modifying the merkleRoots is not ACID. This means that the SafeContract 28 // has to make sure it uses the WAL correctly to guarantee ACID updates to 29 // the underlying file. 30 merkleRoots struct { 31 // cachedSubTrees are cached trees that can be used to more efficiently 32 // compute the merkle root of a contract. 33 cachedSubTrees []*cachedSubTree 34 // uncachedRoots contains the sector roots that are not part of a 35 // cached subTree. The uncachedRoots slice should never get longer than 36 // 2^merkleRootsCacheHeight since that would simply result in a new 37 // cached subTree in cachedSubTrees. 38 uncachedRoots []crypto.Hash 39 40 // rootsFile is the rootsFile of the safe contract that contains the roots. 41 rootsFile *fileSection 42 // numMerkleRoots is the number of merkle roots in file. 43 numMerkleRoots int 44 } 45 46 // cachedSubTree is a cached subTree of a merkle tree. A height of 0 means 47 // that the sum is the hash of a leaf. A subTree of height 1 means sum is 48 // the root of 2 leaves. A subTree of height 2 contains the root of 4 49 // leaves and so on. 50 cachedSubTree struct { 51 height int // height of the subTree 52 sum crypto.Hash // root of the subTree 53 } 54 ) 55 56 // parseRootsFromData takes some data and splits it up into sector roots. It will return an error if the size of the data is not a multiple of crypto.HashSize. 57 func parseRootsFromData(b []byte) ([]crypto.Hash, error) { 58 var roots []crypto.Hash 59 if len(b)%crypto.HashSize != 0 { 60 return roots, errors.New("roots have unexpected length and might be corrupted") 61 } 62 63 var root crypto.Hash 64 for len(b) > 0 { 65 copy(root[:], b[:crypto.HashSize]) 66 roots = append(roots, root) 67 b = b[crypto.HashSize:] 68 } 69 return roots, nil 70 } 71 72 // loadExistingMerkleRoots reads creates a merkleRoots object from existing 73 // merkle roots. If the file has an unexpected length, we truncate it and 74 // return a boolean to indicate that the last write was incomplete and that the 75 // unapplied wal transactions should be applied after loading the roots. 76 func loadExistingMerkleRoots(file *fileSection) (*merkleRoots, bool, error) { 77 mr := &merkleRoots{ 78 rootsFile: file, 79 } 80 applyTxns := false 81 // Get the filesize and truncate the file if necessary. 82 size, err := file.Size() 83 if err != nil { 84 return nil, applyTxns, err 85 } 86 if mod := size % crypto.HashSize; mod != 0 { 87 if err := file.Truncate(size - mod); err != nil { 88 return nil, applyTxns, err 89 } 90 applyTxns = true 91 } 92 // Get the number of roots stored in the file. 93 mr.numMerkleRoots, err = mr.lenFromFile() 94 if err != nil { 95 return nil, applyTxns, err 96 } 97 // Read the roots from the file without reading all of them at once. 98 readOff := int64(0) 99 rootsData := make([]byte, rootsDiskLoadBulkSize) 100 for { 101 n, err := file.ReadAt(rootsData, readOff) 102 if err == io.ErrUnexpectedEOF && n == 0 { 103 break 104 } 105 if err == io.EOF && n == 0 { 106 break 107 } 108 if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { 109 return nil, applyTxns, err 110 } 111 roots, err := parseRootsFromData(rootsData[:n]) 112 if err != nil { 113 return nil, applyTxns, err 114 } 115 mr.appendRootMemory(roots...) 116 readOff += int64(n) 117 } 118 return mr, applyTxns, nil 119 } 120 121 // newCachedSubTree creates a cachedSubTree from exactly 122 // 2^merkleRootsCacheHeight roots. 123 func newCachedSubTree(roots []crypto.Hash) *cachedSubTree { 124 // Sanity check the input length. 125 if len(roots) != merkleRootsPerCache { 126 build.Critical("can't create a cached subTree from the provided number of roots") 127 } 128 return &cachedSubTree{ 129 height: int(merkleRootsCacheHeight + sectorHeight), 130 sum: cachedMerkleRoot(roots), 131 } 132 } 133 134 // newMerkleRoots creates a new merkleRoots object. This doesn't load existing 135 // roots from file and will assume that the file doesn't contain any roots. 136 // Don't use this on a file that contains roots. 137 func newMerkleRoots(file *fileSection) *merkleRoots { 138 return &merkleRoots{ 139 rootsFile: file, 140 } 141 } 142 143 // fileOffsetFromRootIndex calculates the offset of the merkle root at index i from 144 // the beginning of the contract file. 145 func fileOffsetFromRootIndex(i int) int64 { 146 return crypto.HashSize * int64(i) 147 } 148 149 // appendRootMemory appends a root to the in-memory structure of the merkleRoots. If 150 // the length of the uncachedRoots grows too large they will be compressed into 151 // a cachedSubTree. 152 func (mr *merkleRoots) appendRootMemory(roots ...crypto.Hash) { 153 for _, root := range roots { 154 mr.uncachedRoots = append(mr.uncachedRoots, root) 155 if len(mr.uncachedRoots) == merkleRootsPerCache { 156 mr.cachedSubTrees = append(mr.cachedSubTrees, newCachedSubTree(mr.uncachedRoots)) 157 mr.uncachedRoots = mr.uncachedRoots[:0] 158 } 159 } 160 } 161 162 // delete deletes the sector root at a certain index by replacing it with the 163 // last root and truncates the file to truncateSize after that. This ensures 164 // that the operation is indempotent. 165 func (mr *merkleRoots) delete(i int, lastRoot crypto.Hash, truncateSize int64) error { 166 // Swap the element at index i with the lastRoot. This might actually 167 // increase mr.numMerkleRoots since there is a chance that i points to an 168 // index after the end of the file. That's why the insert is executed first 169 // before truncating the file or decreasing the numMerkleRoots field. 170 if err := mr.insert(i, lastRoot); err != nil { 171 return errors.AddContext(err, "failed to swap deleted root with newRoot") 172 } 173 // Truncate the file to truncateSize. 174 if err := mr.rootsFile.Truncate(truncateSize); err != nil { 175 return errors.AddContext(err, "failed to truncate file") 176 } 177 // Adjust the numMerkleRoots field. If the number of roots didn't change we 178 // are done. 179 rootsBefore := mr.numMerkleRoots 180 mr.numMerkleRoots = int(truncateSize / crypto.HashSize) 181 if rootsBefore == mr.numMerkleRoots { 182 return nil 183 } 184 // Sanity check the number of roots. 185 if rootsBefore != mr.numMerkleRoots+1 { 186 build.Critical("a delete should never delete more than one root at once") 187 } 188 // If the last element is uncached we can simply remove it from the slice. 189 if len(mr.uncachedRoots) > 0 { 190 mr.uncachedRoots = mr.uncachedRoots[:len(mr.uncachedRoots)-1] 191 return nil 192 } 193 // If it is not uncached we need to delete the last cached tree and load 194 // its elements into mr.uncachedRoots. This should give us 195 // merkleRootsPerCache-1 uncached roots since we already truncated the file 196 // by 1 root. 197 if err := mr.moveLastCachedSubTreeToUncached(); err != nil { 198 return err 199 } 200 return nil 201 } 202 203 // insert inserts a root by replacing a root at an existing index. 204 func (mr *merkleRoots) insert(index int, root crypto.Hash) error { 205 // If the index does point to an offset beyond the end of the file we fill 206 // in the blanks with empty merkle roots. This usually just means that the 207 // machine crashed during the recovery process and that the next few 208 // updates are probably going to be delete operations that take care of the 209 // blank roots. 210 for index > mr.numMerkleRoots { 211 if err := mr.push(crypto.Hash{}); err != nil { 212 return errors.AddContext(err, "failed to extend roots") 213 } 214 } 215 if index == mr.numMerkleRoots { 216 return mr.push(root) 217 } 218 // Replaced the root on disk. 219 _, err := mr.rootsFile.WriteAt(root[:], fileOffsetFromRootIndex(index)) 220 if err != nil { 221 return errors.AddContext(err, "failed to insert root on disk") 222 } 223 224 // Find out if the root is in mr.cachedSubTree or mr.uncachedRoots. 225 i, cached := mr.isIndexCached(index) 226 // If the root was not cached we can simply replace it in mr.uncachedRoots. 227 if !cached { 228 mr.uncachedRoots[i] = root 229 return nil 230 } 231 // If the root was cached we need to rebuild the cache. 232 if err := mr.rebuildCachedTree(i); err != nil { 233 return errors.AddContext(err, "failed to rebuild cache for inserted root") 234 } 235 return nil 236 } 237 238 // set replaces the full set of roots. 239 func (mr *merkleRoots) set(roots []crypto.Hash) error { 240 for i, root := range roots { 241 _, err := mr.rootsFile.WriteAt(root[:], fileOffsetFromRootIndex(i)) 242 if err != nil { 243 return errors.AddContext(err, "failed to insert root on disk") 244 } 245 } 246 truncateSize := int64(len(roots)) * crypto.HashSize 247 if err := mr.rootsFile.Truncate(truncateSize); err != nil { 248 return errors.AddContext(err, "failed to truncate file") 249 } 250 return nil 251 } 252 253 // isIndexCached determines if the root at index i is already cached in 254 // mr.cachedSubTree or if it is still in mr.uncachedRoots. It will return true 255 // or false and the index of the root in the corresponding data structure. 256 func (mr *merkleRoots) isIndexCached(i int) (int, bool) { 257 if i/merkleRootsPerCache == len(mr.cachedSubTrees) { 258 // Root is not cached. Return the false and the position in 259 // mr.uncachedRoots 260 return i - len(mr.cachedSubTrees)*merkleRootsPerCache, false 261 } 262 return i / merkleRootsPerCache, true 263 } 264 265 // lenFromFile returns the number of merkle roots by computing it from the 266 // filesize. 267 func (mr *merkleRoots) lenFromFile() (int, error) { 268 size, err := mr.rootsFile.Size() 269 if err != nil { 270 return 0, err 271 } 272 273 // Sanity check contract file length. 274 if size%crypto.HashSize != 0 { 275 return 0, errors.New("contract file has unexpected length and might be corrupted") 276 } 277 return int(size / crypto.HashSize), nil 278 } 279 280 // len returns the number of merkle roots. It should always return the same 281 // number as lenFromFile. 282 func (mr *merkleRoots) len() int { 283 return mr.numMerkleRoots 284 } 285 286 // moveLastCachedSubTreeToUncached deletes the last cached subTree and appends 287 // its elements to the uncached roots. 288 func (mr *merkleRoots) moveLastCachedSubTreeToUncached() error { 289 mr.cachedSubTrees = mr.cachedSubTrees[:len(mr.cachedSubTrees)-1] 290 rootIndex := len(mr.cachedSubTrees) * merkleRootsPerCache 291 roots, err := mr.merkleRootsFromIndexFromDisk(rootIndex, mr.numMerkleRoots) 292 if err != nil { 293 return errors.AddContext(err, "failed to read cached tree's roots") 294 } 295 mr.uncachedRoots = append(mr.uncachedRoots, roots...) 296 return nil 297 } 298 299 // push appends a merkle root to the end of the contract. If the number of 300 // uncached merkle roots grows too big we cache them in a new subTree. 301 func (mr *merkleRoots) push(root crypto.Hash) error { 302 // Sanity check the number of uncached roots before adding a new one. 303 if len(mr.uncachedRoots) == merkleRootsPerCache { 304 build.Critical("the number of uncachedRoots is too big. They should've been cached by now") 305 } 306 // Calculate the root offset within the file and write it to disk. 307 rootOffset := fileOffsetFromRootIndex(mr.len()) 308 if _, err := mr.rootsFile.WriteAt(root[:], rootOffset); err != nil { 309 return err 310 } 311 // Add the root to the unached roots. 312 mr.appendRootMemory(root) 313 314 // Increment the number of roots. 315 mr.numMerkleRoots++ 316 return nil 317 } 318 319 // root returns the root of the merkle roots. 320 func (mr *merkleRoots) root() crypto.Hash { 321 tree := crypto.NewTree() 322 for _, st := range mr.cachedSubTrees { 323 if err := tree.PushSubTree(st.height, st.sum[:]); err != nil { 324 // This should never fail. 325 build.Critical(err) 326 } 327 } 328 for _, root := range mr.uncachedRoots { 329 tree.Push(root[:]) 330 } 331 return tree.Root() 332 } 333 334 // checkNewRoot returns the root of the merkleTree after appending the checkNewRoot 335 // without actually appending it. 336 func (mr *merkleRoots) checkNewRoot(newRoot crypto.Hash) crypto.Hash { 337 tree := crypto.NewCachedTree(sectorHeight) 338 for _, st := range mr.cachedSubTrees { 339 if err := tree.PushSubTree(st.height, st.sum); err != nil { 340 // This should never fail. 341 build.Critical(err) 342 } 343 } 344 for _, root := range mr.uncachedRoots { 345 tree.Push(root) 346 } 347 // Push the new root. 348 tree.Push(newRoot) 349 return tree.Root() 350 } 351 352 // merkleRoots reads all the merkle roots from disk and returns them. 353 func (mr *merkleRoots) merkleRoots() (roots []crypto.Hash, err error) { 354 // Get roots. 355 roots, err = mr.merkleRootsFromIndexFromDisk(0, mr.numMerkleRoots) 356 if err != nil { 357 return nil, err 358 } 359 // Sanity check: should have read exactly numMerkleRoots roots. 360 if len(roots) != mr.numMerkleRoots { 361 build.Critical(fmt.Sprintf("Number of merkle roots on disk (%v) doesn't match numMerkleRoots (%v)", 362 len(roots), mr.numMerkleRoots)) 363 } 364 return 365 } 366 367 // merkleRootsFrom index reads all the merkle roots in range [from;to) 368 func (mr *merkleRoots) merkleRootsFromIndexFromDisk(from, to int) ([]crypto.Hash, error) { 369 merkleRoots := make([]crypto.Hash, 0, to-from) 370 remainingData := fileOffsetFromRootIndex(to) - fileOffsetFromRootIndex(from) 371 readOff := fileOffsetFromRootIndex(from) 372 var rootsData []byte 373 for remainingData > 0 { 374 if remainingData > rootsDiskLoadBulkSize { 375 rootsData = make([]byte, rootsDiskLoadBulkSize) 376 } else { 377 rootsData = make([]byte, remainingData) 378 } 379 n, err := mr.rootsFile.ReadAt(rootsData, readOff) 380 if err == io.ErrUnexpectedEOF || err == io.EOF { 381 return nil, errors.New("merkleRootsFromIndexFromDisk failed: roots have unexpected length") 382 } 383 if err != nil { 384 return nil, err 385 } 386 roots, err := parseRootsFromData(rootsData) 387 if err != nil { 388 return nil, err 389 } 390 merkleRoots = append(merkleRoots, roots...) 391 readOff += int64(n) 392 remainingData -= int64(n) 393 } 394 return merkleRoots, nil 395 } 396 397 // prepareDelete is a helper function that returns the lastRoot and trunateSize 398 // arguments for a certain index to call delete with. 399 func (mr *merkleRoots) prepareDelete(index int) (lastRoot crypto.Hash, truncateSize int64, err error) { 400 roots, err := mr.merkleRootsFromIndexFromDisk(mr.numMerkleRoots-1, mr.numMerkleRoots) 401 if err != nil { 402 return crypto.Hash{}, 0, errors.AddContext(err, "failed to get last root") 403 } 404 if len(roots) != 1 { 405 return crypto.Hash{}, 0, fmt.Errorf("expected exactly 1 root but got %v", len(roots)) 406 } 407 return roots[0], int64((mr.numMerkleRoots - 1) * crypto.HashSize), nil 408 } 409 410 // rebuildCachedTree rebuilds the tree in mr.cachedSubTree at index i. 411 func (mr *merkleRoots) rebuildCachedTree(index int) error { 412 // Find the index of the first root of the cached tree on disk. 413 rootIndex := index * merkleRootsPerCache 414 // Read all the roots necessary for creating the cached tree. 415 roots, err := mr.merkleRootsFromIndexFromDisk(rootIndex, rootIndex+(1<<merkleRootsCacheHeight)) 416 if err != nil { 417 return errors.AddContext(err, "failed to read sectors for rebuilding cached tree") 418 } 419 // Replace the old cached tree. 420 mr.cachedSubTrees[index] = newCachedSubTree(roots) 421 return nil 422 }