Skip to content

Commit

Permalink
fix!: remove @libp2p/components (#60)
Browse files Browse the repository at this point in the history
`@libp2p/components` is a choke-point for our dependency graph as it depends on every interface, meaning when one interface revs a major `@libp2p/components` major has to change too which means every module depending on it also needs a major.

Switch instead to constructor injection of simple objects that let modules declare their dependencies on interfaces directly instead of indirectly via `@libp2p/components`

Refs libp2p/js-libp2p-components#6

BREAKING CHANGE: modules no longer implement `Initializable` instead switching to constructor injection
  • Loading branch information
achingbrain authored Oct 12, 2022
1 parent 0072812 commit 35336fb
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 103 deletions.
27 changes: 13 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# @libp2p/pubsub-peer-discovery <!-- omit in toc -->

[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![IRC](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
[![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/workflow/status/libp2p/js-libp2p-interfaces/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery/actions/workflows/js-test-and-release.yml)
[![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-pubsub-peer-discovery/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/libp2p/js-libp2p-pubsub-peer-discovery/actions/workflows/js-test-and-release.yml)

> A libp2p module that uses pubsub for mdns like peer discovery
Expand All @@ -22,7 +21,7 @@
- [Default Topic](#default-topic)
- [Contribute](#contribute)
- [License](#license)
- [Contribution](#contribution)
- [Contribute](#contribute-1)

## Install

Expand Down Expand Up @@ -55,25 +54,25 @@ If you are only interested in listening to the global pubsub topic the minimal c

```js
import { createLibp2p } from 'libp2p'
import { Websockets } from '@libp2p/websockets'
import { Mplex } from '@libp2p/mplex'
import { Noise } from '@libp2p/noise'
import GossipSub from 'libp2p-gossipsub'
import { PubSubPeerDiscovery } from '@libp2p/pubsub-peer-discovery'
import { websockets } from '@libp2p/websockets'
import { mplex } from '@libp2p/mplex'
import { noise } from '@chainsafe/libp2p-noise'
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery'

const node = await createLibp2p({
transports: [
new Websockets()
websockets()
], // Any libp2p transport(s) can be used
streamMuxers: [
new Mplex()
mplex()
],
connectionEncryption: [
new Noise()
noise()
],
pubsub: new GossipSub(), // Can also be `libp2p-floodsub` if desired
peerDiscovery: [
new PubSubPeerDiscovery()
pubsubPeerDiscovery()
]
})
```
Expand All @@ -95,7 +94,7 @@ const topics = [
const node = await createLibp2p({
// ...
peerDiscovery: [
new PubSubPeerDiscovery({
pubsubPeerDiscovery({
interval: 10000,
topics: topics, // defaults to ['_peer-discovery._p2p._pubsub']
listenOnly: false
Expand Down Expand Up @@ -131,6 +130,6 @@ 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
## Contribute

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.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,24 +143,24 @@
"release": "semantic-release"
},
"dependencies": {
"@libp2p/components": "^2.0.4",
"@libp2p/interface-peer-discovery": "^1.0.1",
"@libp2p/interface-peer-id": "^1.0.5",
"@libp2p/interface-peer-info": "^1.0.2",
"@libp2p/interface-pubsub": "^2.0.1",
"@libp2p/interface-pubsub": "^3.0.0",
"@libp2p/interfaces": "^3.0.3",
"@libp2p/logger": "^2.0.1",
"@libp2p/peer-id": "^1.1.15",
"@multiformats/multiaddr": "^10.4.0",
"protons-runtime": "^3.1.0"
"@multiformats/multiaddr": "^11.0.5",
"protons-runtime": "^4.0.1"
},
"devDependencies": {
"@libp2p/interface-address-manager": "^1.0.2",
"@libp2p/interface-peer-discovery-compliance-tests": "^1.0.1",
"@libp2p/interface-address-manager": "^2.0.0",
"@libp2p/interface-peer-discovery-compliance-tests": "^2.0.0",
"@libp2p/peer-id-factory": "^1.0.18",
"aegir": "^37.2.0",
"p-defer": "^4.0.0",
"p-wait-for": "^5.0.0",
"protons": "^5.1.0",
"protons": "^6.0.0",
"sinon": "^14.0.0",
"ts-sinon": "^2.0.2"
}
Expand Down
42 changes: 25 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { logger } from '@libp2p/logger'
import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
import type { Startable } from '@libp2p/interfaces/startable'
import { Multiaddr } from '@multiformats/multiaddr'
import { multiaddr } from '@multiformats/multiaddr'
import { Peer as PBPeer } from './peer.js'
import { peerIdFromKeys } from '@libp2p/peer-id'
import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interface-peer-discovery'
import { Components, Initializable } from '@libp2p/components'
import type { Message } from '@libp2p/interface-pubsub'
import type { Message, PubSub } from '@libp2p/interface-pubsub'
import type { PeerInfo } from '@libp2p/interface-peer-info'
import { symbol } from '@libp2p/interface-peer-discovery'
import type { AddressManager } from '@libp2p/interface-address-manager'
import type { PeerId } from '@libp2p/interface-peer-id'

const log = logger('libp2p:discovery:pubsub')
export const TOPIC = '_peer-discovery._p2p._pubsub'
Expand All @@ -30,17 +31,23 @@ export interface PubsubPeerDiscoveryInit {
listenOnly?: boolean
}

export interface PubSubPeerDiscoveryComponents {
peerId: PeerId
pubsub: PubSub
addressManager: AddressManager
}

/**
* A Peer Discovery Service that leverages libp2p Pubsub to find peers.
*/
export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery, Startable, Initializable {
export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery, Startable {
private readonly interval: number
private readonly listenOnly: boolean
private readonly topics: string[]
private intervalId?: ReturnType<typeof setInterval>
private components: Components = new Components()
private readonly components: PubSubPeerDiscoveryComponents

constructor (init: PubsubPeerDiscoveryInit = {}) {
constructor (components: PubSubPeerDiscoveryComponents, init: PubsubPeerDiscoveryInit = {}) {
super()

const {
Expand All @@ -49,6 +56,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
listenOnly
} = init

this.components = components
this.interval = interval ?? 10000
this.listenOnly = listenOnly ?? false

Expand All @@ -70,10 +78,6 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
return '@libp2p/pubsub-peer-discovery'
}

init (components: Components) {
this.components = components
}

isStarted () {
return this.intervalId != null
}
Expand All @@ -91,7 +95,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
return
}

const pubsub = this.components.getPubSub()
const pubsub = this.components.pubsub

if (pubsub == null) {
throw new Error('PubSub not configured')
Expand All @@ -118,7 +122,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
}

beforeStop () {
const pubsub = this.components.getPubSub()
const pubsub = this.components.pubsub

if (pubsub == null) {
throw new Error('PubSub not configured')
Expand All @@ -144,19 +148,19 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
* Performs a broadcast via Pubsub publish
*/
_broadcast () {
const peerId = this.components.getPeerId()
const peerId = this.components.peerId

if (peerId.publicKey == null) {
throw new Error('PeerId was missing public key')
}

const peer = {
publicKey: peerId.publicKey,
addrs: this.components.getAddressManager().getAddresses().map(ma => ma.bytes)
addrs: this.components.addressManager.getAddresses().map(ma => ma.bytes)
}

const encodedPeer = PBPeer.encode(peer)
const pubsub = this.components.getPubSub()
const pubsub = this.components.pubsub

if (pubsub == null) {
throw new Error('PubSub not configured')
Expand Down Expand Up @@ -186,7 +190,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple

void peerIdFromKeys(peer.publicKey).then(peerId => {
// Ignore if we received our own response
if (peerId.equals(this.components.getPeerId())) {
if (peerId.equals(this.components.peerId)) {
return
}

Expand All @@ -195,7 +199,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
this.dispatchEvent(new CustomEvent<PeerInfo>('peer', {
detail: {
id: peerId,
multiaddrs: peer.addrs.map(b => new Multiaddr(b)),
multiaddrs: peer.addrs.map(b => multiaddr(b)),
protocols: []
}
}))
Expand All @@ -204,3 +208,7 @@ export class PubSubPeerDiscovery extends EventEmitter<PeerDiscoveryEvents> imple
})
}
}

export function pubsubPeerDiscovery (init: PubsubPeerDiscoveryInit = {}): (components: PubSubPeerDiscoveryComponents) => PeerDiscovery {
return (components: PubSubPeerDiscoveryComponents) => new PubSubPeerDiscovery(components, init)
}
6 changes: 3 additions & 3 deletions src/peer.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
syntax = "proto3";

message Peer {
required bytes publicKey = 0;
repeated bytes addrs = 1;
}
bytes publicKey = 1;
repeated bytes addrs = 2;
}
30 changes: 12 additions & 18 deletions src/peer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint-disable import/export */
/* eslint-disable complexity */
/* eslint-disable @typescript-eslint/no-namespace */
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */

import { encodeMessage, decodeMessage, message } from 'protons-runtime'
import type { Uint8ArrayList } from 'uint8arraylist'
Expand All @@ -15,29 +17,25 @@ export namespace Peer {

export const codec = (): Codec<Peer> => {
if (_codec == null) {
_codec = message<Peer>((obj, writer, opts = {}) => {
_codec = message<Peer>((obj, w, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
w.fork()
}

if (obj.publicKey != null) {
writer.uint32(2)
writer.bytes(obj.publicKey)
} else {
throw new Error('Protocol error: required field "publicKey" was not found in object')
if (opts.writeDefaults === true || (obj.publicKey != null && obj.publicKey.byteLength > 0)) {
w.uint32(10)
w.bytes(obj.publicKey)
}

if (obj.addrs != null) {
for (const value of obj.addrs) {
writer.uint32(10)
writer.bytes(value)
w.uint32(18)
w.bytes(value)
}
} else {
throw new Error('Protocol error: required field "addrs" was not found in object')
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
w.ldelim()
}
}, (reader, length) => {
const obj: any = {
Expand All @@ -51,10 +49,10 @@ export namespace Peer {
const tag = reader.uint32()

switch (tag >>> 3) {
case 0:
case 1:
obj.publicKey = reader.bytes()
break
case 1:
case 2:
obj.addrs.push(reader.bytes())
break
default:
Expand All @@ -63,10 +61,6 @@ export namespace Peer {
}
}

if (obj.publicKey == null) {
throw new Error('Protocol error: value for required field "publicKey" was not found in protobuf')
}

return obj
})
}
Expand Down
23 changes: 10 additions & 13 deletions test/compliance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/* eslint-env mocha */

import tests from '@libp2p/interface-peer-discovery-compliance-tests'
import { PubSubPeerDiscovery, TOPIC } from '../src/index.js'
import { pubsubPeerDiscovery, TOPIC } from '../src/index.js'
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { Components } from '@libp2p/components'
import { stubInterface } from 'ts-sinon'
import type { PubSub } from '@libp2p/interface-pubsub'
import { CustomEvent } from '@libp2p/interfaces/events'
import type { AddressManager } from '@libp2p/interface-address-manager'
import { Multiaddr } from '@multiformats/multiaddr'
import { multiaddr } from '@multiformats/multiaddr'
import { Peer as PBPeer } from '../src/peer.js'

describe('compliance tests', () => {
Expand All @@ -21,26 +20,24 @@ describe('compliance tests', () => {

const addressManager = stubInterface<AddressManager>()
addressManager.getAddresses.returns([
new Multiaddr(`/ip4/43.10.1.2/tcp/39832/p2p/${peerId.toString()}`)
multiaddr(`/ip4/43.10.1.2/tcp/39832/p2p/${peerId.toString()}`)
])

const pubsubDiscovery = new PubSubPeerDiscovery()

const components = new Components()
components.setPubSub(stubInterface<PubSub>())
components.setPeerId(await createEd25519PeerId())
components.setAddressManager(addressManager)

pubsubDiscovery.init(components)
const pubsubDiscovery = pubsubPeerDiscovery()({
pubsub: stubInterface<PubSub>(),
peerId: await createEd25519PeerId(),
addressManager
})

intervalId = setInterval(() => {
const peer = PBPeer.encode({
publicKey: peerId.publicKey,
addrs: [
new Multiaddr('/ip4/166.10.1.2/tcp/80').bytes
multiaddr('/ip4/166.10.1.2/tcp/80').bytes
]
}).subarray()

// @ts-expect-error private field
pubsubDiscovery._onMessage(new CustomEvent('message', {
detail: {
type: 'unsigned',
Expand Down
Loading

0 comments on commit 35336fb

Please sign in to comment.