Skip to content
This repository has been archived by the owner on Oct 3, 2023. It is now read-only.

Commit

Permalink
fix!: remove @libp2p/components (#144)
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 d141806 commit 772acc1
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 59 deletions.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,20 @@
"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",
"@multiformats/mafmt": "^11.0.3",
"@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"
Expand Down
28 changes: 17 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -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
*/
Expand All @@ -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<PeerDiscoveryEvents> implements PeerDiscovery, Initializable {
class Bootstrap extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery, Startable {
static tag = 'bootstrap'

private timer?: ReturnType<typeof setTimeout>
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 = []

Expand Down Expand Up @@ -89,10 +95,6 @@ export class Bootstrap extends EventEmitter<PeerDiscoveryEvents> implements Peer
this._init = options
}

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

get [symbol] (): true {
return true
}
Expand Down Expand Up @@ -131,7 +133,7 @@ export class Bootstrap extends EventEmitter<PeerDiscoveryEvents> 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
})
Expand All @@ -156,3 +158,7 @@ export class Bootstrap extends EventEmitter<PeerDiscoveryEvents> implements Peer
this.timer = undefined
}
}

export function bootstrap (init: BootstrapInit): (components: BootstrapComponents) => PeerDiscovery {
return (components: BootstrapComponents) => new Bootstrap(components, init)
}
58 changes: 27 additions & 31 deletions test/bootstrap.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand All @@ -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

Expand All @@ -82,29 +79,28 @@ 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')
expect(tags).to.have.nested.property('[0].value', tagValue, 'bootstrap tag had incorrect value')

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<void>((resolve) => {
r.addEventListener('peer', (evt) => {
Expand All @@ -118,9 +114,9 @@ describe('bootstrap', () => {
})
})

r.start()
await start(r)

await p
r.stop()
await stop(r)
})
})
24 changes: 10 additions & 14 deletions test/compliance.spec.ts
Original file line number Diff line number Diff line change
@@ -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 () {}
})
Expand Down

0 comments on commit 772acc1

Please sign in to comment.