From 123a3e9b41bab07bd34168792b54671905ea40b4 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Wed, 12 Oct 2022 14:55:30 +0100 Subject: [PATCH 1/2] fix!: remove @libp2p/components `@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 https://github.com/libp2p/js-libp2p-components/issues/6 BREAKING CHANGE: modules no longer implement `Initializable` instead switching to constructor injection --- package.json | 6 +++--- src/index.ts | 28 +++++++++++++++++----------- test/bootstrap.spec.ts | 33 ++++++++++++++++----------------- test/compliance.spec.ts | 9 +++------ 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index e9077e0..1fe73b7 100644 --- a/package.json +++ b/package.json @@ -138,9 +138,9 @@ "release": "aegir release" }, "dependencies": { - "@libp2p/components": "^3.0.0", "@libp2p/interface-peer-discovery": "^1.0.1", "@libp2p/interface-peer-info": "^1.0.3", + "@libp2p/interface-peer-store": "^1.2.2", "@libp2p/interfaces": "^3.0.3", "@libp2p/logger": "^2.0.1", "@libp2p/peer-id": "^1.1.15", @@ -148,9 +148,9 @@ "@multiformats/multiaddr": "^11.0.0" }, "devDependencies": { - "@libp2p/interface-peer-discovery-compliance-tests": "^1.0.2", + "@libp2p/interface-peer-discovery-compliance-tests": "^2.0.0", "@libp2p/interface-peer-id": "^1.0.4", - "@libp2p/peer-store": "^4.0.0", + "@libp2p/peer-store": "^5.0.0", "aegir": "^37.5.3", "datastore-core": "^8.0.1", "delay": "^5.0.0" diff --git a/src/index.ts b/src/index.ts index b037e23..e6a08fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,8 @@ import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interface-peer- import type { PeerInfo } from '@libp2p/interface-peer-info' import { peerIdFromString } from '@libp2p/peer-id' import { symbol } from '@libp2p/interface-peer-discovery' -import { Components, Initializable } from '@libp2p/components' +import type { Startable } from '@libp2p/interfaces/dist/src/startable' +import type { PeerStore } from '@libp2p/interface-peer-store' const log = logger('libp2p:bootstrap') @@ -15,7 +16,7 @@ const DEFAULT_BOOTSTRAP_TAG_VALUE = 50 const DEFAULT_BOOTSTRAP_TAG_TTL = 120000 const DEFAULT_BOOTSTRAP_DISCOVERY_TIMEOUT = 1000 -export interface BootstrapOptions { +export interface BootstrapInit { /** * The list of peer addresses in multi-address format */ @@ -42,24 +43,29 @@ export interface BootstrapOptions { tagTTL?: number } +export interface BootstrapComponents { + peerStore: PeerStore +} + /** * Emits 'peer' events on a regular interval for each peer in the provided list. */ -export class Bootstrap extends EventEmitter implements PeerDiscovery, Initializable { +class Bootstrap extends EventEmitter implements PeerDiscovery, Startable { static tag = 'bootstrap' private timer?: ReturnType private readonly list: PeerInfo[] private readonly timeout: number - private components: Components = new Components() - private readonly _init: BootstrapOptions + private readonly components: BootstrapComponents + private readonly _init: BootstrapInit - constructor (options: BootstrapOptions = { list: [] }) { + constructor (components: BootstrapComponents, options: BootstrapInit = { list: [] }) { if (options.list == null || options.list.length === 0) { throw new Error('Bootstrap requires a list of peer addresses') } super() + this.components = components this.timeout = options.timeout ?? DEFAULT_BOOTSTRAP_DISCOVERY_TIMEOUT this.list = [] @@ -89,10 +95,6 @@ export class Bootstrap extends EventEmitter implements Peer this._init = options } - init (components: Components) { - this.components = components - } - get [symbol] (): true { return true } @@ -131,7 +133,7 @@ export class Bootstrap extends EventEmitter implements Peer } for (const peerData of this.list) { - await this.components.getPeerStore().tagPeer(peerData.id, this._init.tagName ?? DEFAULT_BOOTSTRAP_TAG_NAME, { + await this.components.peerStore.tagPeer(peerData.id, this._init.tagName ?? DEFAULT_BOOTSTRAP_TAG_NAME, { value: this._init.tagValue ?? DEFAULT_BOOTSTRAP_TAG_VALUE, ttl: this._init.tagTTL ?? DEFAULT_BOOTSTRAP_TAG_TTL }) @@ -156,3 +158,7 @@ export class Bootstrap extends EventEmitter implements Peer this.timer = undefined } } + +export function bootstrap (init: BootstrapInit): (components: BootstrapComponents) => PeerDiscovery { + return (components: BootstrapComponents) => new Bootstrap(components, init) +} diff --git a/test/bootstrap.spec.ts b/test/bootstrap.spec.ts index 64265fa..a646d33 100644 --- a/test/bootstrap.spec.ts +++ b/test/bootstrap.spec.ts @@ -2,7 +2,7 @@ import { expect } from 'aegir/chai' import { IPFS } from '@multiformats/mafmt' -import { Bootstrap } from '../src/index.js' +import { bootstrap } from '../src/index.js' import peerList from './fixtures/default-peers.js' import partialValidPeerList from './fixtures/some-invalid-peers.js' import { isPeerId } from '@libp2p/interface-peer-id' @@ -12,6 +12,7 @@ import { MemoryDatastore } from 'datastore-core' import { multiaddr } from '@multiformats/multiaddr' import { peerIdFromString } from '@libp2p/peer-id' import delay from 'delay' +import { start, stop } from '@libp2p/interfaces/startable' describe('bootstrap', () => { let components: Components @@ -29,25 +30,25 @@ describe('bootstrap', () => { }) it('should throw if no peer list is provided', () => { - expect(() => new Bootstrap()) + // @ts-expect-error missing args + expect(() => bootstrap()()) .to.throw('Bootstrap requires a list of peer addresses') }) it('should discover bootstrap peers', async function () { this.timeout(5 * 1000) - const r = new Bootstrap({ + const r = bootstrap({ list: peerList, timeout: 100 - }) - r.init(components) + })(components) const p = new Promise((resolve) => r.addEventListener('peer', resolve, { once: true })) - r.start() + await start(r) await p - r.stop() + await stop(r) }) it('should tag bootstrap peers', async function () { @@ -57,19 +58,18 @@ describe('bootstrap', () => { const tagValue = 10 const tagTTL = 50 - const r = new Bootstrap({ + const r = bootstrap({ list: peerList, timeout: 100, tagName, tagValue, tagTTL - }) - r.init(components) + })(components) const p = new Promise((resolve) => r.addEventListener('peer', resolve, { once: true })) - r.start() + await start(r) await p @@ -94,17 +94,16 @@ describe('bootstrap', () => { expect(tags2).to.have.lengthOf(0, 'bootstrap tag did not expire') - r.stop() + await stop(r) }) it('should not fail on malformed peers in peer list', async function () { this.timeout(5 * 1000) - const r = new Bootstrap({ + const r = bootstrap({ list: partialValidPeerList, timeout: 100 - }) - r.init(components) + })(components) const p = new Promise((resolve) => { r.addEventListener('peer', (evt) => { @@ -118,9 +117,9 @@ describe('bootstrap', () => { }) }) - r.start() + await start(r) await p - r.stop() + await stop(r) }) }) diff --git a/test/compliance.spec.ts b/test/compliance.spec.ts index 70ba35e..5fbe00b 100644 --- a/test/compliance.spec.ts +++ b/test/compliance.spec.ts @@ -1,7 +1,7 @@ /* eslint-env mocha */ import tests from '@libp2p/interface-peer-discovery-compliance-tests' -import { Bootstrap } from '../src/index.js' +import { bootstrap } from '../src/index.js' import peerList from './fixtures/default-peers.js' import { Components } from '@libp2p/components' import { PersistentPeerStore } from '@libp2p/peer-store' @@ -18,13 +18,10 @@ describe('compliance tests', () => { }) peerStore.init(components) - const bootstrap = new Bootstrap({ + return bootstrap({ list: peerList, timeout: 100 - }) - bootstrap.init(components) - - return bootstrap + })(components) }, async teardown () {} }) From 3eb0ff72906cc6960abf1180873e27fc6779e460 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Wed, 12 Oct 2022 15:04:02 +0100 Subject: [PATCH 2/2] chore: remove use of components --- package.json | 1 + test/bootstrap.spec.ts | 27 ++++++++++++--------------- test/compliance.spec.ts | 15 +++++++-------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 1fe73b7..71f0764 100644 --- a/package.json +++ b/package.json @@ -150,6 +150,7 @@ "devDependencies": { "@libp2p/interface-peer-discovery-compliance-tests": "^2.0.0", "@libp2p/interface-peer-id": "^1.0.4", + "@libp2p/peer-id-factory": "^1.0.18", "@libp2p/peer-store": "^5.0.0", "aegir": "^37.5.3", "datastore-core": "^8.0.1", diff --git a/test/bootstrap.spec.ts b/test/bootstrap.spec.ts index a646d33..2992100 100644 --- a/test/bootstrap.spec.ts +++ b/test/bootstrap.spec.ts @@ -2,31 +2,28 @@ import { expect } from 'aegir/chai' import { IPFS } from '@multiformats/mafmt' -import { bootstrap } from '../src/index.js' +import { bootstrap, BootstrapComponents } from '../src/index.js' import peerList from './fixtures/default-peers.js' import partialValidPeerList from './fixtures/some-invalid-peers.js' import { isPeerId } from '@libp2p/interface-peer-id' -import { Components } from '@libp2p/components' import { PersistentPeerStore } from '@libp2p/peer-store' import { MemoryDatastore } from 'datastore-core' import { multiaddr } from '@multiformats/multiaddr' import { peerIdFromString } from '@libp2p/peer-id' import delay from 'delay' import { start, stop } from '@libp2p/interfaces/startable' +import { createEd25519PeerId } from '@libp2p/peer-id-factory' describe('bootstrap', () => { - let components: Components + let components: BootstrapComponents - beforeEach(() => { - const datastore = new MemoryDatastore() - const peerStore = new PersistentPeerStore() - - components = new Components({ - peerStore, - datastore - }) - - peerStore.init(components) + beforeEach(async () => { + components = { + peerStore: new PersistentPeerStore({ + peerId: await createEd25519PeerId(), + datastore: new MemoryDatastore() + }) + } }) it('should throw if no peer list is provided', () => { @@ -82,7 +79,7 @@ describe('bootstrap', () => { const bootstrapper0PeerId = peerIdFromString(bootstrapper0PeerIdStr) - const tags = await components.getPeerStore().getTags(bootstrapper0PeerId) + const tags = await components.peerStore.getTags(bootstrapper0PeerId) expect(tags).to.have.lengthOf(1, 'bootstrap tag was not set') expect(tags).to.have.nested.property('[0].name', tagName, 'bootstrap tag had incorrect name') @@ -90,7 +87,7 @@ describe('bootstrap', () => { await delay(tagTTL * 2) - const tags2 = await components.getPeerStore().getTags(bootstrapper0PeerId) + const tags2 = await components.peerStore.getTags(bootstrapper0PeerId) expect(tags2).to.have.lengthOf(0, 'bootstrap tag did not expire') diff --git a/test/compliance.spec.ts b/test/compliance.spec.ts index 5fbe00b..2d923a1 100644 --- a/test/compliance.spec.ts +++ b/test/compliance.spec.ts @@ -3,20 +3,19 @@ import tests from '@libp2p/interface-peer-discovery-compliance-tests' import { bootstrap } from '../src/index.js' import peerList from './fixtures/default-peers.js' -import { Components } from '@libp2p/components' import { PersistentPeerStore } from '@libp2p/peer-store' import { MemoryDatastore } from 'datastore-core' +import { createEd25519PeerId } from '@libp2p/peer-id-factory' describe('compliance tests', () => { tests({ async setup () { - const datastore = new MemoryDatastore() - const peerStore = new PersistentPeerStore() - const components = new Components({ - peerStore, - datastore - }) - peerStore.init(components) + const components = { + peerStore: new PersistentPeerStore({ + peerId: await createEd25519PeerId(), + datastore: new MemoryDatastore() + }) + } return bootstrap({ list: peerList,