Skip to content
Merged
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
10 changes: 10 additions & 0 deletions packages/interface/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ export interface PendingDial {
multiaddrs: Multiaddr[]
}

export type Libp2pStatus = 'starting' | 'started' | 'stopping' | 'stopped'

/**
* Libp2p nodes implement this interface.
*/
Expand Down Expand Up @@ -410,8 +412,16 @@ export interface Libp2p<T extends ServiceMap = ServiceMap> extends Startable, Ty
*/
metrics?: Metrics

/**
* The logger used by this libp2p node
*/
logger: ComponentLogger

/**
* The current status of the libp2p node
*/
status: Libp2pStatus

/**
* Get a deduplicated list of peer advertising multiaddrs by concatenating
* the listen addresses used by transports with any configured
Expand Down
21 changes: 10 additions & 11 deletions packages/libp2p/src/libp2p.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { DefaultUpgrader } from './upgrader.js'
import * as pkg from './version.js'
import type { Components } from './components.js'
import type { Libp2p, Libp2pInit, Libp2pOptions } from './index.js'
import type { PeerRouting, ContentRouting, Libp2pEvents, PendingDial, ServiceMap, AbortOptions, ComponentLogger, Logger, Connection, NewStreamOptions, Stream, Metrics, PeerId, PeerInfo, PeerStore, Topology } from '@libp2p/interface'
import type { PeerRouting, ContentRouting, Libp2pEvents, PendingDial, ServiceMap, AbortOptions, ComponentLogger, Logger, Connection, NewStreamOptions, Stream, Metrics, PeerId, PeerInfo, PeerStore, Topology, Libp2pStatus } from '@libp2p/interface'
import type { StreamHandler, StreamHandlerOptions } from '@libp2p/interface-internal'

export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends TypedEventEmitter<Libp2pEvents> implements Libp2p<T> {
Expand All @@ -34,14 +34,16 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
public metrics?: Metrics
public services: T
public logger: ComponentLogger
public status: Libp2pStatus

public components: Components
#started: boolean
private readonly log: Logger

constructor (init: Libp2pInit<T>) {
super()

this.status = 'stopped'

// event bus - components can listen to this emitter to be notified of system events
// and also cause them to be emitted
const events = new TypedEventEmitter<Libp2pEvents>()
Expand All @@ -58,7 +60,6 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
// This emitter gets listened to a lot
setMaxListeners(Infinity, events)

this.#started = false
this.peerId = init.peerId
this.logger = init.logger ?? defaultLogger()
this.log = this.logger.forComponent('libp2p')
Expand Down Expand Up @@ -196,11 +197,11 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
* Starts the libp2p node and all its subsystems
*/
async start (): Promise<void> {
if (this.#started) {
if (this.status !== 'stopped') {
return
}

this.#started = true
this.status = 'starting'

this.log('libp2p is starting')

Expand All @@ -209,6 +210,7 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
await this.components.start()
await this.components.afterStart?.()

this.status = 'started'
this.safeDispatchEvent('start', { detail: this })
this.log('libp2p has started')
} catch (err: any) {
Expand All @@ -222,26 +224,23 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
* Stop the libp2p node by closing its listeners and open connections
*/
async stop (): Promise<void> {
if (!this.#started) {
if (this.status !== 'started') {
return
}

this.log('libp2p is stopping')

this.#started = false
this.status = 'stopping'

await this.components.beforeStop?.()
await this.components.stop()
await this.components.afterStop?.()

this.status = 'stopped'
this.safeDispatchEvent('stop', { detail: this })
this.log('libp2p has stopped')
}

isStarted (): boolean {
return this.#started
}

getConnections (peerId?: PeerId): Connection[] {
return this.components.connectionManager.getConnections(peerId)
}
Expand Down
49 changes: 49 additions & 0 deletions packages/libp2p/test/core/status.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-env mocha */

import { plaintext } from '@libp2p/plaintext'
import { tcp } from '@libp2p/tcp'
import { expect } from 'aegir/chai'
import { createLibp2pNode, type Libp2pNode } from '../../src/libp2p.js'

const listenAddr = '/ip4/0.0.0.0/tcp/0'

describe('status', () => {
let libp2p: Libp2pNode

after(async () => {
await libp2p.stop()
})

it('should have status', async () => {
libp2p = await createLibp2pNode({
start: false,
addresses: {
listen: [listenAddr]
},
transports: [
tcp()
],
connectionEncryption: [
plaintext()
]
})

expect(libp2p).to.have.property('status', 'stopped')

const startP = libp2p.start()

expect(libp2p).to.have.property('status', 'starting')

await startP

expect(libp2p).to.have.property('status', 'started')

const stopP = libp2p.stop()

expect(libp2p).to.have.property('status', 'stopping')

await stopP

expect(libp2p).to.have.property('status', 'stopped')
})
})