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

fix: allow services to indicate their dependencies #1762

Closed
wants to merge 2 commits into from
Closed
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
3 changes: 2 additions & 1 deletion src/circuit-relay/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { createLimitedRelay } from '../utils.js'
import { AdvertService, type AdvertServiceComponents, type AdvertServiceInit } from './advert-service.js'
import { ReservationStore, type ReservationStoreInit } from './reservation-store.js'
import { ReservationVoucherRecord } from './reservation-voucher.js'
import type { IdentifyService } from '../../identify/index.js'
import type { CircuitRelayService, RelayReservation } from '../index.js'
import type { AddressManager } from '@libp2p/interface-address-manager'
import type { Connection, Stream } from '@libp2p/interface-connection'
Expand Down Expand Up @@ -422,7 +423,7 @@ class CircuitRelayServer extends EventEmitter<RelayServerEvents> implements Star
}
}

export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents) => CircuitRelayService {
export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents & { identify: IdentifyService }) => CircuitRelayService {
return (components) => {
return new CircuitRelayServer(components, init)
}
Expand Down
11 changes: 7 additions & 4 deletions src/circuit-relay/transport/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { StopMessage, HopMessage, Status } from '../pb/index.js'
import { RelayDiscovery, type RelayDiscoveryComponents } from './discovery.js'
import { createListener } from './listener.js'
import { type RelayStoreInit, ReservationStore } from './reservation-store.js'
import type { IdentifyService } from '../../identify/index.js'
import type { AddressManager } from '@libp2p/interface-address-manager'
import type { Connection, Stream } from '@libp2p/interface-connection'
import type { ConnectionGater } from '@libp2p/interface-connection-gater'
Expand Down Expand Up @@ -68,10 +69,10 @@ interface ConnectOptions {
*/
export interface CircuitRelayTransportInit extends RelayStoreInit {
/**
* The number of peers running diable relays to search for and
* connect to. (default: 0)
* The number of peers running relays to search for and connect to.
* (default: 0)
*/
discoverRelays?: number
discoverRelays: number
}

class CircuitRelayTransport implements Transport {
Expand Down Expand Up @@ -318,7 +319,9 @@ class CircuitRelayTransport implements Transport {
}
}

export function circuitRelayTransport (init: CircuitRelayTransportInit = {}): (components: CircuitRelayTransportComponents) => Transport {
export function circuitRelayTransport (init: CircuitRelayTransportInit): (components: CircuitRelayTransportComponents & { identify: IdentifyService }) => Transport
export function circuitRelayTransport (init?: RelayStoreInit): (components: CircuitRelayTransportComponents) => Transport
export function circuitRelayTransport (init: any = {}): (components: any) => Transport {
return (components) => {
return new CircuitRelayTransport(components, init)
}
Expand Down
8 changes: 5 additions & 3 deletions src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { ConnectionProtector } from '@libp2p/interface-connection'
import type { ConnectionGater } from '@libp2p/interface-connection-gater'
import type { ConnectionManager } from '@libp2p/interface-connection-manager'
import type { ContentRouting } from '@libp2p/interface-content-routing'
import type { Libp2pEvents } from '@libp2p/interface-libp2p'
import type { Libp2pEvents, ServiceMap } from '@libp2p/interface-libp2p'
import type { Metrics } from '@libp2p/interface-metrics'
import type { PeerId } from '@libp2p/interface-peer-id'
import type { PeerRouting } from '@libp2p/interface-peer-routing'
Expand All @@ -15,7 +15,7 @@ import type { TransportManager, Upgrader } from '@libp2p/interface-transport'
import type { EventEmitter } from '@libp2p/interfaces/events'
import type { Datastore } from 'interface-datastore'

export interface Components extends Record<string, any>, Startable {
export interface BuiltInComponents extends Startable {
peerId: PeerId
events: EventEmitter<Libp2pEvents>
addressManager: AddressManager
Expand All @@ -32,6 +32,8 @@ export interface Components extends Record<string, any>, Startable {
metrics?: Metrics
}

export type Components<T extends ServiceMap = ServiceMap> = T & BuiltInComponents

export interface ComponentsInit {
peerId?: PeerId
events?: EventEmitter<Libp2pEvents>
Expand Down Expand Up @@ -120,7 +122,7 @@ const NON_SERVICE_PROPERTIES = [
'_invokeStartableMethod'
]

export function defaultComponents (init: ComponentsInit = {}): Components {
export function defaultComponents <T extends Record<string, unknown>> (init: ComponentsInit = {}): Components<T> {
const components = new DefaultComponents(init)

const proxy = new Proxy(components, {
Expand Down
2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { ServiceMap } from '@libp2p/interface-libp2p'
import type { RecursivePartial } from '@libp2p/interfaces'
import type { Multiaddr } from '@multiformats/multiaddr'

const DefaultConfig: Partial<Libp2pInit> = {
const DefaultConfig: Partial<Libp2pInit<any>> = {
addresses: {
listen: [],
announce: [],
Expand Down
4 changes: 3 additions & 1 deletion src/identify/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export const multicodecs = {

export const Message = { Identify }

export function identifyService (init: IdentifyServiceInit = {}): (components: IdentifyServiceComponents) => DefaultIdentifyService {
export type IdentifyService = unknown

export function identifyService (init: IdentifyServiceInit = {}): (components: IdentifyServiceComponents) => IdentifyService {
return (components) => new DefaultIdentifyService(components, init)
}
26 changes: 13 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ import type { KeyChainInit } from '@libp2p/keychain'
import type { PersistentPeerStoreInit } from '@libp2p/peer-store'
import type { Datastore } from 'interface-datastore'

export type ServiceFactoryMap<T extends Record<string, unknown> = Record<string, unknown>> = {
[Property in keyof T]: (components: Components) => T[Property]
export type ServiceFactoryMap<T extends ServiceMap> = {
[Property in keyof T]: (components: Components<T>) => T[Property]
}

/**
* For Libp2p configurations and modules details read the [Configuration Document](./CONFIGURATION.md).
*/
export interface Libp2pInit<T extends ServiceMap = { x: Record<string, unknown> }> {
export interface Libp2pInit<T extends ServiceMap> {
/**
* peerId instance (it will be created if not provided)
*/
Expand Down Expand Up @@ -88,22 +88,22 @@ export interface Libp2pInit<T extends ServiceMap = { x: Record<string, unknown>
/**
* An array that must include at least 1 compliant transport
*/
transports: Array<(components: Components) => Transport>
streamMuxers?: Array<(components: Components) => StreamMuxerFactory>
connectionEncryption?: Array<(components: Components) => ConnectionEncrypter>
peerDiscovery?: Array<(components: Components) => PeerDiscovery>
peerRouters?: Array<(components: Components) => PeerRouting>
contentRouters?: Array<(components: Components) => ContentRouting>
transports: Array<(components: Components<T>) => Transport>
streamMuxers?: Array<(components: Components<T>) => StreamMuxerFactory>
connectionEncryption?: Array<(components: Components<T>) => ConnectionEncrypter>
peerDiscovery?: Array<(components: Components<T>) => PeerDiscovery>
peerRouters?: Array<(components: Components<T>) => PeerRouting>
contentRouters?: Array<(components: Components<T>) => ContentRouting>

/**
* A Metrics implementation can be supplied to collect metrics on this node
*/
metrics?: (components: Components) => Metrics
metrics?: (components: Components<T>) => Metrics

/**
* A ConnectionProtector can be used to create a secure overlay on top of the network using pre-shared keys
*/
connectionProtector?: (components: Components) => ConnectionProtector
connectionProtector?: (components: Components<T>) => ConnectionProtector

/**
* Arbitrary libp2p modules
Expand All @@ -113,7 +113,7 @@ export interface Libp2pInit<T extends ServiceMap = { x: Record<string, unknown>

export type { Libp2p }

export type Libp2pOptions<T extends ServiceMap = Record<string, unknown>> = RecursivePartial<Libp2pInit<T>> & { start?: boolean }
export type Libp2pOptions<T extends ServiceMap> = RecursivePartial<Libp2pInit<T>> & { start?: boolean }

/**
* Returns a new instance of the Libp2p interface, generating a new PeerId
Expand Down Expand Up @@ -141,7 +141,7 @@ export type Libp2pOptions<T extends ServiceMap = Record<string, unknown>> = Recu
* const libp2p = await createLibp2p(options)
* ```
*/
export async function createLibp2p <T extends ServiceMap = { x: Record<string, unknown> }> (options: Libp2pOptions<T>): Promise<Libp2p<T>> {
export async function createLibp2p <T extends ServiceMap> (options: Libp2pOptions<T>): Promise<Libp2p<T>> {
const node = await createLibp2pNode(options)

if (options.start !== false) {
Expand Down
14 changes: 10 additions & 4 deletions src/libp2p.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ import type { Datastore } from 'interface-datastore'

const log = logger('libp2p')

export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends EventEmitter<Libp2pEvents> implements Libp2p<T> {
export class Libp2pNode<T extends ServiceMap = ServiceMap> extends EventEmitter<Libp2pEvents> implements Libp2p<T> {
public peerId: PeerId
public peerStore: PeerStore
public contentRouting: ContentRouting
public peerRouting: PeerRouting
public keychain: KeyChain
public metrics?: Metrics
public services: T
public components: Components<T>

public components: Components
#started: boolean

constructor (init: Libp2pInit<T>) {
Expand All @@ -76,6 +76,11 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
} catch {}

this.#started = false

if (init.peerId == null) {
throw new CodeError('init.peerId is required', codes.ERR_INVALID_PARAMETERS)
}

this.peerId = init.peerId
// @ts-expect-error {} may not be of type T
this.services = {}
Expand Down Expand Up @@ -112,7 +117,7 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
this.components.upgrader = new DefaultUpgrader(this.components, {
connectionEncryption: (init.connectionEncryption ?? []).map((fn, index) => this.configureComponent(`connection-encryption-${index}`, fn(this.components))),
muxers: (init.streamMuxers ?? []).map((fn, index) => this.configureComponent(`stream-muxers-${index}`, fn(this.components))),
inboundUpgradeTimeout: init.connectionManager.inboundUpgradeTimeout
inboundUpgradeTimeout: init.connectionManager?.inboundUpgradeTimeout
})

// Setup the transport manager
Expand Down Expand Up @@ -199,6 +204,7 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
log.error('component %s was null or undefined', name)
}

// @ts-expect-error cannot use string to index Components<T>
this.components[name] = component

return component
Expand Down Expand Up @@ -401,7 +407,7 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
* Returns a new Libp2pNode instance - this exposes more of the internals than the
* libp2p interface and is useful for testing and debugging.
*/
export async function createLibp2pNode <T extends ServiceMap = Record<string, unknown>> (options: Libp2pOptions<T>): Promise<Libp2pNode<T>> {
export async function createLibp2pNode <T extends ServiceMap = ServiceMap> (options: Libp2pOptions<T>): Promise<Libp2pNode<T>> {
if (options.peerId == null) {
const datastore = options.datastore as Datastore | undefined

Expand Down
8 changes: 5 additions & 3 deletions test/circuit-relay/discovery.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { tcp } from '@libp2p/tcp'
import { expect } from 'aegir/chai'
import { pEvent } from 'p-event'
import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '../../src/circuit-relay/index.js'
import { identifyService } from '../../src/identify/index.js'
import { createLibp2p } from '../../src/index.js'
import { plaintext } from '../../src/insecure/index.js'
import { getRelayAddress, hasRelay, MockContentRouting, mockContentRouting } from './utils.js'
Expand All @@ -17,6 +18,7 @@ describe('circuit-relay discovery', () => {

beforeEach(async () => {
// create relay first so it has time to advertise itself via content routing
// @ts-expect-error TODO: typescript cannot infer the service map type
Copy link
Member Author

Choose a reason for hiding this comment

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

I am not sure why TypeScript can't infer this, it seems to work elsewhere.

relay = await createLibp2p({
addresses: {
listen: ['/ip4/127.0.0.1/tcp/0']
Expand All @@ -34,10 +36,10 @@ describe('circuit-relay discovery', () => {
mockContentRouting()
],
services: {
identify: identifyService(),
// @ts-expect-error TODO: typescript cannot infer the service map type
relay: circuitRelayServer({
advertise: {
bootDelay: 10
}
advertise: true
})
}
})
Expand Down
3 changes: 2 additions & 1 deletion test/circuit-relay/hop.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ describe('circuit-relay hop protocol', function () {
peerId,
peerStore,
registrar,
connectionGater
connectionGater,
identify: {}
})

if (isStartable(service)) {
Expand Down
2 changes: 2 additions & 0 deletions test/connection-manager/resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import sinon from 'sinon'
import { RELAY_V2_HOP_CODEC } from '../../src/circuit-relay/constants.js'
import { circuitRelayServer, type CircuitRelayService, circuitRelayTransport } from '../../src/circuit-relay/index.js'
import { codes as ErrorCodes } from '../../src/errors.js'
import { identifyService } from '../../src/identify/index.js'
import { plaintext } from '../../src/insecure/index.js'
import { createLibp2pNode, type Libp2pNode } from '../../src/libp2p.js'
import { MULTIADDRS_WEBSOCKETS } from '../fixtures/browser.js'
Expand Down Expand Up @@ -94,6 +95,7 @@ describe('dialing (resolvable addresses)', () => {
plaintext()
],
services: {
identify: identifyService(),
relay: circuitRelayServer()
},
connectionGater: mockConnectionGater()
Expand Down
10 changes: 5 additions & 5 deletions test/content-routing/content-routing.node.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-env mocha */

import { EventTypes, type KadDHT } from '@libp2p/kad-dht'
import { peerIdFromString } from '@libp2p/peer-id'
import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
Expand All @@ -15,7 +16,6 @@ import { createNode, createPeerId, populateAddressBooks } from '../utils/creator
import { createRoutingOptions } from './utils.js'
import type { ContentRouting } from '@libp2p/interface-content-routing'
import type { PeerInfo } from '@libp2p/interface-peer-info'
import type { KadDHT } from '@libp2p/kad-dht'

describe('content-routing', () => {
describe('no routers', () => {
Expand Down Expand Up @@ -100,7 +100,7 @@ describe('content-routing', () => {
sinon.stub(nodes[0].services.dht, 'findProviders').callsFake(async function * () {
yield {
from: nodes[0].peerId,
type: 0,
type: EventTypes.PROVIDER,
name: 'PROVIDER',
providers: [{
id: nodes[0].peerId,
Expand Down Expand Up @@ -322,7 +322,7 @@ describe('content-routing', () => {
sinon.stub(node.services.dht, 'findProviders').callsFake(async function * () {
yield {
from: providerPeerId,
type: 0,
type: EventTypes.PROVIDER,
name: 'PROVIDER',
providers: [
result
Expand Down Expand Up @@ -362,7 +362,7 @@ describe('content-routing', () => {
sinon.stub(node.services.dht, 'findProviders').callsFake(async function * () {
yield {
from: providerPeerId,
type: 0,
type: EventTypes.PROVIDER,
name: 'PROVIDER',
providers: [
result1
Expand Down Expand Up @@ -423,7 +423,7 @@ describe('content-routing', () => {
sinon.stub(node.services.dht, 'findProviders').callsFake(async function * () {
yield {
from: providerPeerId,
type: 0,
type: EventTypes.PROVIDER,
name: 'PROVIDER',
providers: [
results[0]
Expand Down
7 changes: 5 additions & 2 deletions test/content-routing/dht/operation.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
import pWaitFor from 'p-wait-for'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { identifyService } from '../../../src/identify/index.js'
import { createLibp2p } from '../../../src/index.js'
import { plaintext } from '../../../src/insecure/index.js'
import { createPeerId } from '../../utils/creators/peer.js'
Expand Down Expand Up @@ -66,7 +67,8 @@ describe('DHT subsystem operates correctly', () => {
services: {
dht: kadDHT({
allowQueryWithZeroPeers: true
})
}),
identify: identifyService()
}
})

Expand All @@ -88,7 +90,8 @@ describe('DHT subsystem operates correctly', () => {
services: {
dht: kadDHT({
allowQueryWithZeroPeers: true
})
}),
identify: identifyService()
}
})

Expand Down
2 changes: 1 addition & 1 deletion test/content-routing/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createBaseOptions } from '../utils/base-options.js'
import type { Libp2pOptions } from '../../src/index.js'
import type { KadDHT } from '@libp2p/kad-dht'

export function createRoutingOptions (...overrides: Libp2pOptions[]): Libp2pOptions<{ dht: KadDHT }> {
export function createRoutingOptions (...overrides: Array<Libp2pOptions<any>>): Libp2pOptions<{ dht: KadDHT }> {
return createBaseOptions({
services: {
dht: kadDHT()
Expand Down
3 changes: 2 additions & 1 deletion test/core/encryption.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { webSockets } from '@libp2p/websockets'
import { createLibp2p, type Libp2pOptions } from '../../src/index.js'
import { plaintext } from '../../src/insecure/index.js'
import { createPeerId } from '../utils/creators/peer.js'
import type { ServiceMap } from '@libp2p/interface-libp2p'
import type { PeerId } from '@libp2p/interface-peer-id'

describe('Connection encryption configuration', () => {
Expand All @@ -14,7 +15,7 @@ describe('Connection encryption configuration', () => {
})

it('can be created', async () => {
const config: Libp2pOptions = {
const config: Libp2pOptions<ServiceMap> = {
peerId,
start: false,
transports: [
Expand Down
Loading