Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow custom registrar #3040

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/libp2p/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type { ConnectionManagerInit } from './connection-manager/index.js'
import type { ConnectionMonitorInit } from './connection-monitor.js'
import type { TransportManagerInit } from './transport-manager.js'
import type { Libp2p, ServiceMap, ComponentLogger, NodeInfo, ConnectionProtector, ConnectionEncrypter, ConnectionGater, ContentRouting, Metrics, PeerDiscovery, PeerRouting, StreamMuxerFactory, Transport, PrivateKey } from '@libp2p/interface'
import type { Registrar } from '@libp2p/interface-internal'
import type { PersistentPeerStoreInit } from '@libp2p/peer-store'
import type { DNS } from '@multiformats/dns'
import type { Datastore } from 'interface-datastore'
Expand Down Expand Up @@ -132,6 +133,11 @@ export interface Libp2pInit<T extends ServiceMap = ServiceMap> {
*/
connectionProtector?(components: Components): ConnectionProtector

/**
* A registrar implementation that can be used to modify protocol handling
*/
registrar?(components: Components): Registrar

/**
* Arbitrary libp2p modules
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/libp2p/src/libp2p.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ export class Libp2p<T extends ServiceMap = ServiceMap> extends TypedEventEmitter
// Create the Registrar
this.configureComponent('registrar', new Registrar(this.components))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this should be in an else block on L143?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially it was an if/else but found it useful to have the default implementation initialised so that it can be passed to a custom registrar. Maybe the initialisation of the default could be handled in the custom one instead?

https://github.com/dozyio/js-libp2p-middleware-registrar/blob/092687470ccd768da81d9d2044ce8ae37ce5715b/src/middleware-registry.ts#L101


// Setup a custom registrar that can override the default registrar
if (init.registrar != null) {
this.configureComponent('registrar', init.registrar(this.components))
}

// Addresses {listen, announce, noAnnounce}
this.configureComponent('addressManager', new AddressManager(this.components, init.addresses))

Expand Down
77 changes: 77 additions & 0 deletions packages/libp2p/test/registrar/custom-registrar.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* eslint-env mocha */

import { expect } from 'aegir/chai'
import sinon from 'sinon'
import { createLibp2p } from '../../src/index.js'
import type { Libp2p } from '@libp2p/interface'
import type { Registrar } from '@libp2p/interface-internal'

describe('custom registrar functionality', () => {
let libp2p: Libp2p
let customRegistrar: {
handle: sinon.SinonStub<any, Promise<void>>
unhandle: sinon.SinonStub<any, Promise<void>>
getProtocols: sinon.SinonStub<any, string[]>
register: sinon.SinonStub<any, Promise<string>>
unregister: sinon.SinonStub<any, void>
getHandler: sinon.SinonStub<any, { handler(): void, options: Record<string, unknown> }>
getTopologies: sinon.SinonStub<any, any[]>
}

beforeEach(async (): Promise<void> => {
customRegistrar = {
handle: sinon.stub().resolves(),
unhandle: sinon.stub().resolves(),
getProtocols: sinon.stub().returns(['/custom/1.0.0']),
register: sinon.stub().resolves('custom-topology-id'),
unregister: sinon.stub(),
getHandler: sinon.stub().returns({ handler: (): void => {}, options: {} }),
getTopologies: sinon.stub().returns([])
}

libp2p = await createLibp2p({
registrar: (_components) => customRegistrar as unknown as Registrar
})
})

afterEach(async (): Promise<void> => {
await libp2p?.stop()
sinon.restore()
})

it('should reflect custom getProtocols implementation', (): void => {
const protocols: string[] = libp2p.getProtocols()
expect(protocols).to.deep.equal(['/custom/1.0.0'])
expect(customRegistrar.getProtocols.callCount).to.equal(1)
})

it('should call custom registrar handle method when registering a protocol handler', async (): Promise<void> => {
const testHandler = (): void => {}
await libp2p.handle('/custom/1.0.0', testHandler)
expect(customRegistrar.handle.callCount).to.equal(1)
expect(customRegistrar.handle.firstCall.args).to.deep.equal(['/custom/1.0.0', testHandler, undefined])
})

it('should call custom registrar unhandle method when unregistering a protocol handler', async (): Promise<void> => {
await libp2p.unhandle('/custom/1.0.0')
expect(customRegistrar.unhandle.callCount).to.equal(1)
expect(customRegistrar.unhandle.firstCall.args).to.deep.equal(['/custom/1.0.0'])
})

it('should use custom registrar register method for topologies', async (): Promise<void> => {
const topology = {
onConnect: (): void => {},
onDisconnect: (): void => {}
}
const topologyId: string = await libp2p.register('/custom/1.0.0', topology)
expect(topologyId).to.equal('custom-topology-id')
expect(customRegistrar.register.callCount).to.equal(1)
expect(customRegistrar.register.firstCall.args).to.deep.equal(['/custom/1.0.0', topology])
})

it('should call custom registrar unregister method for topologies', (): void => {
libp2p.unregister('custom-topology-id')
expect(customRegistrar.unregister.callCount).to.equal(1)
expect(customRegistrar.unregister.firstCall.args).to.deep.equal(['custom-topology-id'])
})
})