@libp2p/interface-compliance-tests 3.0.6 → 3.0.7-05abd49f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -3
- package/dist/src/connection/index.d.ts +5 -0
- package/dist/src/connection/index.d.ts.map +1 -0
- package/dist/src/connection/index.js +151 -0
- package/dist/src/connection/index.js.map +1 -0
- package/dist/src/connection-encryption/index.d.ts +5 -0
- package/dist/src/connection-encryption/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/index.js +71 -0
- package/dist/src/connection-encryption/index.js.map +1 -0
- package/dist/src/connection-encryption/utils/index.d.ts +3 -0
- package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/utils/index.js +18 -0
- package/dist/src/connection-encryption/utils/index.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/mocks/connection-encrypter.d.ts +3 -0
- package/dist/src/mocks/connection-encrypter.d.ts.map +1 -0
- package/dist/src/mocks/connection-encrypter.js +98 -0
- package/dist/src/mocks/connection-encrypter.js.map +1 -0
- package/dist/src/mocks/connection-gater.d.ts +3 -0
- package/dist/src/mocks/connection-gater.d.ts.map +1 -0
- package/dist/src/mocks/connection-gater.js +17 -0
- package/dist/src/mocks/connection-gater.js.map +1 -0
- package/dist/src/mocks/connection-manager.d.ts +27 -0
- package/dist/src/mocks/connection-manager.d.ts.map +1 -0
- package/dist/src/mocks/connection-manager.js +145 -0
- package/dist/src/mocks/connection-manager.js.map +1 -0
- package/dist/src/mocks/connection.d.ts +32 -0
- package/dist/src/mocks/connection.d.ts.map +1 -0
- package/dist/src/mocks/connection.js +162 -0
- package/dist/src/mocks/connection.js.map +1 -0
- package/dist/src/mocks/duplex.d.ts +3 -0
- package/dist/src/mocks/duplex.d.ts.map +1 -0
- package/dist/src/mocks/duplex.js +9 -0
- package/dist/src/mocks/duplex.js.map +1 -0
- package/dist/src/mocks/index.d.ts +13 -0
- package/dist/src/mocks/index.d.ts.map +1 -0
- package/dist/src/mocks/index.js +11 -0
- package/dist/src/mocks/index.js.map +1 -0
- package/dist/src/mocks/metrics.d.ts +3 -0
- package/dist/src/mocks/metrics.d.ts.map +1 -0
- package/dist/src/mocks/metrics.js +115 -0
- package/dist/src/mocks/metrics.js.map +1 -0
- package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
- package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
- package/dist/src/mocks/multiaddr-connection.js +51 -0
- package/dist/src/mocks/multiaddr-connection.js.map +1 -0
- package/dist/src/mocks/muxer.d.ts +8 -0
- package/dist/src/mocks/muxer.d.ts.map +1 -0
- package/dist/src/mocks/muxer.js +341 -0
- package/dist/src/mocks/muxer.js.map +1 -0
- package/dist/src/mocks/peer-discovery.d.ts +22 -0
- package/dist/src/mocks/peer-discovery.d.ts.map +1 -0
- package/dist/src/mocks/peer-discovery.js +47 -0
- package/dist/src/mocks/peer-discovery.js.map +1 -0
- package/dist/src/mocks/registrar.d.ts +18 -0
- package/dist/src/mocks/registrar.d.ts.map +1 -0
- package/dist/src/mocks/registrar.js +66 -0
- package/dist/src/mocks/registrar.js.map +1 -0
- package/dist/src/mocks/upgrader.d.ts +10 -0
- package/dist/src/mocks/upgrader.d.ts.map +1 -0
- package/dist/src/mocks/upgrader.js +31 -0
- package/dist/src/mocks/upgrader.js.map +1 -0
- package/dist/src/peer-discovery/index.d.ts +5 -0
- package/dist/src/peer-discovery/index.d.ts.map +1 -0
- package/dist/src/peer-discovery/index.js +66 -0
- package/dist/src/peer-discovery/index.js.map +1 -0
- package/dist/src/stream-muxer/base-test.d.ts +5 -0
- package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/base-test.js +153 -0
- package/dist/src/stream-muxer/base-test.js.map +1 -0
- package/dist/src/stream-muxer/close-test.d.ts +5 -0
- package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/close-test.js +269 -0
- package/dist/src/stream-muxer/close-test.js.map +1 -0
- package/dist/src/stream-muxer/index.d.ts +5 -0
- package/dist/src/stream-muxer/index.d.ts.map +1 -0
- package/dist/src/stream-muxer/index.js +13 -0
- package/dist/src/stream-muxer/index.js.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.js +11 -0
- package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
- package/dist/src/stream-muxer/spawner.d.ts +4 -0
- package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
- package/dist/src/stream-muxer/spawner.js +36 -0
- package/dist/src/stream-muxer/spawner.js.map +1 -0
- package/dist/src/stream-muxer/stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/stress-test.js +23 -0
- package/dist/src/stream-muxer/stress-test.js.map +1 -0
- package/dist/src/transport/dial-test.d.ts +5 -0
- package/dist/src/transport/dial-test.d.ts.map +1 -0
- package/dist/src/transport/dial-test.js +98 -0
- package/dist/src/transport/dial-test.js.map +1 -0
- package/dist/src/transport/filter-test.d.ts +5 -0
- package/dist/src/transport/filter-test.d.ts.map +1 -0
- package/dist/src/transport/filter-test.js +18 -0
- package/dist/src/transport/filter-test.js.map +1 -0
- package/dist/src/transport/index.d.ts +15 -0
- package/dist/src/transport/index.d.ts.map +1 -0
- package/dist/src/transport/index.js +11 -0
- package/dist/src/transport/index.js.map +1 -0
- package/dist/src/transport/listen-test.d.ts +5 -0
- package/dist/src/transport/listen-test.d.ts.map +1 -0
- package/dist/src/transport/listen-test.js +152 -0
- package/dist/src/transport/listen-test.js.map +1 -0
- package/package.json +66 -95
- package/src/connection/index.ts +184 -0
- package/src/connection-encryption/index.ts +97 -0
- package/src/connection-encryption/utils/index.ts +23 -0
- package/src/index.ts +1 -1
- package/src/mocks/connection-encrypter.ts +113 -0
- package/src/mocks/connection-gater.ts +18 -0
- package/src/mocks/connection-manager.ts +209 -0
- package/src/mocks/connection.ts +218 -0
- package/src/mocks/duplex.ts +10 -0
- package/src/mocks/index.ts +12 -0
- package/src/mocks/metrics.ts +162 -0
- package/src/mocks/multiaddr-connection.ts +67 -0
- package/src/mocks/muxer.ts +447 -0
- package/src/mocks/peer-discovery.ts +60 -0
- package/src/mocks/registrar.ts +88 -0
- package/src/mocks/upgrader.ts +49 -0
- package/src/peer-discovery/index.ts +90 -0
- package/src/stream-muxer/base-test.ts +196 -0
- package/src/stream-muxer/close-test.ts +346 -0
- package/src/stream-muxer/index.ts +15 -0
- package/src/stream-muxer/mega-stress-test.ts +14 -0
- package/src/stream-muxer/spawner.ts +54 -0
- package/src/stream-muxer/stress-test.ts +27 -0
- package/src/transport/dial-test.ts +124 -0
- package/src/transport/filter-test.ts +25 -0
- package/src/transport/index.ts +25 -0
- package/src/transport/listen-test.ts +191 -0
- package/dist/typedoc-urls.json +0 -3
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { CodeError } from '@libp2p/interface/errors'
|
|
2
|
+
import { isPeerId, type PeerId } from '@libp2p/interface/peer-id'
|
|
3
|
+
import { PeerMap } from '@libp2p/peer-collections'
|
|
4
|
+
import { peerIdFromString } from '@libp2p/peer-id'
|
|
5
|
+
import { isMultiaddr, type Multiaddr } from '@multiformats/multiaddr'
|
|
6
|
+
import { connectionPair } from './connection.js'
|
|
7
|
+
import type { Libp2pEvents } from '@libp2p/interface'
|
|
8
|
+
import type { Connection } from '@libp2p/interface/connection'
|
|
9
|
+
import type { EventEmitter } from '@libp2p/interface/events'
|
|
10
|
+
import type { Startable } from '@libp2p/interface/startable'
|
|
11
|
+
import type { ConnectionManager, PendingDial } from '@libp2p/interface-internal/connection-manager'
|
|
12
|
+
import type { Registrar } from '@libp2p/interface-internal/registrar'
|
|
13
|
+
|
|
14
|
+
export interface MockNetworkComponents {
|
|
15
|
+
peerId: PeerId
|
|
16
|
+
registrar: Registrar
|
|
17
|
+
connectionManager: ConnectionManager
|
|
18
|
+
events: EventEmitter<Libp2pEvents>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class MockNetwork {
|
|
22
|
+
private components: MockNetworkComponents[] = []
|
|
23
|
+
|
|
24
|
+
addNode (components: MockNetworkComponents): void {
|
|
25
|
+
this.components.push(components)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getNode (peerId: PeerId | Multiaddr []): MockNetworkComponents {
|
|
29
|
+
if (Array.isArray(peerId) && peerId.length > 0) {
|
|
30
|
+
peerId = peerIdFromString(peerId[0].getPeerId() ?? '')
|
|
31
|
+
} else if (isPeerId(peerId)) {
|
|
32
|
+
for (const components of this.components) {
|
|
33
|
+
if (peerId.equals(components.peerId)) {
|
|
34
|
+
return components
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new CodeError('Peer not found', 'ERR_PEER_NOT_FOUND')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
reset (): void {
|
|
43
|
+
this.components = []
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const mockNetwork = new MockNetwork()
|
|
48
|
+
|
|
49
|
+
export interface MockConnectionManagerComponents {
|
|
50
|
+
peerId: PeerId
|
|
51
|
+
registrar: Registrar
|
|
52
|
+
events: EventEmitter<Libp2pEvents>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class MockConnectionManager implements ConnectionManager, Startable {
|
|
56
|
+
private connections: Connection[] = []
|
|
57
|
+
private readonly components: MockConnectionManagerComponents
|
|
58
|
+
private started = false
|
|
59
|
+
|
|
60
|
+
constructor (components: MockConnectionManagerComponents) {
|
|
61
|
+
this.components = components
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
isStarted (): boolean {
|
|
65
|
+
return this.started
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async start (): Promise<void> {
|
|
69
|
+
this.started = true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async stop (): Promise<void> {
|
|
73
|
+
for (const connection of this.connections) {
|
|
74
|
+
await this.closeConnections(connection.remotePeer)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.started = false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getConnections (peerId?: PeerId): Connection[] {
|
|
81
|
+
if (peerId != null) {
|
|
82
|
+
return this.connections
|
|
83
|
+
.filter(c => c.remotePeer.toString() === peerId.toString())
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return this.connections
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getConnectionsMap (): PeerMap<Connection[]> {
|
|
90
|
+
const map = new PeerMap<Connection[]>()
|
|
91
|
+
|
|
92
|
+
for (const conn of this.connections) {
|
|
93
|
+
const conns: Connection[] = map.get(conn.remotePeer) ?? []
|
|
94
|
+
conns.push(conn)
|
|
95
|
+
|
|
96
|
+
map.set(conn.remotePeer, conns)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return map
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async openConnection (peerId: PeerId | Multiaddr | Multiaddr[]): Promise<Connection> {
|
|
103
|
+
if (this.components == null) {
|
|
104
|
+
throw new CodeError('Not initialized', 'ERR_NOT_INITIALIZED')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (isMultiaddr(peerId)) {
|
|
108
|
+
throw new CodeError('Dialing multiaddrs not supported', 'ERR_NOT_SUPPORTED')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let existingConnections: Connection[] = []
|
|
112
|
+
|
|
113
|
+
if (Array.isArray(peerId) && peerId.length > 0) {
|
|
114
|
+
existingConnections = this.getConnections(peerIdFromString(peerId[0].getPeerId() ?? ''))
|
|
115
|
+
} else if (isPeerId(peerId)) {
|
|
116
|
+
existingConnections = this.getConnections(peerId)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (existingConnections.length > 0) {
|
|
120
|
+
return existingConnections[0]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const componentsB = mockNetwork.getNode(peerId)
|
|
124
|
+
|
|
125
|
+
const [aToB, bToA] = connectionPair(this.components, componentsB)
|
|
126
|
+
|
|
127
|
+
// track connections
|
|
128
|
+
this.connections.push(aToB)
|
|
129
|
+
;(componentsB.connectionManager as MockConnectionManager).connections.push(bToA)
|
|
130
|
+
|
|
131
|
+
this.components.events.safeDispatchEvent('connection:open', {
|
|
132
|
+
detail: aToB
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
for (const protocol of this.components.registrar.getProtocols()) {
|
|
136
|
+
for (const topology of this.components.registrar.getTopologies(protocol)) {
|
|
137
|
+
topology.onConnect?.(componentsB.peerId, aToB)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.components.events.safeDispatchEvent('peer:connect', { detail: componentsB.peerId })
|
|
142
|
+
|
|
143
|
+
componentsB.events.safeDispatchEvent('connection:open', {
|
|
144
|
+
detail: bToA
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
for (const protocol of componentsB.registrar.getProtocols()) {
|
|
148
|
+
for (const topology of componentsB.registrar.getTopologies(protocol)) {
|
|
149
|
+
topology.onConnect?.(this.components.peerId, bToA)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
componentsB.events.safeDispatchEvent('peer:connect', { detail: this.components.peerId })
|
|
154
|
+
|
|
155
|
+
return aToB
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async closeConnections (peerId: PeerId): Promise<void> {
|
|
159
|
+
if (this.components == null) {
|
|
160
|
+
throw new CodeError('Not initialized', 'ERR_NOT_INITIALIZED')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const connections = this.getConnections(peerId)
|
|
164
|
+
|
|
165
|
+
if (connections.length === 0) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const componentsB = mockNetwork.getNode(peerId)
|
|
170
|
+
|
|
171
|
+
for (const protocol of this.components.registrar.getProtocols()) {
|
|
172
|
+
this.components.registrar.getTopologies(protocol).forEach(topology => {
|
|
173
|
+
topology.onDisconnect?.(componentsB.peerId)
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (const conn of connections) {
|
|
178
|
+
await conn.close()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.connections = this.connections.filter(c => !c.remotePeer.equals(peerId))
|
|
182
|
+
|
|
183
|
+
if (this.connections.filter(c => !c.remotePeer.equals(peerId)).length === 0) {
|
|
184
|
+
componentsB.events.safeDispatchEvent('peer:disconnect', { detail: peerId })
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
await componentsB.connectionManager?.closeConnections(this.components.peerId)
|
|
188
|
+
|
|
189
|
+
if (componentsB.connectionManager?.getConnectionsMap().get(this.components.peerId) == null) {
|
|
190
|
+
componentsB.events.safeDispatchEvent('peer:disconnect', { detail: this.components.peerId })
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async acceptIncomingConnection (): Promise<boolean> {
|
|
195
|
+
return true
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
afterUpgradeInbound (): void {
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getDialQueue (): PendingDial[] {
|
|
203
|
+
return []
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function mockConnectionManager (components: MockConnectionManagerComponents): ConnectionManager {
|
|
208
|
+
return new MockConnectionManager(components)
|
|
209
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as STATUS from '@libp2p/interface/connection/status'
|
|
2
|
+
import { CodeError } from '@libp2p/interface/errors'
|
|
3
|
+
import { logger } from '@libp2p/logger'
|
|
4
|
+
import * as mss from '@libp2p/multistream-select'
|
|
5
|
+
import { peerIdFromString } from '@libp2p/peer-id'
|
|
6
|
+
import { duplexPair } from 'it-pair/duplex'
|
|
7
|
+
import { pipe } from 'it-pipe'
|
|
8
|
+
import { mockMultiaddrConnection } from './multiaddr-connection.js'
|
|
9
|
+
import { mockMuxer } from './muxer.js'
|
|
10
|
+
import { mockRegistrar } from './registrar.js'
|
|
11
|
+
import type { AbortOptions } from '@libp2p/interface'
|
|
12
|
+
import type { MultiaddrConnection, Connection, Stream, ConnectionStat, Direction } from '@libp2p/interface/connection'
|
|
13
|
+
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
14
|
+
import type { StreamMuxer, StreamMuxerFactory } from '@libp2p/interface/stream-muxer'
|
|
15
|
+
import type { Registrar } from '@libp2p/interface-internal/registrar'
|
|
16
|
+
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
17
|
+
import type { Duplex, Source } from 'it-stream-types'
|
|
18
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
19
|
+
|
|
20
|
+
const log = logger('libp2p:mock-connection')
|
|
21
|
+
|
|
22
|
+
export interface MockConnectionOptions {
|
|
23
|
+
direction?: Direction
|
|
24
|
+
registrar?: Registrar
|
|
25
|
+
muxerFactory?: StreamMuxerFactory
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface MockConnectionInit {
|
|
29
|
+
remoteAddr: Multiaddr
|
|
30
|
+
remotePeer: PeerId
|
|
31
|
+
direction: Direction
|
|
32
|
+
maConn: MultiaddrConnection
|
|
33
|
+
muxer: StreamMuxer
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class MockConnection implements Connection {
|
|
37
|
+
public id: string
|
|
38
|
+
public remoteAddr: Multiaddr
|
|
39
|
+
public remotePeer: PeerId
|
|
40
|
+
public direction: Direction
|
|
41
|
+
public stat: ConnectionStat
|
|
42
|
+
public streams: Stream[]
|
|
43
|
+
public tags: string[]
|
|
44
|
+
|
|
45
|
+
private readonly muxer: StreamMuxer
|
|
46
|
+
private readonly maConn: MultiaddrConnection
|
|
47
|
+
|
|
48
|
+
constructor (init: MockConnectionInit) {
|
|
49
|
+
const { remoteAddr, remotePeer, direction, maConn, muxer } = init
|
|
50
|
+
|
|
51
|
+
this.id = `mock-connection-${Math.random()}`
|
|
52
|
+
this.remoteAddr = remoteAddr
|
|
53
|
+
this.remotePeer = remotePeer
|
|
54
|
+
this.direction = direction
|
|
55
|
+
this.stat = {
|
|
56
|
+
status: STATUS.OPEN,
|
|
57
|
+
direction,
|
|
58
|
+
timeline: maConn.timeline,
|
|
59
|
+
multiplexer: 'test-multiplexer',
|
|
60
|
+
encryption: 'yes-yes-very-secure'
|
|
61
|
+
}
|
|
62
|
+
this.streams = []
|
|
63
|
+
this.tags = []
|
|
64
|
+
this.muxer = muxer
|
|
65
|
+
this.maConn = maConn
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async newStream (protocols: string | string[], options?: AbortOptions): Promise<Stream> {
|
|
69
|
+
if (!Array.isArray(protocols)) {
|
|
70
|
+
protocols = [protocols]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (protocols.length === 0) {
|
|
74
|
+
throw new Error('protocols must have a length')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (this.stat.status !== STATUS.OPEN) {
|
|
78
|
+
throw new CodeError('connection must be open to create streams', 'ERR_CONNECTION_CLOSED')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const id = `${Math.random()}`
|
|
82
|
+
const stream = await this.muxer.newStream(id)
|
|
83
|
+
const result = await mss.select(stream, protocols, options)
|
|
84
|
+
|
|
85
|
+
const streamWithProtocol: Stream = {
|
|
86
|
+
...stream,
|
|
87
|
+
...result.stream,
|
|
88
|
+
stat: {
|
|
89
|
+
...stream.stat,
|
|
90
|
+
direction: 'outbound',
|
|
91
|
+
protocol: result.protocol
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.streams.push(streamWithProtocol)
|
|
96
|
+
|
|
97
|
+
return streamWithProtocol
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
addStream (stream: Stream): void {
|
|
101
|
+
this.streams.push(stream)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
removeStream (id: string): void {
|
|
105
|
+
this.streams = this.streams.filter(stream => stream.id !== id)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async close (): Promise<void> {
|
|
109
|
+
this.stat.status = STATUS.CLOSING
|
|
110
|
+
await this.maConn.close()
|
|
111
|
+
this.streams.forEach(s => {
|
|
112
|
+
s.close()
|
|
113
|
+
})
|
|
114
|
+
this.stat.status = STATUS.CLOSED
|
|
115
|
+
this.stat.timeline.close = Date.now()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function mockConnection (maConn: MultiaddrConnection, opts: MockConnectionOptions = {}): Connection {
|
|
120
|
+
const remoteAddr = maConn.remoteAddr
|
|
121
|
+
const remotePeerIdStr = remoteAddr.getPeerId() ?? '12D3KooWCrhmFM1BCPGBkNzbPfDk4cjYmtAYSpZwUBC69Qg2kZyq'
|
|
122
|
+
|
|
123
|
+
if (remotePeerIdStr == null) {
|
|
124
|
+
throw new Error('Remote multiaddr must contain a peer id')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const remotePeer = peerIdFromString(remotePeerIdStr)
|
|
128
|
+
const direction = opts.direction ?? 'inbound'
|
|
129
|
+
const registrar = opts.registrar ?? mockRegistrar()
|
|
130
|
+
const muxerFactory = opts.muxerFactory ?? mockMuxer()
|
|
131
|
+
|
|
132
|
+
const muxer = muxerFactory.createStreamMuxer({
|
|
133
|
+
direction,
|
|
134
|
+
onIncomingStream: (muxedStream) => {
|
|
135
|
+
try {
|
|
136
|
+
mss.handle(muxedStream, registrar.getProtocols())
|
|
137
|
+
.then(({ stream, protocol }) => {
|
|
138
|
+
log('%s: incoming stream opened on %s', direction, protocol)
|
|
139
|
+
muxedStream = { ...muxedStream, ...stream }
|
|
140
|
+
muxedStream.stat.protocol = protocol
|
|
141
|
+
|
|
142
|
+
connection.addStream(muxedStream)
|
|
143
|
+
const { handler } = registrar.getHandler(protocol)
|
|
144
|
+
|
|
145
|
+
handler({ connection, stream: muxedStream })
|
|
146
|
+
}).catch(err => {
|
|
147
|
+
log.error(err)
|
|
148
|
+
})
|
|
149
|
+
} catch (err: any) {
|
|
150
|
+
log.error(err)
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
onStreamEnd: (muxedStream) => {
|
|
154
|
+
connection.removeStream(muxedStream.id)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
void pipe(
|
|
159
|
+
maConn, muxer, maConn
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
const connection = new MockConnection({
|
|
163
|
+
remoteAddr,
|
|
164
|
+
remotePeer,
|
|
165
|
+
direction,
|
|
166
|
+
maConn,
|
|
167
|
+
muxer
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
return connection
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function mockStream (stream: Duplex<AsyncGenerator<Uint8ArrayList>, Source<Uint8ArrayList | Uint8Array>, Promise<void>>): Stream {
|
|
174
|
+
return {
|
|
175
|
+
...stream,
|
|
176
|
+
close: () => {},
|
|
177
|
+
closeRead: () => {},
|
|
178
|
+
closeWrite: () => {},
|
|
179
|
+
abort: () => {},
|
|
180
|
+
reset: () => {},
|
|
181
|
+
stat: {
|
|
182
|
+
direction: 'outbound',
|
|
183
|
+
protocol: '/foo/1.0.0',
|
|
184
|
+
timeline: {
|
|
185
|
+
open: Date.now()
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
metadata: {},
|
|
189
|
+
id: `stream-${Date.now()}`
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface Peer {
|
|
194
|
+
peerId: PeerId
|
|
195
|
+
registrar: Registrar
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function multiaddrConnectionPair (a: { peerId: PeerId, registrar: Registrar }, b: { peerId: PeerId, registrar: Registrar }): [ MultiaddrConnection, MultiaddrConnection ] {
|
|
199
|
+
const [peerBtoPeerA, peerAtoPeerB] = duplexPair<Uint8Array>()
|
|
200
|
+
|
|
201
|
+
return [
|
|
202
|
+
mockMultiaddrConnection(peerAtoPeerB, b.peerId),
|
|
203
|
+
mockMultiaddrConnection(peerBtoPeerA, a.peerId)
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function connectionPair (a: { peerId: PeerId, registrar: Registrar }, b: { peerId: PeerId, registrar: Registrar }): [ Connection, Connection ] {
|
|
208
|
+
const [peerBtoPeerA, peerAtoPeerB] = multiaddrConnectionPair(a, b)
|
|
209
|
+
|
|
210
|
+
return [
|
|
211
|
+
mockConnection(peerBtoPeerA, {
|
|
212
|
+
registrar: a.registrar
|
|
213
|
+
}),
|
|
214
|
+
mockConnection(peerAtoPeerB, {
|
|
215
|
+
registrar: b.registrar
|
|
216
|
+
})
|
|
217
|
+
]
|
|
218
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { mockConnectionEncrypter } from './connection-encrypter.js'
|
|
2
|
+
export { mockConnectionGater } from './connection-gater.js'
|
|
3
|
+
export { mockConnectionManager, mockNetwork } from './connection-manager.js'
|
|
4
|
+
export { mockConnection, mockStream, connectionPair } from './connection.js'
|
|
5
|
+
export { mockMultiaddrConnection, mockMultiaddrConnPair } from './multiaddr-connection.js'
|
|
6
|
+
export { mockMuxer } from './muxer.js'
|
|
7
|
+
export { mockRegistrar } from './registrar.js'
|
|
8
|
+
export { mockUpgrader } from './upgrader.js'
|
|
9
|
+
export { mockDuplex } from './duplex.js'
|
|
10
|
+
export { mockMetrics } from './metrics.js'
|
|
11
|
+
export type { MockUpgraderInit } from './upgrader.js'
|
|
12
|
+
export type { MockNetworkComponents } from './connection-manager.js'
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { MultiaddrConnection, Stream, Connection } from '@libp2p/interface/connection'
|
|
2
|
+
import type { Metric, MetricGroup, StopTimer, Metrics, CalculatedMetricOptions, MetricOptions } from '@libp2p/interface/metrics'
|
|
3
|
+
|
|
4
|
+
class DefaultMetric implements Metric {
|
|
5
|
+
public value: number = 0
|
|
6
|
+
|
|
7
|
+
update (value: number): void {
|
|
8
|
+
this.value = value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
increment (value: number = 1): void {
|
|
12
|
+
this.value += value
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
decrement (value: number = 1): void {
|
|
16
|
+
this.value -= value
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
reset (): void {
|
|
20
|
+
this.value = 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
timer (): StopTimer {
|
|
24
|
+
const start = Date.now()
|
|
25
|
+
|
|
26
|
+
return () => {
|
|
27
|
+
this.value = Date.now() - start
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class DefaultGroupMetric implements MetricGroup {
|
|
33
|
+
public values: Record<string, number> = {}
|
|
34
|
+
|
|
35
|
+
update (values: Record<string, number>): void {
|
|
36
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
37
|
+
this.values[key] = value
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
increment (values: Record<string, number | unknown>): void {
|
|
42
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
43
|
+
this.values[key] = this.values[key] ?? 0
|
|
44
|
+
const inc = typeof value === 'number' ? value : 1
|
|
45
|
+
|
|
46
|
+
this.values[key] += Number(inc)
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
decrement (values: Record<string, number | unknown>): void {
|
|
51
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
52
|
+
this.values[key] = this.values[key] ?? 0
|
|
53
|
+
const dec = typeof value === 'number' ? value : 1
|
|
54
|
+
|
|
55
|
+
this.values[key] -= Number(dec)
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
reset (): void {
|
|
60
|
+
this.values = {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
timer (key: string): StopTimer {
|
|
64
|
+
const start = Date.now()
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
this.values[key] = Date.now() - start
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class MockMetrics implements Metrics {
|
|
73
|
+
public metrics = new Map<string, any>()
|
|
74
|
+
|
|
75
|
+
trackMultiaddrConnection (maConn: MultiaddrConnection): void {
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
trackProtocolStream (stream: Stream, connection: Connection): void {
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
registerMetric (name: string, opts: CalculatedMetricOptions): void
|
|
84
|
+
registerMetric (name: string, opts?: MetricOptions): Metric
|
|
85
|
+
registerMetric (name: string, opts: any): any {
|
|
86
|
+
if (name == null ?? name.trim() === '') {
|
|
87
|
+
throw new Error('Metric name is required')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (opts?.calculate != null) {
|
|
91
|
+
// calculated metric
|
|
92
|
+
this.metrics.set(name, opts.calculate)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const metric = new DefaultMetric()
|
|
97
|
+
this.metrics.set(name, metric)
|
|
98
|
+
|
|
99
|
+
return metric
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
registerCounter (name: string, opts: CalculatedMetricOptions): void
|
|
103
|
+
registerCounter (name: string, opts?: MetricOptions): Metric
|
|
104
|
+
registerCounter (name: string, opts: any): any {
|
|
105
|
+
if (name == null ?? name.trim() === '') {
|
|
106
|
+
throw new Error('Metric name is required')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (opts?.calculate != null) {
|
|
110
|
+
// calculated metric
|
|
111
|
+
this.metrics.set(name, opts.calculate)
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const metric = new DefaultMetric()
|
|
116
|
+
this.metrics.set(name, metric)
|
|
117
|
+
|
|
118
|
+
return metric
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
registerMetricGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
|
|
122
|
+
registerMetricGroup (name: string, opts?: MetricOptions): MetricGroup
|
|
123
|
+
registerMetricGroup (name: string, opts: any): any {
|
|
124
|
+
if (name == null ?? name.trim() === '') {
|
|
125
|
+
throw new Error('Metric name is required')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (opts?.calculate != null) {
|
|
129
|
+
// calculated metric
|
|
130
|
+
this.metrics.set(name, opts.calculate)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const metric = new DefaultGroupMetric()
|
|
135
|
+
this.metrics.set(name, metric)
|
|
136
|
+
|
|
137
|
+
return metric
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
registerCounterGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
|
|
141
|
+
registerCounterGroup (name: string, opts?: MetricOptions): MetricGroup
|
|
142
|
+
registerCounterGroup (name: string, opts: any): any {
|
|
143
|
+
if (name == null ?? name.trim() === '') {
|
|
144
|
+
throw new Error('Metric name is required')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (opts?.calculate != null) {
|
|
148
|
+
// calculated metric
|
|
149
|
+
this.metrics.set(name, opts.calculate)
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const metric = new DefaultGroupMetric()
|
|
154
|
+
this.metrics.set(name, metric)
|
|
155
|
+
|
|
156
|
+
return metric
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function mockMetrics (): () => Metrics {
|
|
161
|
+
return () => new MockMetrics()
|
|
162
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { multiaddr } from '@multiformats/multiaddr'
|
|
2
|
+
import { abortableSource } from 'abortable-iterator'
|
|
3
|
+
import { duplexPair } from 'it-pair/duplex'
|
|
4
|
+
import type { MultiaddrConnection } from '@libp2p/interface/connection'
|
|
5
|
+
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
6
|
+
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
7
|
+
import type { Duplex } from 'it-stream-types'
|
|
8
|
+
|
|
9
|
+
export function mockMultiaddrConnection (source: Duplex<AsyncGenerator<Uint8Array>> & Partial<MultiaddrConnection>, peerId: PeerId): MultiaddrConnection {
|
|
10
|
+
const maConn: MultiaddrConnection = {
|
|
11
|
+
async close () {
|
|
12
|
+
|
|
13
|
+
},
|
|
14
|
+
timeline: {
|
|
15
|
+
open: Date.now()
|
|
16
|
+
},
|
|
17
|
+
remoteAddr: multiaddr(`/ip4/127.0.0.1/tcp/4001/p2p/${peerId.toString()}`),
|
|
18
|
+
...source
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return maConn
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MockMultiaddrConnPairOptions {
|
|
25
|
+
addrs: Multiaddr[]
|
|
26
|
+
remotePeer: PeerId
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns both sides of a mocked MultiaddrConnection
|
|
31
|
+
*/
|
|
32
|
+
export function mockMultiaddrConnPair (opts: MockMultiaddrConnPairOptions): { inbound: MultiaddrConnection, outbound: MultiaddrConnection } {
|
|
33
|
+
const { addrs, remotePeer } = opts
|
|
34
|
+
const controller = new AbortController()
|
|
35
|
+
const [localAddr, remoteAddr] = addrs
|
|
36
|
+
const [inboundStream, outboundStream] = duplexPair<Uint8Array>()
|
|
37
|
+
|
|
38
|
+
const outbound: MultiaddrConnection = {
|
|
39
|
+
...outboundStream,
|
|
40
|
+
remoteAddr: remoteAddr.toString().includes(`/p2p/${remotePeer.toString()}`) ? remoteAddr : remoteAddr.encapsulate(`/p2p/${remotePeer.toString()}`),
|
|
41
|
+
timeline: {
|
|
42
|
+
open: Date.now()
|
|
43
|
+
},
|
|
44
|
+
close: async () => {
|
|
45
|
+
outbound.timeline.close = Date.now()
|
|
46
|
+
controller.abort()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const inbound: MultiaddrConnection = {
|
|
51
|
+
...inboundStream,
|
|
52
|
+
remoteAddr: localAddr,
|
|
53
|
+
timeline: {
|
|
54
|
+
open: Date.now()
|
|
55
|
+
},
|
|
56
|
+
close: async () => {
|
|
57
|
+
inbound.timeline.close = Date.now()
|
|
58
|
+
controller.abort()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Make the sources abortable so we can close them easily
|
|
63
|
+
inbound.source = abortableSource(inbound.source, controller.signal)
|
|
64
|
+
outbound.source = abortableSource(outbound.source, controller.signal)
|
|
65
|
+
|
|
66
|
+
return { inbound, outbound }
|
|
67
|
+
}
|