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