github.com/anacrolix/torrent@v1.61.0/metainfo/bep52.go (about)

     1  package metainfo
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/anacrolix/torrent/merkle"
     7  )
     8  
     9  func ValidatePieceLayers(
    10  	pieceLayers map[string]string,
    11  	fileTree *FileTree,
    12  	pieceLength int64,
    13  ) (err error) {
    14  	fileTree.Walk(nil, func(path []string, ft *FileTree) {
    15  		if err != nil {
    16  			return
    17  		}
    18  		if ft.IsDir() {
    19  			return
    20  		}
    21  		piecesRoot := ft.PiecesRootAsByteArray()
    22  		if !piecesRoot.Ok {
    23  			return
    24  		}
    25  		filePieceLayers, ok := pieceLayers[string(piecesRoot.Value[:])]
    26  		if !ok {
    27  			// BEP 52: "For each file in the file tree that is larger than the piece size it
    28  			// contains one string value.". The reference torrent creator in
    29  			// https://blog.libtorrent.org/2020/09/bittorrent-v2/ also has this. If a file is equal
    30  			// to or smaller than the piece length, we can just use the pieces root instead of the
    31  			// piece layer hash.
    32  			if ft.File.Length > pieceLength {
    33  				err = fmt.Errorf("no piece layers for file %q", path)
    34  			}
    35  			return
    36  		}
    37  		var layerHashes [][32]byte
    38  		layerHashes, err = merkle.CompactLayerToSliceHashes(filePieceLayers)
    39  		root := merkle.RootWithPadHash(layerHashes, HashForPiecePad(pieceLength))
    40  		if root != piecesRoot.Value {
    41  			err = fmt.Errorf("file %q: expected hash %x got %x", path, piecesRoot.Value, root)
    42  			return
    43  		}
    44  	})
    45  	return
    46  }
    47  
    48  // Returns the padding hash for the hash layer corresponding to a piece. It can't be zero because
    49  // that's the bottom-most layer (the hashes for the smallest blocks).
    50  func HashForPiecePad(pieceLength int64) (hash [32]byte) {
    51  	// This should be a power of two, and probably checked elsewhere.
    52  	blocksPerPiece := pieceLength / (1 << 14)
    53  	blockHashes := make([][32]byte, blocksPerPiece)
    54  	return merkle.Root(blockHashes)
    55  }