gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/skyfilefanout.go (about) 1 package renter 2 3 // skyfilefanout.go implements the encoding and decoding of skyfile fanouts. A 4 // fanout is a description of all of the Merkle roots in a file, organized by 5 // chunk. Each chunk has N pieces, and each piece has a Merkle root which is a 6 // 32 byte hash. 7 // 8 // The fanout is encoded such that the first 32 bytes are chunk 0 index 0, the 9 // second 32 bytes are chunk 0 index 1, etc... and then the second chunk is 10 // appended immediately after, and so on. 11 12 import ( 13 "fmt" 14 15 "gitlab.com/NebulousLabs/errors" 16 "gitlab.com/SkynetLabs/skyd/build" 17 "gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem" 18 "gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem/siafile" 19 "go.sia.tech/siad/crypto" 20 ) 21 22 // skyfileEncodeFanoutFromFileNode will create the serialized fanout for 23 // a fileNode. The encoded fanout is just the list of hashes that can be used to 24 // retrieve a file concatenated together, where piece 0 of chunk 0 is first, 25 // piece 1 of chunk 0 is second, etc. This method assumes the special case for 26 // unencrypted 1-of-N files. Because every piece is identical for an unencrypted 27 // 1-of-N file, only the first piece of each chunk is included. 28 func skyfileEncodeFanoutFromFileNode(fileNode *filesystem.FileNode, onePiece bool) ([]byte, error) { 29 // Allocate the memory for the fanout. 30 fanout := make([]byte, 0, fileNode.NumChunks()*crypto.HashSize) 31 32 // findPieceInPieceSet will scan through a piece set and return the first 33 // non-empty piece in the set. If the set is empty, or every piece in the 34 // set is empty, then the emptyHash is returned. 35 var emptyHash crypto.Hash 36 findPieceInPieceSet := func(pieceSet []siafile.Piece) crypto.Hash { 37 for _, piece := range pieceSet { 38 if piece.MerkleRoot != emptyHash { 39 return piece.MerkleRoot 40 } 41 } 42 return emptyHash 43 } 44 45 // Build the fanout one chunk at a time. 46 for i := uint64(0); i < fileNode.NumChunks(); i++ { 47 // Get the pieces for this chunk. 48 allPieces, err := fileNode.Pieces(i) 49 if err != nil { 50 return nil, errors.AddContext(err, "unable to get sector roots from file") 51 } 52 53 // Special case: if only one piece is needed, only use the first piece 54 // that is available. This is because 1-of-N files are encoded more 55 // compactly in the fanout. 56 if onePiece { 57 root := emptyHash 58 for _, pieceSet := range allPieces { 59 root = findPieceInPieceSet(pieceSet) 60 if root != emptyHash { 61 fanout = append(fanout, root[:]...) 62 break 63 } 64 } 65 // If root is still equal to emptyHash it means that we didn't add a 66 // piece root for this chunk. 67 if root == emptyHash { 68 err = fmt.Errorf("No piece root encoded for chunk %v", i) 69 build.Critical(err) 70 return nil, err 71 } 72 continue 73 } 74 75 // Generate all the piece roots 76 for pi, pieceSet := range allPieces { 77 root := findPieceInPieceSet(pieceSet) 78 if root == emptyHash { 79 err = fmt.Errorf("Empty piece root at index %v found for chunk %v", pi, i) 80 build.Critical(err) 81 return nil, err 82 } 83 fanout = append(fanout, root[:]...) 84 } 85 } 86 return fanout, nil 87 }