Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: address management #612

Merged
merged 4 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 118 additions & 12 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
* [`handle`](#handle)
* [`unhandle`](#unhandle)
* [`ping`](#ping)
* [`multiaddrs`](#multiaddrs)
* [`addressManager.getListenAddrs`](#addressmanagergetlistenaddrs)
* [`addressmger.getAnnounceAddrs`](#addressmanagergetannounceaddrs)
* [`addressManager.getNoAnnounceAddrs`](#addressmanagergetnoannounceaddrs)
* [`contentRouting.findProviders`](#contentroutingfindproviders)
* [`contentRouting.provide`](#contentroutingprovide)
* [`contentRouting.put`](#contentroutingput)
Expand Down Expand Up @@ -63,7 +67,7 @@ Creates an instance of Libp2p.
|------|------|-------------|
| 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.addresses] | `{ listen: Array<string>, announce: Array<string>, noAnnounce: Array<string> }` | Addresses for transport listening and to advertise 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) |
Expand Down Expand Up @@ -360,31 +364,106 @@ Pings a given peer and get the operation's latency.
const latency = await libp2p.ping(otherPeerId)
```

### peerRouting.findPeer
## multiaddrs

Iterates over all peer routers in series to find the given peer. If the DHT is enabled, it will be tried first.
Gets the multiaddrs the libp2p node announces to the network. This computes the advertising multiaddrs
of the peer by joining the multiaddrs that libp2p transports are listening on with the announce multiaddrs
provided in the libp2p config. Configured no announce multiaddrs will be filtered out of the advertised addresses.

`libp2p.peerRouting.findPeer(peerId, options)`
`libp2p.multiaddrs`

#### Parameters
#### Returns

| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | ID of the peer to find |
| options | `object` | operation options |
| options.timeout | `number` | maximum time the query should run |
| Type | Description |
|------|-------------|
| `Array<Multiaddr>` | Advertising multiaddrs |

#### Example

```js
// ...
const listenMa = libp2p.multiaddrs
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
```

### addressManager.getListenAddrs

Get the multiaddrs that were provided for listening on libp2p transports.

`libp2p.addressManager.getListenAddrs()`

#### Returns

| Type | Description |
|------|-------------|
| `Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>` | Peer data of a known peer |
| `Array<Multiaddr>` | Provided listening multiaddrs |

#### Example

```js
// ...
const peer = await libp2p.peerRouting.findPeer(peerId, options)
const listenMa = libp2p.addressManager.getListenAddrs()
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
```

### addressManager.getAnnounceAddrs

Get the multiaddrs that were provided to announce to the network.

`libp2p.addressManager.getAnnounceAddrs()`

#### Returns

| Type | Description |
|------|-------------|
| `Array<Multiaddr>` | Provided announce multiaddrs |

#### Example

```js
// ...
const announceMa = libp2p.addressManager.getAnnounceAddrs()
// [ <Multiaddr 047f00000106f9ba - /dns4/peer.io/...> ]
```

### addressManager.getNoAnnounceAddrs

Get the multiaddrs that were provided to not announce to the network.

`libp2p.addressManager.getNoAnnounceAddrs()`

#### Returns

| Type | Description |
|------|-------------|
| `Array<Multiaddr>` | Provided noAnnounce multiaddrs |

#### Example

```js
// ...
const noAnnounceMa = libp2p.addressManager.getNoAnnounceAddrs()
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
```

### transportManager.getAddrs

Get the multiaddrs that libp2p transports are using to listen on.

`libp2p.transportManager.getAddrs()`

#### Returns

| Type | Description |
|------|-------------|
| `Array<Multiaddr>` | listening multiaddrs |

#### Example

```js
// ...
const listenMa = libp2p.transportManager.getAddrs()
// [ <Multiaddr 047f00000106f9ba - /ip4/127.0.0.1/tcp/63930> ]
```

### contentRouting.findProviders
Expand Down Expand Up @@ -533,6 +612,33 @@ const key = '/key'
const { from, val } = await libp2p.contentRouting.get(key)
```

### peerRouting.findPeer

Iterates over all peer routers in series to find the given peer. If the DHT is enabled, it will be tried first.

`libp2p.peerRouting.findPeer(peerId, options)`

#### Parameters

| Name | Type | Description |
|------|------|-------------|
| peerId | [`PeerId`][peer-id] | ID of the peer to find |
| options | `object` | operation options |
| options.timeout | `number` | maximum time the query should run |

#### Returns

| Type | Description |
|------|-------------|
| `Promise<{ id: PeerId, multiaddrs: Multiaddr[] }>` | Peer data of a known peer |

#### Example

```js
// ...
const peer = await libp2p.peerRouting.findPeer(peerId, options)
```

### peerStore.addressBook.add

Adds known `multiaddrs` of a given peer. If the peer is not known, it will be set with the provided multiaddrs.
Expand Down
7 changes: 5 additions & 2 deletions doc/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,12 @@ Moreover, the majority of the modules can be customized via option parameters. T
Besides the `modules` and `config`, libp2p allows other internal options and configurations:
- `datastore`: an instance of [ipfs/interface-datastore](https://github.com/ipfs/interface-datastore/) modules.
- This is used in modules such as the DHT. If it is not provided, `js-libp2p` will use an in memory datastore.
- `peerInfo`: a previously created instance of [libp2p/js-peer-info](https://github.com/libp2p/js-peer-info).
- `peerId`: the identity of the node, an instance of [libp2p/js-peer-id](https://github.com/libp2p/js-peer-id).
- This is particularly useful if you want to reuse the same `peer-id`, as well as for modules like `libp2p-delegated-content-routing`, which need a `peer-id` in their instantiation.
- `addresses`: an object containing `listen`, `announce` and `noAnnounce` properties with `Array<string>`:
- `listen` addresses will be provided to the libp2p underlying transports for listening on them.
- `announce` addresses will be used to compute the advertises that the node should advertise to the network.
- `noAnnounce` addresses will be used as a filter to compute the advertises that the node should advertise to the network.

### Examples

Expand Down Expand Up @@ -533,7 +537,6 @@ const node = await Libp2p.create({

As libp2p is designed to be a modular networking library, its usage will vary based on individual project needs. We've included links to some existing project configurations for your reference, in case you wish to replicate their configuration:


- [libp2p-ipfs-nodejs](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-nodejs.js) - libp2p configuration used by js-ipfs when running in Node.js
- [libp2p-ipfs-browser](https://github.com/ipfs/js-ipfs/tree/master/src/core/runtime/libp2p-browser.js) - libp2p configuration used by js-ipfs when running in a Browser (that supports WebRTC)

Expand Down
9 changes: 9 additions & 0 deletions doc/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ const SECIO = require('libp2p-secio')
const MPLEX = require('libp2p-mplex')

const node = await Libp2p.create({
addresses: {
listen: ['/ip4/127.0.0.1/tcp/8000/ws']
},
modules: {
transport: [WebSockets],
connEncryption: [SECIO],
Expand All @@ -154,6 +157,12 @@ const node = await Libp2p.create({
await node.start()
console.log('libp2p has started')

const listenAddrs = node.transportManager.getAddrs()
console.log('libp2p is listening on the following addresses: ', listenAddrs)

const advertiseAddrs = node.multiaddrs
console.log('libp2p is advertising the following addresses: ', advertiseAddrs)

// stop libp2p
await node.stop()
console.log('libp2p has stopped')
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"libp2p-floodsub": "^0.21.0",
"libp2p-gossipsub": "^0.4.0",
"libp2p-kad-dht": "^0.19.1",
"libp2p-mdns": "^0.14.0",
"libp2p-mdns": "^0.14.1",
"libp2p-mplex": "^0.9.1",
"libp2p-secio": "^0.12.1",
"libp2p-tcp": "^0.14.1",
Expand Down
49 changes: 49 additions & 0 deletions src/address-manager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Address Manager

The Address manager is responsible for keeping an updated register of the peer's addresses. It includes 3 different types of Addresses: `Listen Addresses`, `Announce Addresses` and `No Announce Addresses`.

These Addresses should be specified in your libp2p [configuration](../../doc/CONFIGURATION.md) when you create your node.

## Listen Addresses

A libp2p node should have a set of listen addresses, which will be used by libp2p underlying transports to listen for dials from other nodes in the network.

Before a libp2p node starts, its configured listen addresses will be passed to the AddressManager, so that during startup the libp2p transports can use them to listen for connections. Accordingly, listen addresses should be specified through the libp2p configuration, in order to have the `AddressManager` created with them.

It is important pointing out that libp2p accepts ephemeral listening addresses. In this context, the provided listen addresses might not be exactly the same as the ones used by the transports. For example TCP may replace `/ip4/0.0.0.0/tcp/0` with something like `/ip4/127.0.0.1/tcp/8989`. As a consequence, libp2p should take into account this when determining its advertised addresses.

## Announce Addresses

In some scenarios, a libp2p node will need to announce addresses that it is not listening on. In other words, Announce Addresses are an amendment to the Listen Addresses that aim to enable other nodes to achieve connectivity to this node.

Scenarios for Announce Addresses include:
- when you setup a libp2p node in your private network at home, but you need to announce your public IP Address to the outside world;
- when you want to announce a DNS address, which maps to your public IP Address.

## No Announce Addresses

While we need to add Announce Addresses to enable peers' connectivity, we should also avoid announcing addresses that will not be reachable. No Announce Addresses should be specified so that they are filtered from the advertised multiaddrs.

As stated in the Listen Addresses section, Listen Addresses might be modified by libp2p transports after the successfully bind to those addresses. Libp2p should also take these changes into account so that they can be matched when No Announce Addresses are being filtered out of the advertised multiaddrs.

## Implementation

When a libp2p node is created, the Address Manager will be populated from the provided addresses through the libp2p configuration. Once the node is started, the Transport Manager component will gather the listen addresses from the Address Manager, so that the libp2p transports can attempt to bind to them.

Libp2p will use the the Address Manager as the source of truth when advertising the peers addresses. After all transports are ready, other libp2p components/subsystems will kickoff, namely the Identify Service and the DHT. Both of them will announce the node addresses to the other peers in the network. The announce and noAnnounce addresses will have an important role here and will be gathered by libp2p to compute its current addresses to advertise everytime it is needed.

## Future Considerations

### Dynamic address modifications

In a future iteration, we can enable these addresses to be modified in runtime. For this, the Address Manager should be responsible for notifying interested subsystems of these changes, through an Event Emitter.

#### Modify Listen Addresses

While adding new addresses to listen on runtime should be trivial, removing a listen address might have bad implications for the node, since all the connections using that listen address will be closed. However, libp2p should provide a mechanism for both adding and removing listen addresses in the future.

Every time a new listen address is added, the Address Manager should emit an event with the new multiaddrs to listen. The Transport Manager should listen to this events and act accordingly.

#### Modify Announce Addresses

When the announce addresses are modified, the Address Manager should emit an event so that other subsystems can act accordingly. For example, libp2p identify service should use the libp2p push protocol to inform other peers about these changes.
55 changes: 55 additions & 0 deletions src/address-manager/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict'

const debug = require('debug')
const log = debug('libp2p:addresses')
log.error = debug('libp2p:addresses:error')

const multiaddr = require('multiaddr')

/**
* Responsible for managing this peers addresses.
* Peers can specify their listen, announce and noAnnounce addresses.
* The listen addresses will be used by the libp2p transports to listen for new connections,
* while the announce an noAnnounce addresses will be combined with the listen addresses for
* address adverstising to other peers in the network.
*/
class AddressManager {
/**
* @constructor
* @param {object} [options]
* @param {Array<string>} [options.listen = []] list of multiaddrs string representation to listen.
* @param {Array<string>} [options.announce = []] list of multiaddrs string representation to announce.
* @param {Array<string>} [options.noAnnounce = []] list of multiaddrs string representation to not announce.
*/
constructor ({ listen = [], announce = [], noAnnounce = [] } = {}) {
this.listen = new Set(listen)
this.announce = new Set(announce)
this.noAnnounce = new Set(noAnnounce)
}

/**
* Get peer listen multiaddrs.
* @return {Array<Multiaddr>}
*/
getListenAddrs () {
return Array.from(this.listen).map((a) => multiaddr(a))
}

/**
* Get peer announcing multiaddrs.
* @return {Array<Multiaddr>}
*/
getAnnounceAddrs () {
return Array.from(this.announce).map((a) => multiaddr(a))
}

/**
* Get peer noAnnouncing multiaddrs.
* @return {Array<Multiaddr>}
*/
getNoAnnounceAddrs () {
return Array.from(this.noAnnounce).map((a) => multiaddr(a))
}
}

module.exports = AddressManager
4 changes: 2 additions & 2 deletions src/circuit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class Circuit {
this._connectionManager = libp2p.connectionManager
this._upgrader = upgrader
this._options = libp2p._config.relay
this.addresses = libp2p.addresses
this._libp2p = libp2p
this.peerId = libp2p.peerId
this._registrar.handle(multicodec, this._onProtocol.bind(this))
}
Expand Down Expand Up @@ -122,7 +122,7 @@ class Circuit {
type: CircuitPB.Type.HOP,
srcPeer: {
id: this.peerId.toBytes(),
addrs: this.addresses.listen.map(addr => addr.buffer)
addrs: this._libp2p.multiaddrs.map(addr => addr.buffer)
},
dstPeer: {
id: destinationPeer.toBytes(),
Expand Down
4 changes: 3 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ const Constants = require('./constants')

const DefaultConfig = {
addresses: {
listen: []
listen: [],
announce: [],
noAnnounce: []
},
connectionManager: {
minPeers: 25
Expand Down
Loading