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  }