Skip to content

Commit

Permalink
feat: upgrade the discovery service to random-walk
Browse files Browse the repository at this point in the history
  • Loading branch information
daviddias committed Mar 15, 2018
1 parent be12317 commit b8e0f72
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 112 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ The libp2p-kad-dht module offers 3 APIs: Peer Routing, Content Routing and Peer

[![](https://github.com/libp2p/interface-peer-discovery/raw/master/img/badge.png)](https://github.com/libp2p/interface-peer-discovery)

`libp2p-kad-dht` provides a discovery service called `Random Walk` (random walks on the DHT to discover more nodes). It is accessible through `dht.randomWalk` and exposes the [Peer Discovery interface](https://github.com/libp2p/interface-peer-discovery).

## Contribute

Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/js-libp2p-ipfs/issues)!
Expand Down
52 changes: 9 additions & 43 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const errors = require('./errors')
const privateApi = require('./private')
const Providers = require('./providers')
const Message = require('./message')
const RandomWalk = require('./random-walk')
const assert = require('assert')

/**
Expand Down Expand Up @@ -88,6 +89,13 @@ class KadDHT {
// Inject private apis so we don't clutter up this file
const pa = privateApi(this)
Object.keys(pa).forEach((name) => { this[name] = pa[name] })

/**
* Provider management
*
* @type {RandomWalk}
*/
this.randomWalk = new RandomWalk(this)
}

/**
Expand Down Expand Up @@ -119,7 +127,7 @@ class KadDHT {
*/
stop (callback) {
this._running = false
this.bootstrapStop()
this.randomWalk.stop()
this.providers.stop()
this.network.stop(callback)
}
Expand Down Expand Up @@ -514,48 +522,6 @@ class KadDHT {
callback(null, this.peerBook.get(p))
})
}

/**
* Start the bootstrap process. This means running a number of queries every interval requesting random data.
* This is done to keep the dht healthy over time.
*
* @param {number} [queries=1] - how many queries to run per period
* @param {number} [period=300000] - how often to run the the bootstrap process, in milliseconds (5min)
* @param {number} [maxTimeout=10000] - how long to wait for the the bootstrap query to run, in milliseconds (10s)
* @returns {void}
*/
bootstrapStart (queries, period, maxTimeout) {
if (queries == null) {
queries = 1
}
if (period == null) {
period = 5 * c.minute
}
if (maxTimeout == null) {
maxTimeout = 10 * c.second
}

// Don't run twice
if (this._bootstrapRunning) {
return
}

this._bootstrapRunning = setInterval(
() => this._bootstrap(queries, maxTimeout),
period
)
}

/**
* Stop the bootstrap process.
*
* @returns {void}
*/
bootstrapStop () {
if (this._bootstrapRunning) {
clearInterval(this._bootstrapRunning)
}
}
}

module.exports = KadDHT
68 changes: 0 additions & 68 deletions src/private.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ const waterfall = require('async/waterfall')
const series = require('async/series')
const each = require('async/each')
const timeout = require('async/timeout')
const times = require('async/times')
const crypto = require('libp2p-crypto')
const PeerInfo = require('peer-info')
const multihashing = require('multihashing-async')

const utils = require('./utils')
const errors = require('./errors')
Expand Down Expand Up @@ -569,70 +566,5 @@ module.exports = (dht) => ({
_findProvidersSingle (peer, key, callback) {
const msg = new Message(Message.TYPES.GET_PROVIDERS, key.buffer, 0)
dht.network.sendRequest(peer, msg, callback)
},
/**
* Do the bootstrap work.
*
* @param {number} queries
* @param {number} maxTimeout
* @returns {void}
*
* @private
*/
_bootstrap (queries, maxTimeout) {
dht._log('bootstrap:start')
times(queries, (i, cb) => {
waterfall([
(cb) => this._generateBootstrapId(cb),
(id, cb) => timeout((cb) => {
this._bootstrapQuery(id, cb)
}, maxTimeout)(cb)
], (err) => {
if (err) {
dht._log.error('bootstrap:error', err)
}
dht._log('bootstrap:done')
})
})
},
/**
* The query run during a bootstrap request.
*
* @param {PeerId} id
* @param {function(Error)} callback
* @returns {void}
*
* @private
*/
_bootstrapQuery (id, callback) {
dht._log('bootstrap:query:%s', id.toB58String())
this.findPeer(id, (err, peer) => {
if (err instanceof errors.NotFoundError) {
// expected case, we asked for random stuff after all
return callback()
}
if (err) {
return callback(err)
}
dht._log('bootstrap:query:found', err, peer)
// wait what, there was something found?
callback(new Error(`Bootstrap peer: ACTUALLY FOUND PEER: ${peer}, ${id.toB58String()}`))
})
},
/**
* Generate a random peer id for bootstrapping purposes.
*
* @param {function(Error, PeerId)} callback
* @returns {void}
*
* @private
*/
_generateBootstrapId (callback) {
multihashing(crypto.randomBytes(16), 'sha2-256', (err, digest) => {
if (err) {
return callback(err)
}
callback(null, new PeerId(digest))
})
}
})
125 changes: 125 additions & 0 deletions src/random-walk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
'use strict'

const times = require('async/times')
const crypto = require('libp2p-crypto')
const waterfall = require('async/waterfall')
const timeout = require('async/timeout')
const multihashing = require('multihashing-async')
const PeerId = require('peer-id')
const assert = require('assert')
const errors = require('./errors')
const c = require('./constants')

class RandomWalk {
constructor (kadDHT) {
assert(kadDHT, 'Random Walk needs an instance of the Kademlia DHT')
this._running = false
this._kadDHT = kadDHT
}

/**
* Start the Random Walk process. This means running a number of queries
* every interval requesting random data. This is done to keep the dht
* healthy over time.
*
* @param {number} [queries=1] - how many queries to run per period
* @param {number} [period=300000] - how often to run the the random-walk process, in milliseconds (5min)
* @param {number} [maxTimeout=10000] - how long to wait for the the random-walk query to run, in milliseconds (10s)
* @returns {void}
*/
start (queries, period, maxTimeout) {
if (queries == null) { queries = 1 }
if (period == null) { period = 5 * c.minute }
if (maxTimeout == null) { maxTimeout = 10 * c.second }
// Don't run twice
if (this._running) { return }

this._running = setInterval(
() => this._walk(queries, maxTimeout),
period
)
}

/**
* Stop the random-walk process.
*
* @returns {void}
*/
stop () {
if (this._running) {
clearInterval(this._running)
}
}

/**
* Do the random walk work.
*
* @param {number} queries
* @param {number} maxTimeout
* @returns {void}
*
* @private
*/
_walk (queries, maxTimeout) {
this._kadDHT._log('random-walk:start')

times(queries, (i, cb) => {
waterfall([
(cb) => this._randomPeerId(cb),
(id, cb) => timeout((cb) => {
this._query(id, cb)
}, maxTimeout)(cb)
], (err) => {
if (err) { return this._kadDHT._log.error('random-walk:error', err) }

this._kadDHT._log('random-walk:done')
})
})
}

/**
* The query run during a random walk request.
*
* @param {PeerId} id
* @param {function(Error)} callback
* @returns {void}
*
* @private
*/
_query (id, callback) {
this._kadDHT._log('random-walk:query:%s', id.toB58String())

this._kadDHT.findPeer(id, (err, peer) => {
if (err instanceof errors.NotFoundError) {
// expected case, we asked for random stuff after all
return callback()
}
if (err) {
return callback(err)
}
this._kadDHT._log('random-walk:query:found', err, peer)

// wait what, there was something found? Lucky day!
callback(new Error(`random-walk: ACTUALLY FOUND PEER: ${peer}, ${id.toB58String()}`))
})
}

/**
* Generate a random peer id for random-walk purposes.
*
* @param {function(Error, PeerId)} callback
* @returns {void}
*
* @private
*/
_randomPeerId (callback) {
multihashing(crypto.randomBytes(16), 'sha2-256', (err, digest) => {
if (err) {
return callback(err)
}
callback(null, new PeerId(digest))
})
}
}

module.exports = RandomWalk
2 changes: 1 addition & 1 deletion test/kad-dht.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function connect (a, b, callback) {

function bootstrap (dhts) {
dhts.forEach((dht) => {
dht._bootstrap(3, 10000)
dht.randomWalk._walk(3, 10000)
})
}

Expand Down

0 comments on commit b8e0f72

Please sign in to comment.