-
-
Notifications
You must be signed in to change notification settings - Fork 628
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
448 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// This is an alternate to cmd/torrent which has become bloated with awful argument parsing. Since | ||
// this is my most complicated binary, I will try to build something that satisfies only what I need | ||
// here. | ||
package main | ||
|
||
import ( | ||
"github.com/anacrolix/torrent/metainfo" | ||
"os" | ||
) | ||
|
||
type argError struct { | ||
err error | ||
} | ||
|
||
func assertOk(err error) { | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
func bail(str string) { | ||
panic(str) | ||
} | ||
|
||
func main() { | ||
args := os.Args[1:] | ||
map[string]func(){ | ||
"metainfo": func() { | ||
map[string]func(){ | ||
"validate-v2": func() { | ||
mi, err := metainfo.LoadFromFile(args[2]) | ||
assertOk(err) | ||
info, err := mi.UnmarshalInfo() | ||
assertOk(err) | ||
if !info.HasV2() { | ||
bail("not a v2 torrent") | ||
} | ||
err = metainfo.ValidatePieceLayers(mi.PieceLayers, &info.FileTree, info.PieceLength) | ||
assertOk(err) | ||
}, | ||
}[args[1]]() | ||
}, | ||
}[args[0]]() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package merkle | ||
|
||
import ( | ||
"crypto/sha256" | ||
"fmt" | ||
g "github.com/anacrolix/generics" | ||
"math/bits" | ||
) | ||
|
||
func Root(hashes [][sha256.Size]byte) [sha256.Size]byte { | ||
if len(hashes) <= 1 { | ||
return hashes[0] | ||
} | ||
numHashes := uint(len(hashes)) | ||
if numHashes != RoundUpToPowerOfTwo(uint(len(hashes))) { | ||
panic(fmt.Sprintf("expected power of two number of hashes, got %d", numHashes)) | ||
} | ||
var next [][sha256.Size]byte | ||
for i := 0; i < len(hashes); i += 2 { | ||
left := hashes[i] | ||
right := hashes[i+1] | ||
h := sha256.Sum256(append(left[:], right[:]...)) | ||
next = append(next, h) | ||
} | ||
return Root(next) | ||
} | ||
|
||
func CompactLayerToSliceHashes(compactLayer string) (hashes [][sha256.Size]byte, err error) { | ||
g.MakeSliceWithLength(&hashes, len(compactLayer)/sha256.Size) | ||
for i := range hashes { | ||
n := copy(hashes[i][:], compactLayer[i*sha256.Size:]) | ||
if n != sha256.Size { | ||
err = fmt.Errorf("compact layer has incomplete hash at index %d", i) | ||
return | ||
} | ||
} | ||
return | ||
} | ||
|
||
func RoundUpToPowerOfTwo(n uint) (ret uint) { | ||
return 1 << bits.Len(n-1) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package metainfo | ||
|
||
import ( | ||
"fmt" | ||
"github.com/anacrolix/torrent/merkle" | ||
) | ||
|
||
func ValidatePieceLayers( | ||
pieceLayers map[string]string, | ||
fileTree *FileTree, | ||
pieceLength int64, | ||
) (err error) { | ||
fileTree.Walk(nil, func(path []string, ft *FileTree) { | ||
if err != nil { | ||
return | ||
} | ||
if ft.IsDir() { | ||
return | ||
} | ||
piecesRoot := ft.PiecesRootAsByteArray() | ||
if !piecesRoot.Ok { | ||
return | ||
} | ||
filePieceLayers, ok := pieceLayers[string(piecesRoot.Value[:])] | ||
if !ok { | ||
// BEP 52: "For each file in the file tree that is larger than the piece size it | ||
// contains one string value.". The reference torrent creator in | ||
// https://blog.libtorrent.org/2020/09/bittorrent-v2/ also has this. I'm not sure what | ||
// harm it causes if it's present anyway, possibly it won't be useful to us. | ||
if ft.File.Length > pieceLength { | ||
err = fmt.Errorf("no piece layers for file %q", path) | ||
} | ||
return | ||
} | ||
var layerHashes [][32]byte | ||
layerHashes, err = merkle.CompactLayerToSliceHashes(filePieceLayers) | ||
padHash := HashForPiecePad(pieceLength) | ||
for uint(len(layerHashes)) < merkle.RoundUpToPowerOfTwo(uint(len(layerHashes))) { | ||
layerHashes = append(layerHashes, padHash) | ||
} | ||
var root [32]byte | ||
root = merkle.Root(layerHashes) | ||
if root != piecesRoot.Value { | ||
err = fmt.Errorf("file %q: expected hash %x got %x", path, piecesRoot.Value, root) | ||
return | ||
} | ||
}) | ||
return | ||
} | ||
|
||
// Returns the padding hash for the hash layer corresponding to a piece. It can't be zero because | ||
// that's the bottom-most layer (the hashes for the smallest blocks). | ||
func HashForPiecePad(pieceLength int64) (hash [32]byte) { | ||
// This should be a power of two, and probably checked elsewhere. | ||
blocksPerPiece := pieceLength / (1 << 14) | ||
blockHashes := make([][32]byte, blocksPerPiece) | ||
return merkle.Root(blockHashes) | ||
} |
Oops, something went wrong.