]> Sergey Matveev's repositories - btrtrc.git/blob - metainfo/bep52.go
Implement reading piece hashes from peers
[btrtrc.git] / metainfo / bep52.go
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 }