Compare commits

...

6 Commits

Author SHA1 Message Date
9e0097c88c chore: use kad-dht with renamed peer-store properties 2020-04-27 08:55:04 +02:00
7039cd0270 chore: rename peer-store properties 2020-04-24 16:21:23 +02:00
984d933606 chore: apply suggestions from code review
Co-Authored-By: Jacob Heun <jacobheun@gmail.com>
2020-04-24 14:58:39 +02:00
0f6e8781db chore: remove peer-info usage
BREAKING CHANGE: all API methods with peer-info parameters or return values were changed. You can check the API.md document, in order to check the new values to use
2020-04-24 14:58:39 +02:00
54212cbf26 chore: deprecate old peer store api (#598)
* chore: deprecate old peer-store api

BREAKING CHANGE: the peer-store api changed. Check the API docs for the new specification.

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-04-16 15:20:42 +02:00
be45fc498f feat: address and proto books (#590)
* feat: address and proto books

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

* chore: minor fixes and initial tests added

* chore: integrate new peer-store with code using adapters for other modules

* chore: do not use peerstore.put on get-peer-info

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

* chore: add new peer store tests

* chore: apply suggestions from code review

Co-Authored-By: Jacob Heun <jacobheun@gmail.com>

Co-authored-by: Jacob Heun <jacobheun@gmail.com>
2020-04-14 13:20:36 +02:00
50 changed files with 2460 additions and 1049 deletions

View File

@ -4,7 +4,6 @@ const Libp2p = require('./src')
const { MULTIADDRS_WEBSOCKETS } = require('./test/fixtures/browser')
const Peers = require('./test/fixtures/peers')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const WebSockets = require('libp2p-websockets')
const Muxer = require('libp2p-mplex')
const Crypto = require('libp2p-secio')
@ -14,11 +13,12 @@ let libp2p
const before = async () => {
// Use the last peer
const peerId = await PeerId.createFromJSON(Peers[Peers.length - 1])
const peerInfo = new PeerInfo(peerId)
peerInfo.multiaddrs.add(MULTIADDRS_WEBSOCKETS[0])
libp2p = new Libp2p({
peerInfo,
addresses: {
listen: [MULTIADDRS_WEBSOCKETS[0]]
},
peerId,
modules: {
transport: [WebSockets],
streamMuxer: [Muxer],

View File

@ -17,6 +17,18 @@
* [`contentRouting.put`](#contentroutingput)
* [`contentRouting.get`](#contentroutingget)
* [`contentRouting.getMany`](#contentroutinggetmany)
* [`peerStore.addressBook.add`](#peerstoreaddressbookadd)
* [`peerStore.addressBook.delete`](#peerstoreaddressbookdelete)
* [`peerStore.addressBook.get`](#peerstoreaddressbookget)
* [`peerStore.addressBook.getMultiaddrsForPeer`](#peerstoreaddressbookgetmultiaddrsforpeer)
* [`peerStore.addressBook.set`](#peerstoreaddressbookset)
* [`peerStore.protoBook.add`](#peerstoreprotobookadd)
* [`peerStore.protoBook.delete`](#peerstoreprotobookdelete)
* [`peerStore.protoBook.get`](#peerstoreprotobookget)
* [`peerStore.protoBook.set`](#peerstoreprotobookset)
* [`peerStore.delete`](#peerstoredelete)
* [`peerStore.get`](#peerstoreget)
* [`peerStore.peers`](#peerstorepeers)
* [`pubsub.getSubscribers`](#pubsubgetsubscribers)
* [`pubsub.getTopics`](#pubsubgettopics)
* [`pubsub.publish`](#pubsubpublish)
@ -29,6 +41,8 @@
* [`metrics.forPeer`](#metricsforpeer)
* [`metrics.forProtocol`](#metricsforprotocol)
* [Events](#events)
* [`libp2p`](#libp2p)
* [`libp2p.peerStore`](#libp2ppeerStore)
* [Types](#types)
* [`Stats`](#stats)
@ -44,14 +58,15 @@ Creates an instance of Libp2p.
| Name | Type | Description |
|------|------|-------------|
| options | `Object` | libp2p options |
| options.modules | `Array<Object>` | libp2p modules to use |
| [options.config] | `Object` | libp2p modules configuration and core configuration |
| [options.connectionManager] | `Object` | libp2p Connection Manager configuration |
| [options.datastore] | `Object` | must implement [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore) (in memory datastore will be used if not provided) |
| [options.dialer] | `Object` | libp2p Dialer configuration
| [options.metrics] | `Object` | libp2p Metrics configuration
| [options.peerInfo] | [`PeerInfo`][peer-info] | peerInfo instance (it will be created if not provided) |
| options | `object` | libp2p options |
| options.modules | `Array<object>` | libp2p modules to use |
| [options.addresses] | `{ listen: Array<Multiaddr> }` | Addresses to use for transport listening and to announce to the network |
| [options.config] | `object` | libp2p modules configuration and core configuration |
| [options.connectionManager] | `object` | libp2p Connection Manager configuration |
| [options.datastore] | `object` | must implement [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore) (in memory datastore will be used if not provided) |
| [options.dialer] | `object` | libp2p Dialer configuration
| [options.metrics] | `object` | libp2p Metrics configuration
| [options.peerId] | [`PeerId`][peer-id] | peerId instance (it will be created if not provided) |
For Libp2p configurations and modules details read the [Configuration Document](./CONFIGURATION.md).
@ -73,7 +88,7 @@ const options = {}
const libp2p = await Libp2p.create(options)
```
Note: The [`PeerInfo`][peer-info] option is not required and will be generated if it is not provided.
Note: The [`PeerId`][peer-id] option is not required and will be generated if it is not provided.
<details><summary>Alternative</summary>
As an alternative, it is possible to create a Libp2p instance with the constructor:
@ -92,7 +107,7 @@ const libp2p = new Libp2p(options)
Required keys in the `options` object:
- `peerInfo`: instance of [`PeerInfo`][peer-info] that contains the [`PeerId`][peer-id], Keys and [multiaddrs][multiaddr] of the libp2p Node (optional when using `.create`).
- `peerId`: instance of [`PeerId`][peer-id] that contains the peer Keys (optional when using `.create`).
- `modules.transport`: An array that must include at least 1 compliant transport. See [modules that implement the transport interface](https://github.com/libp2p/js-interfaces/tree/master/src/transport#modules-that-implement-the-interface).
</details>
@ -149,6 +164,10 @@ const libp2p = await Libp2p.create(options)
await libp2p.stop()
```
### addresses
TODO with `address-manager`.
### connections
A Getter that returns a Map of the current Connections libp2p has to other peers.
@ -180,10 +199,12 @@ for (const [peerId, connections] of libp2p.connections) {
| Name | Type | Description |
|------|------|-------------|
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id |
| [options] | `Object` | dial options |
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. |
| [options] | `object` | dial options |
| [options.signal] | [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | An `AbortSignal` instance obtained from an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) that can be used to abort the connection before it completes |
**Note:** If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id. Moreover, if a [`PeerId`][peer-id] is given, the peer will need to have known multiaddrs for it in the PeerStore.
#### Returns
| Type | Description |
@ -194,7 +215,7 @@ for (const [peerId, connections] of libp2p.connections) {
```js
// ...
const conn = await libp2p.dial(remotePeerInfo)
const conn = await libp2p.dial(remotePeerId)
// create a new stream within the connection
const { stream, protocol } = await conn.newStream(['/echo/1.1.0', '/echo/1.0.0'])
@ -215,11 +236,13 @@ Dials to another peer in the network and selects a protocol to communicate with
| Name | Type | Description |
|------|------|-------------|
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id |
| protocols | `String|Array<String>` | A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made. (e.g '/ipfs/bitswap/1.1.0') |
| [options] | `Object` | dial options |
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | The peer to dial. |
| protocols | `string|Array<string>` | A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made. (e.g '/ipfs/bitswap/1.1.0') |
| [options] | `object` | dial options |
| [options.signal] | [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | An `AbortSignal` instance obtained from an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) that can be used to abort the connection before it completes |
**Note:** If a [`Multiaddr`][multiaddr] or its string is provided, it **must** include the peer id. Moreover, if a [`PeerId`][peer-id] is given, the peer will need to have known multiaddrs for it in the PeerStore.
#### Returns
| Type | Description |
@ -232,7 +255,7 @@ Dials to another peer in the network and selects a protocol to communicate with
// ...
const pipe = require('it-pipe')
const { stream, protocol } = await libp2p.dialProtocol(remotePeerInfo, protocols)
const { stream, protocol } = await libp2p.dialProtocol(remotePeerId, protocols)
// Use this new stream like any other duplex stream
pipe([1, 2, 3], stream, consume)
@ -248,7 +271,7 @@ Attempts to gracefully close an open connection to the given peer. If the connec
| Name | Type | Description |
|------|------|-------------|
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to hang up |
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to hang up |
#### Returns
@ -260,7 +283,7 @@ Attempts to gracefully close an open connection to the given peer. If the connec
```js
// ...
await libp2p.hangUp(remotePeerInfo)
await libp2p.hangUp(remotePeerId)
```
### handle
@ -275,7 +298,7 @@ In the event of a new handler for the same protocol being added, the first one i
| Name | Type | Description |
|------|------|-------------|
| protocols | `Array<String>|String` | protocols to register |
| protocols | `Array<string>|string` | protocols to register |
| handler | `function({ connection:*, stream:*, protocol:string })` | handler to call |
@ -300,7 +323,7 @@ Unregisters all handlers with the given protocols
| Name | Type | Description |
|------|------|-------------|
| protocols | `Array<String>|String` | protocols to unregister |
| protocols | `Array<string>|string` | protocols to unregister |
#### Example
@ -319,7 +342,7 @@ Pings a given peer and get the operation's latency.
| Name | Type | Description |
|------|------|-------------|
| peer | [`PeerInfo`][peer-info]\|[`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to ping |
| peer | [`PeerId`][peer-id]\|[`Multiaddr`][multiaddr]\|`string` | peer to ping |
#### Returns
@ -345,20 +368,20 @@ Iterates over all peer routers in series to find the given peer. If the DHT is e
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | ID of the peer to find |
| options | `Object` | operation options |
| options | `object` | operation options |
| options.timeout | `number` | maximum time the query should run |
#### Returns
| Type | Description |
|------|-------------|
| `Promise<PeerInfo>` | Peer info of a known peer |
| `Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>` | Peer data of a known peer |
#### Example
```js
// ...
const peerInfo = await libp2p.peerRouting.findPeer(peerId, options)
const peer = await libp2p.peerRouting.findPeer(peerId, options)
```
### contentRouting.findProviders
@ -373,7 +396,7 @@ Once a content router succeeds, the iteration will stop. If the DHT is enabled,
| Name | Type | Description |
|------|------|-------------|
| cid | [`CID`][cid] | cid to find |
| options | `Object` | operation options |
| options | `object` | operation options |
| options.timeout | `number` | maximum time the query should run |
| options.maxNumProviders | `number` | maximum number of providers to find |
@ -381,14 +404,14 @@ Once a content router succeeds, the iteration will stop. If the DHT is enabled,
| Type | Description |
|------|-------------|
| `AsyncIterator<PeerInfo>` | Async iterator for [`PeerInfo`][peer-info] |
| `AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }` | Async iterator for peer data |
#### Example
```js
// Iterate over the providers found for the given cid
for await (const provider of libp2p.contentRouting.findProviders(cid)) {
console.log(provider)
console.log(provider.id, provider.multiaddrs)
}
```
@ -427,9 +450,9 @@ Writes a value to a key in the DHT.
| Name | Type | Description |
|------|------|-------------|
| key | `String` | key to add to the dht |
| key | `string` | key to add to the dht |
| value | `Buffer` | value to add to the dht |
| [options] | `Object` | put options |
| [options] | `object` | put options |
| [options.minPeers] | `number` | minimum number of peers required to successfully put (default: closestPeers.length) |
#### Returns
@ -458,8 +481,8 @@ Queries the DHT for a value stored for a given key.
| Name | Type | Description |
|------|------|-------------|
| key | `String` | key to get from the dht |
| [options] | `Object` | get options |
| key | `string` | key to get from the dht |
| [options] | `object` | get options |
| [options.timeout] | `number` | maximum time the query should run |
#### Returns
@ -487,9 +510,9 @@ Queries the DHT for the n values stored for the given key (without sorting).
| Name | Type | Description |
|------|------|-------------|
| key | `String` | key to get from the dht |
| key | `string` | key to get from the dht |
| nvals | `number` | number of values aimed |
| [options] | `Object` | get options |
| [options] | `object` | get options |
| [options.timeout] | `number` | maximum time the query should run |
#### Returns
@ -507,6 +530,345 @@ const key = '/key'
const { from, val } = await libp2p.contentRouting.get(key)
```
### peerStore.addressBook.add
Adds known `multiaddrs` of a given peer. If the peer is not known, it will be set with the provided multiaddrs.
`peerStore.addressBook.add(peerId, multiaddrs)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to set |
| multiaddrs | |`Array<Multiaddr>` | [`Multiaddrs`][multiaddr] to add |
#### Returns
| Type | Description |
|------|-------------|
| `AddressBook` | Returns the Address Book component |
#### Example
```js
peerStore.addressBook.add(peerId, multiaddr)
```
### peerStore.addressBook.delete
Delete the provided peer from the book.
`peerStore.addressBook.delete(peerId)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to remove |
#### Returns
| Type | Description |
|------|-------------|
| `boolean` | true if found and removed |
#### Example
```js
peerStore.addressBook.delete(peerId)
// false
peerStore.addressBook.set(peerId, multiaddr)
peerStore.addressBook.delete(peerId)
// true
```
### peerStore.addressBook.get
Get the known [`Addresses`][address] of a provided peer.
`peerStore.addressBook.get(peerId)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to get |
#### Returns
| Type | Description |
|------|-------------|
| `Array<Address>` | Array of peer's [`Addresses`][address] containing the multiaddr and its metadata |
#### Example
```js
peerStore.addressBook.get(peerId)
// undefined
peerStore.addressBook.set(peerId, multiaddr)
peerStore.addressBook.get(peerId)
// [
// {
// multiaddr: /ip4/140.10.2.1/tcp/8000,
// ...
// },
// {
// multiaddr: /ip4/140.10.2.1/ws/8001
// ...
// },
// ]
```
## peerStore.addressBook.getMultiaddrsForPeer
Get the known `Multiaddr` of a provided peer. All returned multiaddrs will include the encapsulated `PeerId` of the peer.
`peerStore.addressBook.getMultiaddrsForPeer(peerId)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to get |
#### Returns
| Type | Description |
|------|-------------|
| `Array<Multiaddr>` | Array of peer's multiaddr |
#### Example
```js
peerStore.addressBook.getMultiaddrsForPeer(peerId)
// undefined
peerStore.addressBook.set(peerId, multiaddr)
peerStore.addressBook.getMultiaddrsForPeer(peerId)
// [
// /ip4/140.10.2.1/tcp/8000/p2p/QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t
// /ip4/140.10.2.1/ws/8001/p2p/QmW8rAgaaA6sRydK1k6vonShQME47aDxaFidbtMevWs73t
// ]
```
### peerStore.addressBook.set
Set known `multiaddrs` of a given peer.
`peerStore.addressBook.set(peerId, multiaddrs)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to set |
| multiaddrs | |`Array<Multiaddr>` | [`Multiaddrs`][multiaddr] to store |
#### Returns
| Type | Description |
|------|-------------|
| `AddressBook` | Returns the Address Book component |
#### Example
```js
peerStore.addressBook.add(peerId, multiaddr)
```
### peerStore.protoBook.add
Add known `protocols` of a given peer.
`peerStore.protoBook.add(peerId, protocols)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to set |
| protocols | `Array<string>` | protocols to add |
#### Returns
| Type | Description |
|------|-------------|
| `ProtoBook` | Returns the Proto Book component |
#### Example
```js
peerStore.protoBook.add(peerId, protocols)
```
### peerStore.protoBook.delete
Delete the provided peer from the book.
`peerStore.protoBook.delete(peerId)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to remove |
#### Returns
| Type | Description |
|------|-------------|
| `boolean` | true if found and removed |
#### Example
```js
peerStore.protoBook.delete(peerId)
// false
peerStore.protoBook.set(peerId, protocols)
peerStore.protoBook.delete(peerId)
// true
```
### peerStore.protoBook.get
Get the known `protocols` of a provided peer.
`peerStore.protoBook.get(peerId)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to get |
#### Returns
| Type | Description |
|------|-------------|
| `Array<string>` | Array of peer's supported protocols |
#### Example
```js
peerStore.protoBook.get(peerId)
// undefined
peerStore.protoBook.set(peerId, [ '/proto/1.0.0', '/proto/1.1.0' ])
peerStore.protoBook.get(peerId)
// [ '/proto/1.0.0', '/proto/1.1.0' ]
```
### peerStore.protoBook.set
Set known `protocols` of a given peer.
`peerStore.protoBook.set(peerId, protocols)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to set |
| protocols | `Array<string>` | protocols to store |
#### Returns
| Type | Description |
|------|-------------|
| `ProtoBook` | Returns the Proto Book component |
#### Example
```js
peerStore.protoBook.set(peerId, protocols)
```
### peerStore.delete
Delete the provided peer from every book.
`peerStore.delete(peerId)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to remove |
#### Returns
| Type | Description |
|------|-------------|
| `boolean` | true if found and removed |
#### Example
```js
peerStore.delete(peerId)
// false
peerStore.addressBook.set(peerId, multiaddrs)
peerStore.protoBook.set(peerId, protocols)
peerStore.delete(peerId)
// true
peerStore.delete(peerId2)
// false
peerStore.addressBook.set(peerId2, multiaddrs)
peerStore.delete(peerId2)
// true
```
### peerStore.get
Get the stored information of a given peer, namely its [`PeerId`][peer-id], known [`Addresses`][address] and supported protocols.
`peerStore.get(peerId)`
#### Parameters
| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | peerId to get |
#### Returns
| Type | Description |
|------|-------------|
| `{ id: PeerId, addresses: Array<Address>, protocols: Array<string> }` | Peer information of the provided peer |
#### Example
```js
peerStore.get(peerId)
// false
peerStore.addressBook.set(peerId, multiaddrs)
peerStore.protoBook.set(peerId, protocols)
peerStore.get(peerId)
// {
// id: {},
// addresses: [...],
// protocols: [...]
// }
```
### peerStore.peers
Get all the stored information of every peer.
`peerStore.peers`
#### Returns
| Type | Description |
|------|-------------|
| `Map<string, { id: PeerId, addresses: Array<Address>, protocols: Array<string> }>` | Peer data of every peer known |
#### Example
```js
for (let [peerIdString, peer] of peerStore.peers.entries()) {
// peer { id, addresses, protocols }
}
```
### pubsub.getSubscribers
Gets a list of the peer-ids that are subscribed to one topic.
@ -523,7 +885,7 @@ Gets a list of the peer-ids that are subscribed to one topic.
| Type | Description |
|------|-------------|
| `Array<String>` | peer-id subscribed to the topic |
| `Array<string>` | peer-id subscribed to the topic |
#### Example
@ -541,7 +903,7 @@ Gets a list of topics the node is subscribed to.
| Type | Description |
|------|-------------|
| `Array<String>` | topics the node is subscribed to |
| `Array<string>` | topics the node is subscribed to |
#### Example
@ -588,7 +950,7 @@ Subscribes the given handler to a pubsub topic.
| Name | Type | Description |
|------|------|-------------|
| topic | `string` | topic to subscribe |
| handler | `function({ from: String, data: Buffer, seqno: Buffer, topicIDs: Array<String>, signature: Buffer, key: Buffer })` | handler for new data on topic |
| handler | `function({ from: string, data: Buffer, seqno: Buffer, topicIDs: Array<string>, signature: Buffer, key: Buffer })` | handler for new data on topic |
#### Returns
@ -618,7 +980,7 @@ Unsubscribes the given handler from a pubsub topic. If no handler is provided, a
| Name | Type | Description |
|------|------|-------------|
| topic | `string` | topic to unsubscribe |
| handler | `function(<Object>)` | handler subscribed |
| handler | `function(<object>)` | handler subscribed |
#### Returns
@ -714,7 +1076,7 @@ Returns the [`Stats`](#stats) object for a given [`PeerId`][peer-id] if it is be
#### Example
```js
const peerStats = libp2p.metrics.forPeer(peerInfo)
const peerStats = libp2p.metrics.forPeer(peerId)
console.log(peerStats.toJSON())
```
@ -745,7 +1107,9 @@ console.log(peerStats.toJSON())
## Events
Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.
Once you have a libp2p instance, you can listen to several events it emits, so that you can be notified of relevant network events.
### libp2p
#### An error has occurred
@ -760,7 +1124,7 @@ Once you have a libp2p instance, you can listen to several events it emits, so t
If `autoDial` option is `true`, applications should **not** attempt to connect to the peer
unless they are performing a specific action. See [peer discovery and auto dial](./PEER_DISCOVERY.md) for more information.
- `peer`: instance of [`PeerInfo`][peer-info]
- `peer`: instance of [`PeerId`][peer-id]
#### A new connection to a peer has been opened
@ -768,7 +1132,7 @@ This event will be triggered anytime a new Connection is established to another
`libp2p.on('peer:connect', (peer) => {})`
- `peer`: instance of [`PeerInfo`][peer-info]
- `peer`: instance of [`PeerId`][peer-id]
#### An existing connection to a peer has been closed
@ -776,7 +1140,29 @@ This event will be triggered anytime we are disconnected from another peer, rega
`libp2p.on('peer:disconnect', (peer) => {})`
- `peer`: instance of [`PeerInfo`][peer-info]
- `peer`: instance of [`PeerId`][peer-id]
### libp2p.peerStore
#### A new peer is added to the peerStore
`libp2p.peerStore.on('peer', (peerId) => {})`
- `peerId`: instance of [`PeerId`][peer-id]
#### Known multiaddrs for a peer change
`libp2p.peerStore.on('change:multiaddrs', ({ peerId, multiaddrs}) => {})`
- `peerId`: instance of [`PeerId`][peer-id]
- `multiaddrs`: array of known [`multiaddr`][multiaddr] for the peer
#### Known protocols for a peer change
`libp2p.peerStore.on('change:protocols', ({ peerId, protocols}) => {})`
- `peerId`: instance of [`PeerId`][peer-id]
- `protocols`: array of known, supported protocols for the peer (string identifiers)
## Types
@ -787,9 +1173,9 @@ This event will be triggered anytime we are disconnected from another peer, rega
- `dataReceived<string>`: The stringified value of total incoming data for this stat.
- `dataSent<string>`: The stringified value of total outgoing data for this stat.
- `movingAverages<object>`: The properties are dependent on the configuration of the moving averages interval. Defaults are listed here.
- `['60000']<Number>`: The calculated moving average at a 1 minute interval.
- `['300000']<Number>`: The calculated moving average at a 5 minute interval.
- `['900000']<Number>`: The calculated moving average at a 15 minute interval.
- `['60000']<number>`: The calculated moving average at a 1 minute interval.
- `['300000']<number>`: The calculated moving average at a 5 minute interval.
- `['900000']<number>`: The calculated moving average at a 15 minute interval.
- `snapshot<object>`: A getter that returns a clone of the raw stats.
- `dataReceived<BigNumber>`: A [`BigNumber`](https://github.com/MikeMcl/bignumber.js/) of the amount of incoming data
- `dataSent<BigNumber>`: A [`BigNumber`](https://github.com/MikeMcl/bignumber.js/) of the amount of outgoing data
@ -798,8 +1184,8 @@ This event will be triggered anytime we are disconnected from another peer, rega
- `['300000']<MovingAverage>`: The [MovingAverage](https://www.npmjs.com/package/moving-averages) at a 5 minute interval.
- `['900000']<MovingAverage>`: The [MovingAverage](https://www.npmjs.com/package/moving-averages) at a 15 minute interval.
[address]: https://github.com/libp2p/js-libp2p/tree/master/src/peer-store/address-book.js
[cid]: https://github.com/multiformats/js-cid
[connection]: https://github.com/libp2p/js-interfaces/tree/master/src/connection
[multiaddr]: https://github.com/multiformats/js-multiaddr
[peer-id]: https://github.com/libp2p/js-peer-id
[peer-info]: https://github.com/libp2p/js-peer-info

View File

@ -57,7 +57,7 @@
"it-protocol-buffers": "^0.2.0",
"latency-monitor": "~0.2.1",
"libp2p-crypto": "^0.17.1",
"libp2p-interfaces": "^0.2.3",
"libp2p-interfaces": "^0.3.0",
"libp2p-utils": "^0.1.2",
"mafmt": "^7.0.0",
"merge-options": "^2.0.0",
@ -69,7 +69,6 @@
"p-fifo": "^1.0.0",
"p-settle": "^4.0.0",
"peer-id": "^0.13.4",
"peer-info": "^0.17.0",
"protons": "^1.0.1",
"retimer": "^2.0.0",
"timeout-abort-controller": "^1.0.0",
@ -84,21 +83,22 @@
"cids": "^0.8.0",
"delay": "^4.3.0",
"dirty-chai": "^2.0.1",
"interop-libp2p": "~0.0.1",
"interop-libp2p": "libp2p/interop#chore/update-libp2p-daemon-with-peerstore",
"ipfs-http-client": "^44.0.0",
"it-concat": "^1.0.0",
"it-pair": "^1.0.0",
"it-pushable": "^1.4.0",
"libp2p-bootstrap": "^0.10.3",
"libp2p-delegated-content-routing": "^0.4.1",
"libp2p-delegated-peer-routing": "^0.4.0",
"libp2p-floodsub": "^0.20.0",
"libp2p-gossipsub": "^0.2.0",
"libp2p-kad-dht": "^0.18.2",
"libp2p-mdns": "^0.13.0",
"libp2p-bootstrap": "^0.11.0",
"libp2p-delegated-content-routing": "^0.5.0",
"libp2p-delegated-peer-routing": "^0.5.0",
"libp2p-floodsub": "^0.21.0",
"libp2p-gossipsub": "^0.4.0",
"libp2p-kad-dht": "^0.19.1",
"libp2p-mdns": "^0.14.0",
"libp2p-mplex": "^0.9.1",
"libp2p-secio": "^0.12.1",
"libp2p-tcp": "^0.14.1",
"libp2p-webrtc-star": "^0.17.0",
"libp2p-webrtc-star": "^0.18.0",
"libp2p-websockets": "^0.13.1",
"nock": "^12.0.0",
"p-defer": "^3.0.0",

View File

@ -1,7 +1,9 @@
'use strict'
const debug = require('debug')
const PeerInfo = require('peer-info')
const log = debug('libp2p:circuit:hop')
log.error = debug('libp2p:circuit:hop:error')
const PeerId = require('peer-id')
const { validateAddrs } = require('./utils')
const StreamHandler = require('./stream-handler')
@ -14,9 +16,6 @@ const { stop } = require('./stop')
const multicodec = require('./../multicodec')
const log = debug('libp2p:circuit:hop')
log.error = debug('libp2p:circuit:hop:error')
module.exports.handleHop = async function handleHop ({
connection,
request,
@ -42,7 +41,7 @@ module.exports.handleHop = async function handleHop ({
// Get the connection to the destination (stop) peer
const destinationPeer = new PeerId(request.dstPeer.id)
const destinationConnection = circuit._registrar.getConnection(new PeerInfo(destinationPeer))
const destinationConnection = circuit._registrar.getConnection(destinationPeer)
if (!destinationConnection && !circuit._options.hop.active) {
log('HOP request received but we are not connected to the destination peer')
return streamHandler.end({

View File

@ -3,7 +3,6 @@
const mafmt = require('mafmt')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const withIs = require('class-is')
const { CircuitRelay: CircuitPB } = require('./protocol')
@ -32,7 +31,8 @@ class Circuit {
this._registrar = libp2p.registrar
this._upgrader = upgrader
this._options = libp2p._config.relay
this.peerInfo = libp2p.peerInfo
this.addresses = libp2p.addresses
this.peerId = libp2p.peerId
this._registrar.handle(multicodec, this._onProtocol.bind(this))
}
@ -107,7 +107,7 @@ class Circuit {
const destinationPeer = PeerId.createFromCID(destinationAddr.getPeerId())
let disconnectOnFailure = false
let relayConnection = this._registrar.getConnection(new PeerInfo(relayPeer))
let relayConnection = this._registrar.getConnection(relayPeer)
if (!relayConnection) {
relayConnection = await this._dialer.connectToPeer(relayAddr, options)
disconnectOnFailure = true
@ -120,8 +120,8 @@ class Circuit {
request: {
type: CircuitPB.Type.HOP,
srcPeer: {
id: this.peerInfo.id.toBytes(),
addrs: this.peerInfo.multiaddrs.toArray().map(addr => addr.buffer)
id: this.peerId.toBytes(),
addrs: this.addresses.listen.map(addr => addr.buffer)
},
dstPeer: {
id: destinationPeer.toBytes(),
@ -130,7 +130,7 @@ class Circuit {
}
})
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerInfo.id.toB58String()}`)
const localAddr = relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toB58String()}`)
const maConn = toConnection({
stream: virtualConnection,
remoteAddr: ma,

View File

@ -4,6 +4,9 @@ const mergeOptions = require('merge-options')
const Constants = require('./constants')
const DefaultConfig = {
addresses: {
listen: []
},
connectionManager: {
minPeers: 25
},

View File

@ -40,7 +40,7 @@ class ConnectionManager {
constructor (libp2p, options) {
this._libp2p = libp2p
this._registrar = libp2p.registrar
this._peerId = libp2p.peerInfo.id.toB58String()
this._peerId = libp2p.peerId.toB58String()
this._options = mergeOptions.call({ ignoreUndefined: true }, defaultOptions, options)
if (this._options.maxConnections < this._options.minConnections) {
throw errcode(new Error('Connection Manager maxConnections must be greater than minConnections'), ERR_INVALID_PARAMETERS)

View File

@ -24,7 +24,7 @@ module.exports = (node) => {
* @param {object} [options]
* @param {number} [options.timeout] How long the query should run
* @param {number} [options.maxNumProviders] - maximum number of providers to find
* @returns {AsyncIterable<PeerInfo>}
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
*/
async * findProviders (key, options) {
if (!routers.length) {
@ -42,8 +42,8 @@ module.exports = (node) => {
})
)
for (const pInfo of result) {
yield pInfo
for (const peer of result) {
yield peer
}
},

View File

@ -4,12 +4,12 @@ const multiaddr = require('multiaddr')
const errCode = require('err-code')
const TimeoutController = require('timeout-abort-controller')
const anySignal = require('any-signal')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const debug = require('debug')
const log = debug('libp2p:dialer')
log.error = debug('libp2p:dialer:error')
const { DialRequest } = require('./dial-request')
const getPeer = require('../get-peer')
const { codes } = require('../errors')
const {
@ -58,18 +58,19 @@ class Dialer {
}
/**
* Connects to a given `PeerId` or `Multiaddr` by dialing all of its known addresses.
* Connects to a given `peer` by dialing all of its known addresses.
* The dial to the first address that is successfully able to upgrade a connection
* will be used.
*
* @param {PeerInfo|Multiaddr} peer The peer to dial
* @param {PeerId|Multiaddr|string} peer The peer to dial
* @param {object} [options]
* @param {AbortSignal} [options.signal] An AbortController signal
* @returns {Promise<Connection>}
*/
async connectToPeer (peer, options = {}) {
const dialTarget = this._createDialTarget(peer)
if (dialTarget.addrs.length === 0) {
if (!dialTarget.addrs.length) {
throw errCode(new Error('The dial request has no addresses'), codes.ERR_NO_VALID_ADDRESSES)
}
const pendingDial = this._pendingDials.get(dialTarget.id) || this._createPendingDial(dialTarget, options)
@ -99,21 +100,29 @@ class Dialer {
/**
* Creates a DialTarget. The DialTarget is used to create and track
* the DialRequest to a given peer.
* If a multiaddr is received it should be the first address attempted.
* @private
* @param {PeerInfo|Multiaddr} peer A PeerId or Multiaddr
* @param {PeerId|Multiaddr|string} peer A PeerId or Multiaddr
* @returns {DialTarget}
*/
_createDialTarget (peer) {
const dialable = Dialer.getDialable(peer)
if (multiaddr.isMultiaddr(dialable)) {
return {
id: dialable.toString(),
addrs: [dialable]
}
const { id, multiaddrs } = getPeer(peer)
if (multiaddrs) {
this.peerStore.addressBook.add(id, multiaddrs)
}
const addrs = this.peerStore.multiaddrsForPeer(dialable)
let addrs = this.peerStore.addressBook.getMultiaddrsForPeer(id)
// If received a multiaddr to dial, it should be the first to use
// But, if we know other multiaddrs for the peer, we should try them too.
if (multiaddr.isMultiaddr(peer)) {
addrs = addrs.filter((addr) => !peer.equals(addr))
addrs.unshift(peer)
}
return {
id: dialable.id.toB58String(),
id: id.toB58String(),
addrs
}
}
@ -178,36 +187,6 @@ class Dialer {
log('token %d released', token)
this.tokens.push(token)
}
/**
* Converts the given `peer` into a `PeerInfo` or `Multiaddr`.
* @static
* @param {PeerInfo|PeerId|Multiaddr|string} peer
* @returns {PeerInfo|Multiaddr}
*/
static getDialable (peer) {
if (PeerInfo.isPeerInfo(peer)) return peer
if (typeof peer === 'string') {
peer = multiaddr(peer)
}
let addr
if (multiaddr.isMultiaddr(peer)) {
addr = peer
try {
peer = PeerId.createFromCID(peer.getPeerId())
} catch (err) {
throw errCode(new Error('The multiaddr did not contain a valid peer id'), codes.ERR_INVALID_PEER)
}
}
if (PeerId.isPeerId(peer)) {
peer = new PeerInfo(peer)
}
addr && peer.multiaddrs.add(addr)
return peer
}
}
module.exports = Dialer

View File

@ -26,5 +26,6 @@ exports.codes = {
ERR_TIMEOUT: 'ERR_TIMEOUT',
ERR_TRANSPORT_UNAVAILABLE: 'ERR_TRANSPORT_UNAVAILABLE',
ERR_TRANSPORT_DIAL_FAILED: 'ERR_TRANSPORT_DIAL_FAILED',
ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL'
ERR_UNSUPPORTED_PROTOCOL: 'ERR_UNSUPPORTED_PROTOCOL',
ERR_INVALID_MULTIADDR: 'ERR_INVALID_MULTIADDR'
}

View File

@ -1,73 +0,0 @@
'use strict'
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const multiaddr = require('multiaddr')
const errCode = require('err-code')
/**
* Converts the given `peer` to a `PeerInfo` instance.
* The `PeerStore` will be checked for the resulting peer, and
* the peer will be updated in the `PeerStore`.
*
* @param {PeerInfo|PeerId|Multiaddr|string} peer
* @param {PeerStore} peerStore
* @returns {PeerInfo}
*/
function getPeerInfo (peer, peerStore) {
if (typeof peer === 'string') {
peer = multiaddr(peer)
}
let addr
if (multiaddr.isMultiaddr(peer)) {
addr = peer
try {
peer = PeerId.createFromB58String(peer.getPeerId())
} catch (err) {
throw errCode(
new Error(`${peer} is not a valid peer type`),
'ERR_INVALID_MULTIADDR'
)
}
}
if (PeerId.isPeerId(peer)) {
peer = new PeerInfo(peer)
}
addr && peer.multiaddrs.add(addr)
return peerStore ? peerStore.put(peer) : peer
}
/**
* If `getPeerInfo` does not return a peer with multiaddrs,
* the `libp2p` PeerRouter will be used to attempt to find the peer.
*
* @async
* @param {PeerInfo|PeerId|Multiaddr|string} peer
* @param {Libp2p} libp2p
* @returns {Promise<PeerInfo>}
*/
function getPeerInfoRemote (peer, libp2p) {
let peerInfo
try {
peerInfo = getPeerInfo(peer, libp2p.peerStore)
} catch (err) {
throw errCode(err, 'ERR_INVALID_PEER_TYPE')
}
// If we don't have an address for the peer, attempt to find it
if (peerInfo.multiaddrs.size < 1) {
return libp2p.peerRouting.findPeer(peerInfo.id)
}
return peerInfo
}
module.exports = {
getPeerInfoRemote,
getPeerInfo
}

40
src/get-peer.js Normal file
View File

@ -0,0 +1,40 @@
'use strict'
const PeerId = require('peer-id')
const multiaddr = require('multiaddr')
const errCode = require('err-code')
const { codes } = require('./errors')
/**
* Converts the given `peer` to a `Peer` object.
* If a multiaddr is received, the addressBook is updated.
* @param {PeerId|Multiaddr|string} peer
* @param {PeerStore} peerStore
* @returns {{ id: PeerId, multiaddrs: Array<Multiaddr> }}
*/
function getPeer (peer) {
if (typeof peer === 'string') {
peer = multiaddr(peer)
}
let addr
if (multiaddr.isMultiaddr(peer)) {
addr = peer
try {
peer = PeerId.createFromB58String(peer.getPeerId())
} catch (err) {
throw errCode(
new Error(`${peer} is not a valid peer type`),
codes.ERR_INVALID_MULTIADDR
)
}
}
return {
id: peer,
multiaddrs: addr ? [addr] : undefined
}
}
module.exports = getPeer

View File

@ -6,7 +6,6 @@ const lp = require('it-length-prefixed')
const pipe = require('it-pipe')
const { collect, take, consume } = require('streaming-iterables')
const PeerInfo = require('peer-info')
const PeerId = require('peer-id')
const multiaddr = require('multiaddr')
const { toBuffer } = require('it-buffer')
@ -27,39 +26,6 @@ const errCode = require('err-code')
const { codes } = require('../errors')
class IdentifyService {
/**
* Replaces the multiaddrs on the given `peerInfo`,
* with the provided `multiaddrs`
* @param {PeerInfo} peerInfo
* @param {Array<Multiaddr>|Array<Buffer>} multiaddrs
*/
static updatePeerAddresses (peerInfo, multiaddrs) {
if (multiaddrs && multiaddrs.length > 0) {
peerInfo.multiaddrs.clear()
multiaddrs.forEach(ma => {
try {
peerInfo.multiaddrs.add(ma)
} catch (err) {
log.error('could not add multiaddr', err)
}
})
}
}
/**
* Replaces the protocols on the given `peerInfo`,
* with the provided `protocols`
* @static
* @param {PeerInfo} peerInfo
* @param {Array<string>} protocols
*/
static updatePeerProtocols (peerInfo, protocols) {
if (protocols && protocols.length > 0) {
peerInfo.protocols.clear()
protocols.forEach(proto => peerInfo.protocols.add(proto))
}
}
/**
* Takes the `addr` and converts it to a Multiaddr if possible
* @param {Buffer|String} addr
@ -81,7 +47,8 @@ class IdentifyService {
* @param {object} options
* @param {Registrar} options.registrar
* @param {Map<string, handler>} options.protocols A reference to the protocols we support
* @param {PeerInfo} options.peerInfo The peer running the identify service
* @param {PeerId} options.peerId The peer running the identify service
* @param {{ listen: Array<Multiaddr>}} options.addresses The peer addresses
*/
constructor (options) {
/**
@ -89,9 +56,11 @@ class IdentifyService {
*/
this.registrar = options.registrar
/**
* @property {PeerInfo}
* @property {PeerId}
*/
this.peerInfo = options.peerInfo
this.peerId = options.peerId
this.addresses = options.addresses || {}
this._protocols = options.protocols
@ -110,7 +79,7 @@ class IdentifyService {
await pipe(
[{
listenAddrs: this.peerInfo.multiaddrs.toArray().map((ma) => ma.buffer),
listenAddrs: this.addresses.listen.map((ma) => ma.buffer),
protocols: Array.from(this._protocols.keys())
}],
pb.encode(Message),
@ -134,7 +103,7 @@ class IdentifyService {
const connections = []
let connection
for (const peer of peerStore.peers.values()) {
if (peer.protocols.has(MULTICODEC_IDENTIFY_PUSH) && (connection = this.registrar.getConnection(peer))) {
if (peer.protocols.includes(MULTICODEC_IDENTIFY_PUSH) && (connection = this.registrar.getConnection(peer.id))) {
connections.push(connection)
}
}
@ -181,7 +150,7 @@ class IdentifyService {
} = message
const id = await PeerId.createFromPubKey(publicKey)
const peerInfo = new PeerInfo(id)
if (connection.remotePeer.toB58String() !== id.toB58String()) {
throw errCode(new Error('identified peer does not match the expected peer'), codes.ERR_INVALID_PEER)
}
@ -189,11 +158,10 @@ class IdentifyService {
// Get the observedAddr if there is one
observedAddr = IdentifyService.getCleanMultiaddr(observedAddr)
// Copy the listenAddrs and protocols
IdentifyService.updatePeerAddresses(peerInfo, listenAddrs)
IdentifyService.updatePeerProtocols(peerInfo, protocols)
// Update peers data in PeerStore
this.registrar.peerStore.addressBook.set(id, listenAddrs.map((addr) => multiaddr(addr)))
this.registrar.peerStore.protoBook.set(id, protocols)
this.registrar.peerStore.replace(peerInfo)
// TODO: Track our observed address so that we can score it
log('received observed address of %s', observedAddr)
}
@ -228,15 +196,15 @@ class IdentifyService {
*/
_handleIdentify ({ connection, stream }) {
let publicKey = Buffer.alloc(0)
if (this.peerInfo.id.pubKey) {
publicKey = this.peerInfo.id.pubKey.bytes
if (this.peerId.pubKey) {
publicKey = this.peerId.pubKey.bytes
}
const message = Message.encode({
protocolVersion: PROTOCOL_VERSION,
agentVersion: AGENT_VERSION,
publicKey,
listenAddrs: this.peerInfo.multiaddrs.toArray().map((ma) => ma.buffer),
listenAddrs: this.addresses.listen.map((ma) => ma.buffer),
observedAddr: connection.remoteAddr.buffer,
protocols: Array.from(this._protocols.keys())
})
@ -273,20 +241,16 @@ class IdentifyService {
return log.error('received invalid message', err)
}
// Update the listen addresses
const peerInfo = new PeerInfo(connection.remotePeer)
// Update peers data in PeerStore
const id = connection.remotePeer
try {
IdentifyService.updatePeerAddresses(peerInfo, message.listenAddrs)
this.registrar.peerStore.addressBook.set(id, message.listenAddrs.map((addr) => multiaddr(addr)))
} catch (err) {
return log.error('received invalid listen addrs', err)
}
// Update the protocols
IdentifyService.updatePeerProtocols(peerInfo, message.protocols)
// Update the peer in the PeerStore
this.registrar.peerStore.replace(peerInfo)
this.registrar.peerStore.protoBook.set(id, message.protocols)
}
}

View File

@ -5,12 +5,12 @@ const debug = require('debug')
const log = debug('libp2p')
log.error = debug('libp2p:error')
const PeerInfo = require('peer-info')
const PeerId = require('peer-id')
const peerRouting = require('./peer-routing')
const contentRouting = require('./content-routing')
const pubsub = require('./pubsub')
const { getPeerInfo } = require('./get-peer-info')
const getPeer = require('./get-peer')
const { validate: validateConfig } = require('./config')
const { codes } = require('./errors')
@ -42,9 +42,12 @@ class Libp2p extends EventEmitter {
this._options = validateConfig(_options)
this.datastore = this._options.datastore
this.peerInfo = this._options.peerInfo
this.peerId = this._options.peerId
this.peerStore = new PeerStore()
// Addresses {listen, announce, noAnnounce}
this.addresses = this._options.addresses
this._modules = this._options.modules
this._config = this._options.config
this._transport = [] // Transport instances/references
@ -56,29 +59,31 @@ class Libp2p extends EventEmitter {
// Setup the Upgrader
this.upgrader = new Upgrader({
localPeer: this.peerInfo.id,
localPeer: this.peerId,
metrics: this.metrics,
onConnection: (connection) => {
const peerInfo = this.peerStore.put(new PeerInfo(connection.remotePeer), { silent: true })
this.registrar.onConnect(peerInfo, connection)
const peerId = connection.remotePeer
this.registrar.onConnect(peerId, connection)
this.connectionManager.onConnect(connection)
this.emit('peer:connect', peerInfo)
this.emit('peer:connect', peerId)
// Run identify for every connection
if (this.identifyService) {
this.identifyService.identify(connection, connection.remotePeer)
this.identifyService.identify(connection, peerId)
.catch(log.error)
}
},
onConnectionEnd: (connection) => {
const peerInfo = Dialer.getDialable(connection.remotePeer)
this.registrar.onDisconnect(peerInfo, connection)
const peerId = connection.remotePeer
this.registrar.onDisconnect(peerId, connection)
this.connectionManager.onDisconnect(connection)
// If there are no connections to the peer, disconnect
if (!this.registrar.getConnection(peerInfo)) {
this.emit('peer:disconnect', peerInfo)
this.metrics && this.metrics.onPeerDisconnected(peerInfo.id)
if (!this.registrar.getConnection(peerId)) {
this.emit('peer:disconnect', peerId)
this.metrics && this.metrics.onPeerDisconnected(peerId)
}
}
})
@ -133,7 +138,8 @@ class Libp2p extends EventEmitter {
// Add the identify service since we can multiplex
this.identifyService = new IdentifyService({
registrar: this.registrar,
peerInfo: this.peerInfo,
peerId: this.peerId,
addresses: this.addresses,
protocols: this.upgrader.protocols
})
this.handle(Object.values(IDENTIFY_PROTOCOLS), this.identifyService.handleMessage)
@ -151,7 +157,7 @@ class Libp2p extends EventEmitter {
const DHT = this._modules.dht
this._dht = new DHT({
dialer: this.dialer,
peerInfo: this.peerInfo,
peerId: this.peerId,
peerStore: this.peerStore,
registrar: this.registrar,
datastore: this.datastore,
@ -261,10 +267,9 @@ class Libp2p extends EventEmitter {
}
/**
* Dials to the provided peer. If successful, the `PeerInfo` of the
* Dials to the provided peer. If successful, the known `Peer` data of the
* peer will be added to the nodes `peerStore`
*
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to dial
* @param {PeerId|Multiaddr|string} peer The peer to dial
* @param {object} options
* @param {AbortSignal} [options.signal]
* @returns {Promise<Connection>}
@ -275,26 +280,23 @@ class Libp2p extends EventEmitter {
/**
* Dials to the provided peer and handshakes with the given protocol.
* If successful, the `PeerInfo` of the peer will be added to the nodes `peerStore`,
* and the `Connection` will be sent in the callback
*
* If successful, the known `Peer` data of the peer will be added to the nodes `peerStore`,
* and the `Connection` will be returned
* @async
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to dial
* @param {PeerId|Multiaddr|string} peer The peer to dial
* @param {string[]|string} protocols
* @param {object} options
* @param {AbortSignal} [options.signal]
* @returns {Promise<Connection|*>}
*/
async dialProtocol (peer, protocols, options) {
const dialable = Dialer.getDialable(peer)
let connection
if (PeerInfo.isPeerInfo(dialable)) {
this.peerStore.put(dialable, { silent: true })
connection = this.registrar.getConnection(dialable)
}
const { id, multiaddrs } = getPeer(peer, this.peerStore)
let connection = this.registrar.getConnection(id)
if (!connection) {
connection = await this.dialer.connectToPeer(dialable, options)
connection = await this.dialer.connectToPeer(peer, options)
} else if (multiaddrs) {
this.peerStore.addressBook.add(id, multiaddrs)
}
// If a protocol was provided, create a new stream
@ -307,28 +309,28 @@ class Libp2p extends EventEmitter {
/**
* Disconnects all connections to the given `peer`
*
* @param {PeerInfo|PeerId|multiaddr|string} peer the peer to close connections to
* @param {PeerId|multiaddr|string} peer the peer to close connections to
* @returns {Promise<void>}
*/
hangUp (peer) {
const peerInfo = getPeerInfo(peer, this.peerStore)
const { id } = getPeer(peer)
return Promise.all(
this.registrar.connections.get(peerInfo.id.toB58String()).map(connection => {
this.registrar.connections.get(id.toB58String()).map(connection => {
return connection.close()
})
)
}
/**
* Pings the given peer
* @param {PeerInfo|PeerId|Multiaddr|string} peer The peer to ping
* Pings the given peer in order to obtain the operation latency.
* @param {PeerId|Multiaddr|string} peer The peer to ping
* @returns {Promise<number>}
*/
async ping (peer) {
const peerInfo = await getPeerInfo(peer, this.peerStore)
ping (peer) {
const { id } = getPeer(peer)
return ping(this, peerInfo)
return ping(this, id)
}
/**
@ -366,17 +368,14 @@ class Libp2p extends EventEmitter {
}
async _onStarting () {
// Listen on the addresses supplied in the peerInfo
const multiaddrs = this.peerInfo.multiaddrs.toArray()
// Listen on the addresses provided
const multiaddrs = this.addresses.listen
await this.transportManager.listen(multiaddrs)
// The addresses may change once the listener starts
// eg /ip4/0.0.0.0/tcp/0 => /ip4/192.168.1.0/tcp/58751
this.peerInfo.multiaddrs.clear()
for (const ma of this.transportManager.getAddrs()) {
this.peerInfo.multiaddrs.add(ma)
}
this.addresses.listen = this.transportManager.getAddrs()
if (this._config.pubsub.enabled) {
this.pubsub && this.pubsub.start()
@ -404,18 +403,18 @@ class Libp2p extends EventEmitter {
this.connectionManager.start()
this.peerStore.on('peer', peerInfo => {
this.emit('peer:discovery', peerInfo)
this._maybeConnect(peerInfo)
this.peerStore.on('peer', peerId => {
this.emit('peer:discovery', peerId)
this._maybeConnect(peerId)
})
// Peer discovery
await this._setupPeerDiscovery()
// Once we start, emit and dial any peers we may have already discovered
for (const peerInfo of this.peerStore.peers.values()) {
this.emit('peer:discovery', peerInfo)
this._maybeConnect(peerInfo)
for (const peer of this.peerStore.peers.values()) {
this.emit('peer:discovery', peer.id)
this._maybeConnect(peer.id)
}
}
@ -423,31 +422,33 @@ class Libp2p extends EventEmitter {
* Called whenever peer discovery services emit `peer` events.
* Known peers may be emitted.
* @private
* @param {PeerInfo} peerInfo
* @param {PeerDara} peer
*/
_onDiscoveryPeer (peerInfo) {
if (peerInfo.id.toB58String() === this.peerInfo.id.toB58String()) {
_onDiscoveryPeer (peer) {
if (peer.id.toB58String() === this.peerId.toB58String()) {
log.error(new Error(codes.ERR_DISCOVERED_SELF))
return
}
this.peerStore.put(peerInfo)
peer.multiaddrs && this.peerStore.addressBook.add(peer.id, peer.multiaddrs)
peer.protocols && this.peerStore.protoBook.set(peer.id, peer.protocols)
}
/**
* Will dial to the given `peerInfo` if the current number of
* Will dial to the given `peerId` if the current number of
* connected peers is less than the configured `ConnectionManager`
* minPeers.
* @private
* @param {PeerInfo} peerInfo
* @param {PeerId} peerId
*/
async _maybeConnect (peerInfo) {
async _maybeConnect (peerId) {
// If auto dialing is on and we have no connection to the peer, check if we should dial
if (this._config.peerDiscovery.autoDial === true && !this.registrar.getConnection(peerInfo)) {
if (this._config.peerDiscovery.autoDial === true && !this.registrar.getConnection(peerId)) {
const minPeers = this._options.connectionManager.minPeers || 0
if (minPeers > this.connectionManager._connections.size) {
log('connecting to discovered peer %s', peerInfo.id.toB58String())
log('connecting to discovered peer %s', peerId.toB58String())
try {
await this.dialer.connectToPeer(peerInfo)
await this.dialer.connectToPeer(peerId)
} catch (err) {
log.error('could not connect to discovered peer', err)
}
@ -478,7 +479,11 @@ class Libp2p extends EventEmitter {
let discoveryService
if (typeof DiscoveryService === 'function') {
discoveryService = new DiscoveryService(Object.assign({}, config, { peerInfo: this.peerInfo, libp2p: this }))
discoveryService = new DiscoveryService(Object.assign({}, config, {
peerId: this.peerId,
multiaddrs: this.addresses.listen,
libp2p: this
}))
} else {
discoveryService = DiscoveryService
}
@ -505,19 +510,19 @@ class Libp2p extends EventEmitter {
}
/**
* Like `new Libp2p(options)` except it will create a `PeerInfo`
* Like `new Libp2p(options)` except it will create a `PeerId`
* instance if one is not provided in options.
* @param {object} options Libp2p configuration options
* @returns {Libp2p}
*/
Libp2p.create = async function create (options = {}) {
if (options.peerInfo) {
if (options.peerId) {
return new Libp2p(options)
}
const peerInfo = await PeerInfo.create()
const peerId = await PeerId.create()
options.peerInfo = peerInfo
options.peerId = peerId
return new Libp2p(options)
}

View File

@ -18,7 +18,7 @@ module.exports = (node) => {
* @param {String} id The id of the peer to find
* @param {object} [options]
* @param {number} [options.timeout] How long the query should run
* @returns {Promise<PeerInfo>}
* @returns {Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>}
*/
findPeer: async (id, options) => { // eslint-disable-line require-await
if (!routers.length) {

View File

@ -1,3 +1,89 @@
# Peerstore
# PeerStore
WIP
Libp2p's PeerStore is responsible for keeping an updated register with the relevant information of the known peers. It should be the single source of truth for all peer data, where a subsystem can learn about peers' data and where someone can listen for updates. The PeerStore comprises four main components: `addressBook`, `keyBook`, `protocolBook` and `metadataBook`.
The PeerStore manages the high level operations on its inner books. Moreover, the PeerStore should be responsible for notifying interested parties of relevant events, through its Event Emitter.
## Data gathering
Several libp2p subsystems will perform operations, which will gather relevant information about peers. Some operations might not have this as an end goal, but can also gather important data.
In a libp2p node's life, it will discover peers through its discovery protocols. In a typical discovery protocol, addresses of the peer are discovered along with its peer id. Once this happens, the PeerStore should collect this information for future (or immediate) usage by other subsystems. When the information is stored, the PeerStore should inform interested parties of the peer discovered (`peer` event).
Taking into account a different scenario, a peer might perform/receive a dial request to/from a unkwown peer. In such a scenario, the PeerStore must store the peer's multiaddr once a connection is established.
After a connection is established with a peer, the Identify protocol will run automatically. A stream is created and peers exchange their information (Multiaddrs, running protocols and their public key). Once this information is obtained, it should be added to the PeerStore. In this specific case, as we are speaking to the source of truth, we should ensure the PeerStore is prioritizing these records. If the recorded `multiaddrs` or `protocols` have changed, interested parties must be informed via the `change:multiaddrs` or `change:protocols` events respectively.
In the background, the Identify Service is also waiting for protocol change notifications of peers via the IdentifyPush protocol. Peers may leverage the `identify-push` message to communicate protocol changes to all connected peers, so that their PeerStore can be updated with the updated protocols. As the `identify-push` also sends complete and updated information, the data in the PeerStore can be replaced.
(To consider: Should we not replace until we get to multiaddr confidence? we might loose true information as we will talk with older nodes on the network.)
While it is currently not supported in js-libp2p, future iterations may also support the [IdentifyDelta protocol](https://github.com/libp2p/specs/pull/176).
It is also possible to gather relevant information for peers from other protocols / subsystems. For instance, in `DHT` operations, nodes can exchange peer data as part of the `DHT` operation. In this case, we can learn additional information about a peer we already know. In this scenario the PeerStore should not replace the existing data it has, just add it.
## Data Consumption
When the PeerStore data is updated, this information might be important for different parties.
Every time a peer needs to dial another peer, it is essential that it knows the multiaddrs used by the peer, in order to perform a successful dial to it. The same is true for pinging a peer. While the `AddressBook` is going to keep its data updated, it will also emit `change:multiaddrs` events so that subsystems/users interested in knowing these changes can be notified instead of polling the `AddressBook`.
Everytime a peer starts/stops supporting a protocol, libp2p subsystems or users might need to act accordingly. `js-libp2p` registrar orchestrates known peers, established connections and protocol topologies. This way, once a protocol is supported for a peer, the topology of that protocol should be informed that a new peer may be used and the subsystem can decide if it should open a new stream with that peer or not. For these situations, the `ProtoBook` will emit `change:protocols` events whenever supported protocols of a peer change.
## PeerStore implementation
The PeerStore wraps four main components: `addressBook`, `keyBook`, `protocolBook` and `metadataBook`. Moreover, it provides a high level API for those components, as well as data events.
### Components
#### Address Book
The `addressBook` keeps the known multiaddrs of a peer. The multiaddrs of each peer may change over time and the Address Book must account for this.
`Map<string, Address>`
A `peerId.toString()` identifier mapping to a `Address` object, which should have the following structure:
```js
{
multiaddr: <Multiaddr>
}
```
#### Key Book
The `keyBook` tracks the keys of the peers.
**Not Yet Implemented**
#### Protocol Book
The `protoBook` holds the identifiers of the protocols supported by each peer. The protocols supported by each peer are dynamic and will change over time.
`Map<string, Set<string>>`
A `peerId.toString()` identifier mapping to a `Set` of protocol identifier strings.
#### Metadata Book
**Not Yet Implemented**
### API
For the complete API documentation, you should check the [API.md](../../doc/API.md).
Access to its underlying books:
- `peerStore.protoBook.*`
- `peerStore.addressBook.*`
### Events
- `peer` - emitted when a new peer is added.
- `change:multiaadrs` - emitted when a known peer has a different set of multiaddrs.
- `change:protocols` - emitted when a known peer supports a different set of protocols.
## Future Considerations
- If multiaddr TTLs are added, the PeerStore may schedule jobs to delete all addresses that exceed the TTL to prevent AddressBook bloating
- Further API methods will probably need to be added in the context of multiaddr validity and confidence.

View File

@ -0,0 +1,203 @@
'use strict'
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-store:address-book')
log.error = debug('libp2p:peer-store:address-book:error')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const Book = require('./book')
const {
ERR_INVALID_PARAMETERS
} = require('../errors')
/**
* The AddressBook is responsible for keeping the known multiaddrs
* of a peer.
*/
class AddressBook extends Book {
/**
* Address object
* @typedef {Object} Address
* @property {Multiaddr} multiaddr peer multiaddr.
*/
/**
* @constructor
* @param {PeerStore} peerStore
*/
constructor (peerStore) {
/**
* PeerStore Event emitter, used by the AddressBook to emit:
* "peer" - emitted when a peer is discovered by the node.
* "change:multiaddrs" - emitted when the known multiaddrs of a peer change.
*/
super(peerStore, 'change:multiaddrs', 'multiaddrs')
/**
* Map known peers to their known Addresses.
* @type {Map<string, Array<Address>>}
*/
this.data = new Map()
}
/**
* Set known multiaddrs of a provided peer.
* @override
* @param {PeerId} peerId
* @param {Array<Multiaddr>} multiaddrs
* @returns {AddressBook}
*/
set (peerId, multiaddrs) {
if (!PeerId.isPeerId(peerId)) {
log.error('peerId must be an instance of peer-id to store data')
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const addresses = this._toAddresses(multiaddrs)
const id = peerId.toB58String()
const rec = this.data.get(id)
// Not replace multiaddrs
if (!addresses.length) {
return this
}
// Already knows the peer
if (rec && rec.length === addresses.length) {
const intersection = rec.filter((mi) => addresses.some((newMi) => mi.multiaddr.equals(newMi.multiaddr)))
// Are new addresses equal to the old ones?
// If yes, no changes needed!
if (intersection.length === rec.length) {
log(`the addresses provided to store are equal to the already stored for ${id}`)
return this
}
}
this.data.set(id, addresses)
this._setPeerId(peerId)
log(`stored provided multiaddrs for ${id}`)
// Notify the existance of a new peer
if (!rec) {
this._ps.emit('peer', peerId)
}
this._ps.emit('change:multiaddrs', {
peerId,
multiaddrs: addresses.map((mi) => mi.multiaddr)
})
return this
}
/**
* Add known addresses of a provided peer.
* If the peer is not known, it is set with the given addresses.
* @override
* @param {PeerId} peerId
* @param {Array<Multiaddr>} multiaddrs
* @returns {AddressBook}
*/
add (peerId, multiaddrs) {
if (!PeerId.isPeerId(peerId)) {
log.error('peerId must be an instance of peer-id to store data')
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const addresses = this._toAddresses(multiaddrs)
const id = peerId.toB58String()
const rec = this.data.get(id)
// Add recorded uniquely to the new array (Union)
rec && rec.forEach((mi) => {
if (!addresses.find(r => r.multiaddr.equals(mi.multiaddr))) {
addresses.push(mi)
}
})
// If the recorded length is equal to the new after the unique union
// The content is the same, no need to update.
if (rec && rec.length === addresses.length) {
log(`the addresses provided to store are already stored for ${id}`)
return this
}
this._setPeerId(peerId)
this.data.set(id, addresses)
log(`added provided multiaddrs for ${id}`)
this._ps.emit('change:multiaddrs', {
peerId,
multiaddrs: addresses.map((mi) => mi.multiaddr)
})
// Notify the existance of a new peer
if (!rec) {
this._ps.emit('peer', peerId)
}
return this
}
/**
* Transforms received multiaddrs into Address.
* @param {Array<Multiaddr>} multiaddrs
* @returns {Array<Address>}
*/
_toAddresses (multiaddrs) {
if (!multiaddrs) {
log.error('multiaddrs must be provided to store data')
throw errcode(new Error('multiaddrs must be provided'), ERR_INVALID_PARAMETERS)
}
// create Address for each address
const addresses = []
multiaddrs.forEach((addr) => {
if (!multiaddr.isMultiaddr(addr)) {
log.error(`multiaddr ${addr} must be an instance of multiaddr`)
throw errcode(new Error(`multiaddr ${addr} must be an instance of multiaddr`), ERR_INVALID_PARAMETERS)
}
addresses.push({
multiaddr: addr
})
})
return addresses
}
/**
* Get the known multiaddrs for a given peer. All returned multiaddrs
* will include the encapsulated `PeerId` of the peer.
* @param {PeerId} peerId
* @returns {Array<Multiaddr>}
*/
getMultiaddrsForPeer (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const record = this.data.get(peerId.toB58String())
if (!record) {
return undefined
}
return record.map((address) => {
const multiaddr = address.multiaddr
const idString = multiaddr.getPeerId()
if (idString && idString === peerId.toB58String()) return multiaddr
return multiaddr.encapsulate(`/p2p/${peerId.toB58String()}`)
})
}
}
module.exports = AddressBook

88
src/peer-store/book.js Normal file
View File

@ -0,0 +1,88 @@
'use strict'
const errcode = require('err-code')
const PeerId = require('peer-id')
const {
ERR_INVALID_PARAMETERS
} = require('../errors')
/**
* The Book is the skeleton for the PeerStore books.
*/
class Book {
constructor (peerStore, eventName, eventProperty) {
this._ps = peerStore
this.eventName = eventName
this.eventProperty = eventProperty
/**
* Map known peers to their data.
* @type {Map<string, Array<Data>}
*/
this.data = new Map()
}
/**
* Set known data of a provided peer.
* @param {PeerId} peerId
* @param {Array<Data>|Data} data
*/
set (peerId, data) {
throw errcode(new Error('set must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED')
}
/**
* Add known data of a provided peer.
* @param {PeerId} peerId
* @param {Array<Data>|Data} data
*/
add (peerId, data) {
throw errcode(new Error('set must be implemented by the subclass'), 'ERR_NOT_IMPLEMENTED')
}
/**
* Get the known data of a provided peer.
* @param {PeerId} peerId
* @returns {Array<Data>}
*/
get (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const rec = this.data.get(peerId.toB58String())
return rec ? [...rec] : undefined
}
/**
* Deletes the provided peer from the book.
* @param {PeerId} peerId
* @returns {boolean}
*/
delete (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
if (!this.data.delete(peerId.toB58String())) {
return false
}
this._ps.emit(this.eventName, {
peerId,
[this.eventProperty]: []
})
return true
}
_setPeerId (peerId) {
if (!this._ps.peerIds.get(peerId)) {
this._ps.peerIds.set(peerId.toB58String(), peerId)
}
}
}
module.exports = Book

View File

@ -6,247 +6,118 @@ const log = debug('libp2p:peer-store')
log.error = debug('libp2p:peer-store:error')
const { EventEmitter } = require('events')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const AddressBook = require('./address-book')
const ProtoBook = require('./proto-book')
const {
ERR_INVALID_PARAMETERS
} = require('../errors')
/**
* Responsible for managing known peers, as well as their addresses and metadata
* @fires PeerStore#peer Emitted when a peer is connected to this node
* @fires PeerStore#change:protocols
* @fires PeerStore#change:multiaddrs
* Responsible for managing known peers, as well as their addresses, protocols and metadata.
* @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.
* @fires PeerStore#change:multiaddrs Emitted when a known peer has a different set of multiaddrs.
*/
class PeerStore extends EventEmitter {
/**
* Peer object
* @typedef {Object} Peer
* @property {PeerId} id peer's peer-id instance.
* @property {Array<Address>} addresses peer's addresses containing its multiaddrs and metadata.
* @property {Array<string>} protocols peer's supported protocols.
*/
constructor () {
super()
/**
* Map of peers
*
* @type {Map<string, PeerInfo>}
* AddressBook containing a map of peerIdStr to Address
*/
this.peers = new Map()
this.addressBook = new AddressBook(this)
// TODO: Track ourselves. We should split `peerInfo` up into its pieces so we get better
// control and observability. This will be the initial step for removing PeerInfo
// https://github.com/libp2p/go-libp2p-core/blob/master/peerstore/peerstore.go
// this.addressBook = new Map()
// this.protoBook = new Map()
/**
* ProtoBook containing a map of peerIdStr to supported protocols.
*/
this.protoBook = new ProtoBook(this)
/**
* TODO: this should only exist until we have the key-book
* Map known peers to their peer-id.
* @type {Map<string, Array<PeerId>}
*/
this.peerIds = new Map()
}
/**
* Stores the peerInfo of a new peer.
* If already exist, its info is updated. If `silent` is set to
* true, no 'peer' event will be emitted. This can be useful if you
* are already in the process of dialing the peer. The peer is technically
* known, but may not have been added to the PeerStore yet.
* @param {PeerInfo} peerInfo
* @param {object} [options]
* @param {boolean} [options.silent] (Default=false)
* @return {PeerInfo}
* Get all the stored information of every peer.
* @returns {Map<string, Peer>}
*/
put (peerInfo, options = { silent: false }) {
if (!PeerInfo.isPeerInfo(peerInfo)) {
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
}
get peers () {
const peersData = new Map()
let peer
// Already know the peer?
if (this.has(peerInfo.id)) {
peer = this.update(peerInfo)
} else {
peer = this.add(peerInfo)
// Emit the peer if silent = false
!options.silent && this.emit('peer', peerInfo)
}
return peer
}
/**
* Add a new peer to the store.
* @param {PeerInfo} peerInfo
* @return {PeerInfo}
*/
add (peerInfo) {
if (!PeerInfo.isPeerInfo(peerInfo)) {
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
}
// Create new instance and add values to it
const newPeerInfo = new PeerInfo(peerInfo.id)
peerInfo.multiaddrs.forEach((ma) => newPeerInfo.multiaddrs.add(ma))
peerInfo.protocols.forEach((p) => newPeerInfo.protocols.add(p))
const connectedMa = peerInfo.isConnected()
connectedMa && newPeerInfo.connect(connectedMa)
const peerProxy = new Proxy(newPeerInfo, {
set: (obj, prop, value) => {
if (prop === 'multiaddrs') {
this.emit('change:multiaddrs', {
peerInfo: obj,
multiaddrs: value.toArray()
})
} else if (prop === 'protocols') {
this.emit('change:protocols', {
peerInfo: obj,
protocols: Array.from(value)
})
}
return Reflect.set(...arguments)
}
})
this.peers.set(peerInfo.id.toB58String(), peerProxy)
return peerProxy
}
/**
* Updates an already known peer.
* @param {PeerInfo} peerInfo
* @return {PeerInfo}
*/
update (peerInfo) {
if (!PeerInfo.isPeerInfo(peerInfo)) {
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
}
const id = peerInfo.id.toB58String()
const recorded = this.peers.get(id)
// pass active connection state
const ma = peerInfo.isConnected()
if (ma) {
recorded.connect(ma)
}
// Verify new multiaddrs
// TODO: better track added and removed multiaddrs
const multiaddrsIntersection = [
...recorded.multiaddrs.toArray()
].filter((m) => peerInfo.multiaddrs.has(m))
if (multiaddrsIntersection.length !== peerInfo.multiaddrs.size ||
multiaddrsIntersection.length !== recorded.multiaddrs.size) {
for (const ma of peerInfo.multiaddrs.toArray()) {
recorded.multiaddrs.add(ma)
}
this.emit('change:multiaddrs', {
peerInfo: recorded,
multiaddrs: recorded.multiaddrs.toArray()
// AddressBook
for (const [idStr, addresses] of this.addressBook.data.entries()) {
const id = PeerId.createFromCID(idStr)
peersData.set(idStr, {
id,
addresses,
protocols: this.protoBook.get(id) || []
})
}
// Update protocols
// TODO: better track added and removed protocols
const protocolsIntersection = new Set(
[...recorded.protocols].filter((p) => peerInfo.protocols.has(p))
)
// ProtoBook
for (const [idStr, protocols] of this.protoBook.data.entries()) {
const pData = peersData.get(idStr)
if (protocolsIntersection.size !== peerInfo.protocols.size ||
protocolsIntersection.size !== recorded.protocols.size) {
for (const protocol of peerInfo.protocols) {
recorded.protocols.add(protocol)
if (!pData) {
peersData.set(idStr, {
id: PeerId.createFromCID(idStr),
addresses: [],
protocols: Array.from(protocols)
})
}
this.emit('change:protocols', {
peerInfo: recorded,
protocols: Array.from(recorded.protocols)
})
}
// Add the public key if missing
if (!recorded.id.pubKey && peerInfo.id.pubKey) {
recorded.id.pubKey = peerInfo.id.pubKey
}
return recorded
return peersData
}
/**
* Get the info to the given id.
* @param {PeerId|string} peerId b58str id
* @returns {PeerInfo}
*/
get (peerId) {
// TODO: deprecate this and just accept `PeerId` instances
if (PeerId.isPeerId(peerId)) {
peerId = peerId.toB58String()
}
return this.peers.get(peerId)
}
/**
* Has the info to the given id.
* @param {PeerId|string} peerId b58str id
* @returns {boolean}
*/
has (peerId) {
// TODO: deprecate this and just accept `PeerId` instances
if (PeerId.isPeerId(peerId)) {
peerId = peerId.toB58String()
}
return this.peers.has(peerId)
}
/**
* Removes the Peer with the matching `peerId` from the PeerStore
* @param {PeerId|string} peerId b58str id
* Delete the information of the given peer in every book.
* @param {PeerId} peerId
* @returns {boolean} true if found and removed
*/
remove (peerId) {
// TODO: deprecate this and just accept `PeerId` instances
if (PeerId.isPeerId(peerId)) {
peerId = peerId.toB58String()
}
return this.peers.delete(peerId)
delete (peerId) {
const addressesDeleted = this.addressBook.delete(peerId)
const protocolsDeleted = this.protoBook.delete(peerId)
return addressesDeleted || protocolsDeleted
}
/**
* Completely replaces the existing peers metadata with the given `peerInfo`
* @param {PeerInfo} peerInfo
* @returns {void}
* Get the stored information of a given peer.
* @param {PeerId} peerId
* @returns {Peer}
*/
replace (peerInfo) {
if (!PeerInfo.isPeerInfo(peerInfo)) {
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
get (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
this.remove(peerInfo.id.toB58String())
this.add(peerInfo)
const id = this.peerIds.get(peerId.toB58String())
const addresses = this.addressBook.get(peerId)
const protocols = this.protoBook.get(peerId)
// This should be cleaned up in PeerStore v2
this.emit('change:multiaddrs', {
peerInfo,
multiaddrs: peerInfo.multiaddrs.toArray()
})
this.emit('change:protocols', {
peerInfo,
protocols: Array.from(peerInfo.protocols)
})
}
if (!addresses && !protocols) {
return undefined
}
/**
* Returns the known multiaddrs for a given `PeerInfo`. All returned multiaddrs
* will include the encapsulated `PeerId` of the peer.
* @param {PeerInfo} peer
* @returns {Array<Multiaddr>}
*/
multiaddrsForPeer (peer) {
return this.put(peer, true).multiaddrs.toArray().map(addr => {
const idString = addr.getPeerId()
if (idString && idString === peer.id.toB58String()) return addr
return addr.encapsulate(`/p2p/${peer.id.toB58String()}`)
})
return {
id: id || peerId,
addresses: addresses || [],
protocols: protocols || []
}
}
}

View File

@ -0,0 +1,128 @@
'use strict'
const errcode = require('err-code')
const debug = require('debug')
const log = debug('libp2p:peer-store:proto-book')
log.error = debug('libp2p:peer-store:proto-book:error')
const PeerId = require('peer-id')
const Book = require('./book')
const {
ERR_INVALID_PARAMETERS
} = require('../errors')
/**
* The ProtoBook is responsible for keeping the known supported
* protocols of a peer.
* @fires ProtoBook#change:protocols
*/
class ProtoBook extends Book {
/**
* @constructor
* @param {PeerStore} peerStore
*/
constructor (peerStore) {
/**
* PeerStore Event emitter, used by the ProtoBook to emit:
* "change:protocols" - emitted when the known protocols of a peer change.
*/
super(peerStore, 'change:protocols', 'protocols')
/**
* Map known peers to their known protocols.
* @type {Map<string, Set<string>>}
*/
this.data = new Map()
}
/**
* Set known protocols of a provided peer.
* If the peer was not known before, it will be added.
* @override
* @param {PeerId} peerId
* @param {Array<string>} protocols
* @returns {ProtoBook}
*/
set (peerId, protocols) {
if (!PeerId.isPeerId(peerId)) {
log.error('peerId must be an instance of peer-id to store data')
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
if (!protocols) {
log.error('protocols must be provided to store data')
throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS)
}
const id = peerId.toB58String()
const recSet = this.data.get(id)
const newSet = new Set(protocols)
const isSetEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value))
// Already knows the peer and the recorded protocols are the same?
// If yes, no changes needed!
if (recSet && isSetEqual(recSet, newSet)) {
log(`the protocols provided to store are equal to the already stored for ${id}`)
return this
}
this.data.set(id, newSet)
this._setPeerId(peerId)
log(`stored provided protocols for ${id}`)
this._ps.emit('change:protocols', {
peerId,
protocols
})
return this
}
/**
* Adds known protocols of a provided peer.
* If the peer was not known before, it will be added.
* @override
* @param {PeerId} peerId
* @param {Array<string>} protocols
* @returns {ProtoBook}
*/
add (peerId, protocols) {
if (!PeerId.isPeerId(peerId)) {
log.error('peerId must be an instance of peer-id to store data')
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
if (!protocols) {
log.error('protocols must be provided to store data')
throw errcode(new Error('protocols must be provided'), ERR_INVALID_PARAMETERS)
}
const id = peerId.toB58String()
const recSet = this.data.get(id) || new Set()
const newSet = new Set([...recSet, ...protocols]) // Set Union
// Any new protocol added?
if (recSet.size === newSet.size) {
log(`the protocols provided to store are already stored for ${id}`)
return this
}
protocols = [...newSet]
this.data.set(id, newSet)
this._setPeerId(peerId)
log(`added provided protocols for ${id}`)
this._ps.emit('change:protocols', {
peerId,
protocols
})
return this
}
}
module.exports = ProtoBook

View File

@ -15,11 +15,11 @@ const { PROTOCOL, PING_LENGTH } = require('./constants')
/**
* Ping a given peer and wait for its response, getting the operation latency.
* @param {Libp2p} node
* @param {PeerInfo} peer
* @param {PeerId} peer
* @returns {Promise<Number>}
*/
async function ping (node, peer) {
log('dialing %s to %s', PROTOCOL, peer.id.toB58String())
log('dialing %s to %s', PROTOCOL, peer.toB58String())
const { stream } = await node.dialProtocol(peer, PROTOCOL)

View File

@ -4,7 +4,7 @@ const errCode = require('err-code')
const { messages, codes } = require('./errors')
module.exports = (node, Pubsub, config) => {
const pubsub = new Pubsub(node.peerInfo, node.registrar, config)
const pubsub = new Pubsub(node.peerId, node.registrar, config)
return {
/**

View File

@ -5,12 +5,13 @@ const errcode = require('err-code')
const log = debug('libp2p:peer-store')
log.error = debug('libp2p:peer-store:error')
const PeerId = require('peer-id')
const {
ERR_INVALID_PARAMETERS
} = require('./errors')
const Topology = require('libp2p-interfaces/src/topology')
const { Connection } = require('libp2p-interfaces/src/connection')
const PeerInfo = require('peer-info')
/**
* Responsible for notifying registered protocols of events in the network.
@ -22,6 +23,7 @@ class Registrar {
* @constructor
*/
constructor ({ peerStore }) {
// Used on topology to listen for protocol changes
this.peerStore = peerStore
/**
@ -69,20 +71,20 @@ class Registrar {
/**
* Add a new connected peer to the record
* TODO: this should live in the ConnectionManager
* @param {PeerInfo} peerInfo
* @param {PeerId} peerId
* @param {Connection} conn
* @returns {void}
*/
onConnect (peerInfo, conn) {
if (!PeerInfo.isPeerInfo(peerInfo)) {
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
onConnect (peerId, conn) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
if (!Connection.isConnection(conn)) {
throw errcode(new Error('conn must be an instance of interface-connection'), ERR_INVALID_PARAMETERS)
}
const id = peerInfo.id.toB58String()
const id = peerId.toB58String()
const storedConn = this.connections.get(id)
if (storedConn) {
@ -95,17 +97,17 @@ class Registrar {
/**
* Remove a disconnected peer from the record
* TODO: this should live in the ConnectionManager
* @param {PeerInfo} peerInfo
* @param {PeerId} peerId
* @param {Connection} connection
* @param {Error} [error]
* @returns {void}
*/
onDisconnect (peerInfo, connection, error) {
if (!PeerInfo.isPeerInfo(peerInfo)) {
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
onDisconnect (peerId, connection, error) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const id = peerInfo.id.toB58String()
const id = peerId.toB58String()
let storedConn = this.connections.get(id)
if (storedConn && storedConn.length > 1) {
@ -113,24 +115,24 @@ class Registrar {
this.connections.set(id, storedConn)
} else if (storedConn) {
for (const [, topology] of this.topologies) {
topology.disconnect(peerInfo, error)
topology.disconnect(peerId, error)
}
this.connections.delete(peerInfo.id.toB58String())
this.connections.delete(id)
}
}
/**
* Get a connection with a peer.
* @param {PeerInfo} peerInfo
* @param {PeerId} peerId
* @returns {Connection}
*/
getConnection (peerInfo) {
if (!PeerInfo.isPeerInfo(peerInfo)) {
throw errcode(new Error('peerInfo must be an instance of peer-info'), ERR_INVALID_PARAMETERS)
getConnection (peerId) {
if (!PeerId.isPeerId(peerId)) {
throw errcode(new Error('peerId must be an instance of peer-id'), ERR_INVALID_PARAMETERS)
}
const connections = this.connections.get(peerInfo.id.toB58String())
const connections = this.connections.get(peerId.toB58String())
// Return the first, open connection
if (connections) {
return connections.find(connection => connection.stat.status === 'open')

View File

@ -317,7 +317,7 @@ class Upgrader {
* Attempts to encrypt the incoming `connection` with the provided `cryptos`.
* @private
* @async
* @param {PeerId} localPeer The initiators PeerInfo
* @param {PeerId} localPeer The initiators PeerId
* @param {*} connection
* @param {Map<string, Crypto>} cryptos
* @returns {CryptoResult} An encrypted connection, remote peer `PeerId` and the protocol of the `Crypto` used
@ -346,7 +346,7 @@ class Upgrader {
* The first `Crypto` module to succeed will be used
* @private
* @async
* @param {PeerId} localPeer The initiators PeerInfo
* @param {PeerId} localPeer The initiators PeerId
* @param {*} connection
* @param {PeerId} remotePeerId
* @param {Map<string, Crypto>} cryptos

View File

@ -56,7 +56,7 @@ describe('content-routing', () => {
// Ring dial
await Promise.all(
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerInfo))
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerId))
)
})
@ -96,9 +96,9 @@ describe('content-routing', () => {
let delegate
beforeEach(async () => {
const [peerInfo] = await peerUtils.createPeerInfo({ fixture: false })
const [peerId] = await peerUtils.createPeerId({ fixture: false })
delegate = new DelegatedContentRouter(peerInfo.id, {
delegate = new DelegatedContentRouter(peerId, {
host: '0.0.0.0',
protocol: 'http',
port: 60197
@ -227,9 +227,9 @@ describe('content-routing', () => {
let delegate
beforeEach(async () => {
const [peerInfo] = await peerUtils.createPeerInfo({ fixture: false })
const [peerId] = await peerUtils.createPeerId({ fixture: false })
delegate = new DelegatedContentRouter(peerInfo.id, {
delegate = new DelegatedContentRouter(peerId, {
host: '0.0.0.0',
protocol: 'http',
port: 60197

View File

@ -32,11 +32,13 @@ describe('DHT subsystem is configurable', () => {
})
it('should start and stop by default once libp2p starts', async () => {
const [peerInfo] = await peerUtils.createPeerInfo(1)
peerInfo.multiaddrs.add(listenAddr)
const [peerId] = await peerUtils.createPeerId(1)
const customOptions = mergeOptions(subsystemOptions, {
peerInfo
peerId,
addresses: {
listen: [listenAddr]
}
})
libp2p = await create(customOptions)
@ -50,11 +52,13 @@ describe('DHT subsystem is configurable', () => {
})
it('should not start if disabled once libp2p starts', async () => {
const [peerInfo] = await peerUtils.createPeerInfo(1)
peerInfo.multiaddrs.add(listenAddr)
const [peerId] = await peerUtils.createPeerId(1)
const customOptions = mergeOptions(subsystemOptions, {
peerInfo,
peerId,
addresses: {
listen: [listenAddr]
},
config: {
dht: {
enabled: false
@ -70,11 +74,13 @@ describe('DHT subsystem is configurable', () => {
})
it('should allow a manual start', async () => {
const [peerInfo] = await peerUtils.createPeerInfo(1)
peerInfo.multiaddrs.add(listenAddr)
const [peerId] = await peerUtils.createPeerId(1)
const customOptions = mergeOptions(subsystemOptions, {
peerInfo,
peerId,
addresses: {
listen: [listenAddr]
},
config: {
dht: {
enabled: false

View File

@ -17,25 +17,28 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/8000')
const remoteListenAddr = multiaddr('/ip4/127.0.0.1/tcp/8001')
describe('DHT subsystem operates correctly', () => {
let peerInfo, remotePeerInfo
let peerId, remotePeerId
let libp2p, remoteLibp2p
let remAddr
beforeEach(async () => {
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
peerInfo.multiaddrs.add(listenAddr)
remotePeerInfo.multiaddrs.add(remoteListenAddr)
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
})
describe('dht started before connect', () => {
beforeEach(async () => {
libp2p = await create(mergeOptions(subsystemOptions, {
peerInfo
peerId,
addresses: {
listen: [listenAddr]
}
}))
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
peerInfo: remotePeerInfo
peerId: remotePeerId,
addresses: {
listen: [remoteListenAddr]
}
}))
await Promise.all([
@ -43,7 +46,8 @@ describe('DHT subsystem operates correctly', () => {
remoteLibp2p.start()
])
remAddr = libp2p.peerStore.multiaddrsForPeer(remotePeerInfo)[0]
libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr])
remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId)[0]
})
afterEach(() => Promise.all([
@ -67,15 +71,14 @@ describe('DHT subsystem operates correctly', () => {
const value = Buffer.from('world')
await libp2p.dialProtocol(remAddr, subsystemMulticodecs)
await Promise.all([
pWaitFor(() => libp2p._dht.routingTable.size === 1),
pWaitFor(() => remoteLibp2p._dht.routingTable.size === 1)
])
await libp2p.contentRouting.put(key, value)
const fetchedValue = await remoteLibp2p.contentRouting.get(key)
expect(fetchedValue).to.eql(value)
})
})
@ -83,11 +86,17 @@ describe('DHT subsystem operates correctly', () => {
describe('dht started after connect', () => {
beforeEach(async () => {
libp2p = await create(mergeOptions(subsystemOptions, {
peerInfo
peerId,
addresses: {
listen: [listenAddr]
}
}))
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
peerInfo: remotePeerInfo,
peerId: remotePeerId,
addresses: {
listen: [remoteListenAddr]
},
config: {
dht: {
enabled: false
@ -98,7 +107,8 @@ describe('DHT subsystem operates correctly', () => {
await libp2p.start()
await remoteLibp2p.start()
remAddr = libp2p.peerStore.multiaddrsForPeer(remotePeerInfo)[0]
libp2p.peerStore.addressBook.set(remotePeerId, [remoteListenAddr])
remAddr = libp2p.peerStore.addressBook.getMultiaddrsForPeer(remotePeerId)[0]
})
afterEach(() => Promise.all([

View File

@ -14,12 +14,11 @@ const peerUtils = require('../utils/creators/peer')
const listenAddr = multiaddr('/ip4/0.0.0.0/tcp/0')
describe('Listening', () => {
let peerInfo
let peerId
let libp2p
before(async () => {
[peerInfo] = await peerUtils.createPeerInfo()
peerInfo.multiaddrs.add(listenAddr)
[peerId] = await peerUtils.createPeerId()
})
after(async () => {
@ -28,7 +27,10 @@ describe('Listening', () => {
it('should replace wildcard host and port with actual host and port on startup', async () => {
libp2p = await create({
peerInfo,
peerId,
addresses: {
listen: [listenAddr]
},
modules: {
transport: [Transport]
}
@ -36,7 +38,7 @@ describe('Listening', () => {
await libp2p.start()
const addrs = libp2p.peerInfo.multiaddrs.toArray()
const addrs = libp2p.addresses.listen
// Should get something like:
// /ip4/127.0.0.1/tcp/50866

View File

@ -20,16 +20,19 @@ describe('ping', () => {
number: 2,
config: baseOptions
})
nodes[0].peerStore.addressBook.set(nodes[1].peerId, nodes[1].addresses.listen)
nodes[1].peerStore.addressBook.set(nodes[0].peerId, nodes[0].addresses.listen)
})
it('ping once from peer0 to peer1', async () => {
const latency = await nodes[0].ping(nodes[1].peerInfo)
const latency = await nodes[0].ping(nodes[1].peerId)
expect(latency).to.be.a('Number')
})
it('ping several times for getting an average', async () => {
const latencies = await pTimes(5, () => nodes[1].ping(nodes[0].peerInfo))
const latencies = await pTimes(5, () => nodes[1].ping(nodes[0].peerId))
const averageLatency = latencies.reduce((p, c) => p + c, 0) / latencies.length
expect(averageLatency).to.be.a('Number')
@ -66,7 +69,7 @@ describe('ping', () => {
)
})
const latency = await nodes[0].ping(nodes[1].peerInfo)
const latency = await nodes[0].ping(nodes[1].peerId)
expect(latency).to.be.a('Number')
})

View File

@ -11,7 +11,6 @@ const Muxer = require('libp2p-mplex')
const Crypto = require('libp2p-secio')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const delay = require('delay')
const pDefer = require('p-defer')
const pSettle = require('p-settle')
@ -31,7 +30,7 @@ const swarmKeyBuffer = Buffer.from(require('../fixtures/swarm.key'))
const mockUpgrader = require('../utils/mockUpgrader')
const createMockConnection = require('../utils/mockConnection')
const Peers = require('../fixtures/peers')
const { createPeerInfo } = require('../utils/creators/peer')
const { createPeerId } = require('../utils/creators/peer')
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
const unsupportedAddr = multiaddr('/ip4/127.0.0.1/tcp/9999/ws/p2p/QmckxVrJw1Yo8LqvmDJNUmdAsKtSbiKWmrXJFyKmUraBoN')
@ -80,9 +79,7 @@ describe('Dialing (direct, TCP)', () => {
it('should be able to connect to a remote node via its stringified multiaddr', async () => {
const dialer = new Dialer({ transportManager: localTM, peerStore })
const dialable = Dialer.getDialable(remoteAddr.toString())
const connection = await dialer.connectToPeer(dialable)
const connection = await dialer.connectToPeer(remoteAddr.toString())
expect(connection).to.exist()
await connection.close()
})
@ -95,21 +92,6 @@ describe('Dialing (direct, TCP)', () => {
.and.to.have.nested.property('._errors[0].code', ErrorCodes.ERR_TRANSPORT_UNAVAILABLE)
})
it('should be able to connect to a given peer info', async () => {
const dialer = new Dialer({
transportManager: localTM,
peerStore: {
multiaddrsForPeer: () => [remoteAddr]
}
})
const peerId = await PeerId.createFromJSON(Peers[0])
const peerInfo = new PeerInfo(peerId)
const connection = await dialer.connectToPeer(peerInfo)
expect(connection).to.exist()
await connection.close()
})
it('should be able to connect to a given peer id', async () => {
const peerStore = new PeerStore()
const dialer = new Dialer({
@ -118,11 +100,9 @@ describe('Dialing (direct, TCP)', () => {
})
const peerId = await PeerId.createFromJSON(Peers[0])
const peerInfo = new PeerInfo(peerId)
peerInfo.multiaddrs.add(remoteAddr)
peerStore.put(peerInfo)
peerStore.addressBook.set(peerId, [remoteAddr])
const connection = await dialer.connectToPeer(peerInfo)
const connection = await dialer.connectToPeer(peerId)
expect(connection).to.exist()
await connection.close()
})
@ -131,13 +111,15 @@ describe('Dialing (direct, TCP)', () => {
const dialer = new Dialer({
transportManager: localTM,
peerStore: {
multiaddrsForPeer: () => [unsupportedAddr]
addressBook: {
add: () => {},
getMultiaddrsForPeer: () => [unsupportedAddr]
}
}
})
const peerId = await PeerId.createFromJSON(Peers[0])
const peerInfo = new PeerInfo(peerId)
await expect(dialer.connectToPeer(peerInfo))
await expect(dialer.connectToPeer(peerId))
.to.eventually.be.rejectedWith(AggregateError)
.and.to.have.nested.property('._errors[0].code', ErrorCodes.ERR_TRANSPORT_UNAVAILABLE)
})
@ -172,7 +154,10 @@ describe('Dialing (direct, TCP)', () => {
transportManager: localTM,
concurrency: 2,
peerStore: {
multiaddrsForPeer: () => addrs
addressBook: {
add: () => {},
getMultiaddrsForPeer: () => addrs
}
}
})
@ -181,10 +166,10 @@ describe('Dialing (direct, TCP)', () => {
const deferredDial = pDefer()
sinon.stub(localTM, 'dial').callsFake(() => deferredDial.promise)
const [peerInfo] = await createPeerInfo()
const [peerId] = await createPeerId()
// Perform 3 multiaddr dials
dialer.connectToPeer(peerInfo)
dialer.connectToPeer(peerId)
// Let the call stack run
await delay(0)
@ -203,30 +188,28 @@ describe('Dialing (direct, TCP)', () => {
})
describe('libp2p.dialer', () => {
let peerInfo
let remotePeerInfo
let peerId, remotePeerId
let libp2p
let remoteLibp2p
let remoteAddr
before(async () => {
const [peerId, remotePeerId] = await Promise.all([
[peerId, remotePeerId] = await Promise.all([
PeerId.createFromJSON(Peers[0]),
PeerId.createFromJSON(Peers[1])
])
peerInfo = new PeerInfo(peerId)
remotePeerInfo = new PeerInfo(remotePeerId)
remoteLibp2p = new Libp2p({
peerInfo: remotePeerInfo,
peerId: remotePeerId,
addresses: {
listen: [listenAddr]
},
modules: {
transport: [Transport],
streamMuxer: [Muxer],
connEncryption: [Crypto]
}
})
remoteLibp2p.peerInfo.multiaddrs.add(listenAddr)
remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
await remoteLibp2p.start()
@ -243,7 +226,7 @@ describe('Dialing (direct, TCP)', () => {
it('should fail if no peer id is provided', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -256,7 +239,7 @@ describe('Dialing (direct, TCP)', () => {
try {
await libp2p.dial(remoteLibp2p.transportManager.getAddrs()[0])
} catch (err) {
expect(err).to.have.property('code', ErrorCodes.ERR_INVALID_PEER)
expect(err).to.have.property('code', ErrorCodes.ERR_INVALID_MULTIADDR)
return
}
@ -265,7 +248,7 @@ describe('Dialing (direct, TCP)', () => {
it('should use the dialer for connecting to a multiaddr', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -286,7 +269,7 @@ describe('Dialing (direct, TCP)', () => {
it('should use the dialer for connecting to a peer', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -295,8 +278,9 @@ describe('Dialing (direct, TCP)', () => {
})
sinon.spy(libp2p.dialer, 'connectToPeer')
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.addresses.listen)
const connection = await libp2p.dial(remotePeerInfo)
const connection = await libp2p.dial(remotePeerId)
expect(connection).to.exist()
const { stream, protocol } = await connection.newStream('/echo/1.0.0')
expect(stream).to.exist()
@ -307,7 +291,7 @@ describe('Dialing (direct, TCP)', () => {
it('should be able to use hangup to close connections', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -324,7 +308,7 @@ describe('Dialing (direct, TCP)', () => {
it('should be able to use hangup by address string to close connections', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -342,7 +326,7 @@ describe('Dialing (direct, TCP)', () => {
it('should use the protectors when provided for connecting', async () => {
const protector = new Protector(swarmKeyBuffer)
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -365,7 +349,7 @@ describe('Dialing (direct, TCP)', () => {
it('should coalesce parallel dials to the same peer (id in multiaddr)', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -374,9 +358,11 @@ describe('Dialing (direct, TCP)', () => {
})
const dials = 10
const fullAddress = remoteAddr.encapsulate(`/p2p/${remoteLibp2p.peerInfo.id.toB58String()}`)
const fullAddress = remoteAddr.encapsulate(`/p2p/${remoteLibp2p.peerId.toB58String()}`)
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.addresses.listen)
const dialResults = await Promise.all([...new Array(dials)].map((_, index) => {
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerInfo)
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerId)
return libp2p.dial(fullAddress)
}))
@ -393,7 +379,7 @@ describe('Dialing (direct, TCP)', () => {
it('should coalesce parallel dials to the same error on failure', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -404,8 +390,9 @@ describe('Dialing (direct, TCP)', () => {
const error = new Error('Boom')
sinon.stub(libp2p.transportManager, 'dial').callsFake(() => Promise.reject(error))
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.addresses.listen)
const dialResults = await pSettle([...new Array(dials)].map((_, index) => {
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerInfo)
if (index % 2 === 0) return libp2p.dial(remoteLibp2p.peerId)
return libp2p.dial(remoteAddr)
}))

View File

@ -14,7 +14,6 @@ const Muxer = require('libp2p-mplex')
const Crypto = require('libp2p-secio')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const AggregateError = require('aggregate-error')
const { AbortError } = require('libp2p-interfaces/src/transport/errors')
@ -87,7 +86,10 @@ describe('Dialing (direct, WebSockets)', () => {
const dialer = new Dialer({
transportManager: localTM,
peerStore: {
multiaddrsForPeer: () => [remoteAddr]
addressBook: {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
})
@ -100,7 +102,10 @@ describe('Dialing (direct, WebSockets)', () => {
const dialer = new Dialer({
transportManager: localTM,
peerStore: {
multiaddrsForPeer: () => [remoteAddr]
addressBook: {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
})
@ -121,7 +126,10 @@ describe('Dialing (direct, WebSockets)', () => {
const dialer = new Dialer({
transportManager: localTM,
peerStore: {
multiaddrsForPeer: () => [remoteAddr]
addressBook: {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
})
const peerId = await PeerId.createFromJSON(Peers[0])
@ -135,7 +143,10 @@ describe('Dialing (direct, WebSockets)', () => {
const dialer = new Dialer({
transportManager: localTM,
peerStore: {
multiaddrsForPeer: () => [unsupportedAddr]
addressBook: {
set: () => {},
getMultiaddrsForPeer: () => [unsupportedAddr]
}
}
})
const peerId = await PeerId.createFromJSON(Peers[0])
@ -150,7 +161,10 @@ describe('Dialing (direct, WebSockets)', () => {
transportManager: localTM,
timeout: 50,
peerStore: {
multiaddrsForPeer: () => [remoteAddr]
addressBook: {
add: () => {},
getMultiaddrsForPeer: () => [remoteAddr]
}
}
})
sinon.stub(localTM, 'dial').callsFake(async (addr, options) => {
@ -172,7 +186,10 @@ describe('Dialing (direct, WebSockets)', () => {
transportManager: localTM,
concurrency: 2,
peerStore: {
multiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
addressBook: {
set: () => {},
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
}
}
})
@ -208,7 +225,10 @@ describe('Dialing (direct, WebSockets)', () => {
transportManager: localTM,
concurrency: 2,
peerStore: {
multiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
addressBook: {
set: () => {},
getMultiaddrsForPeer: () => [remoteAddr, remoteAddr, remoteAddr]
}
}
})
@ -246,13 +266,12 @@ describe('Dialing (direct, WebSockets)', () => {
})
describe('libp2p.dialer', () => {
let peerInfo
let peerId
let libp2p
let remoteLibp2p
before(async () => {
const peerId = await PeerId.createFromJSON(Peers[0])
peerInfo = new PeerInfo(peerId)
peerId = await PeerId.createFromJSON(Peers[0])
})
afterEach(async () => {
@ -267,7 +286,7 @@ describe('Dialing (direct, WebSockets)', () => {
it('should create a dialer', () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -285,7 +304,7 @@ describe('Dialing (direct, WebSockets)', () => {
it('should be able to override dialer options', async () => {
const config = {
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -307,7 +326,7 @@ describe('Dialing (direct, WebSockets)', () => {
it('should use the dialer for connecting', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -316,7 +335,7 @@ describe('Dialing (direct, WebSockets)', () => {
})
sinon.spy(libp2p.dialer, 'connectToPeer')
sinon.spy(libp2p.peerStore, 'put')
sinon.spy(libp2p.peerStore.addressBook, 'add')
const connection = await libp2p.dial(remoteAddr)
expect(connection).to.exist()
@ -325,12 +344,12 @@ describe('Dialing (direct, WebSockets)', () => {
expect(protocol).to.equal('/echo/1.0.0')
await connection.close()
expect(libp2p.dialer.connectToPeer.callCount).to.equal(1)
expect(libp2p.peerStore.put.callCount).to.be.at.least(1)
expect(libp2p.peerStore.addressBook.add.callCount).to.be.at.least(1)
})
it('should run identify automatically after connecting', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -339,24 +358,27 @@ describe('Dialing (direct, WebSockets)', () => {
})
sinon.spy(libp2p.identifyService, 'identify')
sinon.spy(libp2p.peerStore, 'replace')
sinon.spy(libp2p.upgrader, 'onConnection')
const connection = await libp2p.dial(remoteAddr)
expect(connection).to.exist()
sinon.spy(libp2p.peerStore.addressBook, 'set')
sinon.spy(libp2p.peerStore.protoBook, 'set')
// Wait for onConnection to be called
await pWaitFor(() => libp2p.upgrader.onConnection.callCount === 1)
expect(libp2p.identifyService.identify.callCount).to.equal(1)
await libp2p.identifyService.identify.firstCall.returnValue
expect(libp2p.peerStore.replace.callCount).to.equal(1)
expect(libp2p.peerStore.addressBook.set.callCount).to.equal(1)
expect(libp2p.peerStore.protoBook.set.callCount).to.equal(1)
})
it('should be able to use hangup to close connections', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -373,7 +395,7 @@ describe('Dialing (direct, WebSockets)', () => {
it('should abort pending dials on stop', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport],
streamMuxer: [Muxer],

View File

@ -5,31 +5,37 @@ const chai = require('chai')
chai.use(require('dirty-chai'))
chai.use(require('chai-as-promised'))
const { expect } = chai
const sinon = require('sinon')
const multiaddr = require('multiaddr')
const { collect } = require('streaming-iterables')
const pipe = require('it-pipe')
const AggregateError = require('aggregate-error')
const { createPeerInfo } = require('../utils/creators/peer')
const PeerId = require('peer-id')
const { createPeerId } = require('../utils/creators/peer')
const baseOptions = require('../utils/base-options')
const Libp2p = require('../../src')
const { codes: Errors } = require('../../src/errors')
const listenAddr = multiaddr('/ip4/0.0.0.0/tcp/0')
describe('Dialing (via relay, TCP)', () => {
let srcLibp2p
let relayLibp2p
let dstLibp2p
before(async () => {
const peerInfos = await createPeerInfo({ number: 3 })
const peerIds = await createPeerId({ number: 3 })
// Create 3 nodes, and turn HOP on for the relay
;[srcLibp2p, relayLibp2p, dstLibp2p] = peerInfos.map((peerInfo, index) => {
;[srcLibp2p, relayLibp2p, dstLibp2p] = peerIds.map((peerId, index) => {
const opts = baseOptions
index === 1 && (opts.config.relay.hop.enabled = true)
return new Libp2p({
...opts,
peerInfo
addresses: {
listen: [listenAddr]
},
peerId
})
})
@ -38,12 +44,7 @@ describe('Dialing (via relay, TCP)', () => {
beforeEach(() => {
// Start each node
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => {
// Reset multiaddrs and start
libp2p.peerInfo.multiaddrs.clear()
libp2p.peerInfo.multiaddrs.add('/ip4/0.0.0.0/tcp/0')
return libp2p.start()
}))
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(libp2p => libp2p.start()))
})
afterEach(() => {
@ -51,19 +52,20 @@ describe('Dialing (via relay, TCP)', () => {
return Promise.all([srcLibp2p, relayLibp2p, dstLibp2p].map(async libp2p => {
await libp2p.stop()
// Clear the peer stores
for (const peerId of libp2p.peerStore.peers.keys()) {
libp2p.peerStore.remove(peerId)
for (const peerIdStr of libp2p.peerStore.peers.keys()) {
const peerId = PeerId.createFromCID(peerIdStr)
libp2p.peerStore.delete(peerId)
}
}))
})
it('should be able to connect to a peer over a relay with active connections', async () => {
const relayAddr = relayLibp2p.transportManager.getAddrs()[0]
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
const relayIdString = relayLibp2p.peerId.toB58String()
const dialAddr = relayAddr
.encapsulate(`/p2p/${relayIdString}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
const tcpAddrs = dstLibp2p.transportManager.getAddrs()
await dstLibp2p.transportManager.listen([multiaddr(`/p2p-circuit${relayAddr}/p2p/${relayIdString}`)])
@ -71,14 +73,14 @@ describe('Dialing (via relay, TCP)', () => {
const connection = await srcLibp2p.dial(dialAddr)
expect(connection).to.exist()
expect(connection.remotePeer.toBytes()).to.eql(dstLibp2p.peerInfo.id.toBytes())
expect(connection.localPeer.toBytes()).to.eql(srcLibp2p.peerInfo.id.toBytes())
expect(connection.remotePeer.toBytes()).to.eql(dstLibp2p.peerId.toBytes())
expect(connection.localPeer.toBytes()).to.eql(srcLibp2p.peerId.toBytes())
expect(connection.remoteAddr).to.eql(dialAddr)
expect(connection.localAddr).to.eql(
relayAddr // the relay address
.encapsulate(`/p2p/${relayIdString}`) // with its peer id
.encapsulate('/p2p-circuit') // the local peer is connected over the relay
.encapsulate(`/p2p/${srcLibp2p.peerInfo.id.toB58String()}`) // and the local peer id
.encapsulate(`/p2p/${srcLibp2p.peerId.toB58String()}`) // and the local peer id
)
const { stream: echoStream } = await connection.newStream('/echo/1.0.0')
@ -94,11 +96,11 @@ describe('Dialing (via relay, TCP)', () => {
it('should fail to connect to a peer over a relay with inactive connections', async () => {
const relayAddr = relayLibp2p.transportManager.getAddrs()[0]
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
const relayIdString = relayLibp2p.peerId.toB58String()
const dialAddr = relayAddr
.encapsulate(`/p2p/${relayIdString}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
await expect(srcLibp2p.dial(dialAddr))
.to.eventually.be.rejectedWith(AggregateError)
@ -107,27 +109,27 @@ describe('Dialing (via relay, TCP)', () => {
it('should not stay connected to a relay when not already connected and HOP fails', async () => {
const relayAddr = relayLibp2p.transportManager.getAddrs()[0]
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
const relayIdString = relayLibp2p.peerId.toB58String()
const dialAddr = relayAddr
.encapsulate(`/p2p/${relayIdString}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
await expect(srcLibp2p.dial(dialAddr))
.to.eventually.be.rejectedWith(AggregateError)
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
// We should not be connected to the relay, because we weren't before the dial
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerInfo)
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerId)
expect(srcToRelayConn).to.not.exist()
})
it('dialer should stay connected to an already connected relay on hop failure', async () => {
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
const relayIdString = relayLibp2p.peerId.toB58String()
const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`)
const dialAddr = relayAddr
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
await srcLibp2p.dial(relayAddr)
@ -135,17 +137,17 @@ describe('Dialing (via relay, TCP)', () => {
.to.eventually.be.rejectedWith(AggregateError)
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerInfo)
const srcToRelayConn = srcLibp2p.registrar.getConnection(relayLibp2p.peerId)
expect(srcToRelayConn).to.exist()
expect(srcToRelayConn.stat.status).to.equal('open')
})
it('destination peer should stay connected to an already connected relay on hop failure', async () => {
const relayIdString = relayLibp2p.peerInfo.id.toB58String()
const relayIdString = relayLibp2p.peerId.toB58String()
const relayAddr = relayLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${relayIdString}`)
const dialAddr = relayAddr
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerInfo.id.toB58String()}`)
.encapsulate(`/p2p-circuit/p2p/${dstLibp2p.peerId.toB58String()}`)
// Connect the destination peer and the relay
const tcpAddrs = dstLibp2p.transportManager.getAddrs()
@ -153,15 +155,15 @@ describe('Dialing (via relay, TCP)', () => {
expect(dstLibp2p.transportManager.getAddrs()).to.have.deep.members([...tcpAddrs, dialAddr.decapsulate('p2p')])
// Tamper with the our multiaddrs for the circuit message
sinon.stub(srcLibp2p.peerInfo.multiaddrs, 'toArray').returns([{
srcLibp2p.addresses.listen = [{
buffer: Buffer.from('an invalid multiaddr')
}])
}]
await expect(srcLibp2p.dial(dialAddr))
.to.eventually.be.rejectedWith(AggregateError)
.and.to.have.nested.property('._errors[0].code', Errors.ERR_HOP_REQUEST_FAILED)
const dstToRelayConn = dstLibp2p.registrar.getConnection(relayLibp2p.peerInfo)
const dstToRelayConn = dstLibp2p.registrar.getConnection(relayLibp2p.peerId)
expect(dstToRelayConn).to.exist()
expect(dstToRelayConn.stat.status).to.equal('open')
})

View File

@ -9,7 +9,6 @@ const sinon = require('sinon')
const delay = require('delay')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const duplexPair = require('it-pair/duplex')
const multiaddr = require('multiaddr')
const pWaitFor = require('p-wait-for')
@ -35,7 +34,7 @@ describe('Identify', () => {
[localPeer, remotePeer] = (await Promise.all([
PeerId.createFromJSON(Peers[0]),
PeerId.createFromJSON(Peers[1])
])).map(id => new PeerInfo(id))
]))
})
afterEach(() => {
@ -44,27 +43,39 @@ describe('Identify', () => {
it('should be able to identify another peer', async () => {
const localIdentify = new IdentifyService({
peerInfo: localPeer,
peerId: localPeer,
addresses: {
listen: []
},
protocols,
registrar: {
peerStore: {
replace: () => {}
addressBook: {
set: () => { }
},
protoBook: {
set: () => { }
}
}
}
})
const remoteIdentify = new IdentifyService({
peerInfo: remotePeer,
peerId: remotePeer,
addresses: {
listen: []
},
protocols
})
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
const localConnectionMock = { newStream: () => {}, remotePeer: remotePeer.id }
const localConnectionMock = { newStream: () => {}, remotePeer }
const remoteConnectionMock = { remoteAddr: observedAddr }
const [local, remote] = duplexPair()
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY })
sinon.spy(localIdentify.registrar.peerStore, 'replace')
sinon.spy(localIdentify.registrar.peerStore.addressBook, 'set')
sinon.spy(localIdentify.registrar.peerStore.protoBook, 'set')
// Run identify
await Promise.all([
@ -76,29 +87,41 @@ describe('Identify', () => {
})
])
expect(localIdentify.registrar.peerStore.replace.callCount).to.equal(1)
expect(localIdentify.registrar.peerStore.addressBook.set.callCount).to.equal(1)
expect(localIdentify.registrar.peerStore.protoBook.set.callCount).to.equal(1)
// Validate the remote peer gets updated in the peer store
const call = localIdentify.registrar.peerStore.replace.firstCall
expect(call.args[0].id.bytes).to.equal(remotePeer.id.bytes)
const call = localIdentify.registrar.peerStore.addressBook.set.firstCall
expect(call.args[0].id.bytes).to.equal(remotePeer.bytes)
})
it('should throw if identified peer is the wrong peer', async () => {
const localIdentify = new IdentifyService({
peerInfo: localPeer,
peerId: localPeer,
addresses: {
listen: []
},
protocols,
registrar: {
peerStore: {
replace: () => {}
addressBook: {
set: () => { }
},
protoBook: {
set: () => { }
}
}
}
})
const remoteIdentify = new IdentifyService({
peerInfo: remotePeer,
peerId: remotePeer,
addresses: {
listen: []
},
protocols
})
const observedAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
const localConnectionMock = { newStream: () => {}, remotePeer: localPeer.id }
const localConnectionMock = { newStream: () => {}, remotePeer: localPeer }
const remoteConnectionMock = { remoteAddr: observedAddr }
const [local, remote] = duplexPair()
@ -106,7 +129,7 @@ describe('Identify', () => {
// Run identify
const identifyPromise = Promise.all([
localIdentify.identify(localConnectionMock, localPeer.id),
localIdentify.identify(localConnectionMock, localPeer),
remoteIdentify.handleMessage({
connection: remoteConnectionMock,
stream: remote,
@ -121,8 +144,12 @@ describe('Identify', () => {
describe('push', () => {
it('should be able to push identify updates to another peer', async () => {
const listeningAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
const localIdentify = new IdentifyService({
peerInfo: localPeer,
peerId: localPeer,
addresses: {
listen: [listeningAddr]
},
registrar: { getConnection: () => {} },
protocols: new Map([
[multicodecs.IDENTIFY],
@ -131,30 +158,32 @@ describe('Identify', () => {
])
})
const remoteIdentify = new IdentifyService({
peerInfo: remotePeer,
peerId: remotePeer,
addresses: {
listen: []
},
registrar: {
peerStore: {
replace: () => {}
addressBook: {
set: () => {}
},
protoBook: {
set: () => { }
}
}
}
})
// Setup peer protocols and multiaddrs
const localProtocols = new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH, '/echo/1.0.0'])
const listeningAddr = multiaddr('/ip4/127.0.0.1/tcp/1234')
sinon.stub(localPeer.multiaddrs, 'toArray').returns([listeningAddr])
sinon.stub(localPeer, 'protocols').value(localProtocols)
sinon.stub(remotePeer, 'protocols').value(new Set([multicodecs.IDENTIFY, multicodecs.IDENTIFY_PUSH]))
const localConnectionMock = { newStream: () => {} }
const remoteConnectionMock = { remotePeer: localPeer.id }
const remoteConnectionMock = { remotePeer: localPeer }
const [local, remote] = duplexPair()
sinon.stub(localConnectionMock, 'newStream').returns({ stream: local, protocol: multicodecs.IDENTIFY_PUSH })
sinon.spy(IdentifyService, 'updatePeerAddresses')
sinon.spy(IdentifyService, 'updatePeerProtocols')
sinon.spy(remoteIdentify.registrar.peerStore, 'replace')
sinon.spy(remoteIdentify.registrar.peerStore.addressBook, 'set')
sinon.spy(remoteIdentify.registrar.peerStore.protoBook, 'set')
// Run identify
await Promise.all([
@ -166,25 +195,24 @@ describe('Identify', () => {
})
])
expect(IdentifyService.updatePeerAddresses.callCount).to.equal(1)
expect(IdentifyService.updatePeerProtocols.callCount).to.equal(1)
expect(remoteIdentify.registrar.peerStore.replace.callCount).to.equal(1)
const [peerInfo] = remoteIdentify.registrar.peerStore.replace.firstCall.args
expect(peerInfo.id.bytes).to.eql(localPeer.id.bytes)
expect(peerInfo.multiaddrs.toArray()).to.eql([listeningAddr])
expect(peerInfo.protocols).to.eql(localProtocols)
expect(remoteIdentify.registrar.peerStore.addressBook.set.callCount).to.equal(1)
expect(remoteIdentify.registrar.peerStore.protoBook.set.callCount).to.equal(1)
const [peerId, multiaddrs] = remoteIdentify.registrar.peerStore.addressBook.set.firstCall.args
expect(peerId.bytes).to.eql(localPeer.bytes)
expect(multiaddrs).to.eql([listeningAddr])
const [peerId2, protocols] = remoteIdentify.registrar.peerStore.protoBook.set.firstCall.args
expect(peerId2.bytes).to.eql(localPeer.bytes)
expect(protocols).to.eql(Array.from(localProtocols))
})
})
describe('libp2p.dialer.identifyService', () => {
let peerInfo
let peerId
let libp2p
let remoteLibp2p
before(async () => {
const peerId = await PeerId.createFromJSON(Peers[0])
peerInfo = new PeerInfo(peerId)
peerId = await PeerId.createFromJSON(Peers[0])
})
afterEach(async () => {
@ -200,17 +228,19 @@ describe('Identify', () => {
it('should run identify automatically after connecting', async () => {
libp2p = new Libp2p({
...baseOptions,
peerInfo
peerId
})
sinon.spy(libp2p.identifyService, 'identify')
const peerStoreSpy = sinon.spy(libp2p.peerStore, 'replace')
const peerStoreSpySet = sinon.spy(libp2p.peerStore.addressBook, 'set')
const peerStoreSpyAdd = sinon.spy(libp2p.peerStore.addressBook, 'add')
const connection = await libp2p.dialer.connectToPeer(remoteAddr)
expect(connection).to.exist()
// Wait for peer store to be updated
await pWaitFor(() => peerStoreSpy.callCount === 1)
// Dialer._createDialTarget (add), Identify (replace)
await pWaitFor(() => peerStoreSpySet.callCount === 1 && peerStoreSpyAdd.callCount === 1)
expect(libp2p.identifyService.identify.callCount).to.equal(1)
// The connection should have no open streams
@ -221,12 +251,11 @@ describe('Identify', () => {
it('should push protocol updates to an already connected peer', async () => {
libp2p = new Libp2p({
...baseOptions,
peerInfo
peerId
})
sinon.spy(libp2p.identifyService, 'identify')
sinon.spy(libp2p.identifyService, 'push')
sinon.spy(libp2p.peerStore, 'update')
const connection = await libp2p.dialer.connectToPeer(remoteAddr)
expect(connection).to.exist()

View File

@ -74,7 +74,7 @@ describe('libp2p.metrics', () => {
remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
const connection = await libp2p.dial(remoteLibp2p.peerInfo)
const connection = await libp2p.dial(remoteLibp2p.peerId)
const { stream } = await connection.newStream('/echo/1.0.0')
const bytes = randomBytes(512)
@ -109,6 +109,11 @@ describe('libp2p.metrics', () => {
enabled: true,
computeThrottleMaxQueueSize: 1, // compute after every message
movingAverageIntervals: [10]
},
config: {
peerDiscovery: {
autoDial: false
}
}
}
let remoteLibp2p
@ -116,7 +121,7 @@ describe('libp2p.metrics', () => {
remoteLibp2p.handle('/echo/1.0.0', ({ stream }) => pipe(stream, stream))
const connection = await libp2p.dial(remoteLibp2p.peerInfo)
const connection = await libp2p.dial(remoteLibp2p.peerId)
const { stream } = await connection.newStream('/echo/1.0.0')
const bytes = randomBytes(512)

View File

@ -15,18 +15,16 @@ const multiaddr = require('multiaddr')
const Libp2p = require('../../src')
const baseOptions = require('../utils/base-options')
const { createPeerInfo } = require('../utils/creators/peer')
const { createPeerId } = require('../utils/creators/peer')
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
describe('peer discovery scenarios', () => {
let peerInfo, remotePeerInfo1, remotePeerInfo2
let peerId, remotePeerId1, remotePeerId2
let libp2p
before(async () => {
[peerInfo, remotePeerInfo1, remotePeerInfo2] = await createPeerInfo({ number: 3 })
peerInfo.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
remotePeerInfo1.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
remotePeerInfo2.multiaddrs.add(multiaddr('/ip4/127.0.0.1/tcp/0'))
[peerId, remotePeerId1, remotePeerId2] = await createPeerId({ number: 3 })
})
afterEach(async () => {
@ -37,12 +35,15 @@ describe('peer discovery scenarios', () => {
const deferred = defer()
const bootstrappers = [
...remotePeerInfo1.multiaddrs.toArray().map((ma) => `${ma}/p2p/${remotePeerInfo1.id.toB58String()}`),
...remotePeerInfo2.multiaddrs.toArray().map((ma) => `${ma}/p2p/${remotePeerInfo2.id.toB58String()}`)
`${listenAddr}/p2p/${remotePeerId1.toB58String()}`,
`${listenAddr}/p2p/${remotePeerId2.toB58String()}`
]
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo,
peerId,
addresses: {
listen: [listenAddr]
},
modules: {
peerDiscovery: [Bootstrap]
},
@ -58,12 +59,12 @@ describe('peer discovery scenarios', () => {
}))
const expectedPeers = new Set([
remotePeerInfo1.id.toB58String(),
remotePeerInfo2.id.toB58String()
remotePeerId1.toB58String(),
remotePeerId2.toB58String()
])
libp2p.on('peer:discovery', (peerInfo) => {
expectedPeers.delete(peerInfo.id.toB58String())
libp2p.on('peer:discovery', (peerId) => {
expectedPeers.delete(peerId.toB58String())
if (expectedPeers.size === 0) {
libp2p.removeAllListeners('peer:discovery')
deferred.resolve()
@ -78,8 +79,11 @@ describe('peer discovery scenarios', () => {
it('MulticastDNS should discover all peers on the local network', async () => {
const deferred = defer()
const getConfig = (peerInfo) => mergeOptions(baseOptions, {
peerInfo,
const getConfig = (peerId) => mergeOptions(baseOptions, {
peerId,
addresses: {
listen: [listenAddr]
},
modules: {
peerDiscovery: [MulticastDNS]
},
@ -96,17 +100,17 @@ describe('peer discovery scenarios', () => {
}
})
libp2p = new Libp2p(getConfig(peerInfo))
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerInfo1))
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerInfo2))
libp2p = new Libp2p(getConfig(peerId))
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerId1))
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerId2))
const expectedPeers = new Set([
remotePeerInfo1.id.toB58String(),
remotePeerInfo2.id.toB58String()
remotePeerId1.toB58String(),
remotePeerId2.toB58String()
])
libp2p.on('peer:discovery', (peerInfo) => {
expectedPeers.delete(peerInfo.id.toB58String())
libp2p.on('peer:discovery', (peerId) => {
expectedPeers.delete(peerId.toB58String())
if (expectedPeers.size === 0) {
libp2p.removeAllListeners('peer:discovery')
deferred.resolve()
@ -128,8 +132,11 @@ describe('peer discovery scenarios', () => {
it('kad-dht should discover other peers', async () => {
const deferred = defer()
const getConfig = (peerInfo) => mergeOptions(baseOptions, {
peerInfo,
const getConfig = (peerId) => mergeOptions(baseOptions, {
peerId,
addresses: {
listen: [listenAddr]
},
modules: {
dht: KadDht
},
@ -149,16 +156,16 @@ describe('peer discovery scenarios', () => {
}
})
const localConfig = getConfig(peerInfo)
const localConfig = getConfig(peerId)
// Only run random walk on our local node
localConfig.config.dht.randomWalk.enabled = true
libp2p = new Libp2p(localConfig)
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerInfo1))
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerInfo2))
const remoteLibp2p1 = new Libp2p(getConfig(remotePeerId1))
const remoteLibp2p2 = new Libp2p(getConfig(remotePeerId2))
libp2p.on('peer:discovery', (peerInfo) => {
if (peerInfo.id.toB58String() === remotePeerInfo2.id.toB58String()) {
libp2p.on('peer:discovery', (peerId) => {
if (peerId.toB58String() === remotePeerId1.toB58String()) {
libp2p.removeAllListeners('peer:discovery')
deferred.resolve()
}
@ -170,12 +177,15 @@ describe('peer discovery scenarios', () => {
remoteLibp2p2.start()
])
libp2p.peerStore.addressBook.set(remotePeerId1, remoteLibp2p1.addresses.listen)
remoteLibp2p2.peerStore.addressBook.set(remotePeerId1, remoteLibp2p1.addresses.listen)
// Topology:
// A -> B
// C -> B
await Promise.all([
libp2p.dial(remotePeerInfo1),
remoteLibp2p2.dial(remotePeerInfo1)
libp2p.dial(remotePeerId1),
remoteLibp2p2.dial(remotePeerId1)
])
await deferred.promise

View File

@ -9,21 +9,22 @@ const sinon = require('sinon')
const defer = require('p-defer')
const mergeOptions = require('merge-options')
const multiaddr = require('multiaddr')
const WebRTCStar = require('libp2p-webrtc-star')
const Libp2p = require('../../src')
const baseOptions = require('../utils/base-options.browser')
const { createPeerInfo } = require('../utils/creators/peer')
const { createPeerId } = require('../utils/creators/peer')
const { EventEmitter } = require('events')
describe('peer discovery', () => {
describe('basic functions', () => {
let peerInfo
let remotePeerInfo
let peerId
let remotePeerId
let libp2p
before(async () => {
[peerInfo, remotePeerInfo] = await createPeerInfo({ number: 2 })
[peerId, remotePeerId] = await createPeerId({ number: 2 })
})
afterEach(async () => {
@ -34,12 +35,14 @@ describe('peer discovery', () => {
it('should dial know peers on startup', async () => {
libp2p = new Libp2p({
...baseOptions,
peerInfo
peerId
})
libp2p.peerStore.add(remotePeerInfo)
libp2p.peerStore.addressBook.set(remotePeerId, [multiaddr('/ip4/165.1.1.1/tcp/80')])
const deferred = defer()
sinon.stub(libp2p.dialer, 'connectToPeer').callsFake((remotePeerInfo) => {
expect(remotePeerInfo).to.equal(remotePeerInfo)
sinon.stub(libp2p.dialer, 'connectToPeer').callsFake((remotePeerId) => {
expect(remotePeerId).to.equal(remotePeerId)
deferred.resolve()
})
const spy = sinon.spy()
@ -47,7 +50,9 @@ describe('peer discovery', () => {
libp2p.start()
await deferred.promise
expect(spy.getCall(0).args).to.eql([remotePeerInfo])
expect(spy.calledOnce).to.eql(true)
expect(spy.getCall(0).args[0].toString()).to.eql(remotePeerId.toString())
})
it('should ignore self on discovery', async () => {
@ -57,7 +62,7 @@ describe('peer discovery', () => {
mockDiscovery.stop = () => {}
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo,
peerId,
modules: {
peerDiscovery: [mockDiscovery]
}
@ -66,7 +71,7 @@ describe('peer discovery', () => {
await libp2p.start()
const discoverySpy = sinon.spy()
libp2p.on('peer:discovery', discoverySpy)
libp2p._discovery.get('mock').emit('peer', libp2p.peerInfo)
libp2p._discovery.get('mock').emit('peer', { id: libp2p.peerId })
expect(discoverySpy.called).to.eql(false)
})
@ -83,7 +88,7 @@ describe('peer discovery', () => {
const stopSpy = sinon.spy(mockDiscovery, 'stop')
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo,
peerId,
modules: {
peerDiscovery: [mockDiscovery]
}
@ -99,15 +104,15 @@ describe('peer discovery', () => {
})
describe('discovery modules from transports', () => {
let peerInfo, libp2p
let peerId, libp2p
before(async () => {
[peerInfo] = await createPeerInfo()
[peerId] = await createPeerId()
})
it('should add discovery module if present in transports and enabled', async () => {
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo,
peerId,
modules: {
transport: [WebRTCStar]
},
@ -128,7 +133,7 @@ describe('peer discovery', () => {
it('should not add discovery module if present in transports but disabled', async () => {
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo,
peerId,
modules: {
transport: [WebRTCStar]
},

View File

@ -44,7 +44,7 @@ describe('peer-routing', () => {
// Ring dial
await Promise.all(
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerInfo))
nodes.map((peer, i) => peer.dial(nodes[(i + 1) % number].peerId))
)
})
@ -59,7 +59,7 @@ describe('peer-routing', () => {
sinon.stub(nodes[0]._dht, 'findPeer').callsFake(() => {
deferred.resolve()
return nodes[1].peerInfo
return nodes[1].peerId
})
nodes[0].peerRouting.findPeer()
@ -104,7 +104,7 @@ describe('peer-routing', () => {
sinon.stub(delegate, 'findPeer').callsFake(() => {
deferred.resolve()
return 'fake peer-info'
return 'fake peer-id'
})
await node.peerRouting.findPeer()
@ -121,9 +121,9 @@ describe('peer-routing', () => {
'X-Chunked-Output', '1'
])
const peerInfo = await node.peerRouting.findPeer(peerKey)
const peer = await node.peerRouting.findPeer(peerKey)
expect(peerInfo.id.toB58String()).to.equal(peerKey)
expect(peer.id).to.equal(peerKey)
expect(mockApi.isDone()).to.equal(true)
})
@ -188,7 +188,7 @@ describe('peer-routing', () => {
sinon.stub(node._dht, 'findPeer').callsFake(() => {
dhtDeferred.resolve()
return node.peerInfo
return { id: node.peerId }
})
sinon.stub(delegate, 'findPeer').callsFake(() => {
throw new Error('the delegate should not have been called')

View File

@ -0,0 +1,364 @@
'use strict'
/* eslint-env mocha */
const chai = require('chai')
chai.use(require('dirty-chai'))
const { expect } = chai
const pDefer = require('p-defer')
const multiaddr = require('multiaddr')
const PeerStore = require('../../src/peer-store')
const peerUtils = require('../utils/creators/peer')
const {
ERR_INVALID_PARAMETERS
} = require('../../src/errors')
const addr1 = multiaddr('/ip4/127.0.0.1/tcp/8000')
const addr2 = multiaddr('/ip4/127.0.0.1/tcp/8001')
const addr3 = multiaddr('/ip4/127.0.0.1/tcp/8002')
const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item)
describe('addressBook', () => {
let peerId
before(async () => {
[peerId] = await peerUtils.createPeerId()
})
describe('addressBook.set', () => {
let peerStore, ab
beforeEach(() => {
peerStore = new PeerStore()
ab = peerStore.addressBook
})
afterEach(() => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
ab.set('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if no addresses provided', () => {
expect(() => {
ab.set(peerId)
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if invalid multiaddrs are provided', () => {
expect(() => {
ab.set(peerId, 'invalid multiaddr')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('replaces the stored content by default and emit change event', () => {
const defer = pDefer()
const supportedMultiaddrs = [addr1, addr2]
peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => {
expect(peerId).to.exist()
expect(multiaddrs).to.eql(supportedMultiaddrs)
defer.resolve()
})
ab.set(peerId, supportedMultiaddrs)
const addresses = ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrs)
return defer.promise
})
it('emits on set if not storing the exact same content', async () => {
const defer = pDefer()
const supportedMultiaddrsA = [addr1, addr2]
const supportedMultiaddrsB = [addr2]
let changeCounter = 0
peerStore.on('change:multiaddrs', () => {
changeCounter++
if (changeCounter > 1) {
defer.resolve()
}
})
// set 1
ab.set(peerId, supportedMultiaddrsA)
// set 2 (same content)
ab.set(peerId, supportedMultiaddrsB)
const addresses = ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrsB)
await defer.promise
})
it('does not emit on set if it is storing the exact same content', async () => {
const defer = pDefer()
const supportedMultiaddrs = [addr1, addr2]
let changeCounter = 0
peerStore.on('change:multiaddrs', () => {
changeCounter++
if (changeCounter > 1) {
defer.reject()
}
})
// set 1
ab.set(peerId, supportedMultiaddrs)
// set 2 (same content)
ab.set(peerId, supportedMultiaddrs)
// Wait 50ms for incorrect second event
setTimeout(() => {
defer.resolve()
}, 50)
await defer.promise
})
})
describe('addressBook.add', () => {
let peerStore, ab
beforeEach(() => {
peerStore = new PeerStore()
ab = peerStore.addressBook
})
afterEach(() => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
ab.add('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if no addresses provided', () => {
expect(() => {
ab.add(peerId)
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if invalid multiaddrs are provided', () => {
expect(() => {
ab.add(peerId, 'invalid multiaddr')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('adds the new content and emits change event', () => {
const defer = pDefer()
const supportedMultiaddrsA = [addr1, addr2]
const supportedMultiaddrsB = [addr3]
const finalMultiaddrs = supportedMultiaddrsA.concat(supportedMultiaddrsB)
let changeTrigger = 2
peerStore.on('change:multiaddrs', ({ multiaddrs }) => {
changeTrigger--
if (changeTrigger === 0 && arraysAreEqual(multiaddrs, finalMultiaddrs)) {
defer.resolve()
}
})
// Replace
ab.set(peerId, supportedMultiaddrsA)
let addresses = ab.get(peerId)
let multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrsA)
// Add
ab.add(peerId, supportedMultiaddrsB)
addresses = ab.get(peerId)
multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(finalMultiaddrs)
return defer.promise
})
it('emits on add if the content to add not exists', async () => {
const defer = pDefer()
const supportedMultiaddrsA = [addr1]
const supportedMultiaddrsB = [addr2]
const finalMultiaddrs = supportedMultiaddrsA.concat(supportedMultiaddrsB)
let changeCounter = 0
peerStore.on('change:multiaddrs', () => {
changeCounter++
if (changeCounter > 1) {
defer.resolve()
}
})
// set 1
ab.set(peerId, supportedMultiaddrsA)
// set 2 (content already existing)
ab.add(peerId, supportedMultiaddrsB)
const addresses = ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(finalMultiaddrs)
await defer.promise
})
it('does not emit on add if the content to add already exists', async () => {
const defer = pDefer()
const supportedMultiaddrsA = [addr1, addr2]
const supportedMultiaddrsB = [addr2]
let changeCounter = 0
peerStore.on('change:multiaddrs', () => {
changeCounter++
if (changeCounter > 1) {
defer.reject()
}
})
// set 1
ab.set(peerId, supportedMultiaddrsA)
// set 2 (content already existing)
ab.add(peerId, supportedMultiaddrsB)
// Wait 50ms for incorrect second event
setTimeout(() => {
defer.resolve()
}, 50)
await defer.promise
})
})
describe('addressBook.get', () => {
let peerStore, ab
beforeEach(() => {
peerStore = new PeerStore()
ab = peerStore.addressBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
ab.get('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('returns undefined if no multiaddrs are known for the provided peer', () => {
const addresses = ab.get(peerId)
expect(addresses).to.not.exist()
})
it('returns the multiaddrs stored', () => {
const supportedMultiaddrs = [addr1, addr2]
ab.set(peerId, supportedMultiaddrs)
const addresses = ab.get(peerId)
const multiaddrs = addresses.map((mi) => mi.multiaddr)
expect(multiaddrs).to.have.deep.members(supportedMultiaddrs)
})
})
describe('addressBook.getMultiaddrsForPeer', () => {
let peerStore, ab
beforeEach(() => {
peerStore = new PeerStore()
ab = peerStore.addressBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
ab.getMultiaddrsForPeer('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('returns undefined if no multiaddrs are known for the provided peer', () => {
const addresses = ab.getMultiaddrsForPeer(peerId)
expect(addresses).to.not.exist()
})
it('returns the multiaddrs stored', () => {
const supportedMultiaddrs = [addr1, addr2]
ab.set(peerId, supportedMultiaddrs)
const multiaddrs = ab.getMultiaddrsForPeer(peerId)
multiaddrs.forEach((m) => {
expect(m.getPeerId()).to.equal(peerId.toB58String())
})
})
})
describe('addressBook.delete', () => {
let peerStore, ab
beforeEach(() => {
peerStore = new PeerStore()
ab = peerStore.addressBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
ab.delete('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('returns false if no records exist for the peer and no event is emitted', () => {
const defer = pDefer()
peerStore.on('change:multiaddrs', () => {
defer.reject()
})
const deleted = ab.delete(peerId)
expect(deleted).to.equal(false)
// Wait 50ms for incorrect invalid event
setTimeout(() => {
defer.resolve()
}, 50)
return defer.promise
})
it('returns true if the record exists and an event is emitted', () => {
const defer = pDefer()
const supportedMultiaddrs = [addr1, addr2]
ab.set(peerId, supportedMultiaddrs)
// Listen after set
peerStore.on('change:multiaddrs', ({ multiaddrs }) => {
expect(multiaddrs.length).to.eql(0)
defer.resolve()
})
const deleted = ab.delete(peerId)
expect(deleted).to.equal(true)
return defer.promise
})
})
})

View File

@ -4,185 +4,149 @@
const chai = require('chai')
chai.use(require('dirty-chai'))
const { expect } = chai
const sinon = require('sinon')
const pDefer = require('p-defer')
const PeerStore = require('../../src/peer-store')
const multiaddr = require('multiaddr')
const peerUtils = require('../utils/creators/peer')
const addr = multiaddr('/ip4/127.0.0.1/tcp/8000')
const addr1 = multiaddr('/ip4/127.0.0.1/tcp/8000')
const addr2 = multiaddr('/ip4/127.0.0.1/tcp/8001')
const addr3 = multiaddr('/ip4/127.0.0.1/tcp/8002')
const addr4 = multiaddr('/ip4/127.0.0.1/tcp/8003')
const proto1 = '/protocol1'
const proto2 = '/protocol2'
const proto3 = '/protocol3'
describe('peer-store', () => {
let peerStore
beforeEach(() => {
peerStore = new PeerStore()
let peerIds
before(async () => {
peerIds = await peerUtils.createPeerId({
number: 4
})
})
it('should add a new peer and emit it when it does not exist', async () => {
const defer = pDefer()
describe('empty books', () => {
let peerStore
sinon.spy(peerStore, 'put')
sinon.spy(peerStore, 'add')
sinon.spy(peerStore, 'update')
beforeEach(() => {
peerStore = new PeerStore()
})
const [peerInfo] = await peerUtils.createPeerInfo()
it('has an empty map of peers', () => {
const peers = peerStore.peers
expect(peers.size).to.equal(0)
})
peerStore.on('peer', (peer) => {
it('returns false on trying to delete a non existant peerId', () => {
const deleted = peerStore.delete(peerIds[0])
expect(deleted).to.equal(false)
})
it('returns undefined on trying to find a non existant peerId', () => {
const peer = peerStore.get(peerIds[0])
expect(peer).to.not.exist()
})
})
describe('previously populated books', () => {
let peerStore
beforeEach(() => {
peerStore = new PeerStore()
// Add peer0 with { addr1, addr2 } and { proto1 }
peerStore.addressBook.set(peerIds[0], [addr1, addr2])
peerStore.protoBook.set(peerIds[0], [proto1])
// Add peer1 with { addr3 } and { proto2, proto3 }
peerStore.addressBook.set(peerIds[1], [addr3])
peerStore.protoBook.set(peerIds[1], [proto2, proto3])
// Add peer2 with { addr4 }
peerStore.addressBook.set(peerIds[2], [addr4])
// Add peer3 with { addr4 } and { proto2 }
peerStore.addressBook.set(peerIds[3], [addr4])
peerStore.protoBook.set(peerIds[3], [proto2])
})
it('has peers', () => {
const peers = peerStore.peers
expect(peers.size).to.equal(4)
expect(Array.from(peers.keys())).to.have.members([
peerIds[0].toB58String(),
peerIds[1].toB58String(),
peerIds[2].toB58String(),
peerIds[3].toB58String()
])
})
it('returns true on deleting a stored peer', () => {
const deleted = peerStore.delete(peerIds[0])
expect(deleted).to.equal(true)
const peers = peerStore.peers
expect(peers.size).to.equal(3)
expect(Array.from(peers.keys())).to.not.have.members([peerIds[0].toB58String()])
})
it('returns true on deleting a stored peer which is only on one book', () => {
const deleted = peerStore.delete(peerIds[2])
expect(deleted).to.equal(true)
const peers = peerStore.peers
expect(peers.size).to.equal(3)
})
it('gets the stored information of a peer in all its books', () => {
const peer = peerStore.get(peerIds[0])
expect(peer).to.exist()
defer.resolve()
})
peerStore.put(peerInfo)
expect(peer.protocols).to.have.members([proto1])
// Wait for peerStore to emit the peer
await defer.promise
expect(peerStore.put.callCount).to.equal(1)
expect(peerStore.add.callCount).to.equal(1)
expect(peerStore.update.callCount).to.equal(0)
})
it('should update peer when it is already in the store', async () => {
const [peerInfo] = await peerUtils.createPeerInfo()
// Put the peer in the store
peerStore.put(peerInfo)
sinon.spy(peerStore, 'add')
sinon.spy(peerStore, 'update')
// When updating, peer event must not be emitted
peerStore.on('peer', () => {
throw new Error('should not emit twice')
})
// If no multiaddrs change, the event should not be emitted
peerStore.on('change:multiaddrs', () => {
throw new Error('should not emit change:multiaddrs')
})
// If no protocols change, the event should not be emitted
peerStore.on('change:protocols', () => {
throw new Error('should not emit change:protocols')
const peerMultiaddrs = peer.addresses.map((mi) => mi.multiaddr)
expect(peerMultiaddrs).to.have.members([addr1, addr2])
})
peerStore.put(peerInfo)
it('gets the stored information of a peer that is not present in all its books', () => {
const peers = peerStore.get(peerIds[2])
expect(peers).to.exist()
expect(peers.protocols.length).to.eql(0)
expect(peerStore.add.callCount).to.equal(0)
expect(peerStore.update.callCount).to.equal(1)
})
it('should emit the "change:multiaddrs" event when a peer has new multiaddrs', async () => {
const defer = pDefer()
const [createdPeerInfo] = await peerUtils.createPeerInfo()
// Put the peer in the store
peerStore.put(createdPeerInfo)
// When updating, "change:multiaddrs" event must not be emitted
peerStore.on('change:multiaddrs', ({ peerInfo, multiaddrs }) => {
expect(peerInfo).to.exist()
expect(peerInfo.id).to.eql(createdPeerInfo.id)
expect(peerInfo.protocols).to.eql(createdPeerInfo.protocols)
expect(multiaddrs).to.exist()
expect(multiaddrs).to.eql(createdPeerInfo.multiaddrs.toArray())
defer.resolve()
})
// If no protocols change, the event should not be emitted
peerStore.on('change:protocols', () => {
throw new Error('should not emit change:protocols')
const peerMultiaddrs = peers.addresses.map((mi) => mi.multiaddr)
expect(peerMultiaddrs).to.have.members([addr4])
})
createdPeerInfo.multiaddrs.add(addr)
peerStore.put(createdPeerInfo)
it('can find all the peers supporting a protocol', () => {
const peerSupporting2 = []
// Wait for peerStore to emit the event
await defer.promise
})
for (const [, peer] of peerStore.peers.entries()) {
if (peer.protocols.includes(proto2)) {
peerSupporting2.push(peer)
}
}
it('should emit the "change:protocols" event when a peer has new protocols', async () => {
const defer = pDefer()
const [createdPeerInfo] = await peerUtils.createPeerInfo()
// Put the peer in the store
peerStore.put(createdPeerInfo)
// If no multiaddrs change, the event should not be emitted
peerStore.on('change:multiaddrs', () => {
throw new Error('should not emit change:multiaddrs')
})
// When updating, "change:protocols" event must be emitted
peerStore.on('change:protocols', ({ peerInfo, protocols }) => {
expect(peerInfo).to.exist()
expect(peerInfo.id).to.eql(createdPeerInfo.id)
expect(peerInfo.multiaddrs).to.eql(createdPeerInfo.multiaddrs)
expect(protocols).to.exist()
expect(protocols).to.eql(Array.from(createdPeerInfo.protocols))
defer.resolve()
expect(peerSupporting2.length).to.eql(2)
expect(peerSupporting2[0].id.toB58String()).to.eql(peerIds[1].toB58String())
expect(peerSupporting2[1].id.toB58String()).to.eql(peerIds[3].toB58String())
})
createdPeerInfo.protocols.add('/new-protocol/1.0.0')
peerStore.put(createdPeerInfo)
it('can find all the peers listening on a given address', () => {
const peerListenint4 = []
// Wait for peerStore to emit the event
await defer.promise
})
for (const [, peer] of peerStore.peers.entries()) {
const multiaddrs = peer.addresses.map((mi) => mi.multiaddr)
it('should be able to retrieve a peer from store through its b58str id', async () => {
const [peerInfo] = await peerUtils.createPeerInfo()
const id = peerInfo.id
if (multiaddrs.includes(addr4)) {
peerListenint4.push(peer)
}
}
let retrievedPeer = peerStore.get(id)
expect(retrievedPeer).to.not.exist()
// Put the peer in the store
peerStore.put(peerInfo)
retrievedPeer = peerStore.get(id)
expect(retrievedPeer).to.exist()
expect(retrievedPeer.id).to.equal(peerInfo.id)
expect(retrievedPeer.multiaddrs).to.eql(peerInfo.multiaddrs)
expect(retrievedPeer.protocols).to.eql(peerInfo.protocols)
})
it('should be able to remove a peer from store through its b58str id', async () => {
const [peerInfo] = await peerUtils.createPeerInfo()
const id = peerInfo.id
let removed = peerStore.remove(id)
expect(removed).to.eql(false)
// Put the peer in the store
peerStore.put(peerInfo)
expect(peerStore.peers.size).to.equal(1)
removed = peerStore.remove(id)
expect(removed).to.eql(true)
expect(peerStore.peers.size).to.equal(0)
})
it('should be able to get the multiaddrs for a peer', async () => {
const [peerInfo, relayInfo] = await peerUtils.createPeerInfo({ number: 2 })
const id = peerInfo.id
const ma1 = multiaddr('/ip4/127.0.0.1/tcp/4001')
const ma2 = multiaddr('/ip4/127.0.0.1/tcp/4002/ws')
const ma3 = multiaddr(`/ip4/127.0.0.1/tcp/4003/ws/p2p/${relayInfo.id.toB58String()}/p2p-circuit`)
peerInfo.multiaddrs.add(ma1)
peerInfo.multiaddrs.add(ma2)
peerInfo.multiaddrs.add(ma3)
const multiaddrs = peerStore.multiaddrsForPeer(peerInfo)
const expectedAddrs = [
ma1.encapsulate(`/p2p/${id.toB58String()}`),
ma2.encapsulate(`/p2p/${id.toB58String()}`),
ma3.encapsulate(`/p2p/${id.toB58String()}`)
]
expect(multiaddrs).to.eql(expectedAddrs)
expect(peerListenint4.length).to.eql(2)
expect(peerListenint4[0].id.toB58String()).to.eql(peerIds[2].toB58String())
expect(peerListenint4[1].id.toB58String()).to.eql(peerIds[3].toB58String())
})
})
})
describe('peer-store on discovery', () => {
// TODO: implement with discovery
})

View File

@ -0,0 +1,309 @@
'use strict'
/* eslint-env mocha */
const chai = require('chai')
chai.use(require('dirty-chai'))
const { expect } = chai
const pDefer = require('p-defer')
const PeerStore = require('../../src/peer-store')
const peerUtils = require('../utils/creators/peer')
const {
ERR_INVALID_PARAMETERS
} = require('../../src/errors')
const arraysAreEqual = (a, b) => a.length === b.length && a.sort().every((item, index) => b[index] === item)
describe('protoBook', () => {
let peerId
before(async () => {
[peerId] = await peerUtils.createPeerId()
})
describe('protoBook.set', () => {
let peerStore, pb
beforeEach(() => {
peerStore = new PeerStore()
pb = peerStore.protoBook
})
afterEach(() => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.set('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if no protocols provided', () => {
expect(() => {
pb.set(peerId)
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('replaces the stored content by default and emit change event', () => {
const defer = pDefer()
const supportedProtocols = ['protocol1', 'protocol2']
peerStore.once('change:protocols', ({ peerId, protocols }) => {
expect(peerId).to.exist()
expect(protocols).to.have.deep.members(supportedProtocols)
defer.resolve()
})
pb.set(peerId, supportedProtocols)
const protocols = pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocols)
return defer.promise
})
it('emits on set if not storing the exact same content', () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1', 'protocol2']
const supportedProtocolsB = ['protocol2']
let changeCounter = 0
peerStore.on('change:protocols', () => {
changeCounter++
if (changeCounter > 1) {
defer.resolve()
}
})
// set 1
pb.set(peerId, supportedProtocolsA)
// set 2 (same content)
pb.set(peerId, supportedProtocolsB)
const protocols = pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocolsB)
return defer.promise
})
it('does not emit on set if it is storing the exact same content', () => {
const defer = pDefer()
const supportedProtocols = ['protocol1', 'protocol2']
let changeCounter = 0
peerStore.on('change:protocols', () => {
changeCounter++
if (changeCounter > 1) {
defer.reject()
}
})
// set 1
pb.set(peerId, supportedProtocols)
// set 2 (same content)
pb.set(peerId, supportedProtocols)
// Wait 50ms for incorrect second event
setTimeout(() => {
defer.resolve()
}, 50)
return defer.promise
})
})
describe('protoBook.add', () => {
let peerStore, pb
beforeEach(() => {
peerStore = new PeerStore()
pb = peerStore.protoBook
})
afterEach(() => {
peerStore.removeAllListeners()
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.add('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('throwns invalid parameters error if no protocols provided', () => {
expect(() => {
pb.add(peerId)
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('adds the new content and emits change event', () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1', 'protocol2']
const supportedProtocolsB = ['protocol3']
const finalProtocols = supportedProtocolsA.concat(supportedProtocolsB)
let changeTrigger = 2
peerStore.on('change:protocols', ({ protocols }) => {
changeTrigger--
if (changeTrigger === 0 && arraysAreEqual(protocols, finalProtocols)) {
defer.resolve()
}
})
// Replace
pb.set(peerId, supportedProtocolsA)
let protocols = pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocolsA)
// Add
pb.add(peerId, supportedProtocolsB)
protocols = pb.get(peerId)
expect(protocols).to.have.deep.members(finalProtocols)
return defer.promise
})
it('emits on add if the content to add not exists', () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1']
const supportedProtocolsB = ['protocol2']
const finalProtocols = supportedProtocolsA.concat(supportedProtocolsB)
let changeCounter = 0
peerStore.on('change:protocols', () => {
changeCounter++
if (changeCounter > 1) {
defer.resolve()
}
})
// set 1
pb.set(peerId, supportedProtocolsA)
// set 2 (content already existing)
pb.add(peerId, supportedProtocolsB)
const protocols = pb.get(peerId)
expect(protocols).to.have.deep.members(finalProtocols)
return defer.promise
})
it('does not emit on add if the content to add already exists', () => {
const defer = pDefer()
const supportedProtocolsA = ['protocol1', 'protocol2']
const supportedProtocolsB = ['protocol2']
let changeCounter = 0
peerStore.on('change:protocols', () => {
changeCounter++
if (changeCounter > 1) {
defer.reject()
}
})
// set 1
pb.set(peerId, supportedProtocolsA)
// set 2 (content already existing)
pb.add(peerId, supportedProtocolsB)
// Wait 50ms for incorrect second event
setTimeout(() => {
defer.resolve()
}, 50)
return defer.promise
})
})
describe('protoBook.get', () => {
let peerStore, pb
beforeEach(() => {
peerStore = new PeerStore()
pb = peerStore.protoBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.get('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('returns undefined if no protocols are known for the provided peer', () => {
const protocols = pb.get(peerId)
expect(protocols).to.not.exist()
})
it('returns the protocols stored', () => {
const supportedProtocols = ['protocol1', 'protocol2']
pb.set(peerId, supportedProtocols)
const protocols = pb.get(peerId)
expect(protocols).to.have.deep.members(supportedProtocols)
})
})
describe('protoBook.delete', () => {
let peerStore, pb
beforeEach(() => {
peerStore = new PeerStore()
pb = peerStore.protoBook
})
it('throwns invalid parameters error if invalid PeerId is provided', () => {
expect(() => {
pb.delete('invalid peerId')
}).to.throw(ERR_INVALID_PARAMETERS)
})
it('returns false if no records exist for the peer and no event is emitted', () => {
const defer = pDefer()
peerStore.on('change:protocols', () => {
defer.reject()
})
const deleted = pb.delete(peerId)
expect(deleted).to.equal(false)
// Wait 50ms for incorrect invalid event
setTimeout(() => {
defer.resolve()
}, 50)
return defer.promise
})
it('returns true if the record exists and an event is emitted', () => {
const defer = pDefer()
const supportedProtocols = ['protocol1', 'protocol2']
pb.set(peerId, supportedProtocols)
// Listen after set
peerStore.on('change:protocols', ({ protocols }) => {
expect(protocols.length).to.eql(0)
defer.resolve()
})
const deleted = pb.delete(peerId)
expect(deleted).to.equal(true)
return defer.promise
})
})
})

View File

@ -32,11 +32,13 @@ describe('Pubsub subsystem is configurable', () => {
})
it('should start and stop by default once libp2p starts', async () => {
const [peerInfo] = await peerUtils.createPeerInfo()
peerInfo.multiaddrs.add(listenAddr)
const [peerId] = await peerUtils.createPeerId()
const customOptions = mergeOptions(subsystemOptions, {
peerInfo
peerId,
addresses: {
listen: [listenAddr]
}
})
libp2p = await create(customOptions)
@ -50,11 +52,13 @@ describe('Pubsub subsystem is configurable', () => {
})
it('should not start if disabled once libp2p starts', async () => {
const [peerInfo] = await peerUtils.createPeerInfo()
peerInfo.multiaddrs.add(listenAddr)
const [peerId] = await peerUtils.createPeerId()
const customOptions = mergeOptions(subsystemOptions, {
peerInfo,
peerId,
addresses: {
listen: [listenAddr]
},
config: {
pubsub: {
enabled: false
@ -70,11 +74,13 @@ describe('Pubsub subsystem is configurable', () => {
})
it('should allow a manual start', async () => {
const [peerInfo] = await peerUtils.createPeerInfo()
peerInfo.multiaddrs.add(listenAddr)
const [peerId] = await peerUtils.createPeerId()
const customOptions = mergeOptions(subsystemOptions, {
peerInfo,
peerId,
addresses: {
listen: [listenAddr]
},
config: {
pubsub: {
enabled: false

View File

@ -24,14 +24,11 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
const remoteListenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
describe('Pubsub subsystem is able to use different implementations', () => {
let peerInfo, remotePeerInfo
let peerId, remotePeerId
let libp2p, remoteLibp2p
beforeEach(async () => {
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
peerInfo.multiaddrs.add(listenAddr)
remotePeerInfo.multiaddrs.add(remoteListenAddr)
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
})
afterEach(() => Promise.all([
@ -53,14 +50,20 @@ describe('Pubsub subsystem is able to use different implementations', () => {
const data = 'hey!'
libp2p = await create(mergeOptions(baseOptions, {
peerInfo,
peerId,
addresses: {
listen: [listenAddr]
},
modules: {
pubsub: pubsub
}
}))
remoteLibp2p = await create(mergeOptions(baseOptions, {
peerInfo: remotePeerInfo,
peerId: remotePeerId,
addresses: {
listen: [remoteListenAddr]
},
modules: {
pubsub: pubsub
}
@ -71,9 +74,10 @@ describe('Pubsub subsystem is able to use different implementations', () => {
remoteLibp2p.start()
])
const libp2pId = libp2p.peerInfo.id.toB58String()
const libp2pId = libp2p.peerId.toB58String()
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.addresses.listen)
const connection = await libp2p.dialProtocol(remotePeerInfo, multicodec)
const connection = await libp2p.dialProtocol(remotePeerId, multicodec)
expect(connection).to.exist()
libp2p.pubsub.subscribe(topic, (msg) => {

View File

@ -19,30 +19,35 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
const remoteListenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
describe('Pubsub subsystem operates correctly', () => {
let peerInfo, remotePeerInfo
let peerId, remotePeerId
let libp2p, remoteLibp2p
beforeEach(async () => {
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
peerInfo.multiaddrs.add(listenAddr)
remotePeerInfo.multiaddrs.add(remoteListenAddr)
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
})
describe('pubsub started before connect', () => {
beforeEach(async () => {
libp2p = await create(mergeOptions(subsystemOptions, {
peerInfo
peerId,
addresses: {
listen: [listenAddr]
}
}))
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
peerInfo: remotePeerInfo
peerId: remotePeerId,
addresses: {
listen: [remoteListenAddr]
}
}))
await Promise.all([
libp2p.start(),
remoteLibp2p.start()
])
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.addresses.listen)
})
afterEach(() => Promise.all([
@ -55,7 +60,7 @@ describe('Pubsub subsystem operates correctly', () => {
})
it('should get notified of connected peers on dial', async () => {
const connection = await libp2p.dialProtocol(remotePeerInfo, subsystemMulticodecs)
const connection = await libp2p.dialProtocol(remotePeerId, subsystemMulticodecs)
expect(connection).to.exist()
@ -69,9 +74,9 @@ describe('Pubsub subsystem operates correctly', () => {
const defer = pDefer()
const topic = 'test-topic'
const data = 'hey!'
const libp2pId = libp2p.peerInfo.id.toB58String()
const libp2pId = libp2p.peerId.toB58String()
await libp2p.dialProtocol(remotePeerInfo, subsystemMulticodecs)
await libp2p.dialProtocol(remotePeerId, subsystemMulticodecs)
let subscribedTopics = libp2p.pubsub.getTopics()
expect(subscribedTopics).to.not.include(topic)
@ -98,11 +103,17 @@ describe('Pubsub subsystem operates correctly', () => {
describe('pubsub started after connect', () => {
beforeEach(async () => {
libp2p = await create(mergeOptions(subsystemOptions, {
peerInfo
peerId,
addresses: {
listen: [listenAddr]
}
}))
remoteLibp2p = await create(mergeOptions(subsystemOptions, {
peerInfo: remotePeerInfo,
peerId: remotePeerId,
addresses: {
listen: [remoteListenAddr]
},
config: {
pubsub: {
enabled: false
@ -112,6 +123,8 @@ describe('Pubsub subsystem operates correctly', () => {
await libp2p.start()
await remoteLibp2p.start()
libp2p.peerStore.addressBook.set(remotePeerId, remoteLibp2p.addresses.listen)
})
afterEach(() => Promise.all([
@ -124,7 +137,7 @@ describe('Pubsub subsystem operates correctly', () => {
})
it('should get notified of connected peers after starting', async () => {
const connection = await libp2p.dial(remotePeerInfo)
const connection = await libp2p.dial(remotePeerId)
expect(connection).to.exist()
expect(libp2p.pubsub._pubsub.peers.size).to.be.eql(0)
@ -141,11 +154,11 @@ describe('Pubsub subsystem operates correctly', () => {
it('should receive pubsub messages', async function () {
this.timeout(10e3)
const defer = pDefer()
const libp2pId = libp2p.peerInfo.id.toB58String()
const libp2pId = libp2p.peerId.toB58String()
const topic = 'test-topic'
const data = 'hey!'
await libp2p.dial(remotePeerInfo)
await libp2p.dial(remotePeerId)
remoteLibp2p.pubsub.start()

View File

@ -16,20 +16,20 @@ const peerUtils = require('../utils/creators/peer')
const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
describe('registrar on dial', () => {
let peerInfo
let remotePeerInfo
let peerId
let remotePeerId
let libp2p
let remoteLibp2p
let remoteAddr
before(async () => {
[peerInfo, remotePeerInfo] = await peerUtils.createPeerInfo({ number: 2 })
[peerId, remotePeerId] = await peerUtils.createPeerId({ number: 2 })
remoteLibp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo: remotePeerInfo
peerId: remotePeerId
}))
await remoteLibp2p.transportManager.listen([listenAddr])
remoteAddr = remoteLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${remotePeerInfo.id.toB58String()}`)
remoteAddr = remoteLibp2p.transportManager.getAddrs()[0].encapsulate(`/p2p/${remotePeerId.toB58String()}`)
})
after(async () => {
@ -40,7 +40,7 @@ describe('registrar on dial', () => {
it('should inform registrar of a new connection', async () => {
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo
peerId
}))
sinon.spy(remoteLibp2p.registrar, 'onConnect')
@ -48,16 +48,16 @@ describe('registrar on dial', () => {
await libp2p.dial(remoteAddr)
expect(remoteLibp2p.registrar.onConnect.callCount).to.equal(1)
const libp2pConn = libp2p.registrar.getConnection(remotePeerInfo)
const libp2pConn = libp2p.registrar.getConnection(remotePeerId)
expect(libp2pConn).to.exist()
const remoteConn = remoteLibp2p.registrar.getConnection(peerInfo)
const remoteConn = remoteLibp2p.registrar.getConnection(peerId)
expect(remoteConn).to.exist()
})
it('should be closed on libp2p stop', async () => {
libp2p = new Libp2p(mergeOptions(baseOptions, {
peerInfo
peerId
}))
await libp2p.dial(remoteAddr)

View File

@ -6,7 +6,6 @@ chai.use(require('dirty-chai'))
const { expect } = chai
const pDefer = require('p-defer')
const PeerInfo = require('peer-info')
const Topology = require('libp2p-interfaces/src/topology/multicodec-topology')
const PeerStore = require('../../src/peer-store')
const Registrar = require('../../src/registrar')
@ -83,27 +82,25 @@ describe('registrar', () => {
// Setup connections before registrar
const conn = await createMockConnection()
const remotePeerInfo = await PeerInfo.create(conn.remotePeer)
const remotePeerId = conn.remotePeer
// Add protocol to peer
remotePeerInfo.protocols.add(multicodec)
// Add connected peer with protocol to peerStore and registrar
peerStore.protoBook.add(remotePeerId, [multicodec])
// Add connected peer to peerStore and registrar
peerStore.put(remotePeerInfo)
registrar.onConnect(remotePeerInfo, conn)
registrar.onConnect(remotePeerId, conn)
expect(registrar.connections.size).to.eql(1)
const topologyProps = new Topology({
multicodecs: multicodec,
handlers: {
onConnect: (peerInfo, connection) => {
expect(peerInfo.id.toB58String()).to.eql(remotePeerInfo.id.toB58String())
onConnect: (peerId, connection) => {
expect(peerId.toB58String()).to.eql(remotePeerId.toB58String())
expect(connection.id).to.eql(conn.id)
onConnectDefer.resolve()
},
onDisconnect: (peerInfo) => {
expect(peerInfo.id.toB58String()).to.eql(remotePeerInfo.id.toB58String())
onDisconnect: (peerId) => {
expect(peerId.toB58String()).to.eql(remotePeerId.toB58String())
onDisconnectDefer.resolve()
}
@ -117,7 +114,7 @@ describe('registrar', () => {
// Topology created
expect(topology).to.exist()
registrar.onDisconnect(remotePeerInfo)
registrar.onDisconnect(remotePeerId)
expect(registrar.connections.size).to.eql(0)
// Wait for handlers to be called
@ -153,21 +150,19 @@ describe('registrar', () => {
// Setup connections before registrar
const conn = await createMockConnection()
const peerInfo = await PeerInfo.create(conn.remotePeer)
const remotePeerId = conn.remotePeer
// Add connected peer to peerStore and registrar
peerStore.put(peerInfo)
registrar.onConnect(peerInfo, conn)
peerStore.protoBook.set(remotePeerId, [])
registrar.onConnect(remotePeerId, conn)
// Add protocol to peer and update it
peerInfo.protocols.add(multicodec)
peerStore.put(peerInfo)
peerStore.protoBook.add(remotePeerId, [multicodec])
await onConnectDefer.promise
// Remove protocol to peer and update it
peerInfo.protocols.delete(multicodec)
peerStore.replace(peerInfo)
peerStore.protoBook.set(remotePeerId, [])
await onDisconnectDefer.promise
})
@ -189,22 +184,21 @@ describe('registrar', () => {
registrar.register(topologyProps)
// Setup connections before registrar
const [localPeer, remotePeer] = await peerUtils.createPeerInfo({ number: 2 })
const [localPeer, remotePeer] = await peerUtils.createPeerId({ number: 2 })
const conn1 = await createMockConnection({ localPeer: localPeer.id, remotePeer: remotePeer.id })
const conn2 = await createMockConnection({ localPeer: localPeer.id, remotePeer: remotePeer.id })
const peerInfo = await PeerInfo.create(remotePeer.id)
const id = peerInfo.id.toB58String()
const conn1 = await createMockConnection({ localPeer, remotePeer })
const conn2 = await createMockConnection({ localPeer, remotePeer })
const id = remotePeer.toB58String()
// Add connection to registrar
peerStore.put(peerInfo)
registrar.onConnect(peerInfo, conn1)
registrar.onConnect(peerInfo, conn2)
registrar.onConnect(remotePeer, conn1)
registrar.onConnect(remotePeer, conn2)
expect(registrar.connections.get(id).length).to.eql(2)
conn2._stat.status = 'closed'
registrar.onDisconnect(peerInfo, conn2)
registrar.onDisconnect(remotePeer, conn2)
const peerConnections = registrar.connections.get(id)
expect(peerConnections.length).to.eql(1)

View File

@ -11,13 +11,13 @@ module.exports.createMockConnection = async (properties = {}) => {
const localAddr = multiaddr('/ip4/127.0.0.1/tcp/8080')
const remoteAddr = multiaddr('/ip4/127.0.0.1/tcp/8081')
const [localPeer, remotePeer] = await peerUtils.createPeerInfo({ number: 2 })
const [localPeer, remotePeer] = await peerUtils.createPeerId({ number: 2 })
const openStreams = []
let streamId = 0
return new Connection({
localPeer: localPeer.id,
remotePeer: remotePeer.id,
localPeer: localPeer,
remotePeer: remotePeer,
localAddr,
remoteAddr,
stat: {

View File

@ -16,7 +16,6 @@ const { codes: ErrorCodes } = require('../../src/errors')
const Libp2p = require('../../src')
const Peers = require('../fixtures/peers')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
describe('Transport Manager (WebSockets)', () => {
let tm
@ -88,12 +87,11 @@ describe('Transport Manager (WebSockets)', () => {
})
describe('libp2p.transportManager', () => {
let peerInfo
let peerId
let libp2p
before(async () => {
const peerId = await PeerId.createFromJSON(Peers[0])
peerInfo = new PeerInfo(peerId)
peerId = await PeerId.createFromJSON(Peers[0])
})
afterEach(async () => {
@ -104,7 +102,7 @@ describe('libp2p.transportManager', () => {
it('should create a TransportManager', () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport]
}
@ -122,7 +120,7 @@ describe('libp2p.transportManager', () => {
another: 'value'
}
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [spy]
},
@ -146,7 +144,7 @@ describe('libp2p.transportManager', () => {
it('starting and stopping libp2p should start and stop TransportManager', async () => {
libp2p = new Libp2p({
peerInfo,
peerId,
modules: {
transport: [Transport]
}

View File

@ -8,7 +8,6 @@ const sinon = require('sinon')
const Muxer = require('libp2p-mplex')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const pipe = require('it-pipe')
const { collect } = require('streaming-iterables')
const pSettle = require('p-settle')
@ -347,11 +346,10 @@ describe('libp2p.upgrader', () => {
let libp2p
before(async () => {
const ids = await Promise.all([
peers = await Promise.all([
PeerId.createFromJSON(Peers[0]),
PeerId.createFromJSON(Peers[1])
])
peers = ids.map(peerId => new PeerInfo(peerId))
})
afterEach(async () => {
@ -363,7 +361,7 @@ describe('libp2p.upgrader', () => {
it('should create an Upgrader', () => {
const protector = new Protector(swarmKeyBuffer)
libp2p = new Libp2p({
peerInfo: peers[0],
peerId: peers[0],
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -382,7 +380,7 @@ describe('libp2p.upgrader', () => {
it('should be able to register and unregister a handler', () => {
libp2p = new Libp2p({
peerInfo: peers[0],
peerId: peers[0],
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -405,7 +403,7 @@ describe('libp2p.upgrader', () => {
it('should emit connect and disconnect events', async () => {
const remotePeer = peers[1]
libp2p = new Libp2p({
peerInfo: peers[0],
peerId: peers[0],
modules: {
transport: [Transport],
streamMuxer: [Muxer],
@ -414,12 +412,12 @@ describe('libp2p.upgrader', () => {
})
const remoteUpgrader = new Upgrader({
localPeer: remotePeer.id,
localPeer: remotePeer,
muxers: new Map([[Muxer.multicodec, Muxer]]),
cryptos: new Map([[Crypto.protocol, Crypto]])
})
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer: remotePeer.id })
const { inbound, outbound } = mockMultiaddrConnPair({ addrs, remotePeer })
// Spy on emit for easy verification
sinon.spy(libp2p, 'emit')
@ -430,15 +428,16 @@ describe('libp2p.upgrader', () => {
remoteUpgrader.upgradeInbound(inbound)
])
expect(libp2p.emit.callCount).to.equal(1)
let [event, peerInfo] = libp2p.emit.getCall(0).args
let [event, peerId] = libp2p.emit.getCall(0).args
expect(event).to.equal('peer:connect')
expect(peerInfo.id.isEqual(remotePeer.id)).to.equal(true)
expect(peerId.isEqual(remotePeer)).to.equal(true)
// Close and check the disconnect event
await Promise.all(connections.map(conn => conn.close()))
expect(libp2p.emit.callCount).to.equal(2)
;([event, peerInfo] = libp2p.emit.getCall(1).args)
;([event, peerId] = libp2p.emit.getCall(1).args)
expect(event).to.equal('peer:disconnect')
expect(peerInfo.id.isEqual(remotePeer.id)).to.equal(true)
expect(peerId.isEqual(remotePeer)).to.equal(true)
})
})

View File

@ -4,7 +4,6 @@ const pTimes = require('p-times')
const multiaddr = require('multiaddr')
const PeerId = require('peer-id')
const PeerInfo = require('peer-info')
const Libp2p = require('../../../src')
const Peers = require('../../fixtures/peers')
@ -19,37 +18,36 @@ const listenAddr = multiaddr('/ip4/127.0.0.1/tcp/0')
* @param {number} [properties.number] number of peers (default: 1).
* @param {boolean} [properties.fixture] use fixture for peer-id generation (default: true)
* @param {boolean} [properties.started] nodes should start (default: true)
* @param {boolean} [properties.populateAddressBooks] nodes addressBooks should be populated with other peers (default: true)
* @return {Promise<Array<Libp2p>>}
*/
async function createPeer ({ number = 1, fixture = true, started = true, config = defaultOptions } = {}) {
const peerInfos = await createPeerInfo({ number, fixture })
async function createPeer ({ number = 1, fixture = true, started = true, populateAddressBooks = true, config = defaultOptions } = {}) {
const peerIds = await createPeerId({ number, fixture })
const addresses = started ? { listen: [listenAddr] } : {}
const peers = await pTimes(number, (i) => Libp2p.create({
peerInfo: peerInfos[i],
peerId: peerIds[i],
addresses,
...config
}))
if (started) {
await Promise.all(peers.map((p) => {
p.peerInfo.multiaddrs.add(listenAddr)
return p.start()
}))
await Promise.all(peers.map((p) => p.start()))
populateAddressBooks && _populateAddressBooks(peers)
}
return peers
}
/**
* Create Peer-ids.
* @param {Object} [properties]
* @param {number} [properties.number] number of peers (default: 1).
* @param {boolean} [properties.fixture] use fixture for peer-id generation (default: true)
* @return {Promise<Array<PeerInfo>>}
*/
async function createPeerInfo ({ number = 1, fixture = true } = {}) {
const peerIds = await createPeerId({ number, fixture })
return pTimes(number, (i) => PeerInfo.create(peerIds[i]))
function _populateAddressBooks (peers) {
for (let i = 0; i < peers.length; i++) {
for (let j = 0; j < peers.length; j++) {
if (i !== j) {
peers[i].peerStore.addressBook.set(peers[j].peerId, peers[j].addresses.listen)
}
}
}
}
/**
@ -67,5 +65,4 @@ function createPeerId ({ number = 1, fixture = true } = {}) {
}
module.exports.createPeer = createPeer
module.exports.createPeerInfo = createPeerInfo
module.exports.createPeerId = createPeerId

View File

@ -16,13 +16,13 @@ module.exports = async (properties = {}) => {
const localAddr = multiaddr('/ip4/127.0.0.1/tcp/8080')
const remoteAddr = multiaddr('/ip4/127.0.0.1/tcp/8081')
const [localPeer, remotePeer] = await peerUtils.createPeerInfo({ number: 2 })
const [localPeer, remotePeer] = await peerUtils.createPeerId({ number: 2 })
const openStreams = []
let streamId = 0
return new Connection({
localPeer: localPeer.id,
remotePeer: remotePeer.id,
localPeer: localPeer,
remotePeer: remotePeer,
localAddr,
remoteAddr,
stat: {