Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BEP46 — Mutable Torrents #58

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ env:
global:
- secure: lLh4WEpXrERVSJlpDLA0PQ+wRG4ascs3oydkiGV3VQ0G5vTrsTzrCGy3ZTt9q9L28ThKGt5n9VIKOVFahNEM9AWuim/9scXo+bCMttlWctAudw/jtxhr1vZqPw+lb+ZVsWaexv+OgoVwzY12eVP+jh1iGr7M1PcmDh8ardDyGQ0=
- secure: DP949nTUjP1b92+Xq9JfWgaoVU7GWcWhLo6zuQpTv5Y06N6/677Z926SghysdA/OO8OYtHemPIc096oYIBKzK0Lhe772l7f/Lse81gJ78ggWaFrOGZWZ32KapWqVuOxn4OPXn8tBRPrvNN4ApB3I8gY/mfGkPfRnmOIfA2nDZrU=
matrix:
- CXX=g++-4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
31 changes: 29 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var fs = require('fs') // browser exclude
var get = require('simple-get')
var magnet = require('magnet-uri')
var parseTorrentFile = require('parse-torrent-file')
var crypto = require('crypto')

module.exports.toMagnetURI = magnet.encode
module.exports.toTorrentFile = parseTorrentFile.encode
Expand All @@ -20,7 +21,9 @@ module.exports.toTorrentFile = parseTorrentFile.encode
function parseTorrent (torrentId) {
if (typeof torrentId === 'string' && /^(stream-)?magnet:/.test(torrentId)) {
// magnet uri (string)
return magnet(torrentId)
var m = magnet(torrentId)
if (m.xs) throw new Error('Exact source magnets need to call .remote()')
return m
} else if (typeof torrentId === 'string' && (/^[a-f0-9]{40}$/i.test(torrentId) || /^[a-z2-7]{32}$/i.test(torrentId))) {
// info hash (hex/base-32 string)
return magnet('magnet:?xt=urn:btih:' + torrentId)
Expand All @@ -43,7 +46,8 @@ function parseTorrent (torrentId) {
}
}

function parseTorrentRemote (torrentId, cb) {
function parseTorrentRemote (opts, cb) {
var torrentId = (typeof opts === 'object' && !isBlob(opts)) ? opts.torrentId : opts
var parsedTorrent
if (typeof cb !== 'function') throw new Error('second argument must be a Function')

Expand All @@ -63,6 +67,29 @@ function parseTorrentRemote (torrentId, cb) {
if (err) return cb(new Error('Error converting Blob: ' + err.message))
parseOrThrow(torrentBuf)
})
} else if (opts.dht && /^(stream-)?magnet:/.test(torrentId)) {
var m = magnet(torrentId)
if (!m.xs) {
process.nextTick(function () {
cb(new Error('Missing xs (exact source) in magnet URI'))
})
} else {
if ((m = m.xs.match(/^urn:btpk:(.{64})/))) {
var publicKey = m[1].toLowerCase()
var publicKeyBuf = Buffer.from(publicKey, 'hex')
var targetId = crypto.createHash('sha1').update(publicKeyBuf).digest('hex') // XXX missing salt

opts.dht.get(targetId, function (err, res) {
if (err) return cb(new Error('Error finding this publicKey in the DHT'))
if (res.v && !res.v.ih) return cb(new Error('Found publicKey in DHT, but no torrent inside'))
parseOrThrow(res.v.ih)
})
} else {
process.nextTick(function () {
cb(new Error('Can\'t find publicKey in magnet URI'))
})
}
}
} else if (typeof get === 'function' && /^https?:/.test(torrentId)) {
// http, or https url to torrent file
get.concat({
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
},
"devDependencies": {
"airtap": "0.0.4",
"bittorrent-dht": "^7.3.1",
"brfs": "^1.0.0",
"ed25519-supercop": "^1.0.2",
"standard": "*",
"tape": "^4.0.0",
"webtorrent-fixtures": "^1.0.0",
Expand Down
73 changes: 73 additions & 0 deletions test/basic-node.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
var fixtures = require('webtorrent-fixtures')
var http = require('http')
var DHT = require('bittorrent-dht')
var ed = require('ed25519-supercop')
var parseTorrent = require('../')
var crypto = require('crypto')
var test = require('tape')

test('http url to a torrent file, string', function (t) {
Expand Down Expand Up @@ -30,3 +33,73 @@ test('filesystem path to a torrent file, string', function (t) {
t.deepEqual(parsedTorrent, fixtures.leaves.parsedTorrent)
})
})

test('dht put/get of torrent (BEP46)', function (t) {
t.plan(6)

var infoHashBuf = Buffer.from(fixtures.numbers.parsedTorrent.infoHash, 'hex')
t.equal(infoHashBuf.length, 20, 'infoHashBuf is 20 bytes')

var keypair = ed.createKeyPair(ed.createSeed())

var dht = new DHT({ bootstrap: false, verify: ed.verify })
t.once('end', function () {
dht.destroy()
})

dht.on('warning', function (err) { t.fail(err) })
dht.on('error', function (err) { t.fail(err) })

dht.on('ready', function () {
var opts = {
k: keypair.publicKey,
sign: function (buf) {
return ed.sign(buf, keypair.publicKey, keypair.secretKey)
},
seq: 0,
v: {
ih: infoHashBuf
}
}

var expectedHash = crypto.createHash('sha1').update(opts.k).digest()

dht.put(opts, function (_, hash) {
t.equal(
hash.toString('hex'),
expectedHash.toString('hex'),
'hash of the public key'
)

// should perform a dht.get
parseTorrent.remote({
torrentId: 'magnet:?xs=urn:btpk:' + keypair.publicKey.toString('hex'),
dht: dht
}, function (err, parsedTorrent) {
t.ifError(err)
t.equal(infoHashBuf.toString('hex'), parsedTorrent.infoHash,
'got back what we put in'
)

// put a value without infohash (should throw)
opts.v = 'foo'
opts.seq++
dht.put(opts, function (_, hash) {
t.equal(
hash.toString('hex'),
expectedHash.toString('hex'),
'hash of the public key'
)

parseTorrent.remote({
torrentId: 'magnet:?xs=urn:btpk:' + keypair.publicKey.toString('hex'),
dht: dht
}, function (err, parsedTorrent) {
if (err) t.pass(err)
else t.fail('should have errored')
})
})
})
})
})
})
7 changes: 7 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ test('torrent file missing `name` field throws', function (t) {
t.end()
})

test('parsing `xs` should happen with .remote()', function (t) {
t.throws(function () {
parseTorrent('magnet:?xs=urn:btpk:8543d3e6115f0f98c944077a4493dcd543e49c739fd998550a1f614ab36ed63e')
})
t.end()
})

test('parse url-list for webseed support', function (t) {
var torrent = parseTorrent(fixtures.bunny.torrent)
t.deepEqual(torrent.urlList, [ 'http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_stereo_abl.mp4' ])
Expand Down