mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-07-23 20:41:57 +00:00
Compare commits
48 Commits
chore/use-
...
v0.30.10
Author | SHA1 | Date | |
---|---|---|---|
|
8e1fc78353 | ||
|
8895a092b6 | ||
|
f2f361998d | ||
|
5f702f3481 | ||
|
03b34cac7d | ||
|
9c67364caa | ||
|
a1424826e7 | ||
|
3d5bba070b | ||
|
3f314d5e90 | ||
|
4ee3e1973b | ||
|
fc6558b897 | ||
|
3e302570e5 | ||
|
a34d2bbcc3 | ||
|
9941414a91 | ||
|
46cb46188a | ||
|
1af8472dc6 | ||
|
f6a4cad827 | ||
|
b1079474de | ||
|
a150ea60c5 | ||
|
aec8e3d3bb | ||
|
3abf4aeb35 | ||
|
a36b2112aa | ||
|
8d3b61710a | ||
|
5dbbeef311 | ||
|
3e7594f697 | ||
|
ce2a624a09 | ||
|
a64c02838c | ||
|
74d07e5e8c | ||
|
eeda056883 | ||
|
f06e06a006 | ||
|
28f52bbf75 | ||
|
ed5f8f853f | ||
|
0a6bc0d101 | ||
|
b5c9e48b68 | ||
|
9942cbd50c | ||
|
037c965a67 | ||
|
748b552876 | ||
|
961b48bb8d | ||
|
000826db21 | ||
|
45c33675a7 | ||
|
a28c878f4a | ||
|
67067c97d5 | ||
|
f45cd1c4b5 | ||
|
0a02207116 | ||
|
0b854a949f | ||
|
9014ea657a | ||
|
f40697975e | ||
|
6c41e30456 |
@@ -31,6 +31,9 @@ const before = async () => {
|
||||
enabled: true,
|
||||
active: false
|
||||
}
|
||||
},
|
||||
nat: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -45,7 +48,7 @@ const after = async () => {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
bundlesize: { maxSize: '225kB' },
|
||||
bundlesize: { maxSize: '222kB' },
|
||||
hooks: {
|
||||
pre: before,
|
||||
post: after
|
||||
|
74
.github/workflows/main.yml
vendored
74
.github/workflows/main.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
node: [12, 14]
|
||||
node: [14]
|
||||
fail-fast: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -71,4 +71,74 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- chat
|
||||
- run: cd examples && yarn && npm run test -- chat
|
||||
test-connection-encryption-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- connection-encryption
|
||||
test-discovery-mechanisms-example:
|
||||
needs: check
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- discovery-mechanisms
|
||||
test-echo-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- echo
|
||||
test-libp2p-in-the-browser-example:
|
||||
needs: check
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- libp2p-in-the-browser
|
||||
test-peer-and-content-routing-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- peer-and-content-routing
|
||||
test-pnet-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- pnet
|
||||
test-protocol-and-stream-muxing-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- protocol-and-stream-muxing
|
||||
test-pubsub-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- pubsub
|
||||
test-transports-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- transports
|
||||
test-webrtc-direct-example:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: yarn
|
||||
- run: cd examples && yarn && npm run test -- webrtc-direct
|
||||
|
86
CHANGELOG.md
86
CHANGELOG.md
@@ -1,3 +1,89 @@
|
||||
## [0.30.10](https://github.com/libp2p/js-libp2p/compare/v0.30.9...v0.30.10) (2021-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* conn mgr access to moving averages record object ([#897](https://github.com/libp2p/js-libp2p/issues/897)) ([5f702f3](https://github.com/libp2p/js-libp2p/commit/5f702f3481afd4ad4fbc89f0e9b75a6d56b03520))
|
||||
|
||||
|
||||
|
||||
## [0.30.9](https://github.com/libp2p/js-libp2p/compare/v0.30.8...v0.30.9) (2021-02-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transport manager fault tolerance should include tolerance to transport listen fail ([#893](https://github.com/libp2p/js-libp2p/issues/893)) ([3f314d5](https://github.com/libp2p/js-libp2p/commit/3f314d5e90f74583b721386d0c9c5d8363cd4de7))
|
||||
|
||||
|
||||
|
||||
## [0.30.8](https://github.com/libp2p/js-libp2p/compare/v0.30.7...v0.30.8) (2021-02-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* routers should only use dht if enabled ([#885](https://github.com/libp2p/js-libp2p/issues/885)) ([a34d2bb](https://github.com/libp2p/js-libp2p/commit/a34d2bbcc3d69ec3006137a909a7e8c53b9d378e))
|
||||
|
||||
|
||||
|
||||
## [0.30.7](https://github.com/libp2p/js-libp2p/compare/v0.30.6...v0.30.7) (2021-02-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not add observed address received from peers ([#882](https://github.com/libp2p/js-libp2p/issues/882)) ([a36b211](https://github.com/libp2p/js-libp2p/commit/a36b2112aafcee309a02de0cff5440cf69cd53a7))
|
||||
|
||||
|
||||
|
||||
## [0.30.6](https://github.com/libp2p/js-libp2p/compare/v0.30.5...v0.30.6) (2021-01-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* peer discovery type in config ([#878](https://github.com/libp2p/js-libp2p/issues/878)) ([3e7594f](https://github.com/libp2p/js-libp2p/commit/3e7594f69733bf374b374a6065458fa6cae81c5f))
|
||||
* unref nat manager retries ([#877](https://github.com/libp2p/js-libp2p/issues/877)) ([ce2a624](https://github.com/libp2p/js-libp2p/commit/ce2a624a09b3107c0b2b4752e666804ecea54fb5))
|
||||
|
||||
|
||||
|
||||
## [0.30.5](https://github.com/libp2p/js-libp2p/compare/v0.30.4...v0.30.5) (2021-01-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* create has optional peer id type ([#875](https://github.com/libp2p/js-libp2p/issues/875)) ([eeda056](https://github.com/libp2p/js-libp2p/commit/eeda05688330c17b810bf47544ef977386623317))
|
||||
|
||||
|
||||
|
||||
## [0.30.4](https://github.com/libp2p/js-libp2p/compare/v0.30.3...v0.30.4) (2021-01-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add UPnP NAT manager ([#810](https://github.com/libp2p/js-libp2p/issues/810)) ([0a6bc0d](https://github.com/libp2p/js-libp2p/commit/0a6bc0d1013dfd80ab600e8f74c1544b433ece29))
|
||||
|
||||
|
||||
|
||||
## [0.30.3](https://github.com/libp2p/js-libp2p/compare/v0.30.2...v0.30.3) (2021-01-27)
|
||||
|
||||
|
||||
|
||||
## [0.30.2](https://github.com/libp2p/js-libp2p/compare/v0.30.1...v0.30.2) (2021-01-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* store multiaddrs during content and peer routing queries ([#865](https://github.com/libp2p/js-libp2p/issues/865)) ([45c3367](https://github.com/libp2p/js-libp2p/commit/45c33675a7412c66d0fd4e113ef8506077b6f492))
|
||||
|
||||
|
||||
|
||||
## [0.30.1](https://github.com/libp2p/js-libp2p/compare/v0.30.0...v0.30.1) (2021-01-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* event emitter types with local types ([#864](https://github.com/libp2p/js-libp2p/issues/864)) ([6c41e30](https://github.com/libp2p/js-libp2p/commit/6c41e3045608bcae8061d20501be5751dad8157a))
|
||||
|
||||
|
||||
|
||||
# [0.30.0](https://github.com/libp2p/js-libp2p/compare/v0.29.4...v0.30.0) (2020-12-16)
|
||||
|
||||
|
||||
|
@@ -2055,6 +2055,15 @@ This event will be triggered anytime we are disconnected from another peer, rega
|
||||
- `peerId`: instance of [`PeerId`][peer-id]
|
||||
- `protocols`: array of known, supported protocols for the peer (string identifiers)
|
||||
|
||||
### libp2p.addressManager
|
||||
|
||||
#### Our addresses have changed
|
||||
|
||||
This could be in response to a peer telling us about addresses they have observed, or
|
||||
the NatManager performing NAT hole punching.
|
||||
|
||||
`libp2p.addressManager.on('change:addresses', () => {})`
|
||||
|
||||
## Types
|
||||
|
||||
### Stats
|
||||
|
@@ -28,6 +28,9 @@
|
||||
- [Configuring Metrics](#configuring-metrics)
|
||||
- [Configuring PeerStore](#configuring-peerstore)
|
||||
- [Customizing Transports](#customizing-transports)
|
||||
- [Configuring the NAT Manager](#configuring-the-nat-manager)
|
||||
- [Browser support](#browser-support)
|
||||
- [UPnP and NAT-PMP](#upnp-and-nat-pmp)
|
||||
- [Configuration examples](#configuration-examples)
|
||||
|
||||
## Overview
|
||||
@@ -388,6 +391,7 @@ const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const MPLEX = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const ipfsHttpClient = require('ipfs-http-client')
|
||||
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
|
||||
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
|
||||
const PeerId = require('peer-id')
|
||||
@@ -395,17 +399,25 @@ const PeerId = require('peer-id')
|
||||
// create a peerId
|
||||
const peerId = await PeerId.create()
|
||||
|
||||
const delegatedPeerRouting = new DelegatedPeerRouter(ipfsHttpClient({
|
||||
host: 'node0.delegate.ipfs.io' // In production you should setup your own delegates
|
||||
protocol: 'https',
|
||||
port: 443
|
||||
}))
|
||||
|
||||
const delegatedContentRouting = new DelegatedContentRouter(peerId, ipfsHttpClient({
|
||||
host: 'node0.delegate.ipfs.io' // In production you should setup your own delegates
|
||||
protocol: 'https',
|
||||
port: 443
|
||||
}))
|
||||
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [MPLEX],
|
||||
connEncryption: [NOISE],
|
||||
contentRouting: [
|
||||
new DelegatedContentRouter(peerId)
|
||||
],
|
||||
peerRouting: [
|
||||
new DelegatedPeerRouter()
|
||||
],
|
||||
contentRouting: [delegatedContentRouting],
|
||||
peerRouting: [delegatedPeerRouting],
|
||||
},
|
||||
peerId,
|
||||
peerRouting: { // Peer routing configuration
|
||||
@@ -733,6 +745,40 @@ const node = await Libp2p.create({
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuring the NAT Manager
|
||||
|
||||
Network Address Translation (NAT) is a function performed by your router to enable multiple devices on your local network to share a single IPv4 address. It's done transparently for outgoing connections, ensuring the correct response traffic is routed to your computer, but if you wish to accept incoming connections some configuration is necessary.
|
||||
|
||||
The NAT manager can be configured as follows:
|
||||
|
||||
```js
|
||||
const node = await Libp2p.create({
|
||||
config: {
|
||||
nat: {
|
||||
description: 'my-node', // set as the port mapping description on the router, defaults the current libp2p version and your peer id
|
||||
enabled: true, // defaults to true
|
||||
gateway: '192.168.1.1', // leave unset to auto-discover
|
||||
externalIp: '80.1.1.1', // leave unset to auto-discover
|
||||
ttl: 7200, // TTL for port mappings (min 20 minutes)
|
||||
keepAlive: true, // Refresh port mapping after TTL expires
|
||||
pmp: {
|
||||
enabled: false, // defaults to false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
##### Browser support
|
||||
|
||||
Browsers cannot open TCP ports or send the UDP datagrams necessary to configure external port mapping - to accept incoming connections in the browser please use a WebRTC transport.
|
||||
|
||||
##### UPnP and NAT-PMP
|
||||
|
||||
By default under nodejs libp2p will attempt to use [UPnP](https://en.wikipedia.org/wiki/Universal_Plug_and_Play) to configure your router to allow incoming connections to any TCP transports that have been configured.
|
||||
|
||||
[NAT-PMP](http://miniupnp.free.fr/nat-pmp.html) is a feature of some modern routers which performs a similar job to UPnP. NAT-PMP is disabled by default, if enabled libp2p will try to use NAT-PMP and will fall back to UPnP if it fails.
|
||||
|
||||
## Configuration examples
|
||||
|
||||
As libp2p is designed to be a modular networking library, its usage will vary based on individual project needs. We've included links to some existing project configurations for your reference, in case you wish to replicate their configuration:
|
||||
|
@@ -8,7 +8,7 @@ Let us know if you find any issues, or if you want to contribute and add a new t
|
||||
|
||||
- [Transports](./transports)
|
||||
- [Protocol and Stream Muxing](./protocol-and-stream-muxing)
|
||||
- [Encrypted Communications](./encrypted-communications)
|
||||
- [Connection Encryption](./connection-encryption)
|
||||
- [Discovery Mechanisms](./discovery-mechanisms)
|
||||
- [Peer and Content Routing](./peer-and-content-routing)
|
||||
- [PubSub](./pubsub)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const Libp2p = require('../../')
|
||||
const Libp2p = require('../..')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
@@ -1,4 +1,4 @@
|
||||
# Encrypted Communications
|
||||
# Connection Encryption
|
||||
|
||||
libp2p can leverage the encrypted communications from the transports it uses (i.e WebRTC). To ensure that every connection is encrypted, independently of how it was set up, libp2p also supports a set of modules that encrypt every communication established.
|
||||
|
30
examples/connection-encryption/test.js
Normal file
30
examples/connection-encryption/test.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test () {
|
||||
const messageReceived = pDefer()
|
||||
process.stdout.write('1.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '1.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
|
||||
const s = uint8ArrayToString(data)
|
||||
if (s.includes('This information is sent out encrypted to the other peer')) {
|
||||
messageReceived.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await messageReceived.promise
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
@@ -7,15 +7,7 @@ const Mplex = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
|
||||
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/src/core/runtime/config-nodejs.json
|
||||
const bootstrapers = [
|
||||
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
|
||||
]
|
||||
const bootstrapers = require('./bootstrapers')
|
||||
|
||||
;(async () => {
|
||||
const node = await Libp2p.create({
|
||||
|
68
examples/discovery-mechanisms/3.js
Normal file
68
examples/discovery-mechanisms/3.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/* eslint-disable no-console */
|
||||
'use strict'
|
||||
|
||||
const Libp2p = require('../../')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const Gossipsub = require('libp2p-gossipsub')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
const PubsubPeerDiscovery = require('libp2p-pubsub-peer-discovery')
|
||||
|
||||
const createRelayServer = require('libp2p-relay-server')
|
||||
|
||||
const createNode = async (bootstrapers) => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE],
|
||||
pubsub: Gossipsub,
|
||||
peerDiscovery: [Bootstrap, PubsubPeerDiscovery]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
[PubsubPeerDiscovery.tag]: {
|
||||
interval: 1000,
|
||||
enabled: true
|
||||
},
|
||||
[Bootstrap.tag]: {
|
||||
enabled: true,
|
||||
list: bootstrapers
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
const relay = await createRelayServer({
|
||||
listenAddresses: ['/ip4/0.0.0.0/tcp/0']
|
||||
})
|
||||
console.log(`libp2p relay starting with id: ${relay.peerId.toB58String()}`)
|
||||
await relay.start()
|
||||
const relayMultiaddrs = relay.multiaddrs.map((m) => `${m.toString()}/p2p/${relay.peerId.toB58String()}`)
|
||||
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(relayMultiaddrs),
|
||||
createNode(relayMultiaddrs)
|
||||
])
|
||||
|
||||
node1.on('peer:discovery', (peerId) => {
|
||||
console.log(`Peer ${node1.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
|
||||
})
|
||||
node2.on('peer:discovery', (peerId) => {
|
||||
console.log(`Peer ${node2.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
|
||||
})
|
||||
|
||||
;[node1, node2].forEach((node, index) => console.log(`Node ${index} starting with id: ${node.peerId.toB58String()}`))
|
||||
await Promise.all([
|
||||
node1.start(),
|
||||
node2.start()
|
||||
])
|
||||
})();
|
@@ -2,13 +2,13 @@
|
||||
|
||||
A Peer Discovery module enables libp2p to find peers to connect to. Think of these mechanisms as ways to join the rest of the network, as railing points.
|
||||
|
||||
With these system, a libp2p node can both have a set of nodes to always connect on boot (bootstraper nodes), discover nodes through locality (e.g connected in the same LAN) or through serendipity (random walks on a DHT).
|
||||
With this system, a libp2p node can both have a set of nodes to always connect on boot (bootstraper nodes), discover nodes through locality (e.g connected in the same LAN) or through serendipity (random walks on a DHT).
|
||||
|
||||
These mechanisms save configuration and enable a node to operate without any explicit dials, it will just work. Once new peers are discovered, their known data is stored in the peer's PeerStore.
|
||||
|
||||
## 1. Bootstrap list of Peers when booting a node
|
||||
|
||||
For this demo, we will connect to IPFS default bootstrapper nodes and so, we will need to support the same set of features those nodes have, that are: TCP, mplex and NOISE. You can see the complete example at [1.js](./1.js).
|
||||
For this demo, we will connect to IPFS default bootstrapper nodes and so, we will need to support the same set of features those nodes have, that are: TCP, mplex, and NOISE. You can see the complete example at [1.js](./1.js).
|
||||
|
||||
First, we create our libp2p node.
|
||||
|
||||
@@ -16,7 +16,7 @@ First, we create our libp2p node.
|
||||
const Libp2p = require('libp2p')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
|
||||
const node = Libp2p.create({
|
||||
const node = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [ TCP ],
|
||||
streamMuxer: [ Mplex ],
|
||||
@@ -156,10 +156,103 @@ Discovered: QmSSbQpuKrxkoXHm1v4Pi35hPN5hUHMZoBoawEs2Nhvi8m
|
||||
Discovered: QmRcXXhtG8vTqwVBRonKWtV4ovDoC1Fe56WYtcrw694eiJ
|
||||
```
|
||||
|
||||
## 3. Where to find other Peer Discovery Mechanisms
|
||||
## 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.
|
||||
|
||||
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).
|
||||
|
||||
You can create your libp2p nodes as follows:
|
||||
|
||||
```js
|
||||
const Libp2p = require('libp2p')
|
||||
const TCP = require('libp2p-tcp')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const { NOISE } = require('libp2p-noise')
|
||||
const Gossipsub = require('libp2p-gossipsub')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
const PubsubPeerDiscovery = require('libp2p-pubsub-peer-discovery')
|
||||
|
||||
const createNode = async (bootstrapers) => {
|
||||
const node = await Libp2p.create({
|
||||
addresses: {
|
||||
listen: ['/ip4/0.0.0.0/tcp/0']
|
||||
},
|
||||
modules: {
|
||||
transport: [TCP],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE],
|
||||
pubsub: Gossipsub,
|
||||
peerDiscovery: [Bootstrap, PubsubPeerDiscovery]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
[PubsubPeerDiscovery.tag]: {
|
||||
interval: 1000,
|
||||
enabled: true
|
||||
},
|
||||
[Bootstrap.tag]: {
|
||||
enabled: true,
|
||||
list: bootstrapers
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return node
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
const relay = await createRelayServer({
|
||||
listenAddresses: ['/ip4/0.0.0.0/tcp/0']
|
||||
})
|
||||
console.log(`libp2p relay starting with id: ${relay.peerId.toB58String()}`)
|
||||
await relay.start()
|
||||
const relayMultiaddrs = relay.multiaddrs.map((m) => `${m.toString()}/p2p/${relay.peerId.toB58String()}`)
|
||||
|
||||
const [node1, node2] = await Promise.all([
|
||||
createNode(relayMultiaddrs),
|
||||
createNode(relayMultiaddrs)
|
||||
])
|
||||
|
||||
node1.on('peer:discovery', (peerId) => {
|
||||
console.log(`Peer ${node1.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
|
||||
})
|
||||
node2.on('peer:discovery', (peerId) => {
|
||||
console.log(`Peer ${node2.peerId.toB58String()} discovered: ${peerId.toB58String()}`)
|
||||
})
|
||||
|
||||
;[node1, node2].forEach((node, index) => console.log(`Node ${index} starting with id: ${node.peerId.toB58String()}`))
|
||||
await Promise.all([
|
||||
node1.start(),
|
||||
node2.start()
|
||||
])
|
||||
```
|
||||
|
||||
If you run this example, you will see the other peers being discovered.
|
||||
|
||||
```bash
|
||||
> node 3.js
|
||||
libp2p relay starting with id: QmW6FqVV6RsyoGC5zaeFGW9gSWA3LcBRVZrjkKMruh38Bo
|
||||
Node 0 starting with id: QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N
|
||||
Node 1 starting with id: QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv
|
||||
Peer QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N discovered: QmW6FqVV6RsyoGC5zaeFGW9gSWA3LcBRVZrjkKMruh38Bo
|
||||
Peer QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv discovered: QmW6FqVV6RsyoGC5zaeFGW9gSWA3LcBRVZrjkKMruh38Bo
|
||||
Peer QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv discovered: QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N
|
||||
Peer QmezqDTmEjZ5BfMgVqjSpLY19mVVLTQ9bE9mRpZwtGxL8N discovered: QmYWeom2odTkm79DzB68NHULqVHDaNDqHhoyqLdcV1fqdv
|
||||
```
|
||||
|
||||
Taking into account the output, after the relay and both libp2p nodes start, both libp2p nodes will discover the bootstrap node (relay) and connect with it. After establishing a connection with the relay, they will discover each other.
|
||||
|
||||
This is really useful when running libp2p in constrained environments like a browser. You can run a set of `libp2p-relay-server` nodes that will be responsible for both relaying websocket connections between browser nodes and for discovering other browser peers.
|
||||
|
||||
## 4. Where to find other Peer Discovery Mechanisms
|
||||
|
||||
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.
|
||||
- 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 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!
|
||||
|
13
examples/discovery-mechanisms/bootstrapers.js
vendored
Normal file
13
examples/discovery-mechanisms/bootstrapers.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
// Find this list at: https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs-core/src/runtime/config-nodejs.js
|
||||
const bootstrapers = [
|
||||
'/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmZa1sAxajnQjVM8WjWXoMbmPd7NsWhfKsPkErzpm9wGkp',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
|
||||
'/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
|
||||
]
|
||||
|
||||
module.exports = bootstrapers
|
42
examples/discovery-mechanisms/test-1.js
Normal file
42
examples/discovery-mechanisms/test-1.js
Normal file
@@ -0,0 +1,42 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const bootstrapers = require('./bootstrapers')
|
||||
|
||||
const discoveredCopy = 'Discovered:'
|
||||
const connectedCopy = 'Connection established to:'
|
||||
|
||||
async function test () {
|
||||
const discoveredNodes = []
|
||||
const connectedNodes = []
|
||||
|
||||
process.stdout.write('1.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '1.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
// Discovered or Connected
|
||||
if (line.includes(discoveredCopy)) {
|
||||
const id = line.trim().split(discoveredCopy)[1]
|
||||
discoveredNodes.push(id)
|
||||
} else if (line.includes(connectedCopy)) {
|
||||
const id = line.trim().split(connectedCopy)[1]
|
||||
connectedNodes.push(id)
|
||||
}
|
||||
})
|
||||
|
||||
await pWaitFor(() => discoveredNodes.length === bootstrapers.length && connectedNodes.length === bootstrapers.length)
|
||||
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
35
examples/discovery-mechanisms/test-2.js
Normal file
35
examples/discovery-mechanisms/test-2.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const discoveredCopy = 'Discovered:'
|
||||
|
||||
async function test() {
|
||||
const discoveredNodes = []
|
||||
|
||||
process.stdout.write('2.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '2.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (line.includes(discoveredCopy)) {
|
||||
const id = line.trim().split(discoveredCopy)[1]
|
||||
discoveredNodes.push(id)
|
||||
}
|
||||
})
|
||||
|
||||
await pWaitFor(() => discoveredNodes.length === 2)
|
||||
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
35
examples/discovery-mechanisms/test-3.js
Normal file
35
examples/discovery-mechanisms/test-3.js
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const discoveredCopy = 'discovered:'
|
||||
|
||||
async function test() {
|
||||
let discoverCount = 0
|
||||
|
||||
process.stdout.write('3.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '3.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
// Discovered or Connected
|
||||
if (line.includes(discoveredCopy)) {
|
||||
discoverCount++
|
||||
}
|
||||
})
|
||||
|
||||
await pWaitFor(() => discoverCount === 4)
|
||||
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
13
examples/discovery-mechanisms/test.js
Normal file
13
examples/discovery-mechanisms/test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const test1 = require('./test-1')
|
||||
const test2 = require('./test-2')
|
||||
const test3 = require('./test-3')
|
||||
|
||||
async function test () {
|
||||
await test1()
|
||||
await test2()
|
||||
await test3()
|
||||
}
|
||||
|
||||
module.exports = test
|
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const createLibp2p = require('./libp2p-bundle')
|
||||
const createLibp2p = require('./libp2p')
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
async function run() {
|
||||
|
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
const createLibp2p = require('./libp2p-bundle')
|
||||
const createLibp2p = require('./libp2p')
|
||||
const pipe = require('it-pipe')
|
||||
|
||||
async function run() {
|
||||
|
61
examples/echo/test.js
Normal file
61
examples/echo/test.js
Normal file
@@ -0,0 +1,61 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
function startProcess(name) {
|
||||
return execa('node', [path.join(__dirname, name)], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
}
|
||||
|
||||
async function test () {
|
||||
const listenerReady = pDefer()
|
||||
const messageReceived = pDefer()
|
||||
|
||||
// Step 1 process
|
||||
process.stdout.write('node listener.js\n')
|
||||
const listenerProc = startProcess('src/listener.js')
|
||||
listenerProc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const s = uint8ArrayToString(data)
|
||||
|
||||
if (s.includes('Listener ready, listening on:')) {
|
||||
listenerReady.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await listenerReady.promise
|
||||
process.stdout.write('==================================================================\n')
|
||||
|
||||
// Step 2 process
|
||||
process.stdout.write('node dialer.js\n')
|
||||
const dialerProc = startProcess('src/dialer.js')
|
||||
dialerProc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const s = uint8ArrayToString(data)
|
||||
|
||||
if (s.includes('received echo:')) {
|
||||
messageReceived.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await messageReceived.promise
|
||||
process.stdout.write('echo message received\n')
|
||||
|
||||
listenerProc.kill()
|
||||
dialerProc.kill()
|
||||
await Promise.all([
|
||||
listenerProc,
|
||||
dialerProc
|
||||
]).catch((err) => {
|
||||
if (err.signal !== 'SIGTERM') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = test
|
@@ -8,13 +8,14 @@
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "parcel build index.html",
|
||||
"start": "parcel index.html"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-env": "^7.8.3",
|
||||
"@babel/preset-env": "^7.13.0",
|
||||
"libp2p": "../../",
|
||||
"libp2p-bootstrap": "^0.12.1",
|
||||
"libp2p-mplex": "^0.10.0",
|
||||
@@ -23,11 +24,11 @@
|
||||
"libp2p-websockets": "^0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.3",
|
||||
"@babel/core": "^7.8.3",
|
||||
"@babel/cli": "^7.13.10",
|
||||
"@babel/core": "^7.13.0",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-regenerator": "^6.26.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"parcel-bundler": "^1.12.4"
|
||||
"parcel-bundler": "1.12.3"
|
||||
}
|
||||
}
|
||||
|
52
examples/libp2p-in-the-browser/test.js
Normal file
52
examples/libp2p-in-the-browser/test.js
Normal file
@@ -0,0 +1,52 @@
|
||||
'use strict'
|
||||
|
||||
const execa = require('execa')
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
async function run() {
|
||||
let url = ''
|
||||
const proc = execa('parcel', ['./index.html'], {
|
||||
preferLocal: true,
|
||||
localDir: __dirname,
|
||||
cwd: __dirname,
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (chunk) => {
|
||||
/**@type {string} */
|
||||
const out = chunk.toString()
|
||||
|
||||
if (out.includes('Server running at')) {
|
||||
url = out.replace('Server running at ', '')
|
||||
}
|
||||
|
||||
if (out.includes('✨ Built in ')) {
|
||||
try {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
await page.goto(url);
|
||||
await page.waitForFunction(selector => document.querySelector(selector).innerText === 'libp2p started!', '#status')
|
||||
await page.waitForFunction(
|
||||
selector => {
|
||||
const text = document.querySelector(selector).innerText
|
||||
return text.includes('libp2p id is') &&
|
||||
text.includes('Found peer') &&
|
||||
text.includes('Connected to')
|
||||
},
|
||||
'#output',
|
||||
{ timeout: 5000 }
|
||||
)
|
||||
await browser.close();
|
||||
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
} finally {
|
||||
proc.cancel()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
module.exports = run
|
@@ -10,7 +10,12 @@
|
||||
"dependencies": {
|
||||
"execa": "^2.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"libp2p-pubsub-peer-discovery": "^3.0.0",
|
||||
"libp2p-relay-server": "^0.1.2",
|
||||
"p-defer": "^3.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"playwright": "^1.7.1"
|
||||
}
|
||||
}
|
||||
|
36
examples/peer-and-content-routing/test-1.js
Normal file
36
examples/peer-and-content-routing/test-1.js
Normal file
@@ -0,0 +1,36 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test() {
|
||||
process.stdout.write('1.js\n')
|
||||
|
||||
const addrs = []
|
||||
let foundIt = false
|
||||
const proc = execa('node', [path.join(__dirname, '1.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
// Discovered peer
|
||||
if (!foundIt && line.includes('Found it, multiaddrs are:')) {
|
||||
foundIt = true
|
||||
}
|
||||
|
||||
addrs.push(line)
|
||||
})
|
||||
|
||||
await pWaitFor(() => addrs.length === 2)
|
||||
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
40
examples/peer-and-content-routing/test-2.js
Normal file
40
examples/peer-and-content-routing/test-2.js
Normal file
@@ -0,0 +1,40 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const providedCopy = 'is providing'
|
||||
const foundCopy = 'Found provider:'
|
||||
|
||||
async function test() {
|
||||
process.stdout.write('2.js\n')
|
||||
const providedDefer = pDefer()
|
||||
const foundDefer = pDefer()
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '2.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (line.includes(providedCopy)) {
|
||||
providedDefer.resolve()
|
||||
} else if (line.includes(foundCopy)) {
|
||||
foundDefer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
providedDefer.promise,
|
||||
foundDefer.promise
|
||||
])
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
11
examples/peer-and-content-routing/test.js
Normal file
11
examples/peer-and-content-routing/test.js
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const test1 = require('./test-1')
|
||||
const test2 = require('./test-2')
|
||||
|
||||
async function test() {
|
||||
await test1()
|
||||
await test2()
|
||||
}
|
||||
|
||||
module.exports = test
|
30
examples/pnet/test.js
Normal file
30
examples/pnet/test.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test () {
|
||||
const messageReceived = pDefer()
|
||||
process.stdout.write('index.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, 'index.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
|
||||
const s = uint8ArrayToString(data)
|
||||
if (s.includes('This message is sent on a private network')) {
|
||||
messageReceived.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await messageReceived.promise
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
31
examples/protocol-and-stream-muxing/test-1.js
Normal file
31
examples/protocol-and-stream-muxing/test-1.js
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test() {
|
||||
const messageDefer = pDefer()
|
||||
process.stdout.write('1.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '1.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (line.includes('my own protocol, wow!')) {
|
||||
messageDefer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await messageDefer.promise
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
38
examples/protocol-and-stream-muxing/test-2.js
Normal file
38
examples/protocol-and-stream-muxing/test-2.js
Normal file
@@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const messages = [
|
||||
'protocol (a)',
|
||||
'protocol (b)',
|
||||
'another stream on protocol (b)'
|
||||
]
|
||||
|
||||
async function test() {
|
||||
process.stdout.write('2.js\n')
|
||||
|
||||
let count = 0
|
||||
const proc = execa('node', [path.join(__dirname, '2.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (messages.find((m) => line.includes(m))) {
|
||||
count += 1
|
||||
}
|
||||
})
|
||||
|
||||
await pWaitFor(() => count === messages.length)
|
||||
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
37
examples/protocol-and-stream-muxing/test-3.js
Normal file
37
examples/protocol-and-stream-muxing/test-3.js
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const messages = [
|
||||
'from 1 to 2',
|
||||
'from 2 to 1'
|
||||
]
|
||||
|
||||
async function test() {
|
||||
process.stdout.write('3.js\n')
|
||||
|
||||
let count = 0
|
||||
const proc = execa('node', [path.join(__dirname, '3.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (messages.find((m) => line.includes(m))) {
|
||||
count += 1
|
||||
}
|
||||
})
|
||||
|
||||
await pWaitFor(() => count === messages.length)
|
||||
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
13
examples/protocol-and-stream-muxing/test.js
Normal file
13
examples/protocol-and-stream-muxing/test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const test1 = require('./test-1')
|
||||
const test2 = require('./test-2')
|
||||
const test3 = require('./test-3')
|
||||
|
||||
async function test() {
|
||||
await test1()
|
||||
await test2()
|
||||
await test3()
|
||||
}
|
||||
|
||||
module.exports = test
|
67
examples/pubsub/message-filtering/test.js
Normal file
67
examples/pubsub/message-filtering/test.js
Normal file
@@ -0,0 +1,67 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
const stdout = [
|
||||
{
|
||||
topic: 'banana',
|
||||
messageCount: 2
|
||||
},
|
||||
{
|
||||
topic: 'apple',
|
||||
messageCount: 2
|
||||
},
|
||||
{
|
||||
topic: 'car',
|
||||
messageCount: 0
|
||||
},
|
||||
{
|
||||
topic: 'orange',
|
||||
messageCount: 2
|
||||
},
|
||||
]
|
||||
|
||||
async function test () {
|
||||
const defer = pDefer()
|
||||
let topicCount = 0
|
||||
let topicMessageCount = 0
|
||||
|
||||
process.stdout.write('message-filtering/1.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '1.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
// End
|
||||
if (topicCount === stdout.length) {
|
||||
defer.resolve()
|
||||
proc.all.removeAllListeners('data')
|
||||
}
|
||||
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (stdout[topicCount] && line.includes(stdout[topicCount].topic)) {
|
||||
// Validate previous number of messages
|
||||
if (topicCount > 0 && topicMessageCount > stdout[topicCount - 1].messageCount) {
|
||||
defer.reject()
|
||||
throw new Error(`topic ${stdout[topicCount - 1].topic} had ${topicMessageCount} messages instead of ${stdout[topicCount - 1].messageCount}`)
|
||||
}
|
||||
|
||||
topicCount++
|
||||
topicMessageCount = 0
|
||||
} else {
|
||||
topicMessageCount++
|
||||
}
|
||||
})
|
||||
|
||||
await defer.promise
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
30
examples/pubsub/test-1.js
Normal file
30
examples/pubsub/test-1.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test () {
|
||||
const defer = pDefer()
|
||||
process.stdout.write('1.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '1.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (line.includes('node1 received: Bird bird bird, bird is the word!')) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await defer.promise
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
11
examples/pubsub/test.js
Normal file
11
examples/pubsub/test.js
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const test1 = require('./test-1')
|
||||
const testMessageFiltering = require('./message-filtering/test')
|
||||
|
||||
async function test() {
|
||||
await test1()
|
||||
await testMessageFiltering()
|
||||
}
|
||||
|
||||
module.exports = test
|
@@ -22,7 +22,6 @@ async function testExample (dir) {
|
||||
await installDeps(dir)
|
||||
await build(dir)
|
||||
await runTest(dir)
|
||||
// TODO: add browser test setup
|
||||
}
|
||||
|
||||
async function installDeps (dir) {
|
||||
@@ -89,7 +88,7 @@ async function runTest (dir) {
|
||||
return
|
||||
}
|
||||
|
||||
const runTest = require(testFile)
|
||||
const test = require(testFile)
|
||||
|
||||
await runTest()
|
||||
}
|
||||
await test()
|
||||
}
|
||||
|
38
examples/transports/test-1.js
Normal file
38
examples/transports/test-1.js
Normal file
@@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test () {
|
||||
const deferStarted = pDefer()
|
||||
const deferListen = pDefer()
|
||||
|
||||
process.stdout.write('1.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '1.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
|
||||
if (line.includes('node has started (true/false): true')) {
|
||||
deferStarted.resolve()
|
||||
} else if (line.includes('p2p')) {
|
||||
deferListen.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
deferStarted.promise,
|
||||
deferListen.promise
|
||||
])
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
30
examples/transports/test-2.js
Normal file
30
examples/transports/test-2.js
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test () {
|
||||
const defer = pDefer()
|
||||
process.stdout.write('2.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '2.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (line.includes('Hello p2p world!')) {
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await defer.promise
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
41
examples/transports/test-3.js
Normal file
41
examples/transports/test-3.js
Normal file
@@ -0,0 +1,41 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
|
||||
async function test () {
|
||||
const deferNode1 = pDefer()
|
||||
const deferNode2 = pDefer()
|
||||
const deferNode3 = pDefer()
|
||||
|
||||
process.stdout.write('3.js\n')
|
||||
|
||||
const proc = execa('node', [path.join(__dirname, '3.js')], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
|
||||
proc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
const line = uint8ArrayToString(data)
|
||||
|
||||
if (line.includes('node 1 dialed to node 2 successfully')) {
|
||||
deferNode1.resolve()
|
||||
} else if (line.includes('node 2 dialed to node 3 successfully')) {
|
||||
deferNode2.resolve()
|
||||
} else if (line.includes('node 3 failed to dial to node 1 with:')) {
|
||||
deferNode3.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
deferNode1.promise,
|
||||
deferNode2.promise,
|
||||
deferNode3.promise
|
||||
])
|
||||
proc.kill()
|
||||
}
|
||||
|
||||
module.exports = test
|
13
examples/transports/test.js
Normal file
13
examples/transports/test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const test1 = require('./test-1')
|
||||
const test2 = require('./test-2')
|
||||
const test3 = require('./test-3')
|
||||
|
||||
async function test() {
|
||||
await test1()
|
||||
await test2()
|
||||
await test3()
|
||||
}
|
||||
|
||||
module.exports = test
|
33
examples/webrtc-direct/README.md
Normal file
33
examples/webrtc-direct/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
### Webrtc-direct example
|
||||
|
||||
An example that uses [js-libp2p-webrtc-direct](https://github.com/libp2p/js-libp2p-webrtc-direct) for connecting
|
||||
nodejs libp2p and browser libp2p clients. To run the example:
|
||||
|
||||
## 0. Run a nodejs libp2p listener
|
||||
|
||||
When in the root folder of this example, type `node listener.js` in terminal. You should see an address that listens for
|
||||
incoming connections. Below is just an example of such address. In your case the suffix hash (`peerId`) will be different.
|
||||
|
||||
```bash
|
||||
$ node listener.js
|
||||
Listening on:
|
||||
/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct/p2p/QmUKQCzEUhhhobcNSrXU5uzxTqbvF1BjMCGNGZzZU14Kgd
|
||||
```
|
||||
|
||||
## 1. Prepare a browser libp2p dialer
|
||||
Confirm that the above address is the same as the field `list` in `public/dialer.js`:
|
||||
```js
|
||||
peerDiscovery: {
|
||||
[Bootstrap.tag]: {
|
||||
enabled: true,
|
||||
// paste the address into `list`
|
||||
list: ['/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct/p2p/QmUKQCzEUhhhobcNSrXU5uzxTqbvF1BjMCGNGZzZU14Kgd']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Run a browser libp2p dialer
|
||||
When in the root folder of this example, type `npm run dev` in terminal. You should see an address where you can browse
|
||||
the running client. Open this address in your browser. In console
|
||||
logs you should see logs about successful connection with the node client. In the output of node client you should see
|
||||
a log message about successful connection as well.
|
57
examples/webrtc-direct/dialer.js
Normal file
57
examples/webrtc-direct/dialer.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import 'babel-polyfill'
|
||||
const Libp2p = require('libp2p')
|
||||
const WebRTCDirect = require('libp2p-webrtc-direct')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const {NOISE} = require('libp2p-noise')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// use the same peer id as in `listener.js` to avoid copy-pasting of listener's peer id into `peerDiscovery`
|
||||
const hardcodedPeerId = '12D3KooWCuo3MdXfMgaqpLC5Houi1TRoFqgK9aoxok4NK5udMu8m'
|
||||
const libp2p = await Libp2p.create({
|
||||
modules: {
|
||||
transport: [WebRTCDirect],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE],
|
||||
peerDiscovery: [Bootstrap]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
[Bootstrap.tag]: {
|
||||
enabled: true,
|
||||
list: [`/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct/p2p/${hardcodedPeerId}`]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const status = document.getElementById('status')
|
||||
const output = document.getElementById('output')
|
||||
|
||||
output.textContent = ''
|
||||
|
||||
function log (txt) {
|
||||
console.info(txt)
|
||||
output.textContent += `${txt.trim()}\n`
|
||||
}
|
||||
|
||||
// Listen for new peers
|
||||
libp2p.on('peer:discovery', (peerId) => {
|
||||
log(`Found peer ${peerId.toB58String()}`)
|
||||
})
|
||||
|
||||
// Listen for new connections to peers
|
||||
libp2p.connectionManager.on('peer:connect', (connection) => {
|
||||
log(`Connected to ${connection.remotePeer.toB58String()}`)
|
||||
})
|
||||
|
||||
// Listen for peers disconnecting
|
||||
libp2p.connectionManager.on('peer:disconnect', (connection) => {
|
||||
log(`Disconnected from ${connection.remotePeer.toB58String()}`)
|
||||
})
|
||||
|
||||
await libp2p.start()
|
||||
status.innerText = 'libp2p started!'
|
||||
log(`libp2p id is ${libp2p.peerId.toB58String()}`)
|
||||
|
||||
})
|
17
examples/webrtc-direct/index.html
Normal file
17
examples/webrtc-direct/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>js-libp2p parcel.js browser example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1 id="status">Starting libp2p...</h1>
|
||||
</header>
|
||||
<main>
|
||||
<pre id="output"></pre>
|
||||
</main>
|
||||
<script src="./dialer.js"></script>
|
||||
</body>
|
||||
</html>
|
44
examples/webrtc-direct/listener.js
Normal file
44
examples/webrtc-direct/listener.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const Libp2p = require('libp2p')
|
||||
const Bootstrap = require('libp2p-bootstrap')
|
||||
const WebRTCDirect = require('libp2p-webrtc-direct')
|
||||
const Mplex = require('libp2p-mplex')
|
||||
const {NOISE} = require('libp2p-noise')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
;(async () => {
|
||||
// hardcoded peer id to avoid copy-pasting of listener's peer id into the dialer's bootstrap list
|
||||
// generated with cmd `peer-id --type=ed25519`
|
||||
const hardcodedPeerId = await PeerId.createFromJSON({
|
||||
"id": "12D3KooWCuo3MdXfMgaqpLC5Houi1TRoFqgK9aoxok4NK5udMu8m",
|
||||
"privKey": "CAESQAG6Ld7ev6nnD0FKPs033/j0eQpjWilhxnzJ2CCTqT0+LfcWoI2Vr+zdc1vwk7XAVdyoCa2nwUR3RJebPWsF1/I=",
|
||||
"pubKey": "CAESIC33FqCNla/s3XNb8JO1wFXcqAmtp8FEd0SXmz1rBdfy"
|
||||
})
|
||||
const node = await Libp2p.create({
|
||||
peerId: hardcodedPeerId,
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/9090/http/p2p-webrtc-direct']
|
||||
},
|
||||
modules: {
|
||||
transport: [WebRTCDirect],
|
||||
streamMuxer: [Mplex],
|
||||
connEncryption: [NOISE]
|
||||
},
|
||||
config: {
|
||||
peerDiscovery: {
|
||||
[Bootstrap.tag]: {
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
node.connectionManager.on('peer:connect', (connection) => {
|
||||
console.info(`Connected to ${connection.remotePeer.toB58String()}!`)
|
||||
})
|
||||
|
||||
await node.start()
|
||||
|
||||
console.log('Listening on:')
|
||||
node.multiaddrs.forEach((ma) => console.log(`${ma.toString()}/p2p/${node.peerId.toB58String()}`))
|
||||
|
||||
})()
|
31
examples/webrtc-direct/package.json
Normal file
31
examples/webrtc-direct/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "webrtc-direct",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "parcel build index.html",
|
||||
"start": "parcel index.html"
|
||||
},
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.13.10",
|
||||
"@babel/core": "^7.13.10",
|
||||
"babel-plugin-syntax-async-functions": "^6.13.0",
|
||||
"babel-plugin-transform-regenerator": "^6.26.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"parcel-bundler": "1.12.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"libp2p": "../../",
|
||||
"libp2p-bootstrap": "^0.12.1",
|
||||
"libp2p-mplex": "^0.10.1",
|
||||
"libp2p-noise": "^2.0.1",
|
||||
"libp2p-webrtc-direct": "^0.5.0",
|
||||
"peer-id": "^0.14.3"
|
||||
},
|
||||
"browser": {
|
||||
"ipfs": "ipfs/dist/index.min.js"
|
||||
}
|
||||
}
|
93
examples/webrtc-direct/test.js
Normal file
93
examples/webrtc-direct/test.js
Normal file
@@ -0,0 +1,93 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const execa = require('execa')
|
||||
const pDefer = require('p-defer')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const { chromium } = require('playwright');
|
||||
|
||||
function startNode (name, args = []) {
|
||||
return execa('node', [path.join(__dirname, name), ...args], {
|
||||
cwd: path.resolve(__dirname),
|
||||
all: true
|
||||
})
|
||||
}
|
||||
|
||||
function startBrowser (name, args = []) {
|
||||
return execa('parcel', [path.join(__dirname, name), ...args], {
|
||||
preferLocal: true,
|
||||
localDir: __dirname,
|
||||
cwd: __dirname,
|
||||
all: true
|
||||
})
|
||||
}
|
||||
|
||||
async function test () {
|
||||
// Step 1, listener process
|
||||
const listenerProcReady = pDefer()
|
||||
let listenerOutput = ''
|
||||
process.stdout.write('listener.js\n')
|
||||
const listenerProc = startNode('listener.js')
|
||||
|
||||
listenerProc.all.on('data', async (data) => {
|
||||
process.stdout.write(data)
|
||||
listenerOutput += uint8ArrayToString(data)
|
||||
if (listenerOutput.includes('Listening on:') && listenerOutput.includes('12D3KooWCuo3MdXfMgaqpLC5Houi1TRoFqgK9aoxok4NK5udMu8m')) {
|
||||
listenerProcReady.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
await listenerProcReady.promise
|
||||
process.stdout.write('==================================================================\n')
|
||||
|
||||
// Step 2, dialer process
|
||||
process.stdout.write('dialer.js\n')
|
||||
let dialerUrl = ''
|
||||
const dialerProc = startBrowser('index.html')
|
||||
|
||||
dialerProc.all.on('data', async (chunk) => {
|
||||
/**@type {string} */
|
||||
const out = chunk.toString()
|
||||
|
||||
if (out.includes('Server running at')) {
|
||||
dialerUrl = out.replace('Server running at ', '')
|
||||
}
|
||||
|
||||
if (out.includes('✨ Built in ')) {
|
||||
try {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
await page.goto(dialerUrl);
|
||||
await page.waitForFunction(selector => document.querySelector(selector).innerText === 'libp2p started!', '#status')
|
||||
await page.waitForFunction(
|
||||
selector => {
|
||||
const text = document.querySelector(selector).innerText
|
||||
return text.includes('libp2p id is') &&
|
||||
text.includes('Found peer') &&
|
||||
text.includes('Connected to')
|
||||
},
|
||||
'#output',
|
||||
{ timeout: 10000 }
|
||||
)
|
||||
await browser.close();
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
} finally {
|
||||
dialerProc.cancel()
|
||||
listenerProc.kill()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
listenerProc,
|
||||
dialerProc,
|
||||
]).catch((err) => {
|
||||
if (err.signal !== 'SIGTERM') {
|
||||
throw err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = test
|
99
package.json
99
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "libp2p",
|
||||
"version": "0.30.0",
|
||||
"version": "0.30.10",
|
||||
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
|
||||
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
|
||||
"main": "src/index.js",
|
||||
@@ -50,48 +50,61 @@
|
||||
"node": ">=12.0.0",
|
||||
"npm": ">=6.0.0"
|
||||
},
|
||||
"browser": {
|
||||
"@motrix/nat-api": false
|
||||
},
|
||||
"dependencies": {
|
||||
"@motrix/nat-api": "^0.3.1",
|
||||
"abort-controller": "^3.0.0",
|
||||
"aggregate-error": "^3.0.1",
|
||||
"any-signal": "^1.1.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"cids": "^1.0.0",
|
||||
"aggregate-error": "^3.1.0",
|
||||
"any-signal": "^2.1.1",
|
||||
"bignumber.js": "^9.0.1",
|
||||
"cids": "^1.1.5",
|
||||
"class-is": "^1.1.0",
|
||||
"debug": "^4.1.1",
|
||||
"debug": "^4.3.1",
|
||||
"err-code": "^2.0.0",
|
||||
"events": "^3.1.0",
|
||||
"es6-promisify": "^6.1.1",
|
||||
"events": "^3.2.0",
|
||||
"hashlru": "^2.3.0",
|
||||
"interface-datastore": "^2.0.0",
|
||||
"ipfs-utils": "^5.0.1",
|
||||
"it-all": "^1.0.1",
|
||||
"interface-datastore": "^3.0.3",
|
||||
"ipfs-utils": "^6.0.0",
|
||||
"it-all": "^1.0.4",
|
||||
"it-buffer": "^0.1.2",
|
||||
"it-handshake": "^1.0.1",
|
||||
"it-length-prefixed": "^3.0.1",
|
||||
"it-drain": "^1.0.3",
|
||||
"it-filter": "^1.0.1",
|
||||
"it-first": "^1.0.4",
|
||||
"it-handshake": "^1.0.2",
|
||||
"it-length-prefixed": "^3.1.0",
|
||||
"it-map": "^1.0.4",
|
||||
"it-merge": "1.0.0",
|
||||
"it-pipe": "^1.1.0",
|
||||
"it-protocol-buffers": "^0.2.0",
|
||||
"libp2p-crypto": "^0.18.0",
|
||||
"it-take": "1.0.0",
|
||||
"libp2p-crypto": "^0.19.0",
|
||||
"libp2p-interfaces": "^0.8.1",
|
||||
"libp2p-utils": "^0.2.2",
|
||||
"mafmt": "^8.0.0",
|
||||
"merge-options": "^2.0.0",
|
||||
"merge-options": "^3.0.4",
|
||||
"moving-average": "^1.0.0",
|
||||
"multiaddr": "^8.1.0",
|
||||
"multicodec": "^2.0.0",
|
||||
"multicodec": "^2.1.0",
|
||||
"multihashing-async": "^2.0.1",
|
||||
"multistream-select": "^1.0.0",
|
||||
"mutable-proxy": "^1.0.0",
|
||||
"node-forge": "^0.9.1",
|
||||
"node-forge": "^0.10.0",
|
||||
"p-any": "^3.0.0",
|
||||
"p-fifo": "^1.0.0",
|
||||
"p-retry": "^4.2.0",
|
||||
"p-settle": "^4.0.1",
|
||||
"peer-id": "^0.14.2",
|
||||
"private-ip": "^2.0.0",
|
||||
"protons": "^2.0.0",
|
||||
"retimer": "^2.0.0",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"set-delayed-interval": "^1.0.0",
|
||||
"streaming-iterables": "^5.0.2",
|
||||
"timeout-abort-controller": "^1.1.1",
|
||||
"varint": "^5.0.0",
|
||||
"varint": "^6.0.0",
|
||||
"xsalsa20": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -100,20 +113,20 @@
|
||||
"aegir": "^29.2.0",
|
||||
"chai-bytes": "^0.1.2",
|
||||
"chai-string": "^1.5.0",
|
||||
"delay": "^4.3.0",
|
||||
"delay": "^4.4.0",
|
||||
"interop-libp2p": "^0.3.0",
|
||||
"into-stream": "^6.0.0",
|
||||
"ipfs-http-client": "^47.0.1",
|
||||
"ipfs-http-client": "^48.2.2",
|
||||
"it-concat": "^1.0.0",
|
||||
"it-pair": "^1.0.0",
|
||||
"it-pushable": "^1.4.0",
|
||||
"libp2p": ".",
|
||||
"libp2p-bootstrap": "^0.12.0",
|
||||
"libp2p-delegated-content-routing": "^0.8.0",
|
||||
"libp2p-delegated-content-routing": "^0.9.0",
|
||||
"libp2p-delegated-peer-routing": "^0.8.0",
|
||||
"libp2p-floodsub": "^0.24.0",
|
||||
"libp2p-gossipsub": "^0.7.0",
|
||||
"libp2p-kad-dht": "^0.20.0",
|
||||
"libp2p-gossipsub": "^0.8.0",
|
||||
"libp2p-kad-dht": "^0.20.5",
|
||||
"libp2p-mdns": "^0.15.0",
|
||||
"libp2p-mplex": "^0.10.1",
|
||||
"libp2p-noise": "^2.0.0",
|
||||
@@ -125,11 +138,10 @@
|
||||
"nock": "^13.0.3",
|
||||
"p-defer": "^3.0.0",
|
||||
"p-times": "^3.0.0",
|
||||
"p-wait-for": "^3.1.0",
|
||||
"promisify-es6": "^1.0.3",
|
||||
"p-wait-for": "^3.2.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"sinon": "^9.0.2",
|
||||
"uint8arrays": "^1.1.0"
|
||||
"sinon": "^9.2.4",
|
||||
"uint8arrays": "^2.0.5"
|
||||
},
|
||||
"contributors": [
|
||||
"David Dias <daviddias.p@gmail.com>",
|
||||
@@ -142,44 +154,49 @@
|
||||
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
|
||||
"Maciej Krüger <mkg20001@gmail.com>",
|
||||
"Hugo Dias <mail@hugodias.me>",
|
||||
"dirkmc <dirkmdev@gmail.com>",
|
||||
"Volker Mische <volker.mische@gmail.com>",
|
||||
"dirkmc <dirkmdev@gmail.com>",
|
||||
"Richard Littauer <richard.littauer@gmail.com>",
|
||||
"a1300 <matthias-knopp@gmx.net>",
|
||||
"Elven <mon.samuel@qq.com>",
|
||||
"Andrew Nesbitt <andrewnez@gmail.com>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
|
||||
"Giovanni T. Parra <fiatjaf@gmail.com>",
|
||||
"Ryan Bell <ryan@piing.net>",
|
||||
"Thomas Eizinger <thomas@eizinger.io>",
|
||||
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.com>",
|
||||
"Samlior <samlior@foxmail.com>",
|
||||
"Didrik Nordström <didrik@betamos.se>",
|
||||
"Irakli Gozalishvili <rfobic@gmail.com>",
|
||||
"Ethan Lam <elmemphis2000@gmail.com>",
|
||||
"Joel Gustafson <joelg@mit.edu>",
|
||||
"Julien Bouquillon <contact@revolunet.com>",
|
||||
"Kevin Kwok <antimatter15@gmail.com>",
|
||||
"Kevin Lacker <lacker@gmail.com>",
|
||||
"Miguel Mota <miguelmota2@gmail.com>",
|
||||
"Nuno Nogueira <nunofmn@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Philipp Muens <raute1337@gmx.de>",
|
||||
"RasmusErik Voel Jensen <github@solsort.com>",
|
||||
"Diogo Silva <fsdiogo@gmail.com>",
|
||||
"Samlior <samlior@foxmail.com>",
|
||||
"Smite Chow <xiaopengyou@live.com>",
|
||||
"Soeren <nikorpoulsen@gmail.com>",
|
||||
"Sönke Hahn <soenkehahn@gmail.com>",
|
||||
"robertkiel <robert.kiel@validitylabs.org>",
|
||||
"TJKoury <TJKoury@gmail.com>",
|
||||
"Tiago Alves <alvesjtiago@gmail.com>",
|
||||
"Daijiro Wachi <daijiro.wachi@gmail.com>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
"Zane Starr <zcstarr@gmail.com>",
|
||||
"Cindy Wu <ciindy.wu@gmail.com>",
|
||||
"Chris Bratlien <chrisbratlien@gmail.com>",
|
||||
"ebinks <elizabethjbinks@gmail.com>",
|
||||
"Yusef Napora <yusef@napora.org>",
|
||||
"Zane Starr <zcstarr@gmail.com>",
|
||||
"Bernd Strehl <bernd.strehl@gmail.com>",
|
||||
"ebinks <elizabethjbinks@gmail.com>",
|
||||
"Ethan Lam <elmemphis2000@gmail.com>",
|
||||
"isan_rivkin <isanrivkin@gmail.com>",
|
||||
"robertkiel <robert.kiel@validitylabs.org>",
|
||||
"Aleksei <vozhdb@gmail.com>",
|
||||
"Fei Liu <liu.feiwood@gmail.com>",
|
||||
"Felipe Martins <felipebrasil93@gmail.com>",
|
||||
"Florian-Merle <florian.david.merle@gmail.com>",
|
||||
"Francis Gulotta <wizard@roborooter.com>",
|
||||
"Felipe Martins <felipebrasil93@gmail.com>",
|
||||
"isan_rivkin <isanrivkin@gmail.com>",
|
||||
"Dmitriy Ryajov <dryajov@gmail.com>",
|
||||
"Henrique Dias <hacdias@gmail.com>",
|
||||
"Fei Liu <liu.feiwood@gmail.com>"
|
||||
"Irakli Gozalishvili <rfobic@gmail.com>",
|
||||
"Diogo Silva <fsdiogo@gmail.com>",
|
||||
"Joel Gustafson <joelg@mit.edu>"
|
||||
]
|
||||
}
|
||||
|
@@ -1,6 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
/** @typedef {import('../types').EventEmitterFactory} Events */
|
||||
/** @type Events */
|
||||
const EventEmitter = require('events')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
/**
|
||||
* @typedef {import('multiaddr')} Multiaddr
|
||||
@@ -11,7 +15,11 @@ const multiaddr = require('multiaddr')
|
||||
* @property {string[]} [listen = []] - list of multiaddrs string representation to listen.
|
||||
* @property {string[]} [announce = []] - list of multiaddrs string representation to announce.
|
||||
*/
|
||||
class AddressManager {
|
||||
|
||||
/**
|
||||
* @fires AddressManager#change:addresses Emitted when a addresses change.
|
||||
*/
|
||||
class AddressManager extends EventEmitter {
|
||||
/**
|
||||
* Responsible for managing the peer addresses.
|
||||
* Peers can specify their listen and announce addresses.
|
||||
@@ -19,11 +27,18 @@ class AddressManager {
|
||||
* while the announce addresses will be used for the peer addresses' to other peers in the network.
|
||||
*
|
||||
* @class
|
||||
* @param {AddressManagerOptions} [options]
|
||||
* @param {PeerId} peerId - The Peer ID of the node
|
||||
* @param {object} [options]
|
||||
* @param {Array<string>} [options.listen = []] - list of multiaddrs string representation to listen.
|
||||
* @param {Array<string>} [options.announce = []] - list of multiaddrs string representation to announce.
|
||||
*/
|
||||
constructor ({ listen = [], announce = [] } = {}) {
|
||||
this.listen = new Set(listen)
|
||||
this.announce = new Set(announce)
|
||||
constructor (peerId, { listen = [], announce = [] } = {}) {
|
||||
super()
|
||||
|
||||
this.peerId = peerId
|
||||
this.listen = new Set(listen.map(ma => ma.toString()))
|
||||
this.announce = new Set(announce.map(ma => ma.toString()))
|
||||
this.observed = new Set()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,6 +58,45 @@ class AddressManager {
|
||||
getAnnounceAddrs () {
|
||||
return Array.from(this.announce).map((a) => multiaddr(a))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get observed multiaddrs.
|
||||
*
|
||||
* @returns {Array<Multiaddr>}
|
||||
*/
|
||||
getObservedAddrs () {
|
||||
return Array.from(this.observed).map((a) => multiaddr(a))
|
||||
}
|
||||
|
||||
/**
|
||||
* Add peer observed addresses
|
||||
*
|
||||
* @param {string | Multiaddr} addr
|
||||
*/
|
||||
addObservedAddr (addr) {
|
||||
let ma = multiaddr(addr)
|
||||
const remotePeer = ma.getPeerId()
|
||||
|
||||
// strip our peer id if it has been passed
|
||||
if (remotePeer) {
|
||||
const remotePeerId = PeerId.createFromB58String(remotePeer)
|
||||
|
||||
// use same encoding for comparison
|
||||
if (remotePeerId.equals(this.peerId)) {
|
||||
ma = ma.decapsulate(multiaddr(`/p2p/${this.peerId}`))
|
||||
}
|
||||
}
|
||||
|
||||
const addrString = ma.toString()
|
||||
|
||||
// do not trigger the change:addresses event if we already know about this address
|
||||
if (this.observed.has(addrString)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.observed.add(addrString)
|
||||
this.emit('change:addresses')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AddressManager
|
||||
|
@@ -12,7 +12,7 @@ const TextEncoder = require('ipfs-utils/src/text-encoder')
|
||||
* @returns {Promise<CID>}
|
||||
*/
|
||||
module.exports.namespaceToCid = async (namespace) => {
|
||||
const bytes = new TextEncoder('utf8').encode(namespace)
|
||||
const bytes = new TextEncoder().encode(namespace)
|
||||
const hash = await multihashing(bytes, 'sha2-256')
|
||||
|
||||
return new CID(hash)
|
||||
|
@@ -59,6 +59,16 @@ const DefaultConfig = {
|
||||
timeout: 10e3
|
||||
}
|
||||
},
|
||||
nat: {
|
||||
enabled: true,
|
||||
ttl: 7200,
|
||||
keepAlive: true,
|
||||
gateway: null,
|
||||
externalIp: null,
|
||||
pmp: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
peerDiscovery: {
|
||||
autoDial: true
|
||||
},
|
||||
|
@@ -10,7 +10,9 @@ const mergeOptions = require('merge-options')
|
||||
const LatencyMonitor = require('./latency-monitor')
|
||||
const retimer = require('retimer')
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
/** @typedef {import('../types').EventEmitterFactory} Events */
|
||||
/** @type Events */
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
@@ -158,7 +160,7 @@ class ConnectionManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
await tasks
|
||||
await Promise.all(tasks)
|
||||
this.connections.clear()
|
||||
}
|
||||
|
||||
|
@@ -5,9 +5,9 @@
|
||||
* This code is based on `latency-monitor` (https://github.com/mlucool/latency-monitor) by `mlucool` (https://github.com/mlucool), available under Apache License 2.0 (https://github.com/mlucool/latency-monitor/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/* global window */
|
||||
const globalThis = require('ipfs-utils/src/globalthis')
|
||||
const { EventEmitter } = require('events')
|
||||
/** @typedef {import('../types').EventEmitterFactory} Events */
|
||||
/** @type Events */
|
||||
const EventEmitter = require('events')
|
||||
const VisibilityChangeEmitter = require('./visibility-change-emitter')
|
||||
const debug = require('debug')('latency-monitor:LatencyMonitor')
|
||||
|
||||
@@ -72,9 +72,9 @@ class LatencyMonitor extends EventEmitter {
|
||||
that.asyncTestFn = asyncTestFn // If there is no asyncFn, we measure latency
|
||||
|
||||
// If process: use high resolution timer
|
||||
if (globalThis.process && globalThis.process.hrtime) {
|
||||
if (globalThis.process && globalThis.process.hrtime) { // eslint-disable-line no-undef
|
||||
debug('Using process.hrtime for timing')
|
||||
that.now = globalThis.process.hrtime
|
||||
that.now = globalThis.process.hrtime // eslint-disable-line no-undef
|
||||
that.getDeltaMS = (startTime) => {
|
||||
const hrtime = that.now(startTime)
|
||||
return (hrtime[0] * 1000) + (hrtime[1] / 1000000)
|
||||
|
@@ -6,7 +6,9 @@
|
||||
*/
|
||||
'use strict'
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
/** @typedef {import('../types').EventEmitterFactory} Events */
|
||||
/** @type Events */
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
|
||||
|
||||
|
@@ -1,10 +1,16 @@
|
||||
'use strict'
|
||||
|
||||
const errCode = require('err-code')
|
||||
const { messages, codes } = require('./errors')
|
||||
const { messages, codes } = require('../errors')
|
||||
const {
|
||||
storeAddresses,
|
||||
uniquePeers,
|
||||
requirePeers,
|
||||
maybeLimitSource
|
||||
} = require('./utils')
|
||||
|
||||
const all = require('it-all')
|
||||
const pAny = require('p-any')
|
||||
const merge = require('it-merge')
|
||||
const { pipe } = require('it-pipe')
|
||||
|
||||
/**
|
||||
* @typedef {import('peer-id')} PeerId
|
||||
@@ -21,22 +27,21 @@ const pAny = require('p-any')
|
||||
class ContentRouting {
|
||||
/**
|
||||
* @class
|
||||
* @param {import('./')} libp2p
|
||||
* @param {import('..')} libp2p
|
||||
*/
|
||||
constructor (libp2p) {
|
||||
this.libp2p = libp2p
|
||||
this.routers = libp2p._modules.contentRouting || []
|
||||
this.dht = libp2p._dht
|
||||
|
||||
// If we have the dht, make it first
|
||||
if (this.dht) {
|
||||
this.routers.unshift(this.dht)
|
||||
// If we have the dht, add it to the available content routers
|
||||
if (this.dht && libp2p._config.dht.enabled) {
|
||||
this.routers.push(this.dht)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all content routers in series to find providers of the given key.
|
||||
* Once a content router succeeds, iteration will stop.
|
||||
* Iterates over all content routers in parallel to find providers of the given key.
|
||||
*
|
||||
* @param {CID} key - The CID key of the content to find
|
||||
* @param {object} [options]
|
||||
@@ -44,25 +49,20 @@ class ContentRouting {
|
||||
* @param {number} [options.maxNumProviders] - maximum number of providers to find
|
||||
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
|
||||
*/
|
||||
async * findProviders (key, options) {
|
||||
async * findProviders (key, options = {}) {
|
||||
if (!this.routers.length) {
|
||||
throw errCode(new Error('No content this.routers available'), 'NO_ROUTERS_AVAILABLE')
|
||||
}
|
||||
|
||||
const result = await pAny(
|
||||
this.routers.map(async (router) => {
|
||||
const provs = await all(router.findProviders(key, options))
|
||||
|
||||
if (!provs || !provs.length) {
|
||||
throw errCode(new Error('not found'), 'NOT_FOUND')
|
||||
}
|
||||
return provs
|
||||
})
|
||||
yield * pipe(
|
||||
merge(
|
||||
...this.routers.map(router => router.findProviders(key, options))
|
||||
),
|
||||
(source) => storeAddresses(source, this.libp2p.peerStore),
|
||||
(source) => uniquePeers(source),
|
||||
(source) => maybeLimitSource(source, options.maxNumProviders),
|
||||
(source) => requirePeers(source)
|
||||
)
|
||||
|
||||
for (const peer of result) {
|
||||
yield peer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
89
src/content-routing/utils.js
Normal file
89
src/content-routing/utils.js
Normal file
@@ -0,0 +1,89 @@
|
||||
'use strict'
|
||||
|
||||
const errCode = require('err-code')
|
||||
const filter = require('it-filter')
|
||||
const map = require('it-map')
|
||||
const take = require('it-take')
|
||||
|
||||
/**
|
||||
* @typedef {import('peer-id')} PeerId
|
||||
* @typedef {import('multiaddr')} Multiaddr
|
||||
*/
|
||||
|
||||
/**
|
||||
* Store the multiaddrs from every peer in the passed peer store
|
||||
*
|
||||
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
|
||||
* @param {import('../peer-store')} peerStore
|
||||
*/
|
||||
function storeAddresses (source, peerStore) {
|
||||
return map(source, (peer) => {
|
||||
// ensure we have the addresses for a given peer
|
||||
peerStore.addressBook.add(peer.id, peer.multiaddrs)
|
||||
|
||||
return peer
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter peers by unique peer id
|
||||
*
|
||||
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
|
||||
*/
|
||||
function uniquePeers (source) {
|
||||
/** @type Set<string> */
|
||||
const seen = new Set()
|
||||
|
||||
return filter(source, (peer) => {
|
||||
// dedupe by peer id
|
||||
if (seen.has(peer.id.toString())) {
|
||||
return false
|
||||
}
|
||||
|
||||
seen.add(peer.id.toString())
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Require at least `min` peers to be yielded from `source`
|
||||
*
|
||||
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
|
||||
* @param {number} min
|
||||
*/
|
||||
async function * requirePeers (source, min = 1) {
|
||||
let seen = 0
|
||||
|
||||
for await (const peer of source) {
|
||||
seen++
|
||||
|
||||
yield peer
|
||||
}
|
||||
|
||||
if (seen < min) {
|
||||
throw errCode(new Error('not found'), 'NOT_FOUND')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If `max` is passed, only take that number of peers from the source
|
||||
* otherwise take all the peers
|
||||
*
|
||||
* @param {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>} source
|
||||
* @param {number} [max]
|
||||
*/
|
||||
function maybeLimitSource (source, max) {
|
||||
if (max) {
|
||||
return take(source, max)
|
||||
}
|
||||
|
||||
return source
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
storeAddresses,
|
||||
uniquePeers,
|
||||
requirePeers,
|
||||
maybeLimitSource
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
|
||||
const errCode = require('err-code')
|
||||
const AbortController = require('abort-controller').default
|
||||
const anySignal = require('any-signal')
|
||||
const { anySignal } = require('any-signal')
|
||||
const FIFO = require('p-fifo')
|
||||
const pAny = require('p-any')
|
||||
|
||||
@@ -67,7 +67,7 @@ class DialRequest {
|
||||
let conn
|
||||
try {
|
||||
const signal = dialAbortControllers[i].signal
|
||||
conn = await this.dialAction(addr, { ...options, signal: anySignal([signal, options.signal]) })
|
||||
conn = await this.dialAction(addr, { ...options, signal: options.signal ? anySignal([signal, options.signal]) : signal })
|
||||
// Remove the successful AbortController so it is not aborted
|
||||
dialAbortControllers.splice(i, 1)
|
||||
} finally {
|
||||
|
@@ -7,7 +7,7 @@ const log = Object.assign(debug('libp2p:dialer'), {
|
||||
const errCode = require('err-code')
|
||||
const multiaddr = require('multiaddr')
|
||||
const TimeoutController = require('timeout-abort-controller')
|
||||
const anySignal = require('any-signal')
|
||||
const { anySignal } = require('any-signal')
|
||||
|
||||
const DialRequest = require('./dial-request')
|
||||
const { publicAddressesFirst } = require('libp2p-utils/src/address-sort')
|
||||
|
@@ -43,6 +43,7 @@ class IdentifyService {
|
||||
constructor ({ libp2p }) {
|
||||
this._libp2p = libp2p
|
||||
this.peerStore = libp2p.peerStore
|
||||
this.addressManager = libp2p.addressManager
|
||||
this.connectionManager = libp2p.connectionManager
|
||||
this.peerId = libp2p.peerId
|
||||
|
||||
@@ -201,8 +202,9 @@ class IdentifyService {
|
||||
this.peerStore.protoBook.set(id, protocols)
|
||||
this.peerStore.metadataBook.set(id, 'AgentVersion', uint8ArrayFromString(message.agentVersion))
|
||||
|
||||
// TODO: Track our observed address so that we can score it
|
||||
// TODO: Add and score our observed addr
|
||||
log('received observed address of %s', observedAddr)
|
||||
// this.addressManager.addObservedAddr(observedAddr)
|
||||
}
|
||||
|
||||
/**
|
||||
|
68
src/index.js
68
src/index.js
@@ -4,11 +4,13 @@ const debug = require('debug')
|
||||
const log = Object.assign(debug('libp2p'), {
|
||||
error: debug('libp2p:err')
|
||||
})
|
||||
const { EventEmitter } = require('events')
|
||||
const globalThis = require('ipfs-utils/src/globalthis')
|
||||
/** @typedef {import('./types').EventEmitterFactory} Events */
|
||||
/** @type Events */
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const errCode = require('err-code')
|
||||
const PeerId = require('peer-id')
|
||||
const multiaddr = require('multiaddr')
|
||||
|
||||
const PeerRouting = require('./peer-routing')
|
||||
const ContentRouting = require('./content-routing')
|
||||
@@ -32,6 +34,8 @@ const Registrar = require('./registrar')
|
||||
const ping = require('./ping')
|
||||
const IdentifyService = require('./identify')
|
||||
const IDENTIFY_PROTOCOLS = IdentifyService.multicodecs
|
||||
const NatManager = require('./nat-manager')
|
||||
const { updateSelfPeerRecord } = require('./record/utils')
|
||||
|
||||
/**
|
||||
* @typedef {import('multiaddr')} Multiaddr
|
||||
@@ -47,9 +51,6 @@ const IDENTIFY_PROTOCOLS = IdentifyService.multicodecs
|
||||
* @typedef {Object} PeerStoreOptions
|
||||
* @property {boolean} persistence
|
||||
*
|
||||
* @typedef {Object} PeerDiscoveryOptions
|
||||
* @property {boolean} autoDial
|
||||
*
|
||||
* @typedef {Object} RelayOptions
|
||||
* @property {boolean} enabled
|
||||
* @property {import('./circuit').RelayAdvertiseOptions} advertise
|
||||
@@ -58,7 +59,7 @@ const IDENTIFY_PROTOCOLS = IdentifyService.multicodecs
|
||||
*
|
||||
* @typedef {Object} Libp2pConfig
|
||||
* @property {Object} [dht] dht module options
|
||||
* @property {PeerDiscoveryOptions} [peerDiscovery]
|
||||
* @property {Object} [peerDiscovery]
|
||||
* @property {Pubsub} [pubsub] pubsub module options
|
||||
* @property {RelayOptions} [relay]
|
||||
* @property {Record<string, Object>} [transport] transport options indexed by transport key
|
||||
@@ -78,10 +79,12 @@ const IDENTIFY_PROTOCOLS = IdentifyService.multicodecs
|
||||
* @property {import('./transport-manager').TransportManagerOptions} [transportManager]
|
||||
* @property {PeerStoreOptions & import('./peer-store/persistent').PersistentPeerStoreOptions} [peerStore]
|
||||
* @property {Libp2pConfig} [config]
|
||||
*
|
||||
* @typedef {Object} constructorOptions
|
||||
* @property {PeerId} peerId
|
||||
*
|
||||
* @typedef {Object} CreateOptions
|
||||
* @property {PeerId} peerId
|
||||
* @property {PeerId} [peerId]
|
||||
*
|
||||
* @extends {EventEmitter}
|
||||
* @fires Libp2p#error Emitted when an error occurs
|
||||
@@ -97,12 +100,14 @@ class Libp2p extends EventEmitter {
|
||||
*/
|
||||
static async create (options) {
|
||||
if (options.peerId) {
|
||||
// @ts-ignore 'Libp2pOptions & CreateOptions' is not assignable to 'Libp2pOptions & constructorOptions'
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
const peerId = await PeerId.create()
|
||||
|
||||
options.peerId = peerId
|
||||
// @ts-ignore 'Libp2pOptions & CreateOptions' is not assignable to 'Libp2pOptions & constructorOptions'
|
||||
return new Libp2p(options)
|
||||
}
|
||||
|
||||
@@ -110,7 +115,7 @@ class Libp2p extends EventEmitter {
|
||||
* Libp2p node.
|
||||
*
|
||||
* @class
|
||||
* @param {Libp2pOptions} _options
|
||||
* @param {Libp2pOptions & constructorOptions} _options
|
||||
*/
|
||||
constructor (_options) {
|
||||
super()
|
||||
@@ -132,7 +137,14 @@ class Libp2p extends EventEmitter {
|
||||
|
||||
// Addresses {listen, announce, noAnnounce}
|
||||
this.addresses = this._options.addresses
|
||||
this.addressManager = new AddressManager(this._options.addresses)
|
||||
this.addressManager = new AddressManager(this.peerId, this._options.addresses)
|
||||
|
||||
// when addresses change, update our peer record
|
||||
this.addressManager.on('change:addresses', () => {
|
||||
updateSelfPeerRecord(this).catch(err => {
|
||||
log.error('Error updating self peer record', err)
|
||||
})
|
||||
})
|
||||
|
||||
this._modules = this._options.modules
|
||||
this._config = this._options.config
|
||||
@@ -186,6 +198,14 @@ class Libp2p extends EventEmitter {
|
||||
faultTolerance: this._options.transportManager.faultTolerance
|
||||
})
|
||||
|
||||
// Create the Nat Manager
|
||||
this.natManager = new NatManager({
|
||||
peerId: this.peerId,
|
||||
addressManager: this.addressManager,
|
||||
transportManager: this.transportManager,
|
||||
...this._options.config.nat
|
||||
})
|
||||
|
||||
// Create the Registrar
|
||||
this.registrar = new Registrar({
|
||||
peerStore: this.peerStore,
|
||||
@@ -241,7 +261,7 @@ class Libp2p extends EventEmitter {
|
||||
// Attach private network protector
|
||||
if (this._modules.connProtector) {
|
||||
this.upgrader.protector = this._modules.connProtector
|
||||
} else if (globalThis.process !== undefined && globalThis.process.env && globalThis.process.env.LIBP2P_FORCE_PNET) {
|
||||
} else if (globalThis.process !== undefined && globalThis.process.env && globalThis.process.env.LIBP2P_FORCE_PNET) { // eslint-disable-line no-undef
|
||||
throw new Error('Private network is enforced, but no protector was provided')
|
||||
}
|
||||
|
||||
@@ -349,6 +369,7 @@ class Libp2p extends EventEmitter {
|
||||
this.metrics && this.metrics.stop()
|
||||
])
|
||||
|
||||
await this.natManager.stop()
|
||||
await this.transportManager.close()
|
||||
|
||||
ping.unmount(this)
|
||||
@@ -444,22 +465,32 @@ class Libp2p extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get peer advertising multiaddrs by concating the addresses used
|
||||
* by transports to listen with the announce addresses.
|
||||
* Duplicated addresses and noAnnounce addresses are filtered out.
|
||||
* Get a deduplicated list of peer advertising multiaddrs by concatenating
|
||||
* the listen addresses used by transports with any configured
|
||||
* announce addresses as well as observed addresses reported by peers.
|
||||
*
|
||||
* If Announce addrs are specified, configured listen addresses will be
|
||||
* ignored though observed addresses will still be included.
|
||||
*
|
||||
* @returns {Multiaddr[]}
|
||||
*/
|
||||
get multiaddrs () {
|
||||
const announceAddrs = this.addressManager.getAnnounceAddrs()
|
||||
if (announceAddrs.length) {
|
||||
return announceAddrs
|
||||
let addrs = this.addressManager.getAnnounceAddrs().map(ma => ma.toString())
|
||||
|
||||
if (!addrs.length) {
|
||||
// no configured announce addrs, add configured listen addresses
|
||||
addrs = this.transportManager.getAddrs().map(ma => ma.toString())
|
||||
}
|
||||
|
||||
addrs = addrs.concat(this.addressManager.getObservedAddrs().map(ma => ma.toString()))
|
||||
|
||||
const announceFilter = this._options.addresses.announceFilter || ((multiaddrs) => multiaddrs)
|
||||
|
||||
// dedupe multiaddrs
|
||||
const addrSet = new Set(addrs)
|
||||
|
||||
// Create advertising list
|
||||
return announceFilter(this.transportManager.getAddrs())
|
||||
return announceFilter(Array.from(addrSet).map(str => multiaddr(str)))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,6 +569,9 @@ class Libp2p extends EventEmitter {
|
||||
const addrs = this.addressManager.getListenAddrs()
|
||||
await this.transportManager.listen(addrs)
|
||||
|
||||
// Manage your NATs
|
||||
this.natManager.start()
|
||||
|
||||
// Start PeerStore
|
||||
await this.peerStore.start()
|
||||
|
||||
|
@@ -4,10 +4,9 @@
|
||||
const sanitize = require('sanitize-filename')
|
||||
const mergeOptions = require('merge-options')
|
||||
const crypto = require('libp2p-crypto')
|
||||
const Datastore = require('interface-datastore')
|
||||
const { Key } = require('interface-datastore')
|
||||
const CMS = require('./cms')
|
||||
const errcode = require('err-code')
|
||||
const { Number } = require('ipfs-utils/src/globalthis')
|
||||
const uint8ArrayToString = require('uint8arrays/to-string')
|
||||
const uint8ArrayFromString = require('uint8arrays/from-string')
|
||||
|
||||
@@ -15,7 +14,7 @@ require('node-forge/lib/sha512')
|
||||
|
||||
/**
|
||||
* @typedef {import('peer-id')} PeerId
|
||||
* @typedef {import('interface-datastore/src/key')} Key
|
||||
* @typedef {import('interface-datastore/src/types').Datastore} Datastore
|
||||
*/
|
||||
|
||||
const keyPrefix = '/pkcs8/'
|
||||
@@ -72,7 +71,7 @@ async function throwDelayed (err) {
|
||||
* @private
|
||||
*/
|
||||
function DsName (name) {
|
||||
return new Datastore.Key(keyPrefix + name)
|
||||
return new Key(keyPrefix + name)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +82,7 @@ function DsName (name) {
|
||||
* @private
|
||||
*/
|
||||
function DsInfoName (name) {
|
||||
return new Datastore.Key(infoPrefix + name)
|
||||
return new Key(infoPrefix + name)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,7 +1,9 @@
|
||||
// @ts-nocheck
|
||||
'use strict'
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
/** @typedef {import('../types').EventEmitterFactory} Events */
|
||||
/** @type Events */
|
||||
const EventEmitter = require('events')
|
||||
const Big = require('bignumber.js')
|
||||
const MovingAverage = require('moving-average')
|
||||
const retimer = require('retimer')
|
||||
@@ -80,7 +82,7 @@ class Stats extends EventEmitter {
|
||||
/**
|
||||
* Returns a clone of the internal movingAverages
|
||||
*
|
||||
* @returns {MovingAverage}
|
||||
* @returns {Object}
|
||||
*/
|
||||
get movingAverages () {
|
||||
return Object.assign({}, this._movingAverages)
|
||||
|
168
src/nat-manager.js
Normal file
168
src/nat-manager.js
Normal file
@@ -0,0 +1,168 @@
|
||||
'use strict'
|
||||
|
||||
const NatAPI = require('@motrix/nat-api')
|
||||
const debug = require('debug')
|
||||
const promisify = require('es6-promisify')
|
||||
const Multiaddr = require('multiaddr')
|
||||
const log = Object.assign(debug('libp2p:nat'), {
|
||||
error: debug('libp2p:nat:err')
|
||||
})
|
||||
const { isBrowser } = require('ipfs-utils/src/env')
|
||||
const retry = require('p-retry')
|
||||
const isPrivateIp = require('private-ip')
|
||||
const pkg = require('../package.json')
|
||||
const errcode = require('err-code')
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('./errors')
|
||||
const isLoopback = require('libp2p-utils/src/multiaddr/is-loopback')
|
||||
|
||||
/**
|
||||
* @typedef {import('peer-id')} PeerId
|
||||
* @typedef {import('./transport-manager')} TransportManager
|
||||
* @typedef {import('./address-manager')} AddressManager
|
||||
*/
|
||||
|
||||
function highPort (min = 1024, max = 65535) {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
|
||||
const DEFAULT_TTL = 7200
|
||||
|
||||
class NatManager {
|
||||
/**
|
||||
* @class
|
||||
* @param {object} options
|
||||
* @param {PeerId} options.peerId - The peer ID of the current node
|
||||
* @param {TransportManager} options.transportManager - A transport manager
|
||||
* @param {AddressManager} options.addressManager - An address manager
|
||||
* @param {boolean} options.enabled - Whether to enable the NAT manager
|
||||
* @param {string} [options.externalIp] - Pass a value to use instead of auto-detection
|
||||
* @param {string} [options.description] - A string value to use for the port mapping description on the gateway
|
||||
* @param {number} [options.ttl] - How long UPnP port mappings should last for in seconds (minimum 1200)
|
||||
* @param {boolean} [options.keepAlive] - Whether to automatically refresh UPnP port mappings when their TTL is reached
|
||||
* @param {string} [options.gateway] - Pass a value to use instead of auto-detection
|
||||
* @param {object} [options.pmp] - PMP options
|
||||
* @param {boolean} [options.pmp.enabled] - Whether to enable PMP as well as UPnP
|
||||
*/
|
||||
constructor ({ peerId, addressManager, transportManager, ...options }) {
|
||||
this._peerId = peerId
|
||||
this._addressManager = addressManager
|
||||
this._transportManager = transportManager
|
||||
|
||||
this._enabled = options.enabled
|
||||
this._externalIp = options.externalIp
|
||||
this._options = {
|
||||
description: options.description || `${pkg.name}@${pkg.version} ${this._peerId}`,
|
||||
ttl: options.ttl || DEFAULT_TTL,
|
||||
autoUpdate: options.keepAlive || true,
|
||||
gateway: options.gateway,
|
||||
enablePMP: Boolean(options.pmp && options.pmp.enabled)
|
||||
}
|
||||
|
||||
if (this._options.ttl < DEFAULT_TTL) {
|
||||
throw errcode(new Error(`NatManager ttl should be at least ${DEFAULT_TTL} seconds`), ERR_INVALID_PARAMETERS)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the NAT manager
|
||||
*/
|
||||
start () {
|
||||
if (isBrowser || !this._enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
// done async to not slow down startup
|
||||
this._start().catch((err) => {
|
||||
// hole punching errors are non-fatal
|
||||
log.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
async _start () {
|
||||
const addrs = this._transportManager.getAddrs()
|
||||
|
||||
for (const addr of addrs) {
|
||||
// try to open uPnP ports for each thin waist address
|
||||
const { family, host, port, transport } = addr.toOptions()
|
||||
|
||||
if (!addr.isThinWaistAddress() || transport !== 'tcp') {
|
||||
// only bare tcp addresses
|
||||
continue
|
||||
}
|
||||
|
||||
if (isLoopback(addr)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (family !== 'ipv4') {
|
||||
// ignore ipv6
|
||||
continue
|
||||
}
|
||||
|
||||
const client = this._getClient()
|
||||
const publicIp = this._externalIp || await client.externalIp()
|
||||
|
||||
if (isPrivateIp(publicIp)) {
|
||||
throw new Error(`${publicIp} is private - please set config.nat.externalIp to an externally routable IP or ensure you are not behind a double NAT`)
|
||||
}
|
||||
|
||||
const publicPort = highPort()
|
||||
|
||||
log(`opening uPnP connection from ${publicIp}:${publicPort} to ${host}:${port}`)
|
||||
|
||||
await client.map({
|
||||
publicPort,
|
||||
privatePort: port,
|
||||
protocol: transport.toUpperCase()
|
||||
})
|
||||
|
||||
this._addressManager.addObservedAddr(Multiaddr.fromNodeAddress({
|
||||
family: 'IPv4',
|
||||
address: publicIp,
|
||||
port: `${publicPort}`
|
||||
}, transport))
|
||||
}
|
||||
}
|
||||
|
||||
_getClient () {
|
||||
if (this._client) {
|
||||
return this._client
|
||||
}
|
||||
|
||||
const client = new NatAPI(this._options)
|
||||
const map = promisify(client.map, { context: client })
|
||||
const destroy = promisify(client.destroy, { context: client })
|
||||
const externalIp = promisify(client.externalIp, { context: client })
|
||||
|
||||
this._client = {
|
||||
// these are all network operations so add a retry
|
||||
map: (...args) => retry(() => map(...args), { onFailedAttempt: log.error, unref: true }),
|
||||
destroy: (...args) => retry(() => destroy(...args), { onFailedAttempt: log.error, unref: true }),
|
||||
externalIp: (...args) => retry(() => externalIp(...args), { onFailedAttempt: log.error, unref: true })
|
||||
}
|
||||
|
||||
return this._client
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the NAT manager
|
||||
*
|
||||
* @async
|
||||
*/
|
||||
async stop () {
|
||||
if (isBrowser || !this._client) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await this._client.destroy()
|
||||
this._client = null
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NatManager
|
@@ -5,16 +5,24 @@ const log = Object.assign(debug('libp2p:peer-routing'), {
|
||||
error: debug('libp2p:peer-routing:err')
|
||||
})
|
||||
const errCode = require('err-code')
|
||||
const {
|
||||
storeAddresses,
|
||||
uniquePeers,
|
||||
requirePeers
|
||||
} = require('./content-routing/utils')
|
||||
|
||||
const all = require('it-all')
|
||||
const pAny = require('p-any')
|
||||
const merge = require('it-merge')
|
||||
const { pipe } = require('it-pipe')
|
||||
const first = require('it-first')
|
||||
const drain = require('it-drain')
|
||||
const filter = require('it-filter')
|
||||
const {
|
||||
setDelayedInterval,
|
||||
clearDelayedInterval
|
||||
} = require('set-delayed-interval')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
/**
|
||||
* @typedef {import('peer-id')} PeerId
|
||||
* @typedef {import('multiaddr')} Multiaddr
|
||||
*/
|
||||
class PeerRouting {
|
||||
@@ -27,9 +35,9 @@ class PeerRouting {
|
||||
this._peerStore = libp2p.peerStore
|
||||
this._routers = libp2p._modules.peerRouting || []
|
||||
|
||||
// If we have the dht, make it first
|
||||
if (libp2p._dht) {
|
||||
this._routers.unshift(libp2p._dht)
|
||||
// If we have the dht, add it to the available peer routers
|
||||
if (libp2p._dht && libp2p._config.dht.enabled) {
|
||||
this._routers.push(libp2p._dht)
|
||||
}
|
||||
|
||||
this._refreshManagerOptions = libp2p._options.peerRouting.refreshManager
|
||||
@@ -55,9 +63,8 @@ class PeerRouting {
|
||||
*/
|
||||
async _findClosestPeersTask () {
|
||||
try {
|
||||
for await (const { id, multiaddrs } of this.getClosestPeers(this._peerId.id)) {
|
||||
this._peerStore.addressBook.add(id, multiaddrs)
|
||||
}
|
||||
// nb getClosestPeers adds the addresses to the address book
|
||||
await drain(this.getClosestPeers(this._peerId.id))
|
||||
} catch (err) {
|
||||
log.error(err)
|
||||
}
|
||||
@@ -71,7 +78,7 @@ class PeerRouting {
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all peer routers in series to find the given peer.
|
||||
* Iterates over all peer routers in parallel to find the given peer.
|
||||
*
|
||||
* @param {PeerId} id - The id of the peer to find
|
||||
* @param {object} [options]
|
||||
@@ -83,16 +90,20 @@ class PeerRouting {
|
||||
throw errCode(new Error('No peer routers available'), 'NO_ROUTERS_AVAILABLE')
|
||||
}
|
||||
|
||||
return pAny(this._routers.map(async (router) => {
|
||||
const result = await router.findPeer(id, options)
|
||||
const output = await pipe(
|
||||
merge(
|
||||
...this._routers.map(router => [router.findPeer(id, options)])
|
||||
),
|
||||
(source) => filter(source, Boolean),
|
||||
(source) => storeAddresses(source, this._peerStore),
|
||||
(source) => first(source)
|
||||
)
|
||||
|
||||
// If we don't have a result, we need to provide an error to keep trying
|
||||
if (!result || Object.keys(result).length === 0) {
|
||||
throw errCode(new Error('not found'), 'NOT_FOUND')
|
||||
}
|
||||
if (output) {
|
||||
return output
|
||||
}
|
||||
|
||||
return result
|
||||
}))
|
||||
throw errCode(new Error('not found'), 'NOT_FOUND')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,20 +119,14 @@ class PeerRouting {
|
||||
throw errCode(new Error('No peer routers available'), 'NO_ROUTERS_AVAILABLE')
|
||||
}
|
||||
|
||||
const result = await pAny(
|
||||
this._routers.map(async (router) => {
|
||||
const peers = await all(router.getClosestPeers(key, options))
|
||||
|
||||
if (!peers || !peers.length) {
|
||||
throw errCode(new Error('not found'), 'NOT_FOUND')
|
||||
}
|
||||
return peers
|
||||
})
|
||||
yield * pipe(
|
||||
merge(
|
||||
...this._routers.map(router => router.getClosestPeers(key, options))
|
||||
),
|
||||
(source) => storeAddresses(source, this._peerStore),
|
||||
(source) => uniquePeers(source),
|
||||
(source) => requirePeers(source)
|
||||
)
|
||||
|
||||
for (const peer of result) {
|
||||
yield peer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,9 @@
|
||||
|
||||
const errcode = require('err-code')
|
||||
|
||||
const { EventEmitter } = require('events')
|
||||
/** @typedef {import('../types').EventEmitterFactory} Events */
|
||||
/** @type Events */
|
||||
const EventEmitter = require('events')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const AddressBook = require('./address-book')
|
||||
|
@@ -63,9 +63,10 @@ module.exports.decodeV1PSK = (pskBuffer) => {
|
||||
const metadata = uint8ArrayToString(pskBuffer).split(/(?:\r\n|\r|\n)/g)
|
||||
const pskTag = metadata.shift()
|
||||
const codec = metadata.shift()
|
||||
const psk = uint8ArrayFromString(metadata.shift(), 'base16')
|
||||
const pskString = metadata.shift()
|
||||
const psk = pskString && uint8ArrayFromString(pskString, 'base16')
|
||||
|
||||
if (psk.byteLength !== KEY_LENGTH) {
|
||||
if (!psk || psk.byteLength !== KEY_LENGTH) {
|
||||
throw new Error(Errors.INVALID_PSK)
|
||||
}
|
||||
|
||||
|
@@ -196,7 +196,7 @@ class TransportManager {
|
||||
// listening on remote addresses as they may be offline. We could then potentially
|
||||
// just wait for any (`p-any`) listener to succeed on each transport before returning
|
||||
const isListening = results.find(r => r.isFulfilled === true)
|
||||
if (!isListening) {
|
||||
if (!isListening && this.faultTolerance !== FAULT_TOLERANCE.NO_FATAL) {
|
||||
throw errCode(new Error(`Transport (${key}) could not listen on any available address`), codes.ERR_NO_VALID_ADDRESSES)
|
||||
}
|
||||
}
|
||||
|
19
src/types.ts
19
src/types.ts
@@ -82,3 +82,22 @@ export type CircuitMessageProto = {
|
||||
CAN_HOP: CAN_HOP
|
||||
}
|
||||
}
|
||||
|
||||
export interface EventEmitterFactory {
|
||||
new(): EventEmitter;
|
||||
}
|
||||
|
||||
export interface EventEmitter {
|
||||
addListener(event: string | symbol, listener: (...args: any[]) => void);
|
||||
on(event: string | symbol, listener: (...args: any[]) => void);
|
||||
once(event: string | symbol, listener: (...args: any[]) => void);
|
||||
removeListener(event: string | symbol, listener: (...args: any[]) => void);
|
||||
off(event: string | symbol, listener: (...args: any[]) => void);
|
||||
removeAllListeners(event?: string | symbol);
|
||||
setMaxListeners(n: number);
|
||||
getMaxListeners(): number;
|
||||
listeners(event: string | symbol): Function[]; // eslint-disable-line @typescript-eslint/ban-types
|
||||
rawListeners(event: string | symbol): Function[]; // eslint-disable-line @typescript-eslint/ban-types
|
||||
emit(event: string | symbol, ...args: any[]): boolean;
|
||||
listenerCount(event: string | symbol): number;
|
||||
}
|
||||
|
@@ -3,23 +3,32 @@
|
||||
|
||||
const { expect } = require('aegir/utils/chai')
|
||||
const multiaddr = require('multiaddr')
|
||||
const PeerId = require('peer-id')
|
||||
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
|
||||
const Peers = require('../fixtures/peers')
|
||||
|
||||
const listenAddresses = ['/ip4/127.0.0.1/tcp/15006/ws', '/ip4/127.0.0.1/tcp/15008/ws']
|
||||
const announceAddreses = ['/dns4/peer.io']
|
||||
|
||||
describe('Address Manager', () => {
|
||||
let peerId
|
||||
|
||||
before(async () => {
|
||||
peerId = await PeerId.createFromJSON(Peers[0])
|
||||
})
|
||||
|
||||
it('should not need any addresses', () => {
|
||||
const am = new AddressManager()
|
||||
const am = new AddressManager(peerId)
|
||||
|
||||
expect(am.listen.size).to.equal(0)
|
||||
expect(am.announce.size).to.equal(0)
|
||||
})
|
||||
|
||||
it('should return listen multiaddrs on get', () => {
|
||||
const am = new AddressManager({
|
||||
const am = new AddressManager(peerId, {
|
||||
listen: listenAddresses
|
||||
})
|
||||
|
||||
@@ -33,7 +42,7 @@ describe('Address Manager', () => {
|
||||
})
|
||||
|
||||
it('should return announce multiaddrs on get', () => {
|
||||
const am = new AddressManager({
|
||||
const am = new AddressManager(peerId, {
|
||||
listen: listenAddresses,
|
||||
announce: announceAddreses
|
||||
})
|
||||
@@ -45,6 +54,75 @@ describe('Address Manager', () => {
|
||||
expect(announceMultiaddrs.length).to.equal(1)
|
||||
expect(announceMultiaddrs[0].equals(multiaddr(announceAddreses[0]))).to.equal(true)
|
||||
})
|
||||
|
||||
it('should add observed addresses', () => {
|
||||
const am = new AddressManager(peerId)
|
||||
|
||||
expect(am.observed).to.be.empty()
|
||||
|
||||
am.addObservedAddr('/ip4/123.123.123.123/tcp/39201')
|
||||
|
||||
expect(am.observed).to.have.property('size', 1)
|
||||
})
|
||||
|
||||
it('should dedupe added observed addresses', () => {
|
||||
const ma = '/ip4/123.123.123.123/tcp/39201'
|
||||
const am = new AddressManager(peerId)
|
||||
|
||||
expect(am.observed).to.be.empty()
|
||||
|
||||
am.addObservedAddr(ma)
|
||||
am.addObservedAddr(ma)
|
||||
am.addObservedAddr(ma)
|
||||
|
||||
expect(am.observed).to.have.property('size', 1)
|
||||
expect(am.observed).to.include(ma)
|
||||
})
|
||||
|
||||
it('should only emit one change:addresses event', () => {
|
||||
const ma = '/ip4/123.123.123.123/tcp/39201'
|
||||
const am = new AddressManager(peerId)
|
||||
let eventCount = 0
|
||||
|
||||
am.on('change:addresses', () => {
|
||||
eventCount++
|
||||
})
|
||||
|
||||
am.addObservedAddr(ma)
|
||||
am.addObservedAddr(ma)
|
||||
am.addObservedAddr(ma)
|
||||
am.addObservedAddr(`${ma}/p2p/${peerId}`)
|
||||
am.addObservedAddr(`${ma}/p2p/${peerId.toB58String()}`)
|
||||
|
||||
expect(eventCount).to.equal(1)
|
||||
})
|
||||
|
||||
it('should strip our peer address from added observed addresses', () => {
|
||||
const ma = '/ip4/123.123.123.123/tcp/39201'
|
||||
const am = new AddressManager(peerId)
|
||||
|
||||
expect(am.observed).to.be.empty()
|
||||
|
||||
am.addObservedAddr(ma)
|
||||
am.addObservedAddr(`${ma}/p2p/${peerId}`)
|
||||
|
||||
expect(am.observed).to.have.property('size', 1)
|
||||
expect(am.observed).to.include(ma)
|
||||
})
|
||||
|
||||
it('should strip our peer address from added observed addresses in difference formats', () => {
|
||||
const ma = '/ip4/123.123.123.123/tcp/39201'
|
||||
const am = new AddressManager(peerId)
|
||||
|
||||
expect(am.observed).to.be.empty()
|
||||
|
||||
am.addObservedAddr(ma)
|
||||
am.addObservedAddr(`${ma}/p2p/${peerId}`) // base32 CID
|
||||
am.addObservedAddr(`${ma}/p2p/${peerId.toB58String()}`) // base58btc
|
||||
|
||||
expect(am.observed).to.have.property('size', 1)
|
||||
expect(am.observed).to.include(ma)
|
||||
})
|
||||
})
|
||||
|
||||
describe('libp2p.addressManager', () => {
|
||||
|
@@ -147,4 +147,26 @@ describe('libp2p.multiaddrs', () => {
|
||||
expect(multiaddrs.includes(listenAddresses[0])).to.equal(false)
|
||||
expect(multiaddrs.includes(listenAddresses[1])).to.equal(false)
|
||||
})
|
||||
|
||||
it('should include observed addresses in returned multiaddrs', async () => {
|
||||
[libp2p] = await peerUtils.createPeer({
|
||||
started: false,
|
||||
config: {
|
||||
...AddressesOptions,
|
||||
addresses: {
|
||||
listen: listenAddresses
|
||||
}
|
||||
}
|
||||
})
|
||||
const ma = '/ip4/83.32.123.53/tcp/43928'
|
||||
|
||||
await libp2p.start()
|
||||
|
||||
expect(libp2p.multiaddrs).to.have.lengthOf(listenAddresses.length)
|
||||
|
||||
libp2p.addressManager.addObservedAddr(ma)
|
||||
|
||||
expect(libp2p.multiaddrs).to.have.lengthOf(listenAddresses.length + 1)
|
||||
expect(libp2p.multiaddrs.map(ma => ma.toString())).to.include(ma)
|
||||
})
|
||||
})
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
const { expect } = require('aegir/utils/chai')
|
||||
const sinon = require('sinon')
|
||||
const { CLOSED } = require('libp2p-interfaces/src/connection/status')
|
||||
|
||||
const delay = require('delay')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
@@ -268,5 +269,40 @@ describe('libp2p.connections', () => {
|
||||
|
||||
await libp2p.stop()
|
||||
})
|
||||
|
||||
it('should be closed status once immediately stopping', async () => {
|
||||
const [libp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
peerId: peerIds[0],
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15003/ws']
|
||||
},
|
||||
modules: baseOptions.modules
|
||||
}
|
||||
})
|
||||
const [remoteLibp2p] = await peerUtils.createPeer({
|
||||
config: {
|
||||
peerId: peerIds[1],
|
||||
addresses: {
|
||||
listen: ['/ip4/127.0.0.1/tcp/15004/ws']
|
||||
},
|
||||
modules: baseOptions.modules
|
||||
}
|
||||
})
|
||||
|
||||
libp2p.peerStore.addressBook.set(remoteLibp2p.peerId, remoteLibp2p.multiaddrs)
|
||||
await libp2p.dial(remoteLibp2p.peerId)
|
||||
|
||||
const totalConns = Array.from(libp2p.connections.values())
|
||||
expect(totalConns.length).to.eql(1)
|
||||
const conns = totalConns[0]
|
||||
expect(conns.length).to.eql(1)
|
||||
const conn = conns[0]
|
||||
|
||||
await libp2p.stop()
|
||||
expect(conn.stat.status).to.eql(CLOSED)
|
||||
|
||||
await remoteLibp2p.stop()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -12,6 +12,8 @@ const CID = require('cids')
|
||||
const ipfsHttpClient = require('ipfs-http-client')
|
||||
const DelegatedContentRouter = require('libp2p-delegated-content-routing')
|
||||
const multiaddr = require('multiaddr')
|
||||
const drain = require('it-drain')
|
||||
const all = require('it-all')
|
||||
|
||||
const peerUtils = require('../utils/creators/peer')
|
||||
const { baseOptions, routingOptions } = require('./utils')
|
||||
@@ -78,10 +80,14 @@ describe('content-routing', () => {
|
||||
|
||||
it('should use the nodes dht to find providers', async () => {
|
||||
const deferred = pDefer()
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
|
||||
sinon.stub(nodes[0]._dht, 'findProviders').callsFake(function * () {
|
||||
deferred.resolve()
|
||||
yield
|
||||
yield {
|
||||
id: providerPeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
})
|
||||
|
||||
await nodes[0].contentRouting.findProviders().next()
|
||||
@@ -101,9 +107,7 @@ describe('content-routing', () => {
|
||||
host: '0.0.0.0',
|
||||
protocol: 'http',
|
||||
port: 60197
|
||||
}), [
|
||||
multiaddr('/ip4/0.0.0.0/tcp/60197')
|
||||
])
|
||||
}))
|
||||
|
||||
;[node] = await peerUtils.createPeer({
|
||||
config: mergeOptions(baseOptions, {
|
||||
@@ -125,6 +129,10 @@ describe('content-routing', () => {
|
||||
|
||||
afterEach(() => node.stop())
|
||||
|
||||
it('should only have one router', () => {
|
||||
expect(node.contentRouting.routers).to.have.lengthOf(1)
|
||||
})
|
||||
|
||||
it('should use the delegate router to provide', () => {
|
||||
const deferred = pDefer()
|
||||
|
||||
@@ -138,10 +146,14 @@ describe('content-routing', () => {
|
||||
|
||||
it('should use the delegate router to find providers', async () => {
|
||||
const deferred = pDefer()
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
|
||||
sinon.stub(delegate, 'findProviders').callsFake(function * () {
|
||||
deferred.resolve()
|
||||
yield
|
||||
yield {
|
||||
id: providerPeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
})
|
||||
|
||||
await node.contentRouting.findProviders().next()
|
||||
@@ -151,25 +163,36 @@ describe('content-routing', () => {
|
||||
|
||||
it('should be able to register as a provider', async () => {
|
||||
const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB')
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
// mock the refs call
|
||||
.post('/api/v0/refs')
|
||||
const provider = 'QmZNgCqZCvTsi3B4Vt7gsSqpkqDpE7M2Y9TDmEhbDb4ceF'
|
||||
|
||||
const mockBlockApi = nock('http://0.0.0.0:60197')
|
||||
// mock the block/stat call
|
||||
.post('/api/v0/block/stat')
|
||||
.query(true)
|
||||
.reply(200, null, [
|
||||
.reply(200, '{"Key":"QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB","Size":"2169"}', [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
])
|
||||
const mockDhtApi = nock('http://0.0.0.0:60197')
|
||||
// mock the dht/provide call
|
||||
.post('/api/v0/dht/provide')
|
||||
.query(true)
|
||||
.reply(200, `{"Extra":"","ID":"QmWKqWXCtRXEeCQTo3FoZ7g4AfnGiauYYiczvNxFCHicbB","Responses":[{"Addrs":["/ip4/0.0.0.0/tcp/0"],"ID":"${provider}"}],"Type":4}\n`, [
|
||||
'Content-Type', 'application/json',
|
||||
'X-Chunked-Output', '1'
|
||||
])
|
||||
|
||||
await node.contentRouting.provide(cid)
|
||||
|
||||
expect(mockApi.isDone()).to.equal(true)
|
||||
expect(mockBlockApi.isDone()).to.equal(true)
|
||||
expect(mockDhtApi.isDone()).to.equal(true)
|
||||
})
|
||||
|
||||
it('should handle errors when registering as a provider', async () => {
|
||||
const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB')
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
// mock the refs call
|
||||
.post('/api/v0/refs')
|
||||
// mock the block/stat call
|
||||
.post('/api/v0/block/stat')
|
||||
.query(true)
|
||||
.reply(502, 'Bad Gateway', ['Content-Type', 'application/json'])
|
||||
|
||||
@@ -232,9 +255,7 @@ describe('content-routing', () => {
|
||||
host: '0.0.0.0',
|
||||
protocol: 'http',
|
||||
port: 60197
|
||||
}), [
|
||||
multiaddr('/ip4/0.0.0.0/tcp/60197')
|
||||
])
|
||||
}))
|
||||
|
||||
;[node] = await peerUtils.createPeer({
|
||||
config: mergeOptions(routingOptions, {
|
||||
@@ -251,6 +272,110 @@ describe('content-routing', () => {
|
||||
|
||||
afterEach(() => node.stop())
|
||||
|
||||
it('should store the multiaddrs of a peer', async () => {
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const result = {
|
||||
id: providerPeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/123.123.123.123/tcp/49320')
|
||||
]
|
||||
}
|
||||
|
||||
sinon.stub(node._dht, 'findProviders').callsFake(function * () {})
|
||||
sinon.stub(delegate, 'findProviders').callsFake(function * () {
|
||||
yield result
|
||||
})
|
||||
|
||||
expect(node.peerStore.addressBook.get(providerPeerId)).to.not.be.ok()
|
||||
|
||||
await drain(node.contentRouting.findProviders('a cid'))
|
||||
|
||||
expect(node.peerStore.addressBook.get(providerPeerId)).to.deep.include({
|
||||
isCertified: false,
|
||||
multiaddr: result.multiaddrs[0]
|
||||
})
|
||||
})
|
||||
|
||||
it('should not wait for routing findProviders to finish before returning results', async () => {
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const result = {
|
||||
id: providerPeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/123.123.123.123/tcp/49320')
|
||||
]
|
||||
}
|
||||
|
||||
const defer = pDefer()
|
||||
|
||||
sinon.stub(node._dht, 'findProviders').callsFake(async function * () { // eslint-disable-line require-yield
|
||||
await defer.promise
|
||||
})
|
||||
sinon.stub(delegate, 'findProviders').callsFake(async function * () {
|
||||
yield result
|
||||
|
||||
await defer.promise
|
||||
})
|
||||
|
||||
for await (const provider of node.contentRouting.findProviders('a cid')) {
|
||||
expect(provider.id).to.deep.equal(providerPeerId)
|
||||
defer.resolve()
|
||||
}
|
||||
})
|
||||
|
||||
it('should dedupe results', async () => {
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const result = {
|
||||
id: providerPeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/123.123.123.123/tcp/49320')
|
||||
]
|
||||
}
|
||||
|
||||
sinon.stub(node._dht, 'findProviders').callsFake(async function * () {
|
||||
yield result
|
||||
})
|
||||
sinon.stub(delegate, 'findProviders').callsFake(async function * () {
|
||||
yield result
|
||||
})
|
||||
|
||||
const results = await all(node.contentRouting.findProviders('a cid'))
|
||||
|
||||
expect(results).to.be.an('array').with.lengthOf(1).that.deep.equals([result])
|
||||
})
|
||||
|
||||
it('should combine multiaddrs when different addresses are returned by different content routers', async () => {
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const result1 = {
|
||||
id: providerPeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/123.123.123.123/tcp/49320')
|
||||
]
|
||||
}
|
||||
const result2 = {
|
||||
id: providerPeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/213.213.213.213/tcp/2344')
|
||||
]
|
||||
}
|
||||
|
||||
sinon.stub(node._dht, 'findProviders').callsFake(async function * () {
|
||||
yield result1
|
||||
})
|
||||
sinon.stub(delegate, 'findProviders').callsFake(async function * () {
|
||||
yield result2
|
||||
})
|
||||
|
||||
await drain(node.contentRouting.findProviders('a cid'))
|
||||
|
||||
expect(node.peerStore.addressBook.get(providerPeerId)).to.deep.include({
|
||||
isCertified: false,
|
||||
multiaddr: result1.multiaddrs[0]
|
||||
}).and.to.deep.include({
|
||||
isCertified: false,
|
||||
multiaddr: result2.multiaddrs[0]
|
||||
})
|
||||
})
|
||||
|
||||
it('should use both the dht and delegate router to provide', async () => {
|
||||
const dhtDeferred = pDefer()
|
||||
const delegatedDeferred = pDefer()
|
||||
@@ -271,15 +396,18 @@ describe('content-routing', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('should only use the dht if it finds providers', async () => {
|
||||
const results = [true]
|
||||
it('should use the dht if the delegate fails to find providers', async () => {
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = [{
|
||||
id: providerPeerId,
|
||||
multiaddrs: []
|
||||
}]
|
||||
|
||||
sinon.stub(node._dht, 'findProviders').callsFake(function * () {
|
||||
yield results[0]
|
||||
})
|
||||
|
||||
sinon.stub(delegate, 'findProviders').callsFake(function * () { // eslint-disable-line require-yield
|
||||
throw new Error('the delegate should not have been called')
|
||||
})
|
||||
|
||||
const providers = []
|
||||
@@ -292,7 +420,11 @@ describe('content-routing', () => {
|
||||
})
|
||||
|
||||
it('should use the delegate if the dht fails to find providers', async () => {
|
||||
const results = [true]
|
||||
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = [{
|
||||
id: providerPeerId,
|
||||
multiaddrs: []
|
||||
}]
|
||||
|
||||
sinon.stub(node._dht, 'findProviders').callsFake(function * () {})
|
||||
|
||||
|
46
test/core/consume-peer-record.spec.js
Normal file
46
test/core/consume-peer-record.spec.js
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const Transport = require('libp2p-websockets')
|
||||
const { NOISE: Crypto } = require('libp2p-noise')
|
||||
|
||||
const Libp2p = require('../../src')
|
||||
const { createPeerId } = require('../utils/creators/peer')
|
||||
|
||||
describe('Consume peer record', () => {
|
||||
let libp2p
|
||||
|
||||
beforeEach(async () => {
|
||||
const [peerId] = await createPeerId()
|
||||
const config = {
|
||||
peerId,
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
connEncryption: [Crypto]
|
||||
}
|
||||
}
|
||||
libp2p = await Libp2p.create(config)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await libp2p.stop()
|
||||
})
|
||||
|
||||
it('should consume peer record when observed addrs are added', async () => {
|
||||
let done
|
||||
|
||||
libp2p.peerStore.addressBook.consumePeerRecord = () => {
|
||||
done()
|
||||
}
|
||||
|
||||
const p = new Promise(resolve => {
|
||||
done = resolve
|
||||
})
|
||||
|
||||
libp2p.addressManager.addObservedAddr('/ip4/123.123.123.123/tcp/3983')
|
||||
|
||||
await p
|
||||
|
||||
libp2p.stop()
|
||||
})
|
||||
})
|
@@ -51,7 +51,7 @@ describe('Dialing (direct, TCP)', () => {
|
||||
peerStore = new PeerStore({ peerId: remotePeerId })
|
||||
remoteTM = new TransportManager({
|
||||
libp2p: {
|
||||
addressManager: new AddressManager({ listen: [listenAddr] }),
|
||||
addressManager: new AddressManager(remotePeerId, { listen: [listenAddr] }),
|
||||
peerId: remotePeerId,
|
||||
peerStore
|
||||
},
|
||||
|
@@ -119,7 +119,6 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
|
||||
await expect(dialer.connectToPeer(unsupportedAddr))
|
||||
.to.eventually.be.rejectedWith(AggregateError)
|
||||
.and.to.have.nested.property('._errors[0].code', ErrorCodes.ERR_TRANSPORT_DIAL_FAILED)
|
||||
})
|
||||
|
||||
it('should be able to connect to a given peer', async () => {
|
||||
@@ -151,7 +150,6 @@ describe('Dialing (direct, WebSockets)', () => {
|
||||
|
||||
await expect(dialer.connectToPeer(peerId))
|
||||
.to.eventually.be.rejectedWith(AggregateError)
|
||||
.and.to.have.nested.property('._errors[0].code', ErrorCodes.ERR_TRANSPORT_DIAL_FAILED)
|
||||
})
|
||||
|
||||
it('should abort dials on queue task timeout', async () => {
|
||||
|
@@ -21,14 +21,15 @@ const PeerStore = require('../../src/peer-store')
|
||||
const baseOptions = require('../utils/base-options.browser')
|
||||
const { updateSelfPeerRecord } = require('../../src/record/utils')
|
||||
const pkg = require('../../package.json')
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
|
||||
const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
|
||||
const remoteAddr = MULTIADDRS_WEBSOCKETS[0]
|
||||
const listenMaddrs = [multiaddr('/ip4/127.0.0.1/tcp/15002/ws')]
|
||||
|
||||
describe('Identify', () => {
|
||||
let localPeer, localPeerStore
|
||||
let remotePeer, remotePeerStore
|
||||
let localPeer, localPeerStore, localAddressManager
|
||||
let remotePeer, remotePeerStore, remoteAddressManager
|
||||
const protocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]
|
||||
|
||||
before(async () => {
|
||||
@@ -42,6 +43,9 @@ describe('Identify', () => {
|
||||
|
||||
remotePeerStore = new PeerStore({ peerId: remotePeer })
|
||||
remotePeerStore.protoBook.set(remotePeer, protocols)
|
||||
|
||||
localAddressManager = new AddressManager(localPeer)
|
||||
remoteAddressManager = new AddressManager(remotePeer)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -110,6 +114,7 @@ describe('Identify', () => {
|
||||
libp2p: {
|
||||
peerId: localPeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
addressManager: localAddressManager,
|
||||
peerStore: localPeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true,
|
||||
@@ -121,6 +126,7 @@ describe('Identify', () => {
|
||||
libp2p: {
|
||||
peerId: remotePeer,
|
||||
connectionManager: new EventEmitter(),
|
||||
addressManager: remoteAddressManager,
|
||||
peerStore: remotePeerStore,
|
||||
multiaddrs: listenMaddrs,
|
||||
isStarted: () => true,
|
||||
|
244
test/nat-manager/nat-manager.node.js
Normal file
244
test/nat-manager/nat-manager.node.js
Normal file
@@ -0,0 +1,244 @@
|
||||
'use strict'
|
||||
/* eslint-env mocha */
|
||||
|
||||
const { expect } = require('aegir/utils/chai')
|
||||
const sinon = require('sinon')
|
||||
const AddressManager = require('../../src/address-manager')
|
||||
const TransportManager = require('../../src/transport-manager')
|
||||
const Transport = require('libp2p-tcp')
|
||||
const mockUpgrader = require('../utils/mockUpgrader')
|
||||
const NatManager = require('../../src/nat-manager')
|
||||
const delay = require('delay')
|
||||
const peers = require('../fixtures/peers')
|
||||
const PeerId = require('peer-id')
|
||||
const {
|
||||
codes: { ERR_INVALID_PARAMETERS }
|
||||
} = require('../../src/errors')
|
||||
|
||||
const DEFAULT_ADDRESSES = [
|
||||
'/ip4/127.0.0.1/tcp/0',
|
||||
'/ip4/0.0.0.0/tcp/0'
|
||||
]
|
||||
|
||||
describe('Nat Manager (TCP)', () => {
|
||||
const teardown = []
|
||||
|
||||
async function createNatManager (addrs = DEFAULT_ADDRESSES, natManagerOptions = {}) {
|
||||
const peerId = await PeerId.createFromJSON(peers[0])
|
||||
const addressManager = new AddressManager(peerId, { listen: addrs })
|
||||
const transportManager = new TransportManager({
|
||||
libp2p: {
|
||||
peerId,
|
||||
addressManager,
|
||||
peerStore: {
|
||||
addressBook: {
|
||||
consumePeerRecord: sinon.stub()
|
||||
}
|
||||
}
|
||||
},
|
||||
upgrader: mockUpgrader,
|
||||
onConnection: () => {},
|
||||
faultTolerance: TransportManager.FaultTolerance.NO_FATAL
|
||||
})
|
||||
const natManager = new NatManager({
|
||||
peerId,
|
||||
addressManager,
|
||||
transportManager,
|
||||
enabled: true,
|
||||
...natManagerOptions
|
||||
})
|
||||
|
||||
natManager._client = {
|
||||
externalIp: sinon.stub().resolves('82.3.1.5'),
|
||||
map: sinon.stub(),
|
||||
destroy: sinon.stub()
|
||||
}
|
||||
|
||||
transportManager.add(Transport.prototype[Symbol.toStringTag], Transport)
|
||||
await transportManager.listen(addressManager.getListenAddrs())
|
||||
|
||||
teardown.push(async () => {
|
||||
await natManager.stop()
|
||||
await transportManager.removeAll()
|
||||
expect(transportManager._transports.size).to.equal(0)
|
||||
})
|
||||
|
||||
return {
|
||||
natManager,
|
||||
addressManager,
|
||||
transportManager
|
||||
}
|
||||
}
|
||||
|
||||
afterEach(() => Promise.all(teardown))
|
||||
|
||||
it('should map TCP connections to external ports', async () => {
|
||||
const {
|
||||
natManager,
|
||||
addressManager,
|
||||
transportManager
|
||||
} = await createNatManager()
|
||||
|
||||
let addressChangedEventFired = false
|
||||
|
||||
addressManager.on('change:addresses', () => {
|
||||
addressChangedEventFired = true
|
||||
})
|
||||
|
||||
natManager._client = {
|
||||
externalIp: sinon.stub().resolves('82.3.1.5'),
|
||||
map: sinon.stub(),
|
||||
destroy: sinon.stub()
|
||||
}
|
||||
|
||||
let observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
await natManager._start()
|
||||
|
||||
observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.not.be.empty()
|
||||
|
||||
const internalPorts = transportManager.getAddrs()
|
||||
.filter(ma => ma.isThinWaistAddress())
|
||||
.map(ma => ma.toOptions())
|
||||
.filter(({ host, transport }) => host !== '127.0.0.1' && transport === 'tcp')
|
||||
.map(({ port }) => port)
|
||||
|
||||
expect(natManager._client.map.called).to.be.true()
|
||||
|
||||
internalPorts.forEach(port => {
|
||||
expect(natManager._client.map.getCall(0).args[0]).to.include({
|
||||
privatePort: port,
|
||||
protocol: 'TCP'
|
||||
})
|
||||
})
|
||||
|
||||
expect(addressChangedEventFired).to.be.true()
|
||||
})
|
||||
|
||||
it('should not map TCP connections when double-natted', async () => {
|
||||
const {
|
||||
natManager,
|
||||
addressManager
|
||||
} = await createNatManager()
|
||||
|
||||
natManager._client.externalIp = sinon.stub().resolves('192.168.1.1')
|
||||
|
||||
let observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
await expect(natManager._start()).to.eventually.be.rejectedWith(/double NAT/)
|
||||
|
||||
observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
expect(natManager._client.map.called).to.be.false()
|
||||
})
|
||||
|
||||
it('should do nothing when disabled', async () => {
|
||||
const {
|
||||
natManager
|
||||
} = await createNatManager(DEFAULT_ADDRESSES, {
|
||||
enabled: false
|
||||
})
|
||||
|
||||
natManager.start()
|
||||
|
||||
await delay(100)
|
||||
|
||||
expect(natManager._client.externalIp.called).to.be.false()
|
||||
expect(natManager._client.map.called).to.be.false()
|
||||
})
|
||||
|
||||
it('should not map non-ipv4 connections to external ports', async () => {
|
||||
const {
|
||||
natManager,
|
||||
addressManager
|
||||
} = await createNatManager([
|
||||
'/ip6/::/tcp/5001'
|
||||
])
|
||||
|
||||
let observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
await natManager._start()
|
||||
|
||||
observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
})
|
||||
|
||||
it('should not map non-ipv6 loopback connections to external ports', async () => {
|
||||
const {
|
||||
natManager,
|
||||
addressManager
|
||||
} = await createNatManager([
|
||||
'/ip6/::1/tcp/5001'
|
||||
])
|
||||
|
||||
let observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
await natManager._start()
|
||||
|
||||
observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
})
|
||||
|
||||
it('should not map non-TCP connections to external ports', async () => {
|
||||
const {
|
||||
natManager,
|
||||
addressManager
|
||||
} = await createNatManager([
|
||||
'/ip4/0.0.0.0/utp'
|
||||
])
|
||||
|
||||
let observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
await natManager._start()
|
||||
|
||||
observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
})
|
||||
|
||||
it('should not map loopback connections to external ports', async () => {
|
||||
const {
|
||||
natManager,
|
||||
addressManager
|
||||
} = await createNatManager([
|
||||
'/ip4/127.0.0.1/tcp/5900'
|
||||
])
|
||||
|
||||
let observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
await natManager._start()
|
||||
|
||||
observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
})
|
||||
|
||||
it('should not map non-thin-waist connections to external ports', async () => {
|
||||
const {
|
||||
natManager,
|
||||
addressManager
|
||||
} = await createNatManager([
|
||||
'/ip4/0.0.0.0/tcp/5900/sctp/49832'
|
||||
])
|
||||
|
||||
let observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
|
||||
await natManager._start()
|
||||
|
||||
observed = addressManager.getObservedAddrs().map(ma => ma.toString())
|
||||
expect(observed).to.be.empty()
|
||||
})
|
||||
|
||||
it('should specify large enough TTL', () => {
|
||||
expect(() => {
|
||||
new NatManager({ ttl: 5 }) // eslint-disable-line no-new
|
||||
}).to.throw().with.property('code', ERR_INVALID_PARAMETERS)
|
||||
})
|
||||
})
|
@@ -10,6 +10,8 @@ const delay = require('delay')
|
||||
const pDefer = require('p-defer')
|
||||
const pWaitFor = require('p-wait-for')
|
||||
const mergeOptions = require('merge-options')
|
||||
const drain = require('it-drain')
|
||||
const all = require('it-all')
|
||||
|
||||
const ipfsHttpClient = require('ipfs-http-client')
|
||||
const DelegatedPeerRouter = require('libp2p-delegated-peer-routing')
|
||||
@@ -82,10 +84,14 @@ describe('peer-routing', () => {
|
||||
|
||||
it('should use the nodes dht to get the closest peers', async () => {
|
||||
const deferred = pDefer()
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
|
||||
sinon.stub(nodes[0]._dht, 'getClosestPeers').callsFake(function * () {
|
||||
deferred.resolve()
|
||||
yield
|
||||
yield {
|
||||
id: remotePeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
})
|
||||
|
||||
await nodes[0].peerRouting.getClosestPeers().next()
|
||||
@@ -126,12 +132,20 @@ describe('peer-routing', () => {
|
||||
|
||||
afterEach(() => node.stop())
|
||||
|
||||
it('should only have one router', () => {
|
||||
expect(node.peerRouting._routers).to.have.lengthOf(1)
|
||||
})
|
||||
|
||||
it('should use the delegate router to find peers', async () => {
|
||||
const deferred = pDefer()
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
|
||||
sinon.stub(delegate, 'findPeer').callsFake(() => {
|
||||
deferred.resolve()
|
||||
return 'fake peer-id'
|
||||
return {
|
||||
id: remotePeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
})
|
||||
|
||||
await node.peerRouting.findPeer()
|
||||
@@ -140,10 +154,14 @@ describe('peer-routing', () => {
|
||||
|
||||
it('should use the delegate router to get the closest peers', async () => {
|
||||
const deferred = pDefer()
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
|
||||
sinon.stub(delegate, 'getClosestPeers').callsFake(function * () {
|
||||
deferred.resolve()
|
||||
yield
|
||||
yield {
|
||||
id: remotePeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
})
|
||||
|
||||
await node.peerRouting.getClosestPeers().next()
|
||||
@@ -152,7 +170,7 @@ describe('peer-routing', () => {
|
||||
})
|
||||
|
||||
it('should be able to find a peer', async () => {
|
||||
const peerKey = 'QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL'
|
||||
const peerKey = PeerId.createFromB58String('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
|
||||
const mockApi = nock('http://0.0.0.0:60197')
|
||||
.post('/api/v0/dht/findpeer')
|
||||
.query(true)
|
||||
@@ -277,55 +295,93 @@ describe('peer-routing', () => {
|
||||
|
||||
afterEach(() => node.stop())
|
||||
|
||||
it('should only use the dht if it finds the peer', async () => {
|
||||
const dhtDeferred = pDefer()
|
||||
|
||||
sinon.stub(node._dht, 'findPeer').callsFake(() => {
|
||||
dhtDeferred.resolve()
|
||||
return { id: node.peerId }
|
||||
})
|
||||
sinon.stub(delegate, 'findPeer').callsFake(() => {
|
||||
throw new Error('the delegate should not have been called')
|
||||
})
|
||||
|
||||
await node.peerRouting.findPeer('a peer id')
|
||||
await dhtDeferred.promise
|
||||
})
|
||||
|
||||
it('should use the delegate if the dht fails to find the peer', async () => {
|
||||
const results = [true]
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = {
|
||||
id: remotePeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
|
||||
sinon.stub(node._dht, 'findPeer').callsFake(() => {})
|
||||
sinon.stub(delegate, 'findPeer').callsFake(() => {
|
||||
return results
|
||||
})
|
||||
|
||||
const peer = await node.peerRouting.findPeer('a peer id')
|
||||
const peer = await node.peerRouting.findPeer(remotePeerId)
|
||||
expect(peer).to.eql(results)
|
||||
})
|
||||
|
||||
it('should only use the dht if it gets the closest peers', async () => {
|
||||
const results = [true]
|
||||
|
||||
sinon.stub(node._dht, 'getClosestPeers').callsFake(function * () {
|
||||
yield results[0]
|
||||
})
|
||||
|
||||
sinon.stub(delegate, 'getClosestPeers').callsFake(function * () { // eslint-disable-line require-yield
|
||||
throw new Error('the delegate should not have been called')
|
||||
})
|
||||
|
||||
const closest = []
|
||||
for await (const peer of node.peerRouting.getClosestPeers('a cid')) {
|
||||
closest.push(peer)
|
||||
it('should not wait for the dht to return if the delegate does first', async () => {
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = {
|
||||
id: remotePeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
|
||||
expect(closest).to.have.length.above(0)
|
||||
expect(closest).to.eql(results)
|
||||
const defer = pDefer()
|
||||
|
||||
sinon.stub(node._dht, 'findPeer').callsFake(async () => {
|
||||
await defer.promise
|
||||
})
|
||||
sinon.stub(delegate, 'findPeer').callsFake(() => {
|
||||
return results
|
||||
})
|
||||
|
||||
const peer = await node.peerRouting.findPeer(remotePeerId)
|
||||
expect(peer).to.eql(results)
|
||||
|
||||
defer.resolve()
|
||||
})
|
||||
|
||||
it('should not wait for the delegate to return if the dht does first', async () => {
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = {
|
||||
id: remotePeerId,
|
||||
multiaddrs: []
|
||||
}
|
||||
|
||||
const defer = pDefer()
|
||||
|
||||
sinon.stub(node._dht, 'findPeer').callsFake(() => {
|
||||
return results
|
||||
})
|
||||
sinon.stub(delegate, 'findPeer').callsFake(async () => {
|
||||
await defer.promise
|
||||
})
|
||||
|
||||
const peer = await node.peerRouting.findPeer(remotePeerId)
|
||||
expect(peer).to.eql(results)
|
||||
|
||||
defer.resolve()
|
||||
})
|
||||
|
||||
it('should store the addresses of the found peer', async () => {
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = {
|
||||
id: remotePeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/123.123.123.123/tcp/38982')
|
||||
]
|
||||
}
|
||||
|
||||
const spy = sinon.spy(node.peerStore.addressBook, 'add')
|
||||
|
||||
sinon.stub(node._dht, 'findPeer').callsFake(() => {
|
||||
return results
|
||||
})
|
||||
sinon.stub(delegate, 'findPeer').callsFake(() => {})
|
||||
|
||||
await node.peerRouting.findPeer(remotePeerId)
|
||||
|
||||
expect(spy.calledWith(results.id, results.multiaddrs)).to.be.true()
|
||||
})
|
||||
|
||||
it('should use the delegate if the dht fails to get the closest peer', async () => {
|
||||
const results = [true]
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = [{
|
||||
id: remotePeerId,
|
||||
multiaddrs: []
|
||||
}]
|
||||
|
||||
sinon.stub(node._dht, 'getClosestPeers').callsFake(function * () { })
|
||||
|
||||
@@ -333,14 +389,55 @@ describe('peer-routing', () => {
|
||||
yield results[0]
|
||||
})
|
||||
|
||||
const closest = []
|
||||
for await (const peer of node.peerRouting.getClosestPeers('a cid')) {
|
||||
closest.push(peer)
|
||||
}
|
||||
const closest = await all(node.peerRouting.getClosestPeers('a cid'))
|
||||
|
||||
expect(closest).to.have.length.above(0)
|
||||
expect(closest).to.eql(results)
|
||||
})
|
||||
|
||||
it('should store the addresses of the closest peer', async () => {
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const result = {
|
||||
id: remotePeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/123.123.123.123/tcp/38982')
|
||||
]
|
||||
}
|
||||
|
||||
const spy = sinon.spy(node.peerStore.addressBook, 'add')
|
||||
|
||||
sinon.stub(node._dht, 'getClosestPeers').callsFake(function * () { })
|
||||
|
||||
sinon.stub(delegate, 'getClosestPeers').callsFake(function * () {
|
||||
yield result
|
||||
})
|
||||
|
||||
await drain(node.peerRouting.getClosestPeers('a cid'))
|
||||
|
||||
expect(spy.calledWith(result.id, result.multiaddrs)).to.be.true()
|
||||
})
|
||||
|
||||
it('should dedupe closest peers', async () => {
|
||||
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
|
||||
const results = [{
|
||||
id: remotePeerId,
|
||||
multiaddrs: [
|
||||
multiaddr('/ip4/123.123.123.123/tcp/38982')
|
||||
]
|
||||
}]
|
||||
|
||||
sinon.stub(node._dht, 'getClosestPeers').callsFake(function * () {
|
||||
yield * results
|
||||
})
|
||||
|
||||
sinon.stub(delegate, 'getClosestPeers').callsFake(function * () {
|
||||
yield * results
|
||||
})
|
||||
|
||||
const peers = await all(node.peerRouting.getClosestPeers('a cid'))
|
||||
|
||||
expect(peers).to.be.an('array').with.a.lengthOf(1).that.deep.equals(results)
|
||||
})
|
||||
})
|
||||
|
||||
describe('peer routing refresh manager service', () => {
|
||||
|
@@ -209,7 +209,7 @@ describe('libp2p.transportManager (dial only)', () => {
|
||||
throw new Error('it should fail to start if multiaddr fails to listen')
|
||||
})
|
||||
|
||||
it('does not fail to start if multiaddr fails to listen when supporting dial only mode', async () => {
|
||||
it('does not fail to start if provided listen multiaddr are not compatible to configured transports (when supporting dial only mode)', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerId,
|
||||
addresses: {
|
||||
@@ -226,4 +226,22 @@ describe('libp2p.transportManager (dial only)', () => {
|
||||
|
||||
await libp2p.start()
|
||||
})
|
||||
|
||||
it('does not fail to start if provided listen multiaddr fail to listen on configured transports (when supporting dial only mode)', async () => {
|
||||
libp2p = new Libp2p({
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [multiaddr('/ip4/127.0.0.1/tcp/12345/p2p/QmWDn2LY8nannvSWJzruUYoLZ4vV83vfCBwd8DipvdgQc3/p2p-circuit')]
|
||||
},
|
||||
transportManager: {
|
||||
faultTolerance: FaultTolerance.NO_FATAL
|
||||
},
|
||||
modules: {
|
||||
transport: [Transport],
|
||||
connEncryption: [Crypto]
|
||||
}
|
||||
})
|
||||
|
||||
await libp2p.start()
|
||||
})
|
||||
})
|
||||
|
@@ -16,6 +16,9 @@ module.exports = {
|
||||
hop: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
nat: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user