bep52.go 1.6 KB

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