Giter VIP home page Giter VIP logo

bittorrent-dht's Introduction

bittorrent-dht build npm npm downloads gittip

Simple, robust, BitTorrent DHT implementation

Node.js implementation of the BitTorrent DHT protocol. BitTorrent DHT is the main peer discovery layer for BitTorrent, which allows for trackerless torrents. DHTs are awesome!

This module is used by WebTorrent.

features

  • complete implementation of the DHT protocol in JavaScript
  • follows the spec
  • robust and well-tested (comprehensive test suite, and used by WebTorrent and peerflix)
  • efficient recursive lookup algorithm minimizes UDP traffic
  • supports multiple, concurrent lookups using the same routing table

install

npm install bittorrent-dht

example

npm install magnet-uri
var DHT    = require('bittorrent-dht')
var magnet = require('magnet-uri')

var uri = 'magnet:?xt=urn:btih:e3811b9539cacff680e418124272177c47477157'
var parsed = magnet(uri)

console.log(parsed.infoHash) // 'e3811b9539cacff680e418124272177c47477157'

var dht = new DHT()

dht.listen(20000, function () {
  console.log('now listening')
})

dht.on('ready', function () {
  // DHT is ready to use (i.e. the routing table contains at least K nodes, discovered
  // via the bootstrap nodes)

  // find peers for the given torrent info hash
  dht.lookup(parsed.infoHash)
})

dht.on('peer', function (addr, hash, from) {
  console.log('found potential peer ' + addr + ' through ' + from)
})

api

dht = new DHT([opts])

Create a new dht instance.

If opts is specified, then the default options (shown below) will be overridden.

{
  nodeId: '',   // 160-bit DHT node ID (Buffer or hex string, default: randomly generated)
  bootstrap: [], // bootstrap servers (default: router.bittorrent.com:6881, router.utorrent.com:6881, dht.transmissionbt.com:6881)
  data: {} // seed data that will be served to peers using immutable or mutable GET
}

data, if present, is an object of the following form:

{
  "<SHA1 key>": {
    "v:" <value raw bytes>,
    "expires": unix timestamp, // when value should expire
    // the following only present if the value is mutable
    "seq": integer,
    "salt": <optional arbitrary bytes>,
    "key": <ed25519 public key>,
    "sig": <ed25519 signature>,
  },
  ...
}

dht.lookup(infoHash, [callback])

Find peers for the given info hash.

This does a recursive lookup in the DHT. Potential peers that are discovered are emitted as peer events. See the peer event below for more info.

infoHash can be a string or Buffer. callback is called when the recursive lookup has terminated, and is called with two paramaters. The first is an Error or null. The second is an array of the K closest nodes. You usually don't need to use this info and can simply listen for peer events.

Note: dht.lookup() should only be called after the ready event has fired, otherwise the lookup may fail because the DHT routing table doesn't contain enough nodes.

dht.immutableGet(key, [callback])

Retrieves immutable data for the given key.

This behaves similarly to lookup in that the routing table will be filled as the iterative lookup proceeds.

callback is called with err and data, where data is the under-1000 byte value associated with the key. It is checked for integrity with its key internally.

As the data is immutable, the callback will be called as soon as any node answers with correct data.

dht.mutableGet(publicKey, [salt], [callback])

Retrieves mutable data for the given public key and optional "salt", or sub-key.

This behaves similarly to lookup in that the routing table will be filled as the iterative lookup proceeds.

callback is called with err, data, and meta, where data is the under-1000 byte value associated with the key, and meta is an object containing the seq sequence number sig signature, and key public key associated with the value.

As the data is mutable, the callback will only be called once all K nodes who could possibly be storing the data answer or timeout. The data with the highest seq number will be returned.

dht.listen([port], [onlistening])

Make the DHT listen on the given port. If port is undefined, an available port is automatically picked.

If onlistening is defined, it is attached to the listening event.

dht.announce(infoHash, port, [callback])

Announce that the peer, controlling the querying node, is downloading a torrent on a port.

If dht.announce is called soon (< 5 minutes) after dht.lookup, then the routing table generated during the lookup can be re-used, because the tokens sent by each node will still be valid.

If dht.announce is called and there is no cached routing table, then a dht.lookup will first be performed to discover relevant nodes and get valid tokens from each of them. This will take longer.

A "token" is an opaque value that must be presented for a node to announce that its controlling peer is downloading a torrent. It must present the token received from the same queried node in a recent query for peers. This is to prevent malicious hosts from signing up other hosts for torrents. All token management is handled internally by this module.

callback will be called when the announce operation has completed, and is called with a single parameter that is an Error or null.

key = dht.immutablePut(data, [callback])

Put some data in the DHT. data an arbitrary byte array, under 1000 bytes. The data will be hashed with SHA1 to determine its location in the DHT.

callback will be called when the data is stored with sufficient replication, and will be called with a node-style err parameter and the key at which the data was stored.

The method itself will also return the key used, as a convenience:

key = dht.immutablePut(data);

key = dht.mutablePut(data, {opts}, [callback])

Put some data in the DHT. data an arbitrary byte array, under 1000 bytes. opts contains the follwing named arguments:

  • type (either mutable or immutable, default 'immutable').
    • If immutable, all other options will be ignored, and the value will be SHA1-hashed to determine its location in the DHT.
    • If mutable, the other options MUST be specified:
      • signingKeyPair: an ed25519 key pair with which to sign the data.
      • salt: an optional arbitrary value to disambiguate this mutable put from other mutable puts using the same signing key. Think of it as a 'personal' name space for your public key.
      • seq: a non-negative integer to break ties between data stored with the same signing key and salt: if a node already has a value, then it will reject additional PUTs to the same key unless the value is "newer", i.e., the requested sequence number is greater than its value's sequence number.

TODO: The cas operation is not supported.

callback will be called when the data is stored with sufficient replication, and will be called with a node-style err parameter and the key at which the data was stored.

The method itself will also return the key used, as a convenience:

key = dht.mutablePut(data, opts);

obj = dht.getStoredData()

Returns the data stored by this peer in as a javascript object, keyed by the actual DHT key used. Along with dht.toArray(), this can be used to restore the state of the DHT between restarts.

arr = dht.toArray()

Returns the nodes in the DHT as an array. This is useful for persisting the DHT to disk between restarts of a BitTorrent client (as recommended by the spec). Each node in the array is an object with id (hex string) and addr (string) properties.

To restore the DHT nodes when instantiating a new DHT object, simply pass in the array as the value of the bootstrap option.

var dht1 = new DHT()

// some time passes ...

// destroy the dht
var arr = dht1.toArray()
var data = dht1.getStoredData()
dht1.destroy()

// some time passes ...

// initialize a new dht with the same routing table as the first
var dht2 = new DHT({ bootstrap: arr, data: data })

dht.addNode(addr, [nodeId])

Manually add a node to the DHT routing table. If there is space in the routing table (or an unresponsive node can be evicted to make space), the node will be added. If not, the node will not be added. This is useful to call when a peer wire sends a PORT message to share their DHT port.

If nodeId is undefined, then the peer will be pinged to learn their node id. If the peer does not respond, the will not be added to the routing table.

dht.destroy([callback])

Destroy the DHT. Closes the socket and cleans up large data structure resources.

events

dht.on('ready', function () { ... })

Emitted when the DHT is ready to handle lookups (i.e. the routing table is sufficiently populated via the bootstrap nodes).

Note: If you initialize the DHT with the { bootstrap: false } option, then the 'ready' event will fire on the next tick even if there are not any nodes in the routing table. It is assumed that you will manually populate the routing table with dht.addNode if you pass this option.

dht.on('listening', function (port) { ... })

Emitted when the DHT is listening.

dht.on('peer', function (addr, infoHash, from) { ... })

Emitted when a potential peer is found. addr is of the form IP_ADDRESS:PORT. infoHash is the torrent info hash of the swarm that the peer belongs to. Emitted in response to a lookup(infoHash) call.

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

Emitted when the DHT gets an unexpected message from another DHT node. This is purely informational.

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

Emitted when the DHT has a fatal error.

internal events

dht.on('node', function (addr, nodeId, from) { ... })

Emitted when the DHT finds a new node.

dht.on('announce', function (addr, infoHash) { ... })

Emitted when a peer announces itself in order to be stored in the DHT.

dht.on('put', function (addr, msg) { ... })

Emitted when a peer sends data to be stored in the DHT. msg is simply the contents of the a value in the DHT message.

further reading

license

MIT. Copyright (c) Feross Aboukhadijeh.

bittorrent-dht's People

Contributors

alexandergugel avatar arestov avatar feross avatar ivantodorovich avatar luoyetx avatar mafintosh avatar qqueue avatar rabidaudio avatar transitive-bullshit avatar xaiki avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.