Compare commits

..

1 Commits

Author SHA1 Message Date
Vasco Santos
3d2511db7b chore: use emittery 2021-01-18 16:15:57 +01:00
53 changed files with 235 additions and 1706 deletions

View File

@@ -31,9 +31,6 @@ const before = async () => {
enabled: true,
active: false
}
},
nat: {
enabled: false
}
}
})
@@ -48,7 +45,7 @@ const after = async () => {
}
module.exports = {
bundlesize: { maxSize: '215kB' },
bundlesize: { maxSize: '225kB' },
hooks: {
pre: before,
post: after

View File

@@ -71,39 +71,4 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: yarn
- 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-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-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-pnet-example:
needs: check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- pnet
- run: cd examples && yarn && npm run test -- chat

View File

@@ -1,62 +1,3 @@
## [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)

View File

@@ -2055,15 +2055,6 @@ 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

View File

@@ -28,9 +28,6 @@
- [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
@@ -736,40 +733,6 @@ 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:

View File

@@ -1,30 +0,0 @@
'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

View File

@@ -7,7 +7,15 @@ const Mplex = require('libp2p-mplex')
const { NOISE } = require('libp2p-noise')
const Bootstrap = require('libp2p-bootstrap')
const bootstrapers = require('./bootstrapers')
// 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'
]
;(async () => {
const node = await Libp2p.create({

View File

@@ -1,13 +0,0 @@
'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

View File

@@ -1,42 +0,0 @@
'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

View File

@@ -1,35 +0,0 @@
'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

View File

@@ -1,11 +0,0 @@
'use strict'
const test1 = require('./test-1')
const test2 = require('./test-2')
async function test () {
await test1()
await test2()
}
module.exports = test

View File

@@ -6,7 +6,7 @@
*/
const PeerId = require('peer-id')
const createLibp2p = require('./libp2p')
const createLibp2p = require('./libp2p-bundle')
const pipe = require('it-pipe')
async function run() {

View File

@@ -6,7 +6,7 @@
*/
const PeerId = require('peer-id')
const createLibp2p = require('./libp2p')
const createLibp2p = require('./libp2p-bundle')
const pipe = require('it-pipe')
async function run() {

View File

@@ -1,61 +0,0 @@
'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

View File

@@ -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')

View File

@@ -1,4 +1,4 @@
# Connection Encryption
# Encrypted Communications
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.

View File

@@ -8,7 +8,6 @@
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "parcel build index.html",
"start": "parcel index.html"
},
"keywords": [],

View File

@@ -1,52 +0,0 @@
'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

View File

@@ -12,8 +12,5 @@
"fs-extra": "^8.1.0",
"p-defer": "^3.0.0",
"which": "^2.0.1"
},
"devDependencies": {
"playwright": "^1.7.1"
}
}

View File

@@ -1,30 +0,0 @@
'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

View File

@@ -22,6 +22,7 @@ async function testExample (dir) {
await installDeps(dir)
await build(dir)
await runTest(dir)
// TODO: add browser test setup
}
async function installDeps (dir) {
@@ -88,7 +89,7 @@ async function runTest (dir) {
return
}
const test = require(testFile)
const runTest = require(testFile)
await test()
}
await runTest()
}

View File

@@ -1,6 +1,6 @@
{
"name": "libp2p",
"version": "0.30.7",
"version": "0.30.0",
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",
@@ -50,61 +50,49 @@
"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.1.0",
"any-signal": "^2.1.1",
"bignumber.js": "^9.0.1",
"cids": "^1.1.5",
"aggregate-error": "^3.0.1",
"any-signal": "^1.1.0",
"bignumber.js": "^9.0.0",
"cids": "^1.0.0",
"class-is": "^1.1.0",
"debug": "^4.3.1",
"debug": "^4.1.1",
"emittery": "^0.8.1",
"err-code": "^2.0.0",
"events": "^3.2.0",
"events": "^3.1.0",
"hashlru": "^2.3.0",
"interface-datastore": "^3.0.3",
"ipfs-utils": "^6.0.0",
"it-all": "^1.0.4",
"interface-datastore": "^2.0.0",
"ipfs-utils": "^5.0.1",
"it-all": "^1.0.1",
"it-buffer": "^0.1.2",
"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-handshake": "^1.0.1",
"it-length-prefixed": "^3.0.1",
"it-pipe": "^1.1.0",
"it-protocol-buffers": "^0.2.0",
"it-take": "1.0.0",
"libp2p-crypto": "^0.19.0",
"libp2p-crypto": "^0.18.0",
"libp2p-interfaces": "^0.8.1",
"libp2p-utils": "^0.2.2",
"mafmt": "^8.0.0",
"merge-options": "^3.0.4",
"merge-options": "^2.0.0",
"moving-average": "^1.0.0",
"multiaddr": "^8.1.0",
"multicodec": "^2.1.0",
"multicodec": "^2.0.0",
"multihashing-async": "^2.0.1",
"multistream-select": "^1.0.0",
"mutable-proxy": "^1.0.0",
"node-forge": "^0.10.0",
"node-forge": "^0.9.1",
"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",
"promisify-es6": "^1.0.3",
"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": "^6.0.0",
"varint": "^5.0.0",
"xsalsa20": "^1.0.2"
},
"devDependencies": {
@@ -113,20 +101,20 @@
"aegir": "^29.2.0",
"chai-bytes": "^0.1.2",
"chai-string": "^1.5.0",
"delay": "^4.4.0",
"delay": "^4.3.0",
"interop-libp2p": "^0.3.0",
"into-stream": "^6.0.0",
"ipfs-http-client": "^48.2.2",
"ipfs-http-client": "^47.0.1",
"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.9.0",
"libp2p-delegated-content-routing": "^0.8.0",
"libp2p-delegated-peer-routing": "^0.8.0",
"libp2p-floodsub": "^0.24.0",
"libp2p-gossipsub": "^0.8.0",
"libp2p-kad-dht": "^0.20.5",
"libp2p-gossipsub": "^0.7.0",
"libp2p-kad-dht": "^0.20.0",
"libp2p-mdns": "^0.15.0",
"libp2p-mplex": "^0.10.1",
"libp2p-noise": "^2.0.0",
@@ -138,10 +126,11 @@
"nock": "^13.0.3",
"p-defer": "^3.0.0",
"p-times": "^3.0.0",
"p-wait-for": "^3.2.0",
"p-wait-for": "^3.1.0",
"promisify-es6": "^1.0.3",
"rimraf": "^3.0.2",
"sinon": "^9.2.4",
"uint8arrays": "^2.0.5"
"sinon": "^9.0.2",
"uint8arrays": "^1.1.0"
},
"contributors": [
"David Dias <daviddias.p@gmail.com>",
@@ -154,45 +143,44 @@
"Friedel Ziegelmayer <dignifiedquire@gmail.com>",
"Maciej Krüger <mkg20001@gmail.com>",
"Hugo Dias <mail@hugodias.me>",
"Volker Mische <volker.mische@gmail.com>",
"dirkmc <dirkmdev@gmail.com>",
"Volker Mische <volker.mische@gmail.com>",
"Richard Littauer <richard.littauer@gmail.com>",
"a1300 <matthias-knopp@gmx.net>",
"Ryan Bell <ryan@piing.net>",
"Andrew Nesbitt <andrewnez@gmail.com>",
"Elven <mon.samuel@qq.com>",
"Andrew Nesbitt <andrewnez@gmail.com>",
"Giovanni T. Parra <fiatjaf@gmail.com>",
"Samlior <samlior@foxmail.com>",
"Ryan Bell <ryan@piing.net>",
"Thomas Eizinger <thomas@eizinger.io>",
"ᴠɪᴄᴛᴏʀ ʙᴊᴇʟᴋʜᴏʟᴍ <victorbjelkholm@gmail.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>",
"Ethan Lam <elmemphis2000@gmail.com>",
"Nuno Nogueira <nunofmn@gmail.com>",
"Dmitriy Ryajov <dryajov@gmail.com>",
"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>",
"Tiago Alves <alvesjtiago@gmail.com>",
"Daijiro Wachi <daijiro.wachi@gmail.com>",
"Yusef Napora <yusef@napora.org>",
"Zane Starr <zcstarr@gmail.com>",
"robertkiel <robert.kiel@validitylabs.org>",
"Cindy Wu <ciindy.wu@gmail.com>",
"Chris Bratlien <chrisbratlien@gmail.com>",
"ebinks <elizabethjbinks@gmail.com>",
"Francis Gulotta <wizard@roborooter.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Bernd Strehl <bernd.strehl@gmail.com>",
"Henrique Dias <hacdias@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"Florian-Merle <florian.david.merle@gmail.com>",
"Francis Gulotta <wizard@roborooter.com>",
"Felipe Martins <felipebrasil93@gmail.com>",
"isan_rivkin <isanrivkin@gmail.com>",
"Henrique Dias <hacdias@gmail.com>",
"Fei Liu <liu.feiwood@gmail.com>"
]
}

View File

@@ -1,10 +1,6 @@
'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
@@ -15,11 +11,7 @@ const PeerId = require('peer-id')
* @property {string[]} [listen = []] - list of multiaddrs string representation to listen.
* @property {string[]} [announce = []] - list of multiaddrs string representation to announce.
*/
/**
* @fires AddressManager#change:addresses Emitted when a addresses change.
*/
class AddressManager extends EventEmitter {
class AddressManager {
/**
* Responsible for managing the peer addresses.
* Peers can specify their listen and announce addresses.
@@ -27,18 +19,11 @@ class AddressManager extends EventEmitter {
* while the announce addresses will be used for the peer addresses' to other peers in the network.
*
* @class
* @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.
* @param {AddressManagerOptions} [options]
*/
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()
constructor ({ listen = [], announce = [] } = {}) {
this.listen = new Set(listen)
this.announce = new Set(announce)
}
/**
@@ -58,45 +43,6 @@ class AddressManager extends EventEmitter {
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

View File

@@ -12,7 +12,7 @@ const TextEncoder = require('ipfs-utils/src/text-encoder')
* @returns {Promise<CID>}
*/
module.exports.namespaceToCid = async (namespace) => {
const bytes = new TextEncoder().encode(namespace)
const bytes = new TextEncoder('utf8').encode(namespace)
const hash = await multihashing(bytes, 'sha2-256')
return new CID(hash)

View File

@@ -59,16 +59,6 @@ const DefaultConfig = {
timeout: 10e3
}
},
nat: {
enabled: true,
ttl: 7200,
keepAlive: true,
gateway: null,
externalIp: null,
pmp: {
enabled: false
}
},
peerDiscovery: {
autoDial: true
},

View File

@@ -10,9 +10,7 @@ const mergeOptions = require('merge-options')
const LatencyMonitor = require('./latency-monitor')
const retimer = require('retimer')
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const Emittery = require('emittery')
const PeerId = require('peer-id')
@@ -58,7 +56,7 @@ const defaultOptions = {
* @fires ConnectionManager#peer:connect Emitted when a new peer is connected.
* @fires ConnectionManager#peer:disconnect Emitted when a peer is disconnected.
*/
class ConnectionManager extends EventEmitter {
class ConnectionManager extends Emittery {
/**
* Responsible for managing known connections.
*
@@ -139,7 +137,7 @@ class ConnectionManager extends EventEmitter {
async stop () {
this._autoDialTimeout && this._autoDialTimeout.clear()
this._timer && this._timer.clear()
this._latencyMonitor && this._latencyMonitor.removeListener('data', this._onLatencyMeasure)
this._latencyMonitor && this._latencyMonitor.off('data', this._onLatencyMeasure)
this._started = false
await this._close()
@@ -160,7 +158,7 @@ class ConnectionManager extends EventEmitter {
}
}
await Promise.all(tasks)
await tasks
this.connections.clear()
}

View File

@@ -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)
*/
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
/* global window */
const globalThis = require('ipfs-utils/src/globalthis')
const Emittery = require('emittery')
const VisibilityChangeEmitter = require('./visibility-change-emitter')
const debug = require('debug')('latency-monitor:LatencyMonitor')
@@ -31,7 +31,7 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
* the asyncTestFn and timing how long it takes the callback to be called. It can also periodically emit stats about this.
* This can be disabled and stats can be pulled via setting dataEmitIntervalMs = 0.
*
* @extends {EventEmitter}
* @extends {Emittery}
*
* The default implementation is an event loop latency monitor. This works by firing periodic events into the event loop
* and timing how long it takes to get back.
@@ -44,7 +44,7 @@ const debug = require('debug')('latency-monitor:LatencyMonitor')
* const monitor = new LatencyMonitor({latencyCheckIntervalMs: 1000, dataEmitIntervalMs: 60000, asyncTestFn:ping});
* monitor.on('data', (summary) => console.log('Ping Pong Latency: %O', summary));
*/
class LatencyMonitor extends EventEmitter {
class LatencyMonitor extends Emittery {
/**
* @class
* @param {LatencyMonitorOptions} [options]
@@ -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) { // eslint-disable-line no-undef
if (globalThis.process && globalThis.process.hrtime) {
debug('Using process.hrtime for timing')
that.now = globalThis.process.hrtime // eslint-disable-line no-undef
that.now = globalThis.process.hrtime
that.getDeltaMS = (startTime) => {
const hrtime = that.now(startTime)
return (hrtime[0] * 1000) + (hrtime[1] / 1000000)

View File

@@ -6,9 +6,7 @@
*/
'use strict'
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const Emittery = require('emittery')
const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
@@ -34,7 +32,7 @@ const debug = require('debug')('latency-monitor:VisibilityChangeEmitter')
* // To access the visibility state directly, call:
* console.log('Am I focused now? ' + myVisibilityEmitter.isVisible());
*/
class VisibilityChangeEmitter extends EventEmitter {
class VisibilityChangeEmitter extends Emittery {
/**
* Creates a VisibilityChangeEmitter
*

View File

@@ -1,16 +1,10 @@
'use strict'
const errCode = require('err-code')
const { messages, codes } = require('../errors')
const {
storeAddresses,
uniquePeers,
requirePeers,
maybeLimitSource
} = require('./utils')
const { messages, codes } = require('./errors')
const merge = require('it-merge')
const { pipe } = require('it-pipe')
const all = require('it-all')
const pAny = require('p-any')
/**
* @typedef {import('peer-id')} PeerId
@@ -27,21 +21,22 @@ const { pipe } = require('it-pipe')
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, add it to the available content routers
// If we have the dht, make it first
if (this.dht) {
this.routers.push(this.dht)
this.routers.unshift(this.dht)
}
}
/**
* Iterates over all content routers in parallel to find providers of the given key.
* Iterates over all content routers in series to find providers of the given key.
* Once a content router succeeds, iteration will stop.
*
* @param {CID} key - The CID key of the content to find
* @param {object} [options]
@@ -49,20 +44,25 @@ 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')
}
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)
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
})
)
for (const peer of result) {
yield peer
}
}
/**

View File

@@ -1,89 +0,0 @@
'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
}

View File

@@ -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: options.signal ? anySignal([signal, options.signal]) : signal })
conn = await this.dialAction(addr, { ...options, signal: anySignal([signal, options.signal]) })
// Remove the successful AbortController so it is not aborted
dialAbortControllers.splice(i, 1)
} finally {

View File

@@ -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')

View File

@@ -43,7 +43,6 @@ class IdentifyService {
constructor ({ libp2p }) {
this._libp2p = libp2p
this.peerStore = libp2p.peerStore
this.addressManager = libp2p.addressManager
this.connectionManager = libp2p.connectionManager
this.peerId = libp2p.peerId
@@ -202,9 +201,8 @@ class IdentifyService {
this.peerStore.protoBook.set(id, protocols)
this.peerStore.metadataBook.set(id, 'AgentVersion', uint8ArrayFromString(message.agentVersion))
// TODO: Add and score our observed addr
// TODO: Track our observed address so that we can score it
log('received observed address of %s', observedAddr)
// this.addressManager.addObservedAddr(observedAddr)
}
/**

View File

@@ -4,13 +4,11 @@ const debug = require('debug')
const log = Object.assign(debug('libp2p'), {
error: debug('libp2p:err')
})
/** @typedef {import('./types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const Emittery = require('emittery')
const globalThis = require('ipfs-utils/src/globalthis')
const errCode = require('err-code')
const PeerId = require('peer-id')
const multiaddr = require('multiaddr')
const PeerRouting = require('./peer-routing')
const ContentRouting = require('./content-routing')
@@ -34,8 +32,6 @@ 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
@@ -51,6 +47,9 @@ const { updateSelfPeerRecord } = require('./record/utils')
* @typedef {Object} PeerStoreOptions
* @property {boolean} persistence
*
* @typedef {Object} PeerDiscoveryOptions
* @property {boolean} autoDial
*
* @typedef {Object} RelayOptions
* @property {boolean} enabled
* @property {import('./circuit').RelayAdvertiseOptions} advertise
@@ -59,7 +58,7 @@ const { updateSelfPeerRecord } = require('./record/utils')
*
* @typedef {Object} Libp2pConfig
* @property {Object} [dht] dht module options
* @property {Object} [peerDiscovery]
* @property {PeerDiscoveryOptions} [peerDiscovery]
* @property {Pubsub} [pubsub] pubsub module options
* @property {RelayOptions} [relay]
* @property {Record<string, Object>} [transport] transport options indexed by transport key
@@ -79,18 +78,16 @@ const { updateSelfPeerRecord } = require('./record/utils')
* @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}
* @extends {Emittery}
* @fires Libp2p#error Emitted when an error occurs
* @fires Libp2p#peer:discovery Emitted when a peer is discovered
*/
class Libp2p extends EventEmitter {
class Libp2p extends Emittery {
/**
* Like `new Libp2p(options)` except it will create a `PeerId`
* instance if one is not provided in options.
@@ -100,14 +97,12 @@ 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)
}
@@ -115,7 +110,7 @@ class Libp2p extends EventEmitter {
* Libp2p node.
*
* @class
* @param {Libp2pOptions & constructorOptions} _options
* @param {Libp2pOptions} _options
*/
constructor (_options) {
super()
@@ -137,14 +132,7 @@ class Libp2p extends EventEmitter {
// Addresses {listen, announce, noAnnounce}
this.addresses = 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.addressManager = new AddressManager(this._options.addresses)
this._modules = this._options.modules
this._config = this._options.config
@@ -198,14 +186,6 @@ 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,
@@ -261,7 +241,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) { // eslint-disable-line no-undef
} else if (globalThis.process !== undefined && globalThis.process.env && globalThis.process.env.LIBP2P_FORCE_PNET) {
throw new Error('Private network is enforced, but no protector was provided')
}
@@ -304,17 +284,19 @@ class Libp2p extends EventEmitter {
*
* @param {string} eventName
* @param {...any} args
* @returns {boolean}
* @returns {Promise<void>}
*/
emit (eventName, ...args) {
// TODO: do we still need this?
// @ts-ignore _events does not exist in libp2p
if (eventName === 'error' && !this._events.error) {
log.error(args)
return false
} else {
return super.emit(eventName, ...args)
}
return new Promise((resolve) => {
// TODO: do we still need this?
// @ts-ignore _events does not exist in libp2p
if (eventName === 'error' && !this._events.error) {
log.error(args)
resolve()
} else {
super.emit(eventName, args).then(resolve)
}
})
}
/**
@@ -369,7 +351,6 @@ class Libp2p extends EventEmitter {
this.metrics && this.metrics.stop()
])
await this.natManager.stop()
await this.transportManager.close()
ping.unmount(this)
@@ -465,32 +446,22 @@ class Libp2p extends EventEmitter {
}
/**
* 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.
* 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.
*
* @returns {Multiaddr[]}
*/
get multiaddrs () {
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())
const announceAddrs = this.addressManager.getAnnounceAddrs()
if (announceAddrs.length) {
return announceAddrs
}
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(Array.from(addrSet).map(str => multiaddr(str)))
return announceFilter(this.transportManager.getAddrs())
}
/**
@@ -569,9 +540,6 @@ 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()

View File

@@ -4,9 +4,10 @@
const sanitize = require('sanitize-filename')
const mergeOptions = require('merge-options')
const crypto = require('libp2p-crypto')
const { Key } = require('interface-datastore')
const Datastore = 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')
@@ -14,7 +15,7 @@ require('node-forge/lib/sha512')
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('interface-datastore/src/types').Datastore} Datastore
* @typedef {import('interface-datastore/src/key')} Key
*/
const keyPrefix = '/pkcs8/'
@@ -71,7 +72,7 @@ async function throwDelayed (err) {
* @private
*/
function DsName (name) {
return new Key(keyPrefix + name)
return new Datastore.Key(keyPrefix + name)
}
/**
@@ -82,7 +83,7 @@ function DsName (name) {
* @private
*/
function DsInfoName (name) {
return new Key(infoPrefix + name)
return new Datastore.Key(infoPrefix + name)
}
/**

View File

@@ -1,14 +1,12 @@
// @ts-nocheck
'use strict'
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const Emittery = require('emittery')
const Big = require('bignumber.js')
const MovingAverage = require('moving-average')
const retimer = require('retimer')
class Stats extends EventEmitter {
class Stats extends Emittery {
/**
* A queue based manager for stat processing
*

View File

@@ -1,168 +0,0 @@
'use strict'
const NatAPI = require('@motrix/nat-api')
const debug = require('debug')
const promisify = require('promisify-es6')
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

View File

@@ -5,24 +5,16 @@ 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 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 all = require('it-all')
const pAny = require('p-any')
const {
setDelayedInterval,
clearDelayedInterval
} = require('set-delayed-interval')
const PeerId = require('peer-id')
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('multiaddr')} Multiaddr
*/
class PeerRouting {
@@ -35,9 +27,9 @@ class PeerRouting {
this._peerStore = libp2p.peerStore
this._routers = libp2p._modules.peerRouting || []
// If we have the dht, add it to the available peer routers
// If we have the dht, make it first
if (libp2p._dht) {
this._routers.push(libp2p._dht)
this._routers.unshift(libp2p._dht)
}
this._refreshManagerOptions = libp2p._options.peerRouting.refreshManager
@@ -63,8 +55,9 @@ class PeerRouting {
*/
async _findClosestPeersTask () {
try {
// nb getClosestPeers adds the addresses to the address book
await drain(this.getClosestPeers(this._peerId.id))
for await (const { id, multiaddrs } of this.getClosestPeers(this._peerId.id)) {
this._peerStore.addressBook.add(id, multiaddrs)
}
} catch (err) {
log.error(err)
}
@@ -78,7 +71,7 @@ class PeerRouting {
}
/**
* Iterates over all peer routers in parallel to find the given peer.
* Iterates over all peer routers in series to find the given peer.
*
* @param {PeerId} id - The id of the peer to find
* @param {object} [options]
@@ -90,20 +83,16 @@ class PeerRouting {
throw errCode(new Error('No peer routers available'), 'NO_ROUTERS_AVAILABLE')
}
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)
)
return pAny(this._routers.map(async (router) => {
const result = await router.findPeer(id, options)
if (output) {
return output
}
// 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')
}
throw errCode(new Error('not found'), 'NOT_FOUND')
return result
}))
}
/**
@@ -119,14 +108,20 @@ class PeerRouting {
throw errCode(new Error('No peer routers available'), 'NO_ROUTERS_AVAILABLE')
}
yield * pipe(
merge(
...this._routers.map(router => router.getClosestPeers(key, options))
),
(source) => storeAddresses(source, this._peerStore),
(source) => uniquePeers(source),
(source) => requirePeers(source)
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
})
)
for (const peer of result) {
yield peer
}
}
}

View File

@@ -2,9 +2,7 @@
const errcode = require('err-code')
/** @typedef {import('../types').EventEmitterFactory} Events */
/** @type Events */
const EventEmitter = require('events')
const Emittery = require('emittery')
const PeerId = require('peer-id')
const AddressBook = require('./address-book')
@@ -21,7 +19,7 @@ const {
*/
/**
* @extends {EventEmitter}
* @extends {Emittery}
*
* @fires PeerStore#peer Emitted when a new peer is added.
* @fires PeerStore#change:protocols Emitted when a known peer supports a different set of protocols.
@@ -29,7 +27,7 @@ const {
* @fires PeerStore#change:pubkey Emitted emitted when a peer's public key is known.
* @fires PeerStore#change:metadata Emitted when the known metadata of a peer change.
*/
class PeerStore extends EventEmitter {
class PeerStore extends Emittery {
/**
* Peer object
*

View File

@@ -91,7 +91,7 @@ class PersistentPeerStore extends PeerStore {
*/
async stop () {
log('PeerStore is stopping')
this.removeAllListeners()
this.clearListeners()
await this._commitData()
log('PeerStore stopped')
}

View File

@@ -63,10 +63,9 @@ module.exports.decodeV1PSK = (pskBuffer) => {
const metadata = uint8ArrayToString(pskBuffer).split(/(?:\r\n|\r|\n)/g)
const pskTag = metadata.shift()
const codec = metadata.shift()
const pskString = metadata.shift()
const psk = pskString && uint8ArrayFromString(pskString, 'base16')
const psk = uint8ArrayFromString(metadata.shift(), 'base16')
if (!psk || psk.byteLength !== KEY_LENGTH) {
if (psk.byteLength !== KEY_LENGTH) {
throw new Error(Errors.INVALID_PSK)
}

View File

@@ -82,22 +82,3 @@ 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;
}

View File

@@ -3,32 +3,23 @@
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(peerId)
const am = new AddressManager()
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(peerId, {
const am = new AddressManager({
listen: listenAddresses
})
@@ -42,7 +33,7 @@ describe('Address Manager', () => {
})
it('should return announce multiaddrs on get', () => {
const am = new AddressManager(peerId, {
const am = new AddressManager({
listen: listenAddresses,
announce: announceAddreses
})
@@ -54,75 +45,6 @@ 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', () => {

View File

@@ -147,26 +147,4 @@ 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)
})
})

View File

@@ -3,7 +3,6 @@
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')
@@ -269,40 +268,5 @@ 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()
})
})
})

View File

@@ -12,8 +12,6 @@ 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')
@@ -80,14 +78,10 @@ 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 {
id: providerPeerId,
multiaddrs: []
}
yield
})
await nodes[0].contentRouting.findProviders().next()
@@ -144,14 +138,10 @@ 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 {
id: providerPeerId,
multiaddrs: []
}
yield
})
await node.contentRouting.findProviders().next()
@@ -161,36 +151,25 @@ describe('content-routing', () => {
it('should be able to register as a provider', async () => {
const cid = new CID('QmU621oD8AhHw6t25vVyfYKmL9VV3PTgc52FngEhTGACFB')
const provider = 'QmZNgCqZCvTsi3B4Vt7gsSqpkqDpE7M2Y9TDmEhbDb4ceF'
const mockBlockApi = nock('http://0.0.0.0:60197')
// mock the block/stat call
.post('/api/v0/block/stat')
const mockApi = nock('http://0.0.0.0:60197')
// mock the refs call
.post('/api/v0/refs')
.query(true)
.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`, [
.reply(200, null, [
'Content-Type', 'application/json',
'X-Chunked-Output', '1'
])
await node.contentRouting.provide(cid)
expect(mockBlockApi.isDone()).to.equal(true)
expect(mockDhtApi.isDone()).to.equal(true)
expect(mockApi.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 block/stat call
.post('/api/v0/block/stat')
// mock the refs call
.post('/api/v0/refs')
.query(true)
.reply(502, 'Bad Gateway', ['Content-Type', 'application/json'])
@@ -272,110 +251,6 @@ 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()
@@ -396,18 +271,15 @@ describe('content-routing', () => {
])
})
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: []
}]
it('should only use the dht if it finds providers', async () => {
const results = [true]
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 = []
@@ -420,11 +292,7 @@ describe('content-routing', () => {
})
it('should use the delegate if the dht fails to find providers', async () => {
const [providerPeerId] = await peerUtils.createPeerId({ fixture: false })
const results = [{
id: providerPeerId,
multiaddrs: []
}]
const results = [true]
sinon.stub(node._dht, 'findProviders').callsFake(function * () {})

View File

@@ -1,46 +0,0 @@
'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()
})
})

View File

@@ -51,7 +51,7 @@ describe('Dialing (direct, TCP)', () => {
peerStore = new PeerStore({ peerId: remotePeerId })
remoteTM = new TransportManager({
libp2p: {
addressManager: new AddressManager(remotePeerId, { listen: [listenAddr] }),
addressManager: new AddressManager({ listen: [listenAddr] }),
peerId: remotePeerId,
peerStore
},

View File

@@ -21,15 +21,14 @@ 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, localAddressManager
let remotePeer, remotePeerStore, remoteAddressManager
let localPeer, localPeerStore
let remotePeer, remotePeerStore
const protocols = [multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]
before(async () => {
@@ -43,9 +42,6 @@ describe('Identify', () => {
remotePeerStore = new PeerStore({ peerId: remotePeer })
remotePeerStore.protoBook.set(remotePeer, protocols)
localAddressManager = new AddressManager(localPeer)
remoteAddressManager = new AddressManager(remotePeer)
})
afterEach(() => {
@@ -114,7 +110,6 @@ describe('Identify', () => {
libp2p: {
peerId: localPeer,
connectionManager: new EventEmitter(),
addressManager: localAddressManager,
peerStore: localPeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true,
@@ -126,7 +121,6 @@ describe('Identify', () => {
libp2p: {
peerId: remotePeer,
connectionManager: new EventEmitter(),
addressManager: remoteAddressManager,
peerStore: remotePeerStore,
multiaddrs: listenMaddrs,
isStarted: () => true,

View File

@@ -1,244 +0,0 @@
'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)
})
})

View File

@@ -10,8 +10,6 @@ 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')
@@ -84,14 +82,10 @@ 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 {
id: remotePeerId,
multiaddrs: []
}
yield
})
await nodes[0].peerRouting.getClosestPeers().next()
@@ -134,14 +128,10 @@ describe('peer-routing', () => {
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 {
id: remotePeerId,
multiaddrs: []
}
return 'fake peer-id'
})
await node.peerRouting.findPeer()
@@ -150,14 +140,10 @@ 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 {
id: remotePeerId,
multiaddrs: []
}
yield
})
await node.peerRouting.getClosestPeers().next()
@@ -166,7 +152,7 @@ describe('peer-routing', () => {
})
it('should be able to find a peer', async () => {
const peerKey = PeerId.createFromB58String('QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL')
const peerKey = 'QmTp9VkYvnHyrqKQuFPiuZkiX9gPcqj6x5LJ1rmWuSySnL'
const mockApi = nock('http://0.0.0.0:60197')
.post('/api/v0/dht/findpeer')
.query(true)
@@ -291,93 +277,55 @@ 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 [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
const results = {
id: remotePeerId,
multiaddrs: []
}
const results = [true]
sinon.stub(node._dht, 'findPeer').callsFake(() => {})
sinon.stub(delegate, 'findPeer').callsFake(() => {
return results
})
const peer = await node.peerRouting.findPeer(remotePeerId)
const peer = await node.peerRouting.findPeer('a peer id')
expect(peer).to.eql(results)
})
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: []
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)
}
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()
expect(closest).to.have.length.above(0)
expect(closest).to.eql(results)
})
it('should use the delegate if the dht fails to get the closest peer', async () => {
const [remotePeerId] = await peerUtils.createPeerId({ fixture: false })
const results = [{
id: remotePeerId,
multiaddrs: []
}]
const results = [true]
sinon.stub(node._dht, 'getClosestPeers').callsFake(function * () { })
@@ -385,55 +333,14 @@ describe('peer-routing', () => {
yield results[0]
})
const closest = await all(node.peerRouting.getClosestPeers('a cid'))
const closest = []
for await (const peer of node.peerRouting.getClosestPeers('a cid')) {
closest.push(peer)
}
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', () => {

View File

@@ -16,9 +16,6 @@ module.exports = {
hop: {
enabled: false
}
},
nat: {
enabled: false
}
}
}