@libp2p/interface-compliance-tests 3.0.7-ea8a0637 → 3.0.7-eabf6f36
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 +9 -0
- 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 +150 -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 +19 -0
- package/dist/src/connection-encryption/utils/index.js.map +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/is-valid-tick.d.ts.map +1 -1
- package/dist/src/is-valid-tick.js.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 +29 -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 +167 -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 +60 -0
- package/dist/src/mocks/multiaddr-connection.js.map +1 -0
- package/dist/src/mocks/muxer.d.ts +36 -0
- package/dist/src/mocks/muxer.d.ts.map +1 -0
- package/dist/src/mocks/muxer.js +213 -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/pubsub/api.d.ts +6 -0
- package/dist/src/pubsub/api.d.ts.map +1 -0
- package/dist/src/pubsub/api.js +87 -0
- package/dist/src/pubsub/api.js.map +1 -0
- package/dist/src/pubsub/connection-handlers.d.ts +6 -0
- package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
- package/dist/src/pubsub/connection-handlers.js +329 -0
- package/dist/src/pubsub/connection-handlers.js.map +1 -0
- package/dist/src/pubsub/emit-self.d.ts +6 -0
- package/dist/src/pubsub/emit-self.d.ts.map +1 -0
- package/dist/src/pubsub/emit-self.js +80 -0
- package/dist/src/pubsub/emit-self.js.map +1 -0
- package/dist/src/pubsub/index.d.ts +18 -0
- package/dist/src/pubsub/index.d.ts.map +1 -0
- package/dist/src/pubsub/index.js +17 -0
- package/dist/src/pubsub/index.js.map +1 -0
- package/dist/src/pubsub/messages.d.ts +6 -0
- package/dist/src/pubsub/messages.d.ts.map +1 -0
- package/dist/src/pubsub/messages.js +48 -0
- package/dist/src/pubsub/messages.js.map +1 -0
- package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
- package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
- package/dist/src/pubsub/multiple-nodes.js +350 -0
- package/dist/src/pubsub/multiple-nodes.js.map +1 -0
- package/dist/src/pubsub/two-nodes.d.ts +6 -0
- package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
- package/dist/src/pubsub/two-nodes.js +217 -0
- package/dist/src/pubsub/two-nodes.js.map +1 -0
- package/dist/src/pubsub/utils.d.ts +6 -0
- package/dist/src/pubsub/utils.d.ts.map +1 -0
- package/dist/src/pubsub/utils.js +22 -0
- package/dist/src/pubsub/utils.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 +357 -0
- package/dist/src/stream-muxer/close-test.js.map +1 -0
- package/dist/src/stream-muxer/fixtures/pb/message.d.ts +13 -0
- package/dist/src/stream-muxer/fixtures/pb/message.d.ts.map +1 -0
- package/dist/src/stream-muxer/fixtures/pb/message.js +67 -0
- package/dist/src/stream-muxer/fixtures/pb/message.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 +37 -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 +72 -5
- package/src/connection/index.ts +182 -0
- package/src/connection-encryption/index.ts +97 -0
- package/src/connection-encryption/utils/index.ts +24 -0
- package/src/index.ts +0 -1
- package/src/is-valid-tick.ts +0 -1
- package/src/mocks/connection-encrypter.ts +113 -0
- package/src/mocks/connection-gater.ts +18 -0
- package/src/mocks/connection-manager.ts +211 -0
- package/src/mocks/connection.ts +226 -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 +76 -0
- package/src/mocks/muxer.ts +303 -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/pubsub/api.ts +114 -0
- package/src/pubsub/connection-handlers.ts +413 -0
- package/src/pubsub/emit-self.ts +99 -0
- package/src/pubsub/index.ts +34 -0
- package/src/pubsub/messages.ts +59 -0
- package/src/pubsub/multiple-nodes.ts +440 -0
- package/src/pubsub/two-nodes.ts +273 -0
- package/src/pubsub/utils.ts +29 -0
- package/src/stream-muxer/base-test.ts +196 -0
- package/src/stream-muxer/close-test.ts +442 -0
- package/src/stream-muxer/fixtures/pb/message.proto +7 -0
- package/src/stream-muxer/fixtures/pb/message.ts +87 -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 +55 -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
|
@@ -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,76 @@
|
|
|
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
|
+
abort: () => {},
|
|
15
|
+
timeline: {
|
|
16
|
+
open: Date.now()
|
|
17
|
+
},
|
|
18
|
+
remoteAddr: multiaddr(`/ip4/127.0.0.1/tcp/4001/p2p/${peerId.toString()}`),
|
|
19
|
+
...source
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return maConn
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface MockMultiaddrConnPairOptions {
|
|
26
|
+
addrs: Multiaddr[]
|
|
27
|
+
remotePeer: PeerId
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns both sides of a mocked MultiaddrConnection
|
|
32
|
+
*/
|
|
33
|
+
export function mockMultiaddrConnPair (opts: MockMultiaddrConnPairOptions): { inbound: MultiaddrConnection, outbound: MultiaddrConnection } {
|
|
34
|
+
const { addrs, remotePeer } = opts
|
|
35
|
+
const controller = new AbortController()
|
|
36
|
+
const [localAddr, remoteAddr] = addrs
|
|
37
|
+
const [inboundStream, outboundStream] = duplexPair<Uint8Array>()
|
|
38
|
+
|
|
39
|
+
const outbound: MultiaddrConnection = {
|
|
40
|
+
...outboundStream,
|
|
41
|
+
remoteAddr: remoteAddr.toString().includes(`/p2p/${remotePeer.toString()}`) ? remoteAddr : remoteAddr.encapsulate(`/p2p/${remotePeer.toString()}`),
|
|
42
|
+
timeline: {
|
|
43
|
+
open: Date.now()
|
|
44
|
+
},
|
|
45
|
+
close: async () => {
|
|
46
|
+
outbound.timeline.close = Date.now()
|
|
47
|
+
controller.abort()
|
|
48
|
+
},
|
|
49
|
+
abort: (err: Error) => {
|
|
50
|
+
outbound.timeline.close = Date.now()
|
|
51
|
+
controller.abort(err)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const inbound: MultiaddrConnection = {
|
|
56
|
+
...inboundStream,
|
|
57
|
+
remoteAddr: localAddr,
|
|
58
|
+
timeline: {
|
|
59
|
+
open: Date.now()
|
|
60
|
+
},
|
|
61
|
+
close: async () => {
|
|
62
|
+
inbound.timeline.close = Date.now()
|
|
63
|
+
controller.abort()
|
|
64
|
+
},
|
|
65
|
+
abort: (err: Error) => {
|
|
66
|
+
outbound.timeline.close = Date.now()
|
|
67
|
+
controller.abort(err)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Make the sources abortable so we can close them easily
|
|
72
|
+
inbound.source = abortableSource(inbound.source, controller.signal)
|
|
73
|
+
outbound.source = abortableSource(outbound.source, controller.signal)
|
|
74
|
+
|
|
75
|
+
return { inbound, outbound }
|
|
76
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { AbstractStream, type AbstractStreamInit } from '@libp2p/interface/stream-muxer/stream'
|
|
2
|
+
import { type Logger, logger } from '@libp2p/logger'
|
|
3
|
+
import { abortableSource } from 'abortable-iterator'
|
|
4
|
+
import map from 'it-map'
|
|
5
|
+
import * as ndjson from 'it-ndjson'
|
|
6
|
+
import { pipe } from 'it-pipe'
|
|
7
|
+
import { type Pushable, pushable } from 'it-pushable'
|
|
8
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
9
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
10
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
11
|
+
import type { AbortOptions } from '@libp2p/interface'
|
|
12
|
+
import type { Direction, Stream } from '@libp2p/interface/connection'
|
|
13
|
+
import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface/stream-muxer'
|
|
14
|
+
import type { Source } from 'it-stream-types'
|
|
15
|
+
|
|
16
|
+
let muxers = 0
|
|
17
|
+
let streams = 0
|
|
18
|
+
|
|
19
|
+
interface DataMessage {
|
|
20
|
+
id: string
|
|
21
|
+
type: 'data'
|
|
22
|
+
direction: Direction
|
|
23
|
+
chunk: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ResetMessage {
|
|
27
|
+
id: string
|
|
28
|
+
type: 'reset'
|
|
29
|
+
direction: Direction
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface CloseMessage {
|
|
33
|
+
id: string
|
|
34
|
+
type: 'close'
|
|
35
|
+
direction: Direction
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CreateMessage {
|
|
39
|
+
id: string
|
|
40
|
+
type: 'create'
|
|
41
|
+
direction: 'outbound'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type StreamMessage = DataMessage | ResetMessage | CloseMessage | CreateMessage
|
|
45
|
+
|
|
46
|
+
export interface MockMuxedStreamInit extends AbstractStreamInit {
|
|
47
|
+
push: Pushable<StreamMessage>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class MuxedStream extends AbstractStream {
|
|
51
|
+
private readonly push: Pushable<StreamMessage>
|
|
52
|
+
|
|
53
|
+
constructor (init: MockMuxedStreamInit) {
|
|
54
|
+
super(init)
|
|
55
|
+
|
|
56
|
+
this.push = init.push
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
sendNewStream (): void {
|
|
60
|
+
// If initiator, open a new stream
|
|
61
|
+
const createMsg: CreateMessage = {
|
|
62
|
+
id: this.id,
|
|
63
|
+
type: 'create',
|
|
64
|
+
direction: 'outbound'
|
|
65
|
+
}
|
|
66
|
+
this.push.push(createMsg)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
sendData (data: Uint8ArrayList): void {
|
|
70
|
+
const dataMsg: DataMessage = {
|
|
71
|
+
id: this.id,
|
|
72
|
+
type: 'data',
|
|
73
|
+
chunk: uint8ArrayToString(data.subarray(), 'base64pad'),
|
|
74
|
+
direction: this.direction
|
|
75
|
+
}
|
|
76
|
+
this.push.push(dataMsg)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
sendReset (): void {
|
|
80
|
+
const resetMsg: ResetMessage = {
|
|
81
|
+
id: this.id,
|
|
82
|
+
type: 'reset',
|
|
83
|
+
direction: this.direction
|
|
84
|
+
}
|
|
85
|
+
this.push.push(resetMsg)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
sendCloseWrite (): void {
|
|
89
|
+
const closeMsg: CloseMessage = {
|
|
90
|
+
id: this.id,
|
|
91
|
+
type: 'close',
|
|
92
|
+
direction: this.direction
|
|
93
|
+
}
|
|
94
|
+
this.push.push(closeMsg)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
sendCloseRead (): void {
|
|
98
|
+
// does not support close read, only close write
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class MockMuxer implements StreamMuxer {
|
|
103
|
+
public source: AsyncGenerator<Uint8Array>
|
|
104
|
+
public input: Pushable<Uint8Array>
|
|
105
|
+
public streamInput: Pushable<StreamMessage>
|
|
106
|
+
public name: string
|
|
107
|
+
public protocol: string = '/mock-muxer/1.0.0'
|
|
108
|
+
|
|
109
|
+
private readonly closeController: AbortController
|
|
110
|
+
private readonly registryInitiatorStreams: Map<string, MuxedStream>
|
|
111
|
+
private readonly registryRecipientStreams: Map<string, MuxedStream>
|
|
112
|
+
private readonly options: StreamMuxerInit
|
|
113
|
+
|
|
114
|
+
private readonly log: Logger
|
|
115
|
+
|
|
116
|
+
constructor (init?: StreamMuxerInit) {
|
|
117
|
+
this.name = `muxer:${muxers++}`
|
|
118
|
+
this.log = logger(`libp2p:mock-muxer:${this.name}`)
|
|
119
|
+
this.registryInitiatorStreams = new Map()
|
|
120
|
+
this.registryRecipientStreams = new Map()
|
|
121
|
+
this.log('create muxer')
|
|
122
|
+
this.options = init ?? { direction: 'inbound' }
|
|
123
|
+
this.closeController = new AbortController()
|
|
124
|
+
// receives data from the muxer at the other end of the stream
|
|
125
|
+
this.source = this.input = pushable({
|
|
126
|
+
onEnd: () => {
|
|
127
|
+
for (const stream of this.registryInitiatorStreams.values()) {
|
|
128
|
+
stream.destroy()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (const stream of this.registryRecipientStreams.values()) {
|
|
132
|
+
stream.destroy()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// receives messages from all of the muxed streams
|
|
138
|
+
this.streamInput = pushable<StreamMessage>({
|
|
139
|
+
objectMode: true
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// receive incoming messages
|
|
144
|
+
async sink (source: Source<Uint8ArrayList | Uint8Array>): Promise<void> {
|
|
145
|
+
try {
|
|
146
|
+
await pipe(
|
|
147
|
+
abortableSource(source, this.closeController.signal),
|
|
148
|
+
(source) => map(source, buf => uint8ArrayToString(buf.subarray())),
|
|
149
|
+
ndjson.parse<StreamMessage>,
|
|
150
|
+
async (source) => {
|
|
151
|
+
for await (const message of source) {
|
|
152
|
+
this.log.trace('-> %s %s %s', message.type, message.direction, message.id)
|
|
153
|
+
this.handleMessage(message)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
this.log('muxed stream ended')
|
|
159
|
+
this.input.end()
|
|
160
|
+
} catch (err: any) {
|
|
161
|
+
this.log('muxed stream errored', err)
|
|
162
|
+
this.input.end(err)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
handleMessage (message: StreamMessage): void {
|
|
167
|
+
let muxedStream: MuxedStream | undefined
|
|
168
|
+
|
|
169
|
+
const registry = message.direction === 'outbound' ? this.registryRecipientStreams : this.registryInitiatorStreams
|
|
170
|
+
|
|
171
|
+
if (message.type === 'create') {
|
|
172
|
+
if (registry.has(message.id)) {
|
|
173
|
+
throw new Error(`Already had stream for ${message.id}`)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
muxedStream = this.createStream(message.id, 'inbound')
|
|
177
|
+
registry.set(muxedStream.id, muxedStream)
|
|
178
|
+
|
|
179
|
+
if (this.options.onIncomingStream != null) {
|
|
180
|
+
this.options.onIncomingStream(muxedStream)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
muxedStream = registry.get(message.id)
|
|
185
|
+
|
|
186
|
+
if (muxedStream == null) {
|
|
187
|
+
this.log.error(`No stream found for ${message.id}`)
|
|
188
|
+
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (message.type === 'data') {
|
|
193
|
+
muxedStream.sourcePush(new Uint8ArrayList(uint8ArrayFromString(message.chunk, 'base64pad')))
|
|
194
|
+
} else if (message.type === 'reset') {
|
|
195
|
+
this.log('-> reset stream %s %s', muxedStream.direction, muxedStream.id)
|
|
196
|
+
muxedStream.reset()
|
|
197
|
+
} else if (message.type === 'close') {
|
|
198
|
+
this.log('-> closing stream %s %s', muxedStream.direction, muxedStream.id)
|
|
199
|
+
muxedStream.remoteCloseWrite()
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
get streams (): Stream[] {
|
|
204
|
+
return Array.from(this.registryRecipientStreams.values())
|
|
205
|
+
.concat(Array.from(this.registryInitiatorStreams.values()))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
newStream (name?: string): Stream {
|
|
209
|
+
if (this.closeController.signal.aborted) {
|
|
210
|
+
throw new Error('Muxer already closed')
|
|
211
|
+
}
|
|
212
|
+
this.log('newStream %s', name)
|
|
213
|
+
const storedStream = this.createStream(name, 'outbound')
|
|
214
|
+
this.registryInitiatorStreams.set(storedStream.id, storedStream)
|
|
215
|
+
|
|
216
|
+
return storedStream
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
createStream (name?: string, direction: Direction = 'outbound'): MuxedStream {
|
|
220
|
+
const id = name ?? `${streams++}`
|
|
221
|
+
|
|
222
|
+
this.log('createStream %s %s', direction, id)
|
|
223
|
+
|
|
224
|
+
const muxedStream: MuxedStream = new MuxedStream({
|
|
225
|
+
id,
|
|
226
|
+
direction,
|
|
227
|
+
push: this.streamInput,
|
|
228
|
+
onEnd: () => {
|
|
229
|
+
this.log('stream ended')
|
|
230
|
+
|
|
231
|
+
if (direction === 'outbound') {
|
|
232
|
+
this.registryInitiatorStreams.delete(muxedStream.id)
|
|
233
|
+
} else {
|
|
234
|
+
this.registryRecipientStreams.delete(muxedStream.id)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (this.options.onStreamEnd != null) {
|
|
238
|
+
this.options.onStreamEnd(muxedStream)
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
log: logger(`libp2p:mock-muxer:stream:${direction}:${id}`)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
return muxedStream
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async close (options?: AbortOptions): Promise<void> {
|
|
248
|
+
if (this.closeController.signal.aborted) {
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.log('closing muxed streams')
|
|
253
|
+
|
|
254
|
+
await Promise.all(
|
|
255
|
+
this.streams.map(async s => s.close())
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
this.closeController.abort()
|
|
259
|
+
this.input.end()
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
abort (err: Error): void {
|
|
263
|
+
if (this.closeController.signal.aborted) {
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
this.log('aborting muxed streams')
|
|
268
|
+
|
|
269
|
+
this.streams.forEach(s => {
|
|
270
|
+
s.abort(err)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
this.closeController.abort(err)
|
|
274
|
+
this.input.end(err)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
class MockMuxerFactory implements StreamMuxerFactory {
|
|
279
|
+
public protocol: string = '/mock-muxer/1.0.0'
|
|
280
|
+
|
|
281
|
+
createStreamMuxer (init?: StreamMuxerInit): StreamMuxer {
|
|
282
|
+
const mockMuxer = new MockMuxer(init)
|
|
283
|
+
|
|
284
|
+
void Promise.resolve().then(async () => {
|
|
285
|
+
void pipe(
|
|
286
|
+
mockMuxer.streamInput,
|
|
287
|
+
ndjson.stringify,
|
|
288
|
+
(source) => map(source, str => new Uint8ArrayList(uint8ArrayFromString(str))),
|
|
289
|
+
async (source) => {
|
|
290
|
+
for await (const buf of source) {
|
|
291
|
+
mockMuxer.input.push(buf.subarray())
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
return mockMuxer
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function mockMuxer (): MockMuxerFactory {
|
|
302
|
+
return new MockMuxerFactory()
|
|
303
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EventEmitter } from '@libp2p/interface/events'
|
|
2
|
+
import { peerDiscovery } from '@libp2p/interface/peer-discovery'
|
|
3
|
+
import * as PeerIdFactory from '@libp2p/peer-id-factory'
|
|
4
|
+
import { multiaddr } from '@multiformats/multiaddr'
|
|
5
|
+
import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interface/peer-discovery'
|
|
6
|
+
import type { PeerInfo } from '@libp2p/interface/peer-info'
|
|
7
|
+
|
|
8
|
+
interface MockDiscoveryInit {
|
|
9
|
+
discoveryDelay?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Emits 'peer' events on discovery.
|
|
14
|
+
*/
|
|
15
|
+
export class MockDiscovery extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery {
|
|
16
|
+
public readonly options: MockDiscoveryInit
|
|
17
|
+
private _isRunning: boolean
|
|
18
|
+
private _timer: any
|
|
19
|
+
|
|
20
|
+
constructor (init = {}) {
|
|
21
|
+
super()
|
|
22
|
+
|
|
23
|
+
this.options = init
|
|
24
|
+
this._isRunning = false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
readonly [peerDiscovery] = this
|
|
28
|
+
|
|
29
|
+
start (): void {
|
|
30
|
+
this._isRunning = true
|
|
31
|
+
this._discoverPeer()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
stop (): void {
|
|
35
|
+
clearTimeout(this._timer)
|
|
36
|
+
this._isRunning = false
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
isStarted (): boolean {
|
|
40
|
+
return this._isRunning
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_discoverPeer (): void {
|
|
44
|
+
if (!this._isRunning) return
|
|
45
|
+
|
|
46
|
+
PeerIdFactory.createEd25519PeerId()
|
|
47
|
+
.then(peerId => {
|
|
48
|
+
this._timer = setTimeout(() => {
|
|
49
|
+
this.safeDispatchEvent<PeerInfo>('peer', {
|
|
50
|
+
detail: {
|
|
51
|
+
id: peerId,
|
|
52
|
+
multiaddrs: [multiaddr('/ip4/127.0.0.1/tcp/8000')],
|
|
53
|
+
protocols: []
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}, this.options.discoveryDelay ?? 1000)
|
|
57
|
+
})
|
|
58
|
+
.catch(() => {})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import merge from 'merge-options'
|
|
2
|
+
import type { Connection } from '@libp2p/interface/connection'
|
|
3
|
+
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
4
|
+
import type { Topology } from '@libp2p/interface/topology'
|
|
5
|
+
import type { IncomingStreamData, Registrar, StreamHandler, StreamHandlerOptions, StreamHandlerRecord } from '@libp2p/interface-internal/registrar'
|
|
6
|
+
|
|
7
|
+
export class MockRegistrar implements Registrar {
|
|
8
|
+
private readonly topologies = new Map<string, Array<{ id: string, topology: Topology }>>()
|
|
9
|
+
private readonly handlers = new Map<string, StreamHandlerRecord>()
|
|
10
|
+
|
|
11
|
+
getProtocols (): string[] {
|
|
12
|
+
return Array.from(this.handlers.keys()).sort()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async handle (protocol: string, handler: StreamHandler, opts?: StreamHandlerOptions): Promise<void> {
|
|
16
|
+
const options = merge.bind({ ignoreUndefined: true })({
|
|
17
|
+
maxInboundStreams: 1,
|
|
18
|
+
maxOutboundStreams: 1
|
|
19
|
+
}, opts)
|
|
20
|
+
|
|
21
|
+
if (this.handlers.has(protocol)) {
|
|
22
|
+
throw new Error(`Handler already registered for protocol ${protocol}`)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.handlers.set(protocol, {
|
|
26
|
+
handler,
|
|
27
|
+
options
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async unhandle (protocol: string): Promise<void> {
|
|
32
|
+
this.handlers.delete(protocol)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getHandler (protocol: string): StreamHandlerRecord {
|
|
36
|
+
const handler = this.handlers.get(protocol)
|
|
37
|
+
|
|
38
|
+
if (handler == null) {
|
|
39
|
+
throw new Error(`No handler registered for protocol ${protocol}`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return handler
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async register (protocol: string, topology: Topology): Promise<string> {
|
|
46
|
+
const id = `topology-id-${Math.random()}`
|
|
47
|
+
let topologies = this.topologies.get(protocol)
|
|
48
|
+
|
|
49
|
+
if (topologies == null) {
|
|
50
|
+
topologies = []
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
topologies.push({
|
|
54
|
+
id,
|
|
55
|
+
topology
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
this.topologies.set(protocol, topologies)
|
|
59
|
+
|
|
60
|
+
return id
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
unregister (id: string | string[]): void {
|
|
64
|
+
if (!Array.isArray(id)) {
|
|
65
|
+
id = [id]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
id.forEach(id => this.topologies.delete(id))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getTopologies (protocol: string): Topology[] {
|
|
72
|
+
return (this.topologies.get(protocol) ?? []).map(t => t.topology)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function mockRegistrar (): Registrar {
|
|
77
|
+
return new MockRegistrar()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function mockIncomingStreamEvent (protocol: string, conn: Connection, remotePeer: PeerId): Promise<IncomingStreamData> {
|
|
81
|
+
return {
|
|
82
|
+
...await conn.newStream([protocol]),
|
|
83
|
+
// @ts-expect-error incomplete implementation
|
|
84
|
+
connection: {
|
|
85
|
+
remotePeer
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|