diff --git a/package.json b/package.json index e9077e0..71f0764 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,10 @@ "@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-id-factory": "^1.0.18", + "@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..2992100 100644 --- a/test/bootstrap.spec.ts +++ b/test/bootstrap.spec.ts @@ -2,52 +2,50 @@ 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', () => { - 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 +55,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 @@ -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,21 +87,20 @@ 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') - 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 +114,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..2d923a1 100644 --- a/test/compliance.spec.ts +++ b/test/compliance.spec.ts @@ -1,30 +1,26 @@ /* 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' 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() + }) + } - const bootstrap = new Bootstrap({ + return bootstrap({ list: peerList, timeout: 100 - }) - bootstrap.init(components) - - return bootstrap + })(components) }, async teardown () {} })