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 }