Skip to content

Commit

Permalink
deps!: update to libp2p v1 deps (#128)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: requires libp2p v1 or later
  • Loading branch information
achingbrain committed Dec 4, 2023
1 parent c5eed68 commit 9d0da56
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 96 deletions.
78 changes: 28 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,31 @@
# @libp2p/pubsub-peer-discovery <!-- omit in toc -->

[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-pubsub-peer-discovery.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-pubsub-peer-discovery)
[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p-pubsub-peer-discovery/js-test-and-release.yml?branch=master\&style=flat-square)](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery/actions/workflows/js-test-and-release.yml?query=branch%3Amain)

> A libp2p module that uses pubsub for mdns like peer discovery
## Table of contents <!-- omit in toc -->

- [Install](#install)
- [Browser `<script>` tag](#browser-script-tag)
- [Design](#design)
- [Flow](#flow)
- [Security Considerations](#security-considerations)
- [Usage](#usage)
- [Requirements](#requirements)
- [Usage in js-libp2p](#usage-in-js-libp2p)
- [Customizing Pubsub Peer Discovery](#customizing-pubsub-peer-discovery)
- [Options](#options)
- [Default Topic](#default-topic)
- [API Docs](#api-docs)
- [License](#license)
- [Contribution](#contribution)

## Install

```console
$ npm i @libp2p/pubsub-peer-discovery
```

### Browser `<script>` tag

Loading this module through a script tag will make it's exports available as `Libp2pPubsubPeerDiscovery` in the global namespace.

```html
<script src="https://unpkg.com/@libp2p/pubsub-peer-discovery/dist/index.min.js"></script>
```

## Design
# About

### Flow
When the discovery module is started by libp2p it subscribes to the discovery pubsub topic(s)

- When the discovery module is started by libp2p it subscribes to the discovery pubsub topic(s)
- It will immediately broadcast your peer data via pubsub and repeat the broadcast on the configured `interval`
It will immediately broadcast your peer data via pubsub and repeat the broadcast on the configured `interval`

### Security Considerations
## Security Considerations

It is worth noting that this module does not include any message signing for broadcasts. The reason for this is that libp2p-pubsub supports message signing and enables it by default, which means the message you received has been verified to be from the originator, so we can trust that the peer information we have received is indeed from the peer who owns it. This doesn't mean the peer can't falsify its own records, but this module isn't currently concerned with that scenario.

## Usage

### Requirements
## Requirements

This module *MUST* be used on a libp2p node that is running [Pubsub](https://github.com/libp2p/js-libp2p-pubsub). If Pubsub does not exist, or is not running, this module will not work.

To run a PubSub service, include a `pubsub` implementation in your services map such as `@chainsafe/libp2p-gossipsub`.

For more information see the [docs on customizing libp2p](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-libp2p).
For more information see the [docs on customizing libp2p](https://github.com/libp2p/js-libp2p/blob/main/doc/CONFIGURATION.md#customizing-libp2p).

### Usage in js-libp2p
## Example - Usage in js-libp2p

See the [js-libp2p configuration docs](https://github.com/libp2p/js-libp2p/blob/master/doc/CONFIGURATION.md#customizing-peer-discovery) for how to include this module as a peer discovery module in js-libp2p.
See the [js-libp2p configuration docs](https://github.com/libp2p/js-libp2p/blob/main/doc/CONFIGURATION.md#customizing-peer-discovery) for how to include this module as a peer discovery module in js-libp2p.

If you are only interested in listening to the global pubsub topic the minimal configuration for using this with libp2p is:

Expand Down Expand Up @@ -94,7 +58,7 @@ const node = await createLibp2p({
})
```

### Customizing Pubsub Peer Discovery
## Example - Customizing Pubsub Peer Discovery

There are a few options you can use to customize `Pubsub Peer Discovery`. You can see the detailed [options](#options) below.

Expand All @@ -120,29 +84,43 @@ const node = await createLibp2p({
})
```

#### Options
## Options

| Name | Type | Description |
| ---------- | --------------- | -------------------------------------------------------------------------------------------------------------- |
| interval | `number` | How often (in `ms`), after initial broadcast, your node should broadcast your peer data. Default (`10000ms`) |
| topics | `Array<string>` | An Array of topic strings. If set, the default topic will not be used and must be included explicitly here |
| listenOnly | `boolean` | If true it will not broadcast peer data. Dont set this unless you have a specific reason to. Default (`false`) |

#### Default Topic
## Default Topic

The default pubsub topic the module subscribes to is `_peer-discovery._p2p._pubsub`, which is also set on `PubsubPeerDiscovery.TOPIC`.

## API Docs
# Install

```console
$ npm i @libp2p/pubsub-peer-discovery
```

## Browser `<script>` tag

Loading this module through a script tag will make it's exports available as `Libp2pPubsubPeerDiscovery` in the global namespace.

```html
<script src="https://unpkg.com/@libp2p/pubsub-peer-discovery/dist/index.min.js"></script>
```

# API Docs

- <https://libp2p.github.io/js-libp2p-pubsub-peer-discovery>

## License
# License

Licensed under either of

- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)

## Contribution
# Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,18 @@
"docs": "aegir docs"
},
"dependencies": {
"@libp2p/interface": "^0.1.2",
"@libp2p/interface-internal": "^0.1.5",
"@libp2p/logger": "^3.0.2",
"@libp2p/peer-id": "^3.0.2",
"@libp2p/interface": "^1.0.1",
"@libp2p/interface-internal": "^1.0.1",
"@libp2p/peer-id": "^4.0.1",
"@multiformats/multiaddr": "^12.0.0",
"protons-runtime": "^5.0.0",
"uint8arraylist": "^2.4.3"
"uint8arraylist": "^2.4.3",
"uint8arrays": "^4.0.9"
},
"devDependencies": {
"@libp2p/interface-compliance-tests": "^4.0.6",
"@libp2p/peer-id-factory": "^3.0.4",
"@libp2p/interface-compliance-tests": "^5.0.5",
"@libp2p/logger": "^4.0.1",
"@libp2p/peer-id-factory": "^4.0.0",
"aegir": "^41.0.5",
"p-defer": "^4.0.0",
"p-wait-for": "^5.0.0",
Expand Down
128 changes: 108 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,103 @@
import { CustomEvent, EventEmitter } from '@libp2p/interface/events'
import { peerDiscovery } from '@libp2p/interface/peer-discovery'
import { logger } from '@libp2p/logger'
/**
* @packageDocumentation
*
* When the discovery module is started by libp2p it subscribes to the discovery pubsub topic(s)
*
* It will immediately broadcast your peer data via pubsub and repeat the broadcast on the configured `interval`
*
* ## Security Considerations
*
* It is worth noting that this module does not include any message signing for broadcasts. The reason for this is that libp2p-pubsub supports message signing and enables it by default, which means the message you received has been verified to be from the originator, so we can trust that the peer information we have received is indeed from the peer who owns it. This doesn't mean the peer can't falsify its own records, but this module isn't currently concerned with that scenario.
*
* ## Requirements
*
* This module *MUST* be used on a libp2p node that is running [Pubsub](https://github.com/libp2p/js-libp2p-pubsub). If Pubsub does not exist, or is not running, this module will not work.
*
* To run a PubSub service, include a `pubsub` implementation in your services map such as `@chainsafe/libp2p-gossipsub`.
*
* For more information see the [docs on customizing libp2p](https://github.com/libp2p/js-libp2p/blob/main/doc/CONFIGURATION.md#customizing-libp2p).
*
* @example Usage in js-libp2p
*
* See the [js-libp2p configuration docs](https://github.com/libp2p/js-libp2p/blob/main/doc/CONFIGURATION.md#customizing-peer-discovery) for how to include this module as a peer discovery module in js-libp2p.
*
* If you are only interested in listening to the global pubsub topic the minimal configuration for using this with libp2p is:
*
* ```js
* import { createLibp2p } from 'libp2p'
* import { websockets } from '@libp2p/websockets'
* import { yamux } from '@chainsafe/libp2p-yamux'
* import { noise } from '@chainsafe/libp2p-noise'
* import { gossipsub } from '@chainsafe/libp2p-gossipsub'
* import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery'
* import { identify } from 'libp2p/identify'
*
* const node = await createLibp2p({
* transports: [
* websockets()
* ], // Any libp2p transport(s) can be used
* streamMuxers: [
* mplex()
* ],
* connectionEncryption: [
* yamux()
* ],
* peerDiscovery: [
* pubsubPeerDiscovery()
* ],
* services: {
* pubsub: gossipsub(),
* identify: identify()
* }
* })
* ```
*
* @example Customizing Pubsub Peer Discovery
*
* There are a few options you can use to customize `Pubsub Peer Discovery`. You can see the detailed [options](#options) below.
*
* ```js
* // ... Other imports from above
* import PubSubPeerDiscovery from '@libp2p/pubsub-peer-discovery'
*
* // Custom topics
* const topics = [
* `myApp._peer-discovery._p2p._pubsub`, // It's recommended but not required to extend the global space
* '_peer-discovery._p2p._pubsub' // Include if you want to participate in the global space
* ]
*
* const node = await createLibp2p({
* // ...
* peerDiscovery: [
* pubsubPeerDiscovery({
* interval: 10000,
* topics: topics, // defaults to ['_peer-discovery._p2p._pubsub']
* listenOnly: false
* })
* ]
* })
* ```
*
* ## Options
*
* | Name | Type | Description |
* | ---------- | --------------- | -------------------------------------------------------------------------------------------------------------- |
* | interval | `number` | How often (in `ms`), after initial broadcast, your node should broadcast your peer data. Default (`10000ms`) |
* | topics | `Array<string>` | An Array of topic strings. If set, the default topic will not be used and must be included explicitly here |
* | listenOnly | `boolean` | If true it will not broadcast peer data. Dont set this unless you have a specific reason to. Default (`false`) |
*
* ## Default Topic
*
* The default pubsub topic the module subscribes to is `_peer-discovery._p2p._pubsub`, which is also set on `PubsubPeerDiscovery.TOPIC`.
*/

import { TypedEventEmitter, peerDiscoverySymbol } from '@libp2p/interface'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { multiaddr } from '@multiformats/multiaddr'
import { Peer as PBPeer } from './peer.js'
import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interface/peer-discovery'
import type { PeerId } from '@libp2p/interface/peer-id'
import type { PeerInfo } from '@libp2p/interface/peer-info'
import type { Message, PubSub } from '@libp2p/interface/pubsub'
import type { Startable } from '@libp2p/interface/startable'
import type { AddressManager } from '@libp2p/interface-internal/address-manager'

const log = logger('libp2p:discovery:pubsub')
import type { PeerDiscovery, PeerDiscoveryEvents, PeerId, PeerInfo, Message, PubSub, Startable, ComponentLogger, Logger } from '@libp2p/interface'
import type { AddressManager } from '@libp2p/interface-internal'

export const TOPIC = '_peer-discovery._p2p._pubsub'

export interface PubsubPeerDiscoveryInit {
Expand All @@ -35,20 +121,22 @@ export interface PubSubPeerDiscoveryComponents {
peerId: PeerId
pubsub?: PubSub
addressManager: AddressManager
logger: ComponentLogger
}

/**
* A Peer Discovery Service that leverages libp2p Pubsub to find peers.
*/
export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery, Startable {
public readonly [peerDiscovery] = true
export class PubSubPeerDiscovery extends TypedEventEmitter<PeerDiscoveryEvents> implements PeerDiscovery, Startable {
public readonly [peerDiscoverySymbol] = true
public readonly [Symbol.toStringTag] = '@libp2p/pubsub-peer-discovery'

private readonly interval: number
private readonly listenOnly: boolean
private readonly topics: string[]
private intervalId?: ReturnType<typeof setInterval>
private readonly components: PubSubPeerDiscoveryComponents
private readonly log: Logger

constructor (components: PubSubPeerDiscoveryComponents, init: PubsubPeerDiscoveryInit = {}) {
super()
Expand All @@ -62,6 +150,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
this.components = components
this.interval = interval ?? 10000
this.listenOnly = listenOnly ?? false
this.log = components.logger.forComponent('libp2p:discovery:pubsub')

// Ensure we have topics
if (Array.isArray(topics) && topics.length > 0) {
Expand Down Expand Up @@ -162,7 +251,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
}

for (const topic of this.topics) {
log('broadcasting our peer data on topic %s', topic)
this.log('broadcasting our peer data on topic %s', topic)
void pubsub.publish(topic, encodedPeer)
}
}
Expand All @@ -189,17 +278,16 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
return
}

log('discovered peer %p on %s', peerId, message.topic)
this.log('discovered peer %p on %s', peerId, message.topic)

this.dispatchEvent(new CustomEvent<PeerInfo>('peer', {
this.safeDispatchEvent<PeerInfo>('peer', {
detail: {
id: peerId,
multiaddrs: peer.addrs.map(b => multiaddr(b)),
protocols: []
multiaddrs: peer.addrs.map(b => multiaddr(b))
}
}))
})
}).catch(err => {
log.error(err)
this.log.error(err)
})
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/peer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
/* eslint-disable @typescript-eslint/no-empty-interface */

import { encodeMessage, decodeMessage, message } from 'protons-runtime'
import type { Codec } from 'protons-runtime'
import { type Codec, decodeMessage, encodeMessage, message } from 'protons-runtime'
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
import type { Uint8ArrayList } from 'uint8arraylist'

export interface Peer {
Expand Down Expand Up @@ -40,7 +40,7 @@ export namespace Peer {
}
}, (reader, length) => {
const obj: any = {
publicKey: new Uint8Array(0),
publicKey: uint8ArrayAlloc(0),
addrs: []
}

Expand All @@ -50,15 +50,18 @@ export namespace Peer {
const tag = reader.uint32()

switch (tag >>> 3) {
case 1:
case 1: {
obj.publicKey = reader.bytes()
break
case 2:
}
case 2: {
obj.addrs.push(reader.bytes())
break
default:
}
default: {
reader.skipType(tag & 7)
break
}
}
}

Expand Down
Loading

0 comments on commit 9d0da56

Please sign in to comment.