mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-04-24 18:12:14 +00:00
docs: fix examples in documentation (#1379)
This PR fixes all the errors in the code snippets in the readme files contained in the examples directory and it fixes the doc/GETTING_STARTED.md file. I have also updated the old package names, e.g. `libp2p-websockets` -> `@libp2p/websockets`. Resolves #1367 and resolves #1361 Co-authored-by: saul <saul@organicdesign.nz>
This commit is contained in:
parent
a11260c753
commit
62198414b3
@ -34,12 +34,12 @@ Now that we have libp2p installed, let's configure the minimum needed to get you
|
|||||||
|
|
||||||
Libp2p uses Transports to establish connections between peers over the network. Transports are the components responsible for performing the actual exchange of data between libp2p nodes. You can configure any number of Transports, but you only need 1 to start with. Supporting more Transports will improve the ability of your node to speak to a larger number of nodes on the network, as matching Transports are required for two nodes to communicate with one another.
|
Libp2p uses Transports to establish connections between peers over the network. Transports are the components responsible for performing the actual exchange of data between libp2p nodes. You can configure any number of Transports, but you only need 1 to start with. Supporting more Transports will improve the ability of your node to speak to a larger number of nodes on the network, as matching Transports are required for two nodes to communicate with one another.
|
||||||
|
|
||||||
You should select Transports according to the runtime of your application; Node.js or the browser. You can see a list of some of the available Transports in the [configuration readme](./CONFIGURATION.md#transport). For this guide let's install `libp2p-websockets`, as it can be used in both Node.js and the browser.
|
You should select Transports according to the runtime of your application; Node.js or the browser. You can see a list of some of the available Transports in the [configuration readme](./CONFIGURATION.md#transport). For this guide let's install `@libp2p/websockets`, as it can be used in both Node.js and the browser.
|
||||||
|
|
||||||
Start by installing `libp2p-websockets`:
|
Start by installing `@libp2p/websockets`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install libp2p-websockets
|
npm install @libp2p/websockets
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that we have the module installed, let's configure libp2p to use the Transport. We'll use the [`Libp2p.create`](./API.md#create) method, which takes a single configuration object as its only parameter. We can add the Transport by passing it into the `modules.transport` array:
|
Now that we have the module installed, let's configure libp2p to use the Transport. We'll use the [`Libp2p.create`](./API.md#create) method, which takes a single configuration object as its only parameter. We can add the Transport by passing it into the `modules.transport` array:
|
||||||
@ -66,13 +66,13 @@ If you want to know more about libp2p transports, you should read the following
|
|||||||
|
|
||||||
Encryption is an important part of communicating on the libp2p network. Every connection must be encrypted to help ensure security for everyone. As such, Connection Encryption (Crypto) is a required component of libp2p.
|
Encryption is an important part of communicating on the libp2p network. Every connection must be encrypted to help ensure security for everyone. As such, Connection Encryption (Crypto) is a required component of libp2p.
|
||||||
|
|
||||||
There are a growing number of Crypto modules being developed for libp2p. As those are released they will be tracked in the [Connection Encryption section of the configuration readme](./CONFIGURATION.md#connection-encryption). For now, we are going to configure our node to use the `libp2p-noise` module.
|
There are a growing number of Crypto modules being developed for libp2p. As those are released they will be tracked in the [Connection Encryption section of the configuration readme](./CONFIGURATION.md#connection-encryption). For now, we are going to configure our node to use the `@chainsafe/libp2p-noise` module.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install @chainsafe/libp2p-noise
|
npm install @chainsafe/libp2p-noise
|
||||||
```
|
```
|
||||||
|
|
||||||
With `libp2p-noise` installed, we can add it to our existing configuration by importing it and adding it to the `modules.connEncryption` array:
|
With `@chainsafe/libp2p-noise` installed, we can add it to our existing configuration by importing it and adding it to the `modules.connEncryption` array:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { createLibp2p } from 'libp2p'
|
import { createLibp2p } from 'libp2p'
|
||||||
@ -96,12 +96,12 @@ If you want to know more about libp2p connection encryption, you should read the
|
|||||||
|
|
||||||
While multiplexers are not strictly required, they are highly recommended as they improve the effectiveness and efficiency of connections for the various protocols libp2p runs. Adding a multiplexer to your configuration will allow libp2p to run several of its internal protocols, like Identify, as well as allow your application to easily run any number of protocols over a single connection.
|
While multiplexers are not strictly required, they are highly recommended as they improve the effectiveness and efficiency of connections for the various protocols libp2p runs. Adding a multiplexer to your configuration will allow libp2p to run several of its internal protocols, like Identify, as well as allow your application to easily run any number of protocols over a single connection.
|
||||||
|
|
||||||
Looking at the [available stream multiplexing](./CONFIGURATION.md#stream-multiplexing) modules, js-libp2p currently only supports `libp2p-mplex`, so we will use that here. Bear in mind that future libp2p Transports might have `multiplexing` capabilities already built-in (such as `QUIC`).
|
Looking at the [available stream multiplexing](./CONFIGURATION.md#stream-multiplexing) modules, js-libp2p currently only supports `@libp2p/mplex`, so we will use that here. Bear in mind that future libp2p Transports might have `multiplexing` capabilities already built-in (such as `QUIC`).
|
||||||
|
|
||||||
You can install `libp2p-mplex` and add it to your libp2p node as follows in the next example.
|
You can install `@libp2p/mplex` and add it to your libp2p node as follows in the next example.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install libp2p-mplex
|
npm install @libp2p/mplex
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -148,12 +148,9 @@ const node = await createLibp2p({
|
|||||||
await node.start()
|
await node.start()
|
||||||
console.log('libp2p has started')
|
console.log('libp2p has started')
|
||||||
|
|
||||||
const listenAddrs = node.transportManager.getAddrs()
|
const listenAddrs = node.getMultiaddrs()
|
||||||
console.log('libp2p is listening on the following addresses: ', listenAddrs)
|
console.log('libp2p is listening on the following addresses: ', listenAddrs)
|
||||||
|
|
||||||
const advertiseAddrs = node.multiaddrs
|
|
||||||
console.log('libp2p is advertising the following addresses: ', advertiseAddrs)
|
|
||||||
|
|
||||||
// stop libp2p
|
// stop libp2p
|
||||||
await node.stop()
|
await node.stop()
|
||||||
console.log('libp2p has stopped')
|
console.log('libp2p has stopped')
|
||||||
@ -170,17 +167,17 @@ Peer discovery is an important part of creating a well connected libp2p node. A
|
|||||||
For each discovered peer libp2p will emit a `peer:discovery` event which includes metadata about that peer. You can read the [Events](./API.md#events) in the API doc to learn more.
|
For each discovered peer libp2p will emit a `peer:discovery` event which includes metadata about that peer. You can read the [Events](./API.md#events) in the API doc to learn more.
|
||||||
|
|
||||||
Looking at the [available peer discovery](./CONFIGURATION.md#peer-discovery) protocols, there are several options to be considered:
|
Looking at the [available peer discovery](./CONFIGURATION.md#peer-discovery) protocols, there are several options to be considered:
|
||||||
- If you already know the addresses of some other network peers, you should consider using `libp2p-bootstrap` as this is the easiest way of getting your peer into the network.
|
- If you already know the addresses of some other network peers, you should consider using `@libp2p/bootstrap` as this is the easiest way of getting your peer into the network.
|
||||||
- If it is likely that you will have other peers on your local network, `libp2p-mdns` is a must if you're node is not running in the browser. It allows peers to discover each other when on the same local network.
|
- If it is likely that you will have other peers on your local network, `@libp2p/mdns` is a must if you're node is not running in the browser. It allows peers to discover each other when on the same local network.
|
||||||
- If your application is browser based you can use the `libp2p-webrtc-star` Transport, which includes a rendezvous based peer sharing service.
|
- If your application is browser based you can use the `@libp2p/webrtc-star` Transport, which includes a rendezvous based peer sharing service.
|
||||||
- A random walk approach can be used via `libp2p-kad-dht`, to crawl the network and find new peers along the way.
|
- A random walk approach can be used via `@libp2p/kad-dht`, to crawl the network and find new peers along the way.
|
||||||
|
|
||||||
For this guide we will configure `libp2p-bootstrap` as this is useful for joining the public network.
|
For this guide we will configure `@libp2p/bootstrap` as this is useful for joining the public network.
|
||||||
|
|
||||||
Let's install `libp2p-bootstrap`.
|
Let's install `@libp2p/bootstrap`.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install libp2p-bootstrap
|
npm install @libp2p/bootstrap
|
||||||
```
|
```
|
||||||
|
|
||||||
We can provide specific configurations for each protocol within a `config.peerDiscovery` property in the options as shown below.
|
We can provide specific configurations for each protocol within a `config.peerDiscovery` property in the options as shown below.
|
||||||
@ -221,12 +218,12 @@ const node = await createLibp2p({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
node.on('peer:discovery', (peer) => {
|
node.addEventListener('peer:discovery', (evt) => {
|
||||||
console.log('Discovered %s', peer.id.toB58String()) // Log discovered peer
|
console.log('Discovered %s', evt.detail.id.toString()) // Log discovered peer
|
||||||
})
|
})
|
||||||
|
|
||||||
node.connectionManager.on('peer:connect', (connection) => {
|
node.connectionManager.addEventListener('peer:connect', (evt) => {
|
||||||
console.log('Connected to %s', connection.remotePeer.toB58String()) // Log connected peer
|
console.log('Connected to %s', evt.detail.remotePeer.toString()) // Log connected peer
|
||||||
})
|
})
|
||||||
|
|
||||||
// start libp2p
|
// start libp2p
|
||||||
|
@ -24,7 +24,7 @@ import { Mplex } from '@libp2p/mplex'
|
|||||||
const node = await createLibp2p({
|
const node = await createLibp2p({
|
||||||
transports: [new WebSockets()],
|
transports: [new WebSockets()],
|
||||||
connectionEncryption: [new Noise()],
|
connectionEncryption: [new Noise()],
|
||||||
streamMuxers: [new Mplex()]
|
streamMuxers: [new Mplex()],
|
||||||
addresses: {
|
addresses: {
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0/ws']
|
listen: ['/ip4/0.0.0.0/tcp/0/ws']
|
||||||
// TODO check "What is next?" section
|
// TODO check "What is next?" section
|
||||||
@ -43,9 +43,9 @@ const node = await createLibp2p({
|
|||||||
|
|
||||||
await node.start()
|
await node.start()
|
||||||
|
|
||||||
console.log(`Node started with id ${node.peerId.toB58String()}`)
|
console.log(`Node started with id ${node.peerId.toString()}`)
|
||||||
console.log('Listening on:')
|
console.log('Listening on:')
|
||||||
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
|
node.getMultiaddrs().forEach((ma) => console.log(ma.toString()))
|
||||||
```
|
```
|
||||||
|
|
||||||
The Relay HOP advertise functionality is **NOT** required to be enabled. However, if you are interested in advertising on the network that this node is available to be used as a HOP Relay you can enable it. A content router module or Rendezvous needs to be configured to leverage this option.
|
The Relay HOP advertise functionality is **NOT** required to be enabled. However, if you are interested in advertising on the network that this node is available to be used as a HOP Relay you can enable it. A content router module or Rendezvous needs to be configured to leverage this option.
|
||||||
@ -94,17 +94,17 @@ const node = await createLibp2p({
|
|||||||
})
|
})
|
||||||
|
|
||||||
await node.start()
|
await node.start()
|
||||||
console.log(`Node started with id ${node.peerId.toB58String()}`)
|
console.log(`Node started with id ${node.peerId.toString()}`)
|
||||||
|
|
||||||
const conn = await node.dial(relayAddr)
|
const conn = await node.dial(relayAddr)
|
||||||
|
|
||||||
console.log(`Connected to the HOP relay ${conn.remotePeer.toString()}`)
|
console.log(`Connected to the HOP relay ${conn.remotePeer.toString()}`)
|
||||||
|
|
||||||
// Wait for connection and relay to be bind for the example purpose
|
// Wait for connection and relay to be bind for the example purpose
|
||||||
node.peerStore.on('change:multiaddrs', ({ peerId }) => {
|
node.peerStore.addEventListener('change:multiaddrs', (evt) => {
|
||||||
// Updated self multiaddrs?
|
// Updated self multiaddrs?
|
||||||
if (peerId.equals(node.peerId)) {
|
if (evt.detail.peerId.equals(node.peerId)) {
|
||||||
console.log(`Advertising with a relay address of ${node.multiaddrs[0].toString()}/p2p/${node.peerId.toB58String()}`)
|
console.log(`Advertising with a relay address of ${node.getMultiaddrs()[0].toString()}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
@ -151,7 +151,7 @@ const node = await createLibp2p({
|
|||||||
})
|
})
|
||||||
|
|
||||||
await node.start()
|
await node.start()
|
||||||
console.log(`Node started with id ${node.peerId.toB58String()}`)
|
console.log(`Node started with id ${node.peerId.toString()}`)
|
||||||
|
|
||||||
const conn = await node.dial(autoRelayNodeAddr)
|
const conn = await node.dial(autoRelayNodeAddr)
|
||||||
console.log(`Connected to the auto relay node via ${conn.remoteAddr.toString()}`)
|
console.log(`Connected to the auto relay node via ${conn.remoteAddr.toString()}`)
|
||||||
@ -176,4 +176,4 @@ As you can see from the output, the remote address of the established connection
|
|||||||
|
|
||||||
Before moving into production, there are a few things that you should take into account.
|
Before moving into production, there are a few things that you should take into account.
|
||||||
|
|
||||||
A relay node should not advertise its private address in a real world scenario, as the node would not be reachable by others. You should provide an array of public addresses in the libp2p `addresses.announce` option. If you are using websockets, bear in mind that due to browser’s security policies you cannot establish unencrypted connection from secure context. The simplest solution is to setup SSL with nginx and proxy to the node and setup a domain name for the certificate.
|
A relay node should not advertise its private address in a real world scenario, as the node would not be reachable by others. You should provide an array of public addresses in the libp2p `addresses.announce` option. If you are using websockets, bear in mind that due to browser’s security policies you cannot establish unencrypted connection from secure context. The simplest solution is to setup SSL with nginx and proxy to the node and setup a domain name for the certificate.
|
||||||
|
@ -15,6 +15,9 @@ First, we create our libp2p node.
|
|||||||
```JavaScript
|
```JavaScript
|
||||||
import { createLibp2p } from 'libp2p'
|
import { createLibp2p } from 'libp2p'
|
||||||
import { Bootstrap } from '@libp2p/bootstrap'
|
import { Bootstrap } from '@libp2p/bootstrap'
|
||||||
|
import { TCP } from '@libp2p/tcp'
|
||||||
|
import { Mplex } from '@libp2p/mplex'
|
||||||
|
import { Noise } from '@chainsafe/libp2p-noise'
|
||||||
|
|
||||||
const node = await createLibp2p({
|
const node = await createLibp2p({
|
||||||
transports: [
|
transports: [
|
||||||
@ -52,7 +55,6 @@ Now, once we create and start the node, we can listen for events such as `peer:d
|
|||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
const node = await createLibp2p({
|
const node = await createLibp2p({
|
||||||
peerId,
|
|
||||||
addresses: {
|
addresses: {
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||||
},
|
},
|
||||||
@ -73,13 +75,13 @@ const node = await createLibp2p({
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
node.connectionManager.on('peer:connect', (connection) => {
|
node.connectionManager.addEventListener('peer:connect', (evt) => {
|
||||||
console.log('Connection established to:', connection.remotePeer.toB58String()) // Emitted when a new connection has been created
|
console.log('Connection established to:', evt.detail.remotePeer.toString()) // Emitted when a new connection has been created
|
||||||
})
|
})
|
||||||
|
|
||||||
node.on('peer:discovery', (peerId) => {
|
node.addEventListener('peer:discovery', (evt) => {
|
||||||
// No need to dial, autoDial is on
|
// No need to dial, autoDial is on
|
||||||
console.log('Discovered:', peerId.toB58String())
|
console.log('Discovered:', evt.detail.id.toString())
|
||||||
})
|
})
|
||||||
|
|
||||||
await node.start()
|
await node.start()
|
||||||
@ -105,16 +107,19 @@ Connection established to: QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb
|
|||||||
|
|
||||||
## 2. MulticastDNS to find other peers in the network
|
## 2. MulticastDNS to find other peers in the network
|
||||||
|
|
||||||
For this example, we need `libp2p-mdns`, go ahead and `npm install` it. You can find the complete solution at [2.js](./2.js).
|
For this example, we need `@libp2p/mdns`, go ahead and `npm install` it. You can find the complete solution at [2.js](./2.js).
|
||||||
|
|
||||||
Update your libp2p configuration to include MulticastDNS.
|
Update your libp2p configuration to include MulticastDNS.
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
import { createLibp2p } from 'libp2p'
|
import { createLibp2p } from 'libp2p'
|
||||||
import { MulticastDNS } from '@libp2p/mdns'
|
import { MulticastDNS } from '@libp2p/mdns'
|
||||||
|
import { TCP } from '@libp2p/tcp'
|
||||||
|
import { Mplex } from '@libp2p/mplex'
|
||||||
|
import { Noise } from '@chainsafe/libp2p-noise'
|
||||||
|
|
||||||
const createNode = () => {
|
const createNode = () => {
|
||||||
return Libp2p.create({
|
return createLibp2p({
|
||||||
addresses: {
|
addresses: {
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||||
},
|
},
|
||||||
@ -144,8 +149,8 @@ const [node1, node2] = await Promise.all([
|
|||||||
createNode()
|
createNode()
|
||||||
])
|
])
|
||||||
|
|
||||||
node1.on('peer:discovery', (peer) => console.log('Discovered:', peerId.toB58String()))
|
node1.addEventListener('peer:discovery', (evt) => console.log('Discovered:', evt.detail.id.toString()))
|
||||||
node2.on('peer:discovery', (peer) => console.log('Discovered:', peerId.toB58String()))
|
node2.addEventListener('peer:discovery', (evt) => console.log('Discovered:', evt.detail.id.toString()))
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
node1.start(),
|
node1.start(),
|
||||||
@ -163,7 +168,7 @@ Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
|||||||
|
|
||||||
## 3. Pubsub based Peer Discovery
|
## 3. Pubsub based Peer Discovery
|
||||||
|
|
||||||
For this example, we need [`libp2p-pubsub-peer-discovery`](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery/), go ahead and `npm install` it. You also need to spin up a set of [`libp2p-relay-servers`](https://github.com/libp2p/js-libp2p-relay-server). These servers act as relay servers and a peer discovery source.
|
For this example, we need [`@libp2p/pubsub-peer-discovery`](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery/), go ahead and `npm install` it. You also need to spin up a set of [`libp2p-relay-servers`](https://github.com/libp2p/js-libp2p-relay-server). These servers act as relay servers and a peer discovery source.
|
||||||
|
|
||||||
In the context of this example, we will create and run the `libp2p-relay-server` in the same code snippet. You can find the complete solution at [3.js](./3.js).
|
In the context of this example, we will create and run the `libp2p-relay-server` in the same code snippet. You can find the complete solution at [3.js](./3.js).
|
||||||
|
|
||||||
@ -174,9 +179,9 @@ import { createLibp2p } from 'libp2p'
|
|||||||
import { TCP } from '@libp2p/tcp'
|
import { TCP } from '@libp2p/tcp'
|
||||||
import { Mplex } from '@libp2p/mplex'
|
import { Mplex } from '@libp2p/mplex'
|
||||||
import { Noise } from '@chainsafe/libp2p-noise'
|
import { Noise } from '@chainsafe/libp2p-noise'
|
||||||
import { Gossipsub } from 'libp2p-gossipsub'
|
import { GossipSub } from '@chainsafe/libp2p-gossipsub'
|
||||||
import { Bootstrap } from '@libp2p/bootstrap'
|
import { Bootstrap } from '@libp2p/bootstrap'
|
||||||
const PubsubPeerDiscovery from 'libp2p-pubsub-peer-discovery')
|
import { PubSubPeerDiscovery } from '@libp2p/pubsub-peer-discovery'
|
||||||
|
|
||||||
const createNode = async (bootstrapers) => {
|
const createNode = async (bootstrapers) => {
|
||||||
const node = await createLibp2p({
|
const node = await createLibp2p({
|
||||||
@ -184,23 +189,25 @@ const createNode = async (bootstrapers) => {
|
|||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||||
},
|
},
|
||||||
transports: [
|
transports: [
|
||||||
new TCP()
|
new TCP()
|
||||||
],
|
],
|
||||||
streamMuxers: [
|
streamMuxers: [
|
||||||
new Mplex()
|
new Mplex()
|
||||||
],
|
],
|
||||||
connectionEncryption: [
|
connectionEncryption: [
|
||||||
new Noise()
|
new Noise()
|
||||||
],
|
],
|
||||||
peerDiscovery: [
|
pubsub: new GossipSub({ allowPublishToZeroPeers: true }),
|
||||||
new Bootstrap({
|
peerDiscovery: [
|
||||||
interval: 60e3,
|
new Bootstrap({
|
||||||
list: bootstrapers
|
interval: 60e3,
|
||||||
}),
|
list: bootstrapers
|
||||||
new PubsubPeerDiscovery({
|
}),
|
||||||
interval: 1000
|
new PubSubPeerDiscovery({
|
||||||
})
|
interval: 1000
|
||||||
])
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
@ -209,28 +216,49 @@ const createNode = async (bootstrapers) => {
|
|||||||
We will use the `libp2p-relay-server` as bootstrap nodes for the libp2p nodes, so that they establish a connection with the relay after starting. As a result, after they establish a connection with the relay, the pubsub discovery will kick in and the relay will advertise them.
|
We will use the `libp2p-relay-server` as bootstrap nodes for the libp2p nodes, so that they establish a connection with the relay after starting. As a result, after they establish a connection with the relay, the pubsub discovery will kick in and the relay will advertise them.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const relay = await createRelayServer({
|
const relay = await createLibp2p({
|
||||||
addresses: {
|
addresses: {
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
listen: [
|
||||||
}
|
'/ip4/0.0.0.0/tcp/0'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
transports: [new TCP()],
|
||||||
|
streamMuxers: [new Mplex()],
|
||||||
|
connectionEncryption: [new Noise()],
|
||||||
|
pubsub: new GossipSub({ allowPublishToZeroPeers: true }),
|
||||||
|
peerDiscovery: [
|
||||||
|
new PubSubPeerDiscovery({
|
||||||
|
interval: 1000
|
||||||
|
})
|
||||||
|
],
|
||||||
|
relay: {
|
||||||
|
enabled: true, // Allows you to dial and accept relayed connections. Does not make you a relay.
|
||||||
|
hop: {
|
||||||
|
enabled: true // Allows you to be a relay for other peers
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
console.log(`libp2p relay starting with id: ${relay.peerId.toB58String()}`)
|
|
||||||
|
console.log(`libp2p relay starting with id: ${relay.peerId.toString()}`)
|
||||||
|
|
||||||
await relay.start()
|
await relay.start()
|
||||||
const relayMultiaddrs = relay.multiaddrs.map((m) => `${m.toString()}/p2p/${relay.peerId.toB58String()}`)
|
|
||||||
|
const relayMultiaddrs = relay.getMultiaddrs()
|
||||||
|
|
||||||
const [node1, node2] = await Promise.all([
|
const [node1, node2] = await Promise.all([
|
||||||
createNode(relayMultiaddrs),
|
createNode(relayMultiaddrs),
|
||||||
createNode(relayMultiaddrs)
|
createNode(relayMultiaddrs)
|
||||||
])
|
])
|
||||||
|
|
||||||
node1.on('peer:discovery', (peerId) => {
|
node1.addEventListener('peer:discovery', (evt) => {
|
||||||
console.log(`Peer ${node1.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
|
console.log(`Peer ${node1.peerId.toString()} discovered: ${evt.detail.id.toString()}`)
|
||||||
})
|
})
|
||||||
node2.on('peer:discovery', (peerId) => {
|
node2.addEventListener('peer:discovery', (evt) => {
|
||||||
console.log(`Peer ${node2.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
|
console.log(`Peer ${node2.peerId.toString()} discovered: ${evt.detail.id.toString()}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
;[node1, node2].forEach((node, index) => console.log(`Node ${index} starting with id: ${node.peerId.toB58String()}`))
|
;[node1, node2].forEach((node, index) => console.log(`Node ${index} starting with id: ${node.peerId.toString()}`))
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
node1.start(),
|
node1.start(),
|
||||||
node2.start()
|
node2.start()
|
||||||
@ -258,6 +286,6 @@ This is really useful when running libp2p in constrained environments like a bro
|
|||||||
|
|
||||||
There are plenty more Peer Discovery Mechanisms out there, you can:
|
There are plenty more Peer Discovery Mechanisms out there, you can:
|
||||||
|
|
||||||
- Find one in [libp2p-webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star). Yes, a transport with discovery capabilities! This happens because WebRTC requires a rendezvous point for peers to exchange [SDP](https://tools.ietf.org/html/rfc4317) offer, which means we have one or more points that can introduce peers to each other. Think of it as MulticastDNS for the Web, as in MulticastDNS only works in LAN.
|
- Find one in [@libp2p/webrtc-star](https://github.com/libp2p/js-libp2p-webrtc-star). Yes, a transport with discovery capabilities! This happens because WebRTC requires a rendezvous point for peers to exchange [SDP](https://tools.ietf.org/html/rfc4317) offer, which means we have one or more points that can introduce peers to each other. Think of it as MulticastDNS for the Web, as in MulticastDNS only works in LAN.
|
||||||
- Any DHT will offer you a discovery capability. You can simple _random-walk_ the routing tables to find other peers to connect to. For example [libp2p-kad-dht](https://github.com/libp2p/js-libp2p-kad-dht) can be used for peer discovery. An example of how to configure it to enable random walks can be found [here](https://github.com/libp2p/js-libp2p/blob/v0.28.4/doc/CONFIGURATION.md#customizing-dht).
|
- Any DHT will offer you a discovery capability. You can simple _random-walk_ the routing tables to find other peers to connect to. For example [@libp2p/kad-dht](https://github.com/libp2p/js-libp2p-kad-dht) can be used for peer discovery. An example of how to configure it to enable random walks can be found [here](https://github.com/libp2p/js-libp2p/blob/v0.28.4/doc/CONFIGURATION.md#customizing-dht).
|
||||||
- You can create your own Discovery service, a registry, a list, a radio beacon, you name it!
|
- You can create your own Discovery service, a registry, a list, a radio beacon, you name it!
|
||||||
|
@ -15,34 +15,37 @@ First, let's update our config to support Peer Routing and Content Routing.
|
|||||||
```JavaScript
|
```JavaScript
|
||||||
import { createLibp2p } from 'libp2p'
|
import { createLibp2p } from 'libp2p'
|
||||||
import { KadDHT } from '@libp2p/kad-dht'
|
import { KadDHT } from '@libp2p/kad-dht'
|
||||||
|
import { TCP } from '@libp2p/tcp'
|
||||||
|
import { Mplex } from '@libp2p/mplex'
|
||||||
|
import { Noise } from '@chainsafe/libp2p-noise'
|
||||||
|
|
||||||
const node = await createLibp2p({
|
const createNode = async () => {
|
||||||
addresses: {
|
const node = await createLibp2p({
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
addresses: {
|
||||||
},
|
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||||
transports: [
|
},
|
||||||
new TCP()
|
transports: [new TCP()],
|
||||||
],
|
streamMuxers: [new Mplex()],
|
||||||
streamMuxers: [
|
connectionEncryption: [new Noise()],
|
||||||
new Mplex()
|
dht: new KadDHT()
|
||||||
],
|
})
|
||||||
connEncryption: [
|
|
||||||
new Noise()
|
await node.start()
|
||||||
],
|
return node
|
||||||
// we add the DHT module that will enable Peer and Content Routing
|
}
|
||||||
dht: KadDHT
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Once that is done, we can use the createNode function we developed in the previous example to create 3 nodes. Connect node 1 to node 2 and node 2 to node 3. We will use node 2 as a way to find the whereabouts of node 3
|
Once that is done, we can use the createNode function we developed in the previous example to create 3 nodes. Connect node 1 to node 2 and node 2 to node 3. We will use node 2 as a way to find the whereabouts of node 3
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
const node1 = nodes[0]
|
const [node1, node2, node3] = await Promise.all([
|
||||||
const node2 = nodes[1]
|
createNode(),
|
||||||
const node3 = nodes[2]
|
createNode(),
|
||||||
|
createNode()
|
||||||
|
])
|
||||||
|
|
||||||
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
await node1.peerStore.addressBook.set(node2.peerId, node2.getMultiaddrs())
|
||||||
await node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
await node2.peerStore.addressBook.set(node3.peerId, node3.getMultiaddrs())
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
node1.dial(node2.peerId),
|
node1.dial(node2.peerId),
|
||||||
@ -50,12 +53,12 @@ await Promise.all([
|
|||||||
])
|
])
|
||||||
|
|
||||||
// Set up of the cons might take time
|
// Set up of the cons might take time
|
||||||
await delay(100)
|
await new Promise(resolve => setTimeout(resolve, 100))
|
||||||
|
|
||||||
const peer = await node1.peerRouting.findPeer(node3.peerId)
|
const peer = await node1.peerRouting.findPeer(node3.peerId)
|
||||||
|
|
||||||
console.log('Found it, multiaddrs are:')
|
console.log('Found it, multiaddrs are:')
|
||||||
peer.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${peer.id.toB58String()}`))
|
peer.multiaddrs.forEach((ma) => console.log(ma.toString()))
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see the output being something like:
|
You should see the output being something like:
|
||||||
@ -78,12 +81,17 @@ You can find this example completed in [2.js](./2.js), however as you will see i
|
|||||||
Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide` and `contentRouting.findProviders`.
|
Instead of calling `peerRouting.findPeer`, we will use `contentRouting.provide` and `contentRouting.findProviders`.
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
|
import { CID } from 'multiformats/cid'
|
||||||
|
import all from 'it-all'
|
||||||
|
|
||||||
|
const cid = CID.parse('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
|
||||||
await node1.contentRouting.provide(cid)
|
await node1.contentRouting.provide(cid)
|
||||||
console.log('Node %s is providing %s', node1.peerId.toB58String(), cid.toString())
|
|
||||||
|
|
||||||
const provs = await all(node3.contentRouting.findProviders(cid, { timeout: 5000 }))
|
console.log('Node %s is providing %s', node1.peerId.toString(), cid.toString())
|
||||||
|
|
||||||
console.log('Found provider:', providers[0].id.toB58String())
|
const providers = await all(node3.contentRouting.findProviders(cid, { timeout: 5000 }))
|
||||||
|
|
||||||
|
console.log('Found provider:', providers[0].id.toString())
|
||||||
```
|
```
|
||||||
|
|
||||||
The output of your program should look like:
|
The output of your program should look like:
|
||||||
|
@ -6,21 +6,25 @@ The feature of agreeing on a protocol over an established connection is what we
|
|||||||
|
|
||||||
# 1. Handle multiple protocols
|
# 1. Handle multiple protocols
|
||||||
|
|
||||||
Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `libp2p-tcp`, `peer-id`, `it-pipe`, `it-buffer` and `streaming-iterables`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js).
|
Let's see _protocol multiplexing_ in action! You will need the following modules for this example: `libp2p`, `@libp2p/tcp`, `@libp2p/peer-id`, `it-pipe`, `it-buffer` and `streaming-iterables`. This example reuses the base left by the [Transports](../transports) example. You can see the complete solution at [1.js](./1.js).
|
||||||
|
|
||||||
After creating the nodes, we need to tell libp2p which protocols to handle.
|
After creating the nodes, we need to tell libp2p which protocols to handle.
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
import { pipe } from 'it-pipe'
|
import { pipe } from 'it-pipe'
|
||||||
const { map } from 'streaming-iterables')
|
import { map } from 'streaming-iterables'
|
||||||
const { toBuffer } from 'it-buffer')
|
import { toBuffer } from 'it-buffer'
|
||||||
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
||||||
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
const node1 = nodes[0]
|
const [node1, node2] = await Promise.all([
|
||||||
const node2 = nodes[1]
|
createNode(),
|
||||||
|
createNode()
|
||||||
|
])
|
||||||
|
|
||||||
// Add node's 2 data to the PeerStore
|
// Add node's 2 data to the PeerStore
|
||||||
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
await node1.peerStore.addressBook.set(node2.peerId, node2.getMultiaddrs())
|
||||||
|
|
||||||
// Here we are telling libp2p that if someone dials this node to talk with the `/your-protocol`
|
// Here we are telling libp2p that if someone dials this node to talk with the `/your-protocol`
|
||||||
// multicodec, the protocol identifier, please call this handler and give it the stream
|
// multicodec, the protocol identifier, please call this handler and give it the stream
|
||||||
@ -30,7 +34,7 @@ node2.handle('/your-protocol', ({ stream }) => {
|
|||||||
stream,
|
stream,
|
||||||
source => (async function () {
|
source => (async function () {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
console.log(msg.toString())
|
console.log(uint8ArrayToString(msg.subarray()))
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
)
|
)
|
||||||
@ -43,7 +47,7 @@ After the protocol is _handled_, now we can dial to it.
|
|||||||
const stream = await node1.dialProtocol(node2.peerId, ['/your-protocol'])
|
const stream = await node1.dialProtocol(node2.peerId, ['/your-protocol'])
|
||||||
|
|
||||||
await pipe(
|
await pipe(
|
||||||
['my own protocol, wow!'],
|
[uint8ArrayFromString('my own protocol, wow!')],
|
||||||
stream
|
stream
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@ -56,7 +60,7 @@ node2.handle('/another-protocol/1.0.1', ({ stream }) => {
|
|||||||
stream,
|
stream,
|
||||||
async function (source) {
|
async function (source) {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
console.log(msg.toString())
|
console.log(uint8ArrayToString(msg.subarray()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -65,7 +69,7 @@ node2.handle('/another-protocol/1.0.1', ({ stream }) => {
|
|||||||
const stream = await node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0'])
|
const stream = await node1.dialProtocol(node2.peerId, ['/another-protocol/1.0.0'])
|
||||||
|
|
||||||
await pipe(
|
await pipe(
|
||||||
['my own protocol, wow!'],
|
[uint8ArrayFromString('my own protocol, wow!')],
|
||||||
stream
|
stream
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@ -75,8 +79,8 @@ This feature is super power for network protocols. It works in the same way as v
|
|||||||
There is still one last feature, you can provide multiple protocols for the same handler. If you have a backwards incompatible change, but it only requires minor changes to the code, you may prefer to do protocol checking instead of having multiple handlers
|
There is still one last feature, you can provide multiple protocols for the same handler. If you have a backwards incompatible change, but it only requires minor changes to the code, you may prefer to do protocol checking instead of having multiple handlers
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
node2.handle(['/another-protocol/1.0.0', '/another-protocol/2.0.0'], ({ protocol, stream }) => {
|
node2.handle(['/another-protocol/1.0.0', '/another-protocol/2.0.0'], ({ stream }) => {
|
||||||
if (protocol === '/another-protocol/2.0.0') {
|
if (stream.stat.protocol === '/another-protocol/2.0.0') {
|
||||||
// handle backwards compatibility
|
// handle backwards compatibility
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +88,7 @@ node2.handle(['/another-protocol/1.0.0', '/another-protocol/2.0.0'], ({ protocol
|
|||||||
stream,
|
stream,
|
||||||
async function (source) {
|
async function (source) {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
console.log(msg.toString())
|
console.log(uint8ArrayToString(msg.subarray()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -107,27 +111,27 @@ import { TCP } from '@libp2p/tcp'
|
|||||||
import { Mplex } from '@libp2p/mplex'
|
import { Mplex } from '@libp2p/mplex'
|
||||||
//...
|
//...
|
||||||
|
|
||||||
const createNode = () => {
|
createLibp2p({
|
||||||
return Libp2p.create({
|
//...
|
||||||
transports: [
|
transports: [
|
||||||
new TCP()
|
new TCP()
|
||||||
],
|
],
|
||||||
streamMuxers: [
|
streamMuxers: [
|
||||||
new Mplex()
|
new Mplex()
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
With this, we can dial as many times as we want to a peer and always reuse the same established underlying connection.
|
With this, we can dial as many times as we want to a peer and always reuse the same established underlying connection.
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
node2.handle(['/a', '/b'], ({ protocol, stream }) => {
|
node2.handle(['/a', '/b'], ({ stream }) => {
|
||||||
pipe(
|
pipe(
|
||||||
stream,
|
stream,
|
||||||
async function (source) {
|
async function (source) {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
console.log(`from: ${protocol}, msg: ${msg.toString()}`)
|
console.log(`from: ${stream.stat.protocol}, msg: ${uint8ArrayToString(msg.subarray())}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -135,19 +139,19 @@ node2.handle(['/a', '/b'], ({ protocol, stream }) => {
|
|||||||
|
|
||||||
const stream = await node1.dialProtocol(node2.peerId, ['/a'])
|
const stream = await node1.dialProtocol(node2.peerId, ['/a'])
|
||||||
await pipe(
|
await pipe(
|
||||||
['protocol (a)'],
|
[uint8ArrayFromString('protocol (a)')],
|
||||||
stream
|
stream
|
||||||
)
|
)
|
||||||
|
|
||||||
const stream2 = await node1.dialProtocol(node2.peerId, ['/b'])
|
const stream2 = await node1.dialProtocol(node2.peerId, ['/b'])
|
||||||
await pipe(
|
await pipe(
|
||||||
['protocol (b)'],
|
[uint8ArrayFromString('protocol (b)')],
|
||||||
stream2
|
stream2
|
||||||
)
|
)
|
||||||
|
|
||||||
const stream3 = await node1.dialProtocol(node2.peerId, ['/b'])
|
const stream3 = await node1.dialProtocol(node2.peerId, ['/b'])
|
||||||
await pipe(
|
await pipe(
|
||||||
['another stream on protocol (b)'],
|
[uint8ArrayFromString('another stream on protocol (b)')],
|
||||||
stream3
|
stream3
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@ -172,15 +176,13 @@ You can see this working on example [3.js](./3.js).
|
|||||||
As we've seen earlier, we can create our node with this createNode function.
|
As we've seen earlier, we can create our node with this createNode function.
|
||||||
```js
|
```js
|
||||||
const createNode = async () => {
|
const createNode = async () => {
|
||||||
const node = await Libp2p.create({
|
const node = await createLibp2p({
|
||||||
addresses: {
|
addresses: {
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||||
},
|
},
|
||||||
modules: {
|
transports: [new TCP()],
|
||||||
transport: [TCP],
|
streamMuxers: [new Mplex()],
|
||||||
streamMuxer: [MPLEX],
|
connectionEncryption: [new Noise()],
|
||||||
connEncryption: [NOISE]
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await node.start()
|
await node.start()
|
||||||
@ -199,7 +201,7 @@ const [node1, node2] = await Promise.all([
|
|||||||
|
|
||||||
Since, we want to connect these nodes `node1` & `node2`, we add our `node2` multiaddr in key-value pair in `node1` peer store.
|
Since, we want to connect these nodes `node1` & `node2`, we add our `node2` multiaddr in key-value pair in `node1` peer store.
|
||||||
```js
|
```js
|
||||||
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
await node1.peerStore.addressBook.set(node2.peerId, node2.getMultiaddrs())
|
||||||
```
|
```
|
||||||
|
|
||||||
You may notice that we are only adding `node2` to `node1` peer store. This is because we want to dial up a bidirectional connection between these two nodes.
|
You may notice that we are only adding `node2` to `node1` peer store. This is because we want to dial up a bidirectional connection between these two nodes.
|
||||||
@ -211,7 +213,7 @@ node1.handle('/node-1', ({ stream }) => {
|
|||||||
stream,
|
stream,
|
||||||
async function (source) {
|
async function (source) {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
console.log(msg.toString())
|
console.log(uint8ArrayToString(msg.subarray()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -222,7 +224,7 @@ node2.handle('/node-2', ({ stream }) => {
|
|||||||
stream,
|
stream,
|
||||||
async function (source) {
|
async function (source) {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
console.log(msg.toString())
|
console.log(uint8ArrayToString(msg.subarray()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -231,14 +233,14 @@ node2.handle('/node-2', ({ stream }) => {
|
|||||||
// Dialing node2 from node1
|
// Dialing node2 from node1
|
||||||
const stream1 = await node1.dialProtocol(node2.peerId, ['/node-2'])
|
const stream1 = await node1.dialProtocol(node2.peerId, ['/node-2'])
|
||||||
await pipe(
|
await pipe(
|
||||||
['from 1 to 2'],
|
[uint8ArrayFromString('from 1 to 2')],
|
||||||
stream1
|
stream1
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dialing node1 from node2
|
// Dialing node1 from node2
|
||||||
const stream2 = await node2.dialProtocol(node1.peerId, ['/node-1'])
|
const stream2 = await node2.dialProtocol(node1.peerId, ['/node-1'])
|
||||||
await pipe(
|
await pipe(
|
||||||
['from 2 to 1'],
|
[uint8ArrayFromString('from 2 to 1')],
|
||||||
stream2
|
stream2
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@ -258,14 +260,14 @@ The code below will result into an error as `the dial address is not valid`.
|
|||||||
// Dialing from node2 to node1
|
// Dialing from node2 to node1
|
||||||
const stream2 = await node2.dialProtocol(node1.peerId, ['/node-1'])
|
const stream2 = await node2.dialProtocol(node1.peerId, ['/node-1'])
|
||||||
await pipe(
|
await pipe(
|
||||||
['from 2 to 1'],
|
[uint8ArrayFromString('from 2 to 1')],
|
||||||
stream2
|
stream2
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dialing from node1 to node2
|
// Dialing from node1 to node2
|
||||||
const stream1 = await node1.dialProtocol(node2.peerId, ['/node-2'])
|
const stream1 = await node1.dialProtocol(node2.peerId, ['/node-2'])
|
||||||
await pipe(
|
await pipe(
|
||||||
['from 1 to 2'],
|
[uint8ArrayFromString('from 1 to 2')],
|
||||||
stream1
|
stream1
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Publish Subscribe
|
# Publish Subscribe
|
||||||
|
|
||||||
Publish Subscribe is also included on the stack. Currently, we have two PubSub implementation available [libp2p-floodsub](https://github.com/libp2p/js-libp2p-floodsub) and [libp2p-gossipsub](https://github.com/ChainSafe/js-libp2p-gossipsub), with many more being researched at [research-pubsub](https://github.com/libp2p/research-pubsub).
|
Publish Subscribe is also included on the stack. Currently, we have two PubSub implementation available [@libp2p/floodsub](https://github.com/libp2p/js-libp2p-floodsub) and [@chainsafe/libp2p-gossipsub](https://github.com/ChainSafe/js-libp2p-gossipsub), with many more being researched at [research-pubsub](https://github.com/libp2p/research-pubsub).
|
||||||
|
|
||||||
We've seen many interesting use cases appear with this, here are some highlights:
|
We've seen many interesting use cases appear with this, here are some highlights:
|
||||||
|
|
||||||
@ -22,54 +22,60 @@ First, let's update our libp2p configuration with a pubsub implementation.
|
|||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
import { createLibp2p } from 'libp2p'
|
import { createLibp2p } from 'libp2p'
|
||||||
import { Gossipsub } from 'libp2p-gossipsub'
|
import { GossipSub } from '@chainsafe/libp2p-gossipsub'
|
||||||
|
import { TCP } from '@libp2p/tcp'
|
||||||
|
import { Mplex } from '@libp2p/mplex'
|
||||||
|
import { Noise } from '@chainsafe/libp2p-noise'
|
||||||
|
|
||||||
const node = await createLibp2p({
|
const createNode = async () => {
|
||||||
addresses: {
|
const node = await createLibp2p({
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
addresses: {
|
||||||
},
|
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||||
transports: [
|
},
|
||||||
new TCP()
|
transports: [new TCP()],
|
||||||
],
|
streamMuxers: [new Mplex()],
|
||||||
streamMuxers: [
|
connectionEncryption: [new Noise()],
|
||||||
new Mplex()
|
// we add the Pubsub module we want
|
||||||
],
|
pubsub: new GossipSub({ allowPublishToZeroPeers: true })
|
||||||
connectionEncryption: [
|
})
|
||||||
new Noise()
|
|
||||||
],
|
await node.start()
|
||||||
// we add the Pubsub module we want
|
|
||||||
pubsub: new Gossipsub()
|
return node
|
||||||
})
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Once that is done, we only need to create a few libp2p nodes, connect them and everything is ready to start using pubsub.
|
Once that is done, we only need to create a few libp2p nodes, connect them and everything is ready to start using pubsub.
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
const { fromString } from 'uint8arrays/from-string')
|
import { fromString as uint8ArrayFromString } from "uint8arrays/from-string";
|
||||||
const { toString } from 'uint8arrays/to-string')
|
import { toString as uint8ArrayToString } from "uint8arrays/to-string";
|
||||||
|
|
||||||
const topic = 'news'
|
const topic = 'news'
|
||||||
|
|
||||||
const node1 = nodes[0]
|
const [node1, node2] = await Promise.all([
|
||||||
const node2 = nodes[1]
|
createNode(),
|
||||||
|
createNode()
|
||||||
|
])
|
||||||
|
|
||||||
// Add node's 2 data to the PeerStore
|
// Add node's 2 data to the PeerStore
|
||||||
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
await node1.peerStore.addressBook.set(node2.peerId, node2.getMultiaddrs())
|
||||||
await node1.dial(node2.peerId)
|
await node1.dial(node2.peerId)
|
||||||
|
|
||||||
node1.pubsub.on(topic, (msg) => {
|
node1.pubsub.addEventListener("message", (evt) => {
|
||||||
console.log(`node1 received: ${toString(msg.data)}`)
|
console.log(`node1 received: ${uint8ArrayToString(evt.detail.data)} on topic ${evt.detail.topic}`)
|
||||||
})
|
})
|
||||||
await node1.pubsub.subscribe(topic)
|
await node1.pubsub.subscribe(topic)
|
||||||
|
|
||||||
// Will not receive own published messages by default
|
// Will not receive own published messages by default
|
||||||
node2.pubsub.on(topic, (msg) => {
|
node2.pubsub.addEventListener("message", (evt) => {
|
||||||
console.log(`node2 received: ${toString(msg.data)}`)
|
console.log(`node2 received: ${uint8ArrayToString(evt.detail.data)} on topic ${evt.detail.topic}`)
|
||||||
})
|
})
|
||||||
await node2.pubsub.subscribe(topic)
|
await node2.pubsub.subscribe(topic)
|
||||||
|
|
||||||
// node2 publishes "news" every second
|
// node2 publishes "news" every second
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
node2.pubsub.publish(topic, fromString('Bird bird bird, bird is the word!')).catch(err => {
|
node2.pubsub.publish(topic, uint8ArrayFromString('Bird bird bird, bird is the word!')).catch(err => {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
@ -87,14 +93,7 @@ node1 received: Bird bird bird, bird is the word!
|
|||||||
You can change the pubsub `emitSelf` option if you want the publishing node to receive its own messages.
|
You can change the pubsub `emitSelf` option if you want the publishing node to receive its own messages.
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
const defaults = {
|
new GossipSub({ allowPublishToZeroPeers: true, emitSelf: true })
|
||||||
config: {
|
|
||||||
pubsub: {
|
|
||||||
enabled: true,
|
|
||||||
emitSelf: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The output of the program should look like:
|
The output of the program should look like:
|
||||||
|
@ -8,23 +8,27 @@ First, let's update our libp2p configuration with a pubsub implementation.
|
|||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
import { createLibp2p } from 'libp2p'
|
import { createLibp2p } from 'libp2p'
|
||||||
import { Gossipsub } from 'libp2p-gossipsub'
|
import { GossipSub } from '@chainsafe/libp2p-gossipsub'
|
||||||
|
import { TCP } from '@libp2p/tcp'
|
||||||
|
import { Mplex } from '@libp2p/mplex'
|
||||||
|
import { Noise } from '@chainsafe/libp2p-noise'
|
||||||
|
|
||||||
const node = await createLibp2p({
|
const createNode = async () => {
|
||||||
addresses: {
|
const node = await createLibp2p({
|
||||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
addresses: {
|
||||||
},
|
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||||
transports: [
|
},
|
||||||
new TCP()
|
transports: [new TCP()],
|
||||||
],
|
streamMuxers: [new Mplex()],
|
||||||
streamMuxers: [
|
connectionEncryption: [new Noise()],
|
||||||
new Mplex()
|
// we add the Pubsub module we want
|
||||||
],
|
pubsub: new GossipSub({ allowPublishToZeroPeers: true })
|
||||||
connectionEncryption: [
|
})
|
||||||
new Noise()
|
|
||||||
],
|
await node.start()
|
||||||
pubsub: new Gossipsub()
|
|
||||||
})
|
return node
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, create three nodes and connect them together. In this example, we will connect the nodes in series. Node 1 connected with node 2 and node 2 connected with node 3.
|
Then, create three nodes and connect them together. In this example, we will connect the nodes in series. Node 1 connected with node 2 and node 2 connected with node 3.
|
||||||
@ -36,16 +40,19 @@ const [node1, node2, node3] = await Promise.all([
|
|||||||
createNode(),
|
createNode(),
|
||||||
])
|
])
|
||||||
|
|
||||||
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
await node1.peerStore.addressBook.set(node2.peerId, node2.getMultiaddrs())
|
||||||
await node1.dial(node2.peerId)
|
await node1.dial(node2.peerId)
|
||||||
|
|
||||||
await node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
await node2.peerStore.addressBook.set(node3.peerId, node3.getMultiaddrs())
|
||||||
await node2.dial(node3.peerId)
|
await node2.dial(node3.peerId)
|
||||||
```
|
```
|
||||||
|
|
||||||
Now we' can subscribe to the fruit topic and log incoming messages.
|
Now we' can subscribe to the fruit topic and log incoming messages.
|
||||||
|
|
||||||
```JavaScript
|
```JavaScript
|
||||||
|
import { fromString as uint8ArrayFromString } from "uint8arrays/from-string";
|
||||||
|
import { toString as uint8ArrayToString } from "uint8arrays/to-string";
|
||||||
|
|
||||||
const topic = 'fruit'
|
const topic = 'fruit'
|
||||||
|
|
||||||
node1.pubsub.addEventListener('message', (msg) => {
|
node1.pubsub.addEventListener('message', (msg) => {
|
||||||
@ -116,4 +123,4 @@ node3 received: apple
|
|||||||
node1 received: orange
|
node1 received: orange
|
||||||
node2 received: orange
|
node2 received: orange
|
||||||
node3 received: orange
|
node3 received: orange
|
||||||
```
|
```
|
||||||
|
@ -13,7 +13,7 @@ When using libp2p, you need properly configure it, that is, pick your set of mod
|
|||||||
You will need 4 dependencies total, so go ahead and install all of them with:
|
You will need 4 dependencies total, so go ahead and install all of them with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> npm install libp2p libp2p-tcp @chainsafe/libp2p-noise
|
> npm install libp2p @libp2p/tcp @chainsafe/libp2p-noise
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, in your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
|
Then, in your favorite text editor create a file with the `.js` extension. I've called mine `1.js`.
|
||||||
@ -58,7 +58,7 @@ console.log('node has started (true/false):', node.isStarted())
|
|||||||
// 0, which means "listen in any network interface and pick
|
// 0, which means "listen in any network interface and pick
|
||||||
// a port for me
|
// a port for me
|
||||||
console.log('listening on:')
|
console.log('listening on:')
|
||||||
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
|
node.getMultiaddrs().forEach((ma) => console.log(ma.toString()))
|
||||||
```
|
```
|
||||||
|
|
||||||
Running this should result in something like:
|
Running this should result in something like:
|
||||||
@ -80,15 +80,15 @@ Now that we have our `createNode` function, let's create two nodes and make them
|
|||||||
For this step, we will need some more dependencies.
|
For this step, we will need some more dependencies.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> npm install it-pipe it-to-buffer @libp2p/mplex
|
> npm install it-pipe it-all @libp2p/mplex
|
||||||
```
|
```
|
||||||
|
|
||||||
And we also need to import the modules on our .js file:
|
And we also need to import the modules on our .js file:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { pipe } from 'it-pipe'
|
import { pipe } from 'it-pipe'
|
||||||
import toBuffer from 'it-to-buffer'
|
|
||||||
import { Mplex } from '@libp2p/mplex'
|
import { Mplex } from '@libp2p/mplex'
|
||||||
|
import all from 'it-all'
|
||||||
```
|
```
|
||||||
|
|
||||||
We are going to reuse the `createNode` function from step 1, but this time add a stream multiplexer from `libp2p-mplex`.
|
We are going to reuse the `createNode` function from step 1, but this time add a stream multiplexer from `libp2p-mplex`.
|
||||||
@ -114,38 +114,39 @@ We will also make things simpler by creating another function to print the multi
|
|||||||
```JavaScript
|
```JavaScript
|
||||||
function printAddrs (node, number) {
|
function printAddrs (node, number) {
|
||||||
console.log('node %s is listening on:', number)
|
console.log('node %s is listening on:', number)
|
||||||
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
|
node.getMultiaddrs().forEach((ma) => console.log(ma.toString()))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then add,
|
Then add,
|
||||||
|
|
||||||
```js
|
```js
|
||||||
(async () => {
|
import { fromString as uint8ArrayFromString } from "uint8arrays/from-string";
|
||||||
const [node1, node2] = await Promise.all([
|
import { toString as uint8ArrayToString } from "uint8arrays/to-string";
|
||||||
createNode(),
|
|
||||||
createNode()
|
|
||||||
])
|
|
||||||
|
|
||||||
printAddrs(node1, '1')
|
const [node1, node2] = await Promise.all([
|
||||||
printAddrs(node2, '2')
|
createNode(),
|
||||||
|
createNode()
|
||||||
|
])
|
||||||
|
|
||||||
node2.handle('/print', async ({ stream }) => {
|
printAddrs(node1, '1')
|
||||||
const result = await pipe(
|
printAddrs(node2, '2')
|
||||||
stream,
|
|
||||||
toBuffer
|
|
||||||
)
|
|
||||||
console.log(result.toString())
|
|
||||||
})
|
|
||||||
|
|
||||||
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
node2.handle('/print', async ({ stream }) => {
|
||||||
const stream = await node1.dialProtocol(node2.peerId, '/print')
|
const result = await pipe(
|
||||||
|
stream,
|
||||||
await pipe(
|
all
|
||||||
['Hello', ' ', 'p2p', ' ', 'world', '!'],
|
|
||||||
stream
|
|
||||||
)
|
)
|
||||||
})();
|
console.log(result.map(buf => uint8ArrayToString(buf.subarray())).join(""))
|
||||||
|
})
|
||||||
|
|
||||||
|
await node1.peerStore.addressBook.set(node2.peerId, node2.getMultiaddrs())
|
||||||
|
const stream = await node1.dialProtocol(node2.peerId, '/print')
|
||||||
|
|
||||||
|
await pipe(
|
||||||
|
['Hello', ' ', 'p2p', ' ', 'world', '!'].map(str => uint8ArrayFromString(str)),
|
||||||
|
stream
|
||||||
|
)
|
||||||
```
|
```
|
||||||
For more information refer to the [docs](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md).
|
For more information refer to the [docs](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md).
|
||||||
|
|
||||||
@ -168,10 +169,10 @@ Next, we want nodes to have multiple transports available to increase their chan
|
|||||||
|
|
||||||
What we are going to do in this step is to create 3 nodes: one with TCP, another with TCP+WebSockets and another one with just WebSockets. The full solution can be found on [3.js](./3.js).
|
What we are going to do in this step is to create 3 nodes: one with TCP, another with TCP+WebSockets and another one with just WebSockets. The full solution can be found on [3.js](./3.js).
|
||||||
|
|
||||||
In this example, we will need to also install `libp2p-websockets`:
|
In this example, we will need to also install `@libp2p/websockets`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> npm install libp2p-websockets
|
> npm install @libp2p/websockets
|
||||||
```
|
```
|
||||||
|
|
||||||
We want to create 3 nodes: one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `createNode` function to accept WebSocket connections as well. Moreover, let's upgrade our function to enable us to pick the addresses over which a node will start a listener:
|
We want to create 3 nodes: one with TCP, one with TCP+WebSockets and one with just WebSockets. We need to update our `createNode` function to accept WebSocket connections as well. Moreover, let's upgrade our function to enable us to pick the addresses over which a node will start a listener:
|
||||||
@ -188,7 +189,7 @@ const createNode = async (transports, addresses = []) => {
|
|||||||
addresses: {
|
addresses: {
|
||||||
listen: addresses
|
listen: addresses
|
||||||
},
|
},
|
||||||
transport: transports,
|
transports: transports,
|
||||||
connectionEncryption: [new Noise()],
|
connectionEncryption: [new Noise()],
|
||||||
streamMuxers: [new Mplex()]
|
streamMuxers: [new Mplex()]
|
||||||
})
|
})
|
||||||
@ -207,9 +208,9 @@ import { WebSockets } from '@libp2p/websockets'
|
|||||||
import { TCP } from '@libp2p/tcp'
|
import { TCP } from '@libp2p/tcp'
|
||||||
|
|
||||||
const [node1, node2, node3] = await Promise.all([
|
const [node1, node2, node3] = await Promise.all([
|
||||||
createNode([TCP], '/ip4/0.0.0.0/tcp/0'),
|
createNode([new TCP()], '/ip4/0.0.0.0/tcp/0'),
|
||||||
createNode([TCP, WebSockets], ['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws']),
|
createNode([new TCP(), new WebSockets()], ['/ip4/0.0.0.0/tcp/0', '/ip4/127.0.0.1/tcp/10000/ws']),
|
||||||
createNode([WebSockets], '/ip4/127.0.0.1/tcp/20000/ws')
|
createNode([new WebSockets()], '/ip4/127.0.0.1/tcp/20000/ws')
|
||||||
])
|
])
|
||||||
|
|
||||||
printAddrs(node1, '1')
|
printAddrs(node1, '1')
|
||||||
@ -220,21 +221,21 @@ node1.handle('/print', print)
|
|||||||
node2.handle('/print', print)
|
node2.handle('/print', print)
|
||||||
node3.handle('/print', print)
|
node3.handle('/print', print)
|
||||||
|
|
||||||
await node1.peerStore.addressBook.set(node2.peerId, node2.multiaddrs)
|
await node1.peerStore.addressBook.set(node2.peerId, node2.getMultiaddrs())
|
||||||
await node2.peerStore.addressBook.set(node3.peerId, node3.multiaddrs)
|
await node2.peerStore.addressBook.set(node3.peerId, node3.getMultiaddrs())
|
||||||
await node3.peerStore.addressBook.set(node1.peerId, node1.multiaddrs)
|
await node3.peerStore.addressBook.set(node1.peerId, node1.getMultiaddrs())
|
||||||
|
|
||||||
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
|
// node 1 (TCP) dials to node 2 (TCP+WebSockets)
|
||||||
const stream = await node1.dialProtocol(node2.peerId, '/print')
|
const stream = await node1.dialProtocol(node2.peerId, '/print')
|
||||||
await pipe(
|
await pipe(
|
||||||
['node 1 dialed to node 2 successfully'],
|
['node 1 dialed to node 2 successfully'].map(str => uint8ArrayFromString(str)),
|
||||||
stream
|
stream
|
||||||
)
|
)
|
||||||
|
|
||||||
// node 2 (TCP+WebSockets) dials to node 3 (WebSockets)
|
// node 2 (TCP+WebSockets) dials to node 3 (WebSockets)
|
||||||
const stream2 = await node2.dialProtocol(node3.peerId, '/print')
|
const stream2 = await node2.dialProtocol(node3.peerId, '/print')
|
||||||
await pipe(
|
await pipe(
|
||||||
['node 2 dialed to node 3 successfully'],
|
['node 2 dialed to node 3 successfully'].map(str => uint8ArrayFromString(str)),
|
||||||
stream2
|
stream2
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -254,7 +255,7 @@ function print ({ stream }) {
|
|||||||
stream,
|
stream,
|
||||||
async function (source) {
|
async function (source) {
|
||||||
for await (const msg of source) {
|
for await (const msg of source) {
|
||||||
console.log(msg.toString())
|
console.log(uint8ArrayToString(msg.subarray()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
### Webrtc-direct example
|
### Webrtc-direct example
|
||||||
|
|
||||||
An example that uses [js-libp2p-webrtc-direct](https://github.com/libp2p/js-libp2p-webrtc-direct) for connecting
|
An example that uses [@libp2p/webrtc-direct](https://github.com/libp2p/js-libp2p-webrtc-direct) for connecting
|
||||||
nodejs libp2p and browser libp2p clients. To run the example:
|
nodejs libp2p and browser libp2p clients. To run the example:
|
||||||
|
|
||||||
## 0. Run a nodejs libp2p listener
|
## 0. Run a nodejs libp2p listener
|
||||||
|
Loading…
x
Reference in New Issue
Block a user