-
Notifications
You must be signed in to change notification settings - Fork 5
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
feat: rendezvous protocol initial implementation #6
base: master
Are you sure you want to change the base?
Changes from 31 commits
b8bf044
d7290df
b080670
ebb22d1
9765a95
b6edaf3
0e304f9
b248924
47641f7
b668c8a
7e3c541
1a1590d
b357829
4abd363
7763df2
63d607b
5f45c6f
640b64f
8f6e148
894ad2e
ee10d69
3798fbb
cdf2f6b
9fe0691
52fa2bd
06d53ac
d501d97
a2d5f83
83cd4b7
9b294f5
9bf5bcb
7a569c7
c297156
e1cd224
264ce2a
01ec7bc
5f025d3
2840251
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
'use strict' | ||
|
||
const Libp2p = require('libp2p') | ||
const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser') | ||
const Peers = require('./test/fixtures/peers') | ||
const PeerId = require('peer-id') | ||
const WebSockets = require('libp2p-websockets') | ||
const Muxer = require('libp2p-mplex') | ||
const { NOISE: Crypto } = require('libp2p-noise') | ||
|
||
const { isNode } = require('ipfs-utils/src/env') | ||
const delay = require('delay') | ||
const execa = require('execa') | ||
const pWaitFor = require('p-wait-for') | ||
const isCI = require('is-ci') | ||
|
||
let libp2p | ||
let containerId | ||
|
||
const before = async () => { | ||
// Use the last peer | ||
const peerId = await PeerId.createFromJSON(Peers[Peers.length - 1]) | ||
|
||
libp2p = new Libp2p({ | ||
addresses: { | ||
listen: [MULTIADDRS_WEBSOCKETS[0]] | ||
}, | ||
peerId, | ||
modules: { | ||
transport: [WebSockets], | ||
streamMuxer: [Muxer], | ||
connEncryption: [Crypto] | ||
}, | ||
config: { | ||
relay: { | ||
enabled: true, | ||
hop: { | ||
enabled: true, | ||
active: false | ||
} | ||
} | ||
} | ||
}) | ||
|
||
await libp2p.start() | ||
|
||
// CI runs datastore service | ||
if (isCI || !isNode) { | ||
return | ||
} | ||
|
||
const procResult = execa.commandSync('docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=test-secret-pw -e MYSQL_DATABASE=libp2p_rendezvous_db -d mysql:8 --default-authentication-plugin=mysql_native_password', { | ||
all: true | ||
}) | ||
containerId = procResult.stdout | ||
|
||
console.log(`wait for docker container ${containerId} to be ready`) | ||
|
||
await pWaitFor(() => { | ||
const procCheck = execa.commandSync(`docker logs ${containerId}`) | ||
const logs = procCheck.stdout + procCheck.stderr // Docker/MySQL sends to the stderr the ready for connections... | ||
|
||
return logs.includes('ready for connections') | ||
}, { | ||
interval: 5000 | ||
}) | ||
// Some more time waiting | ||
await delay(12e3) | ||
} | ||
|
||
const after = async () => { | ||
await libp2p.stop() | ||
|
||
if (isCI || !isNode) { | ||
return | ||
} | ||
|
||
console.log('docker container is stopping') | ||
execa.commandSync(`docker stop ${containerId}`) | ||
} | ||
|
||
module.exports = { | ||
bundlesize: { maxSize: '100kB' }, | ||
hooks: { | ||
pre: before, | ||
post: after | ||
}, | ||
webpack: { | ||
node: { | ||
// this is needed until bcrypto stops using node buffers in browser code | ||
Buffer: true | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
* | ||
!src | ||
!README.md | ||
!package.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
name: ci | ||
on: | ||
push: | ||
branches: | ||
- master | ||
pull_request: | ||
branches: | ||
- '**' | ||
|
||
jobs: | ||
check: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: yarn | ||
- run: yarn lint | ||
- uses: gozala/typescript-error-reporter-action@v1.0.8 | ||
- run: yarn build | ||
- run: yarn aegir dep-check | ||
- uses: ipfs/aegir/actions/bundle-size@master | ||
name: size | ||
with: | ||
github_token: ${{ secrets.GITHUB_TOKEN }} | ||
test-node: | ||
needs: check | ||
runs-on: ${{ matrix.os }} | ||
services: | ||
mysql: | ||
image: mysql:5.7 | ||
env: | ||
MYSQL_ROOT_PASSWORD: test-secret-pw | ||
MYSQL_DATABASE: libp2p_rendezvous_db | ||
ports: | ||
- 3306:3306 | ||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 | ||
strategy: | ||
matrix: | ||
os: [ubuntu-latest] | ||
node: [12, 14] | ||
fail-fast: true | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-node@v1 | ||
with: | ||
node-version: ${{ matrix.node }} | ||
- run: yarn | ||
- run: npx nyc --reporter=lcov aegir test -t node -- --bail | ||
- uses: codecov/codecov-action@v1 | ||
test-chrome: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kept the memory datastore previously implemented in the repo. It might be useful for small tests without spinning up MySQL, as well as to run the tests in the browser. The browser tests are not useful as no one should run this in the browser, and I can remove them. For example, for testing the libp2p client in libp2p core, it is easier to use the memory datastore, in order to not have to spin a MySQL db for running the tests |
||
needs: check | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: yarn | ||
- run: npx aegir test -t browser -t webworker --bail | ||
test-firefox: | ||
needs: check | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: yarn | ||
- run: npx aegir test -t browser -t webworker --bail -- --browsers FirefoxHeadless |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
FROM node:lts-buster | ||
|
||
# Install deps | ||
RUN apt-get update && apt-get install -y | ||
|
||
# Get dumb-init to allow quit running interactively | ||
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 && chmod +x /usr/local/bin/dumb-init | ||
|
||
# Setup directories for the `node` user | ||
RUN mkdir -p /home/node/app/rendezvous/node_modules && chown -R node:node /home/node/app/rendezvous | ||
|
||
WORKDIR /home/node/app/rendezvous | ||
|
||
# Install node modules | ||
COPY package.json ./ | ||
# Switch to the node user for installation | ||
USER node | ||
RUN npm install --production | ||
|
||
# Copy over source files under the node user | ||
COPY --chown=node:node ./src ./src | ||
COPY --chown=node:node ./README.md ./ | ||
|
||
# rendezvous defaults to 15002 | ||
EXPOSE 15002 | ||
|
||
# metrics defaults to 8003 | ||
EXPOSE 8003 | ||
|
||
# Available overrides (defaults shown): | ||
# --disableMetrics=false | ||
# Server logging can be enabled via the DEBUG environment variable: | ||
# DEBUG=libp2p:rendezvous:* | ||
CMD [ "/usr/local/bin/dumb-init", "node", "src/server/bin.js"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# Rendezvous Protocol in js-libp2p | ||
|
||
The rendezvous protocol can be used in different contexts across libp2p. For using it, the libp2p network needs to have well known libp2p nodes acting as rendezvous servers. These nodes will have an extra role in the network. They will collect and maintain a list of registrations per rendezvous namespace. Other peers in the network will act as rendezvous clients and will register themselves on given namespaces by messaging a rendezvous server node. Taking into account these registrations, a rendezvous client is able to discover other peers in a given namespace by querying a server. A registration should have a `ttl`, in order to avoid having invalid registrations. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: clarify the language here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fyi, this is mostly just a note for me. |
||
|
||
## Usage | ||
|
||
`js-libp2p` supports the usage of the rendezvous protocol through its configuration. It allows the rendezvous protocol to be enabled and customized. | ||
|
||
You can configure it through libp2p as follows: | ||
|
||
```js | ||
const Libp2p = require('libp2p') | ||
|
||
const node = await Libp2p.create({ | ||
rendezvous: { | ||
enabled: true | ||
} | ||
}) | ||
``` | ||
|
||
## Libp2p Flow | ||
|
||
When a libp2p node with the rendezvous protocol enabled starts, it should start by connecting to a rendezvous server. The rendezvous server can be added to the bootstrap nodes or manually dialed. When a rendezvous server is connected, the node can ask for nodes in given namespaces. An example of a namespace could be a relay namespace, so that undialable nodes can register themselves as reachable through that relay. | ||
|
||
When a libp2p node running the rendezvous protocol is stopping, it will unregister from all the namespaces previously registered. | ||
|
||
## API | ||
|
||
This API allows users to register new rendezvous namespaces, unregister from previously registered namespaces and to discover peers on a given namespace. | ||
|
||
### Options | ||
|
||
| Name | Type | Description | | ||
|------|------|-------------| | ||
| options | `object` | rendezvous parameters | | ||
| options.enabled | `boolean` | is rendezvous enabled | | ||
|
||
### rendezvous.start | ||
|
||
Register the rendezvous protocol topology into libp2p. | ||
|
||
`rendezvous.start()` | ||
|
||
### rendezvous.stop | ||
|
||
Unregister the rendezvous protocol and the streams with other peers will be closed. | ||
|
||
`rendezvous.stop()` | ||
|
||
### rendezvous.register | ||
|
||
Registers the peer in a given namespace. | ||
|
||
`rendezvous.register(namespace, [options])` | ||
|
||
#### Parameters | ||
|
||
| Name | Type | Description | | ||
|------|------|-------------| | ||
| namespace | `string` | namespace to register | | ||
| [options] | `Object` | rendezvous registrations options | | ||
| [options.ttl=7.2e6] | `number` | registration ttl in ms | | ||
|
||
#### Returns | ||
|
||
| Type | Description | | ||
|------|-------------| | ||
| `Promise<number>` | Remaining ttl value | | ||
|
||
#### Example | ||
|
||
```js | ||
// ... | ||
const ttl = await rendezvous.register(namespace) | ||
``` | ||
|
||
### rendezvous.unregister | ||
|
||
Unregisters the peer from a given namespace. | ||
|
||
`rendezvous.unregister(namespace)` | ||
|
||
#### Parameters | ||
|
||
| Name | Type | Description | | ||
|------|------|-------------| | ||
| namespace | `string` | namespace to unregister | | ||
|
||
#### Returns | ||
|
||
| Type | Description | | ||
|------|-------------| | ||
| `Promise<void>` | Operation resolved | | ||
|
||
#### Example | ||
|
||
```js | ||
// ... | ||
await rendezvous.register(namespace) | ||
await rendezvous.unregister(namespace) | ||
``` | ||
|
||
### rendezvous.discover | ||
|
||
Discovers peers registered under a given namespace. | ||
|
||
`rendezvous.discover(namespace, [limit])` | ||
|
||
#### Parameters | ||
|
||
| Name | Type | Description | | ||
|------|------|-------------| | ||
| namespace | `string` | namespace to discover | | ||
| limit | `number` | limit of peers to discover | | ||
|
||
#### Returns | ||
|
||
| Type | Description | | ||
|------|-------------| | ||
| `AsyncIterable<{ signedPeerRecord: Envelope, ns: string, ttl: number }>` | Async Iterable registrations | | ||
|
||
#### Example | ||
|
||
```js | ||
// ... | ||
await rendezvous.register(namespace) | ||
|
||
for await (const reg of rendezvous.discover(namespace)) { | ||
console.log(reg.signedPeerRecord, reg.ns, reg.ttl) | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Github actions does not support running services (mysql) when running tests on other OS... We can move into travis, but I feel we can just run it in ubuntu as I don't expect people will run a Rendezvous server in Windows/MacOS