Skip to content

Commit

Permalink
feat: rendezvous integration
Browse files Browse the repository at this point in the history
  • Loading branch information
vasco-santos committed Nov 9, 2020
1 parent 49aa13a commit 699b4e5
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 17 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ List of packages currently in existence for libp2p
| **peer routing** |
| [`libp2p-delegated-peer-routing`](//github.com/libp2p/js-libp2p-delegated-peer-routing) | [![npm](https://img.shields.io/npm/v/libp2p-delegated-peer-routing.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-delegated-peer-routing/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-delegated-peer-routing) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-delegated-peer-routing/master)](https://travis-ci.com/libp2p/js-libp2p-delegated-peer-routing) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-delegated-peer-routing) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-kad-dht`](//github.com/libp2p/js-libp2p-kad-dht) | [![npm](https://img.shields.io/npm/v/libp2p-kad-dht.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-kad-dht/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-kad-dht.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-kad-dht) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-kad-dht/master)](https://travis-ci.com/libp2p/js-libp2p-kad-dht) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-kad-dht/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-kad-dht) | [Vasco Santos](mailto:vasco.santos@moxy.studio) |
| **service discovery** |
| [`libp2p-rendezvous`](//github.com/libp2p/js-libp2p-rendezvous) | [![npm](https://img.shields.io/npm/v/libp2p-rendezvous.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-rendezvous/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-rendezvous.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-rendezvous) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-rendezvous/master)](https://travis-ci.com/libp2p/js-libp2p-rendezvous) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-rendezvous/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-rendezvous) | [Vasco Santos](mailto:santos.vasco10@gmail.com) |
| **utilities** |
| [`libp2p-crypto`](//github.com/libp2p/js-libp2p-crypto) | [![npm](https://img.shields.io/npm/v/libp2p-crypto.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-crypto/master)](https://travis-ci.com/libp2p/js-libp2p-crypto) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto) | [Jacob Heun](mailto:jacobheun@gmail.com) |
| [`libp2p-crypto-secp256k1`](//github.com/libp2p/js-libp2p-crypto-secp256k1) | [![npm](https://img.shields.io/npm/v/libp2p-crypto-secp256k1.svg?maxAge=86400&style=flat-square)](//github.com/libp2p/js-libp2p-crypto-secp256k1/releases) | [![Deps](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-crypto-secp256k1) | [![Travis CI](https://flat.badgen.net/travis/libp2p/js-libp2p-crypto-secp256k1/master)](https://travis-ci.com/libp2p/js-libp2p-crypto-secp256k1) | [![codecov](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1/branch/master/graph/badge.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-crypto-secp256k1) | [Friedel Ziegelmayer](mailto:dignifiedquire@gmail.com) |
Expand Down
63 changes: 63 additions & 0 deletions doc/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [Setup with Content and Peer Routing](#setup-with-content-and-peer-routing)
- [Setup with Relay](#setup-with-relay)
- [Setup with Auto Relay](#setup-with-auto-relay)
- [Setup with Rendezvous](#setup-with-rendezvous)
- [Setup with Keychain](#setup-with-keychain)
- [Configuring Dialing](#configuring-dialing)
- [Configuring Connection Manager](#configuring-connection-manager)
Expand Down Expand Up @@ -457,6 +458,68 @@ const node = await Libp2p.create({
})
```

#### Setup with Rendezvous

You will need to setup a rendezvous server, which will be used by rendezvous client nodes.

A rendezvous server can be configured as follows:

```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const Rendezvous = require('libp2p-rendezvous')

const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE],
rendezvous: Rendezvous
},
config: {
rendezvous: { // Rendezvous options (this config is part of libp2p core configurations)
server: {
enabled: true, // Allows you to be a rendezvous server for other peers
gcInterval: 3e5 // Interval for gc to check outdated rendezvous registrations
}
}
}
})
```

A rendezvous client only needs the rendezvous module. However, it will need to discover and get connected with a rendezvous server. A good option is to leverage the bootstrap module for this.

```js
const Libp2p = require('libp2p')
const TCP = require('libp2p-tcp')
const MPLEX = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const Rendezvous = require('libp2p-rendezvous')
const Bootstrap = require('libp2p-bootstrap')

const node = await Libp2p.create({
modules: {
transport: [TCP],
streamMuxer: [MPLEX],
connEncryption: [NOISE],
rendezvous: Rendezvous,
peerDiscovery: [Bootstrap]
},
config: {
peerDiscovery: {
bootstrap: {
enabled: true,
list: [
// Insert rendezvous servers multiaddrs
]
}
}
}
})
```

#### Setup with Keychain

Libp2p allows you to setup a secure keychain to manage your keys. The keychain configuration object should have the following properties:
Expand Down
3 changes: 3 additions & 0 deletions package-list.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
["libp2p/js-libp2p-delegated-peer-routing", "libp2p-delegated-peer-routing"],
["libp2p/js-libp2p-kad-dht", "libp2p-kad-dht"],

"service discovery",
["libp2p/js-libp2p-rendezvous", "libp2p-rendezvous"],

"utilities",
["libp2p/js-libp2p-crypto", "libp2p-crypto"],
["libp2p/js-libp2p-crypto-secp256k1", "libp2p-crypto-secp256k1"],
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
"libp2p-mdns": "^0.15.0",
"libp2p-mplex": "^0.10.1",
"libp2p-noise": "^2.0.0",
"libp2p-rendezvous": "libp2p/js-libp2p-rendezvous#feat/rendezvous-protocol-full-implementation",
"libp2p-secio": "^0.13.1",
"libp2p-tcp": "^0.15.1",
"libp2p-webrtc-star": "^0.20.0",
Expand Down
5 changes: 5 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ const DefaultConfig = {
maxListeners: 2
}
},
rendezvous: {
server: {
enabled: false
}
},
transport: {}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ class Libp2p extends EventEmitter {
this.pubsub = PubsubAdapter(Pubsub, this, this._config.pubsub)
}

// Create rendezvous if provided
if (this._modules.rendezvous) {
const Rendezvous = this._modules.rendezvous
this.rendezvous = new Rendezvous({
libp2p: this,
...this._config.rendezvous
})
}

// Attach remaining APIs
// peer and content routing will automatically get modules from _modules and _dht
this.peerRouting = peerRouting(this)
Expand Down Expand Up @@ -269,6 +278,8 @@ class Libp2p extends EventEmitter {
this.metrics && this.metrics.stop()
])

this.rendezvous && this.rendezvous.stop()

await this.transportManager.close()

ping.unmount(this)
Expand Down Expand Up @@ -500,6 +511,8 @@ class Libp2p extends EventEmitter {

this.connectionManager.start()

this.rendezvous && this.rendezvous.start()

// Peer discovery
await this._setupPeerDiscovery()

Expand Down
111 changes: 111 additions & 0 deletions test/discovery/rendezvous.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use strict'
/* eslint-env mocha */

const { expect } = require('aegir/utils/chai')
const pWaitFor = require('p-wait-for')

const Envelope = require('../../src/record/envelope')
const PeerRecord = require('../../src/record/peer-record')

const {
rendezvousClientOptions,
rendezvousServerOptions,
listenAddrs
} = require('./utils')
const peerUtils = require('../utils/creators/peer')

describe('libp2p.rendezvous', () => {
let libp2p, remoteLibp2p, rendezvousLibp2p

// Create Rendezvous server node
before(async () => {
[rendezvousLibp2p] = await peerUtils.createPeer({
number: 1,
fixture: false,
config: {
...rendezvousServerOptions,
addresses: {
listen: listenAddrs
}
}
})
})

// Create libp2p nodes to act as rendezvous clients
before(async () => {
[libp2p, remoteLibp2p] = await peerUtils.createPeer({
number: 2,
populateAddressBooks: false,
config: {
...rendezvousClientOptions,
addresses: {
listen: listenAddrs
},
config: {
peerDiscovery: {
bootstrap: { // Bootstrap rendezvous server
enabled: true,
list: [
`${rendezvousLibp2p.multiaddrs[0]}/p2p/${rendezvousLibp2p.peerId.toB58String()}`
]
}
}
}
}
})
})

// Wait for bootstrap peer connected and identified as rendezvous server
before(async () => {
await pWaitFor(() => Boolean(rendezvousLibp2p.connectionManager.get(libp2p.peerId)) &&
Boolean(rendezvousLibp2p.connectionManager.get(remoteLibp2p.peerId))
)

await pWaitFor(() => libp2p.rendezvous._rendezvousPoints.size === 1 &&
remoteLibp2p.rendezvous._rendezvousPoints.size === 1
)
})

after(() => {
return Promise.all([libp2p, remoteLibp2p, rendezvousLibp2p].map(node => node.stop()))
})

it('should have rendezvous libp2p node as rendezvous server', () => {
expect(libp2p.rendezvous._rendezvousPoints.get(rendezvousLibp2p.peerId.toB58String())).to.exist()
expect(remoteLibp2p.rendezvous._rendezvousPoints.get(rendezvousLibp2p.peerId.toB58String())).to.exist()
})

it('should discover remoteLibp2p when it registers on a namespace', async () => {
const namespace = '/test-namespace'
const registers = []

// libp2p does not discovery any peer registered
for await (const reg of libp2p.rendezvous.discover(namespace)) { // eslint-disable-line
throw new Error('no registers should exist')
}

// remoteLibp2p register itself on namespace
await remoteLibp2p.rendezvous.register(namespace)

// libp2p discover remote libp2p
for await (const reg of libp2p.rendezvous.discover(namespace)) { // eslint-disable-line
registers.push(reg)
}

expect(registers).to.have.lengthOf(1)
expect(registers[0].signedPeerRecord).to.exist()
expect(registers[0].ns).to.eql(namespace)

// Validate peer
const envelope = await Envelope.openAndCertify(registers[0].signedPeerRecord, PeerRecord.DOMAIN)
expect(envelope.peerId.equals(remoteLibp2p.peerId)).to.eql(true)

// Validate multiaddrs
const rec = PeerRecord.createFromProtobuf(envelope.payload)
expect(rec.multiaddrs.length).to.eql(remoteLibp2p.multiaddrs.length)

rec.multiaddrs.forEach((ma, index) => {
expect(ma).to.eql(remoteLibp2p.multiaddrs[index])
})
})
})
41 changes: 41 additions & 0 deletions test/discovery/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'

const Bootstrap = require('libp2p-bootstrap')
const Rendezvous = require('libp2p-rendezvous')

const mergeOptions = require('merge-options')
const { isNode } = require('ipfs-utils/src/env')
const baseOptions = require('../utils/base-options.browser')
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')

module.exports.baseOptions = baseOptions

module.exports.listenAddrs = isNode
? ['/ip4/127.0.0.1/tcp/0/ws'] : [`${MULTIADDRS_WEBSOCKETS[0]}/p2p-circuit`]

module.exports.rendezvousClientOptions = mergeOptions(baseOptions, {
modules: {
rendezvous: Rendezvous,
peerDiscovery: [Bootstrap]
},
config: {
rendezvous: {
server: {
enabled: false
}
}
}
})

module.exports.rendezvousServerOptions = mergeOptions(baseOptions, {
modules: {
rendezvous: Rendezvous
},
config: {
rendezvous: {
server: {
enabled: true
}
}
}
})
17 changes: 0 additions & 17 deletions test/relay/auto-relay.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,24 +462,15 @@ describe('auto-relay', () => {
})

describe('discovery', () => {
<<<<<<< HEAD
let local
let remote
=======
let libp2p
let libp2p2
>>>>>>> feat: auto relay network query for new relays
let relayLibp2p

beforeEach(async () => {
const peerIds = await createPeerId({ number: 3 })

// Create 2 nodes, and turn HOP on for the relay
<<<<<<< HEAD
;[local, remote, relayLibp2p] = peerIds.map((peerId, index) => {
=======
;[libp2p, libp2p2, relayLibp2p] = peerIds.map((peerId, index) => {
>>>>>>> feat: auto relay network query for new relays
const delegate = new DelegatedContentRouter(peerId, ipfsHttpClient({
host: '0.0.0.0',
protocol: 'http',
Expand All @@ -495,12 +486,8 @@ describe('auto-relay', () => {
relay: {
advertise: {
bootDelay: 1000,
<<<<<<< HEAD
ttl: 1000,
enabled: true
=======
ttl: 1000
>>>>>>> feat: auto relay network query for new relays
},
hop: {
enabled: index === 2
Expand Down Expand Up @@ -546,11 +533,7 @@ describe('auto-relay', () => {
])

// Start each node
<<<<<<< HEAD
await Promise.all([local, remote, relayLibp2p].map(libp2p => libp2p.start()))
=======
await Promise.all([libp2p, libp2p2, relayLibp2p].map(libp2p => libp2p.start()))
>>>>>>> feat: auto relay network query for new relays

// Should provide on start
await pWaitFor(() => relayLibp2p.contentRouting.provide.callCount === 1)
Expand Down

0 comments on commit 699b4e5

Please sign in to comment.