Compare commits

..

12 Commits

Author SHA1 Message Date
Vasco Santos
fc6558b897 chore: release version v0.30.8 2021-02-11 14:57:43 +01:00
Vasco Santos
3e302570e5 chore: update contributors 2021-02-11 14:57:43 +01:00
Vasco Santos
a34d2bbcc3 fix: routers should only use dht if enabled (#885) 2021-02-11 14:37:34 +01:00
Vasco Santos
9941414a91 chore: update delegates config docs to use http client (#853) 2021-02-11 11:42:10 +01:00
Vasco Santos
46cb46188a chore: add discovery example with relay and pubsub discovery (#855) 2021-02-11 11:37:11 +01:00
Vasco Santos
1af8472dc6 chore: add transports example (#851) 2021-02-11 11:12:23 +01:00
Vasco Santos
f6a4cad827 chore: add pubsub example tests (#850) 2021-02-10 21:00:40 +01:00
Vasco Santos
b1079474de chore: add protocol and stream muxing example tests (#849) 2021-02-10 15:40:19 +01:00
Vasco Santos
a150ea60c5 chore: add peer and content routing example tests (#848) 2021-02-08 11:03:42 +01:00
Vasco Santos
aec8e3d3bb chore: release version v0.30.7 2021-02-01 18:40:05 +01:00
Vasco Santos
3abf4aeb35 chore: update contributors 2021-02-01 18:40:05 +01:00
Alex Potsides
a36b2112aa fix: do not add observed address received from peers (#882) 2021-02-01 18:32:57 +01:00
37 changed files with 749 additions and 202 deletions

View File

@@ -48,7 +48,7 @@ const after = async () => {
}
module.exports = {
bundlesize: { maxSize: '216kB' },
bundlesize: { maxSize: '220kB' },
hooks: {
pre: before,
post: after

View File

@@ -79,6 +79,13 @@ jobs:
- 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
@@ -93,13 +100,13 @@ jobs:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- libp2p-in-the-browser
test-discovery-mechanisms-example:
test-peer-and-content-routing-example:
needs: check
runs-on: macos-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: yarn
- run: cd examples && yarn && npm run test -- discovery-mechanisms
- run: cd examples && yarn && npm run test -- peer-and-content-routing
test-pnet-example:
needs: check
runs-on: ubuntu-latest
@@ -107,3 +114,24 @@ jobs:
- 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

View File

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

View File

@@ -23,7 +23,6 @@
- [Setup with Auto Relay](#setup-with-auto-relay)
- [Setup with Keychain](#setup-with-keychain)
- [Configuring Dialing](#configuring-dialing)
- [Configuring Address Manager](#configuring-address-manager)
- [Configuring Connection Manager](#configuring-connection-manager)
- [Configuring Transport Manager](#configuring-transport-manager)
- [Configuring Metrics](#configuring-metrics)
@@ -392,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')
@@ -399,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
@@ -550,26 +558,6 @@ const node = await Libp2p.create({
}
```
#### Configuring Address Manager
The address manager receives observed addresses from network peers. We accept observed addresses once a certain number of peers have reported the same observed address within a certain window of time.
```js
const node = await Libp2p.create({
addressManager: {
observedAddresses: {
// we must receive the same observed address from this many
// peers before we start believe it
minConfidence: 4,
// an address must reach the minimum level of confidence within
// this timeout otherwise it will be ignored
maxLifetimeBeforeEviction: (60 * 10) * 1000 // ten minutes in ms
}
},
// ...other options
})
```
#### Configuring Connection Manager
The Connection Manager prunes Connections in libp2p whenever certain limits are exceeded. If Metrics are enabled, you can also configure the Connection Manager to monitor the bandwidth of libp2p and prune connections as needed. You can read more about what Connection Manager does at [./CONNECTION_MANAGER.md](./CONNECTION_MANAGER.md). The configuration values below show the defaults for Connection Manager. See [./CONNECTION_MANAGER.md](./CONNECTION_MANAGER.md#options) for a full description of the parameters.

View 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()
])
})();

View File

@@ -156,7 +156,100 @@ 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 an 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:

View 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

View File

@@ -2,10 +2,12 @@
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

View File

@@ -10,6 +10,8 @@
"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"
},

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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

View 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

View 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

View 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

View 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

View File

@@ -1,6 +1,6 @@
{
"name": "libp2p",
"version": "0.30.6",
"version": "0.30.8",
"description": "JavaScript implementation of libp2p, a modular peer to peer network stack",
"leadMaintainer": "Jacob Heun <jacobheun@gmail.com>",
"main": "src/index.js",

View File

@@ -31,18 +31,14 @@ class AddressManager extends EventEmitter {
* @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 {object} [options.observedAddresses = { minConfidence: 4, maxLifetimeBeforeEviction: 600000 }] - configuration options for observed addresses
*/
constructor (peerId, { listen = [], announce = [], observedAddresses = { minConfidence: 4, maxLifetimeBeforeEviction: (60 * 10) * 1000 } } = {}) {
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 Map()
this.config = {
observedAddresses
}
this.observed = new Set()
}
/**
@@ -69,25 +65,15 @@ class AddressManager extends EventEmitter {
* @returns {Array<Multiaddr>}
*/
getObservedAddrs () {
const output = []
this.observed.forEach(({ confidence }, addr) => {
if (confidence >= this.config.observedAddresses.minConfidence) {
output.push(multiaddr(addr))
}
})
return output
return Array.from(this.observed).map((a) => multiaddr(a))
}
/**
* Add peer observed addresses
*
* @param {string | Multiaddr} addr
* @param {PeerId} reporter
* @param {number} [confidence=1]
*/
addObservedAddr (addr, reporter, confidence = 1) {
addObservedAddr (addr) {
let ma = multiaddr(addr)
const remotePeer = ma.getPeerId()
@@ -101,41 +87,15 @@ class AddressManager extends EventEmitter {
}
}
const now = Date.now()
const addrString = ma.toString()
const wasNewAddr = !this.observed.has(addrString)
let addrRecord = {
confidence,
reporters: [
reporter.toB58String()
],
expires: now + this.config.observedAddresses.maxLifetimeBeforeEviction
// do not trigger the change:addresses event if we already know about this address
if (this.observed.has(addrString)) {
return
}
// we've seen this address before, increase the confidence we have in it
if (!wasNewAddr) {
addrRecord = this.observed.get(addrString)
if (!addrRecord.reporters.includes(reporter.toB58String())) {
addrRecord.confidence++
addrRecord.reporters.push(reporter.toB58String())
addrRecord.expires = now + this.config.observedAddresses.maxLifetimeBeforeEviction
}
}
this.observed.set(addrString, addrRecord)
// only emit event if we've reached the minimum confidence
if (addrRecord.confidence === this.config.observedAddresses.minConfidence) {
this.emit('change:addresses')
}
// evict addresses older than MAX_LOW_CONFIDENCE_ADDR_LIFETIME_MS we are not confident in
this.observed.forEach(({ confidence, expires }, key, map) => {
if (confidence < this.config.observedAddresses.minConfidence && expires < now) {
map.delete(key)
}
})
this.observed.add(addrString)
this.emit('change:addresses')
}
}

View File

@@ -16,12 +16,6 @@ const DefaultConfig = {
announce: [],
noAnnounce: []
},
addressManager: {
observedAddresses: {
minConfidence: 4,
maxLifetimeBeforeEviction: (60 * 10) * 1000
}
},
connectionManager: {
minConnections: 25
},

View File

@@ -35,7 +35,7 @@ class ContentRouting {
this.dht = libp2p._dht
// If we have the dht, add it to the available content routers
if (this.dht) {
if (this.dht && libp2p._config.dht.enabled) {
this.routers.push(this.dht)
}
}

View File

@@ -202,8 +202,9 @@ class IdentifyService {
this.peerStore.protoBook.set(id, protocols)
this.peerStore.metadataBook.set(id, 'AgentVersion', uint8ArrayFromString(message.agentVersion))
// TODO: Add and score our observed addr
log('received observed address of %s', observedAddr)
this.addressManager.addObservedAddr(observedAddr, id)
// this.addressManager.addObservedAddr(observedAddr)
}
/**

View File

@@ -137,10 +137,7 @@ class Libp2p extends EventEmitter {
// Addresses {listen, announce, noAnnounce}
this.addresses = this._options.addresses
this.addressManager = new AddressManager(this.peerId, {
...this._options.addresses,
...this._options.addressManager
})
this.addressManager = new AddressManager(this.peerId, this._options.addresses)
// when addresses change, update our peer record
this.addressManager.on('change:addresses', () => {

View File

@@ -16,11 +16,11 @@ const {
codes: { ERR_INVALID_PARAMETERS }
} = require('./errors')
const isLoopback = require('libp2p-utils/src/multiaddr/is-loopback')
const AddressManager = require('./address-manager')
/**
* @typedef {import('peer-id')} PeerId
* @typedef {import('./transport-manager')} TransportManager
* @typedef {import('./address-manager')} AddressManager
*/
function highPort (min = 1024, max = 65535) {
@@ -118,12 +118,11 @@ class NatManager {
protocol: transport.toUpperCase()
})
// add with high confidence
this._addressManager.addObservedAddr(Multiaddr.fromNodeAddress({
family: 'IPv4',
address: publicIp,
port: `${publicPort}`
}, transport), this._peerId, this._addressManager.config.observedAddresses.minConfidence)
}, transport))
}
}

View File

@@ -36,7 +36,7 @@ class PeerRouting {
this._routers = libp2p._modules.peerRouting || []
// If we have the dht, add it to the available peer routers
if (libp2p._dht) {
if (libp2p._dht && libp2p._config.dht.enabled) {
this._routers.push(libp2p._dht)
}

View File

@@ -15,11 +15,9 @@ const announceAddreses = ['/dns4/peer.io']
describe('Address Manager', () => {
let peerId
let peerIds
before(async () => {
peerId = await PeerId.createFromJSON(Peers[0])
peerIds = await Promise.all(Peers.slice(1).map(peerId => PeerId.createFromJSON(peerId)))
})
it('should not need any addresses', () => {
@@ -62,7 +60,7 @@ describe('Address Manager', () => {
expect(am.observed).to.be.empty()
am.addObservedAddr('/ip4/123.123.123.123/tcp/39201', peerId)
am.addObservedAddr('/ip4/123.123.123.123/tcp/39201')
expect(am.observed).to.have.property('size', 1)
})
@@ -73,12 +71,12 @@ describe('Address Manager', () => {
expect(am.observed).to.be.empty()
am.addObservedAddr(ma, peerId)
am.addObservedAddr(ma, peerId)
am.addObservedAddr(ma, peerId)
am.addObservedAddr(ma)
am.addObservedAddr(ma)
am.addObservedAddr(ma)
expect(am.observed).to.have.property('size', 1)
expect(Array.from(am.observed.keys())).to.include(ma)
expect(am.observed).to.include(ma)
})
it('should only emit one change:addresses event', () => {
@@ -90,25 +88,11 @@ describe('Address Manager', () => {
eventCount++
})
am.addObservedAddr(ma, peerIds[0])
am.addObservedAddr(ma, peerIds[1])
am.addObservedAddr(ma, peerIds[2])
am.addObservedAddr(`${ma}/p2p/${peerId}`, peerIds[3])
am.addObservedAddr(`${ma}/p2p/${peerId.toB58String()}`, peerIds[4])
expect(eventCount).to.equal(1)
})
it('should emit one change:addresses event when specifying confidence', () => {
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, peerId, am.config.observedAddresses.minConfidence)
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)
})
@@ -119,12 +103,11 @@ describe('Address Manager', () => {
expect(am.observed).to.be.empty()
am.addObservedAddr(ma, peerId)
am.addObservedAddr(`${ma}/p2p/${peerId}`, peerId)
am.addObservedAddr(ma)
am.addObservedAddr(`${ma}/p2p/${peerId}`)
expect(am.observed).to.have.property('size', 1)
expect(Array.from(am.observed.keys())).to.include(ma)
expect(am.observed).to.include(ma)
})
it('should strip our peer address from added observed addresses in difference formats', () => {
@@ -133,71 +116,12 @@ describe('Address Manager', () => {
expect(am.observed).to.be.empty()
am.addObservedAddr(ma, peerId)
am.addObservedAddr(`${ma}/p2p/${peerId}`, peerId) // base32 CID
am.addObservedAddr(`${ma}/p2p/${peerId.toB58String()}`, peerId) // base58btc
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(Array.from(am.observed.keys())).to.include(ma)
})
it('should require a number of confirmations before believing address', () => {
const ma = '/ip4/123.123.123.123/tcp/39201'
const am = new AddressManager(peerId)
expect(am.observed).to.be.empty()
am.addObservedAddr(ma, peerId)
expect(am.getObservedAddrs().map(ma => ma.toString())).to.not.include(ma)
for (let i = 0; i < am.config.observedAddresses.minConfidence; i++) {
am.addObservedAddr(ma, peerIds[i])
}
expect(am.getObservedAddrs().map(ma => ma.toString())).to.include(ma)
})
it('should require a number of confirmations from different peers', () => {
const ma = '/ip4/123.123.123.123/tcp/39201'
const am = new AddressManager(peerId)
expect(am.observed).to.be.empty()
am.addObservedAddr(ma, peerId)
expect(am.getObservedAddrs().map(ma => ma.toString())).to.not.include(ma)
for (let i = 0; i < am.config.observedAddresses.minConfidence; i++) {
am.addObservedAddr(ma, peerIds[0])
}
expect(am.getObservedAddrs().map(ma => ma.toString())).to.not.include(ma)
})
it('should evict addresses that do not receive enough confirmations within the timeout', () => {
const ma1 = '/ip4/123.123.123.123/tcp/39201'
const ma2 = '/ip4/124.124.124.124/tcp/39202'
const am = new AddressManager(peerId)
expect(am.observed).to.be.empty()
am.addObservedAddr(ma1, peerId)
const observedAddrs = Array.from(am.observed.values())
expect(Array.from(am.observed.keys())).to.include(ma1)
// make expiry date a while ago
observedAddrs[0].expires = Date.now() - 1000
// will evict any old multiaddrs
am.addObservedAddr(ma2, peerId)
// should have been evicted
expect(Array.from(am.observed.keys())).to.not.include(ma1)
expect(Array.from(am.observed.keys())).to.include(ma2)
expect(am.observed).to.include(ma)
})
})

View File

@@ -164,7 +164,7 @@ describe('libp2p.multiaddrs', () => {
expect(libp2p.multiaddrs).to.have.lengthOf(listenAddresses.length)
libp2p.addressManager.addObservedAddr(ma, libp2p.peerId, libp2p.addressManager.config.observedAddresses.minConfidence)
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

@@ -107,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, {
@@ -131,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()
@@ -253,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, {

View File

@@ -37,7 +37,7 @@ describe('Consume peer record', () => {
done = resolve
})
libp2p.addressManager.addObservedAddr('/ip4/123.123.123.123/tcp/3983', libp2p.peerId, libp2p.addressManager.config.observedAddresses.minConfidence)
libp2p.addressManager.addObservedAddr('/ip4/123.123.123.123/tcp/3983')
await p

View File

@@ -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 () => {

View File

@@ -132,6 +132,10 @@ 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 })