@libp2p/interface-compliance-tests 3.0.7 → 4.0.0-e66f4891
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 +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 +74 -97
- 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
- package/dist/typedoc-urls.json +0 -3
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { mockConnection } from './connection.js'
|
|
2
|
+
import type { Libp2pEvents } from '@libp2p/interface'
|
|
3
|
+
import type { Connection, MultiaddrConnection } from '@libp2p/interface/connection'
|
|
4
|
+
import type { EventEmitter } from '@libp2p/interface/events'
|
|
5
|
+
import type { Upgrader, UpgraderOptions } from '@libp2p/interface/transport'
|
|
6
|
+
import type { Registrar } from '@libp2p/interface-internal/registrar'
|
|
7
|
+
|
|
8
|
+
export interface MockUpgraderInit {
|
|
9
|
+
registrar?: Registrar
|
|
10
|
+
events?: EventEmitter<Libp2pEvents>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class MockUpgrader implements Upgrader {
|
|
14
|
+
private readonly registrar?: Registrar
|
|
15
|
+
private readonly events?: EventEmitter<Libp2pEvents>
|
|
16
|
+
|
|
17
|
+
constructor (init: MockUpgraderInit) {
|
|
18
|
+
this.registrar = init.registrar
|
|
19
|
+
this.events = init.events
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async upgradeOutbound (multiaddrConnection: MultiaddrConnection, opts: UpgraderOptions = {}): Promise<Connection> {
|
|
23
|
+
const connection = mockConnection(multiaddrConnection, {
|
|
24
|
+
direction: 'outbound',
|
|
25
|
+
registrar: this.registrar,
|
|
26
|
+
...opts
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
this.events?.safeDispatchEvent('connection:open', { detail: connection })
|
|
30
|
+
|
|
31
|
+
return connection
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async upgradeInbound (multiaddrConnection: MultiaddrConnection, opts: UpgraderOptions = {}): Promise<Connection> {
|
|
35
|
+
const connection = mockConnection(multiaddrConnection, {
|
|
36
|
+
direction: 'inbound',
|
|
37
|
+
registrar: this.registrar,
|
|
38
|
+
...opts
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
this.events?.safeDispatchEvent('connection:open', { detail: connection })
|
|
42
|
+
|
|
43
|
+
return connection
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function mockUpgrader (init: MockUpgraderInit = {}): Upgrader {
|
|
48
|
+
return new MockUpgrader(init)
|
|
49
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { start, stop } from '@libp2p/interface/startable'
|
|
2
|
+
import { isMultiaddr } from '@multiformats/multiaddr'
|
|
3
|
+
import { expect } from 'aegir/chai'
|
|
4
|
+
import delay from 'delay'
|
|
5
|
+
import pDefer from 'p-defer'
|
|
6
|
+
import type { TestSetup } from '../index.js'
|
|
7
|
+
import type { PeerDiscovery } from '@libp2p/interface/peer-discovery'
|
|
8
|
+
|
|
9
|
+
export default (common: TestSetup<PeerDiscovery>): void => {
|
|
10
|
+
describe('interface-peer-discovery compliance tests', () => {
|
|
11
|
+
let discovery: PeerDiscovery
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
discovery = await common.setup()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
afterEach('ensure discovery was stopped', async () => {
|
|
18
|
+
await stop(discovery)
|
|
19
|
+
|
|
20
|
+
await common.teardown()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('can start the service', async () => {
|
|
24
|
+
await start(discovery)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('can start and stop the service', async () => {
|
|
28
|
+
await start(discovery)
|
|
29
|
+
await stop(discovery)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should not fail to stop the service if it was not started', async () => {
|
|
33
|
+
await stop(discovery)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should not fail to start the service if it is already started', async () => {
|
|
37
|
+
await start(discovery)
|
|
38
|
+
await start(discovery)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should emit a peer event after start', async () => {
|
|
42
|
+
const defer = pDefer()
|
|
43
|
+
|
|
44
|
+
discovery.addEventListener('peer', (evt) => {
|
|
45
|
+
const { id, multiaddrs } = evt.detail
|
|
46
|
+
expect(id).to.exist()
|
|
47
|
+
expect(id)
|
|
48
|
+
.to.have.property('type')
|
|
49
|
+
.that.is.oneOf(['RSA', 'Ed25519', 'secp256k1'])
|
|
50
|
+
expect(multiaddrs).to.exist()
|
|
51
|
+
|
|
52
|
+
multiaddrs.forEach((m) => expect(isMultiaddr(m)).to.eql(true))
|
|
53
|
+
|
|
54
|
+
defer.resolve()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
await start(discovery)
|
|
58
|
+
|
|
59
|
+
await defer.promise
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should not receive a peer event before start', async () => {
|
|
63
|
+
discovery.addEventListener('peer', () => {
|
|
64
|
+
throw new Error('should not receive a peer event before start')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
await delay(2000)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should not receive a peer event after stop', async () => {
|
|
71
|
+
const deferStart = pDefer()
|
|
72
|
+
|
|
73
|
+
discovery.addEventListener('peer', () => {
|
|
74
|
+
deferStart.resolve()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
await start(discovery)
|
|
78
|
+
|
|
79
|
+
await deferStart.promise
|
|
80
|
+
|
|
81
|
+
await stop(discovery)
|
|
82
|
+
|
|
83
|
+
discovery.addEventListener('peer', () => {
|
|
84
|
+
throw new Error('should not receive a peer event after stop')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
await delay(2000)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { isStartable, start, stop } from '@libp2p/interface/startable'
|
|
2
|
+
import { expect } from 'aegir/chai'
|
|
3
|
+
import delay from 'delay'
|
|
4
|
+
import pDefer from 'p-defer'
|
|
5
|
+
import pWaitFor from 'p-wait-for'
|
|
6
|
+
import sinon from 'sinon'
|
|
7
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
8
|
+
import { mockNetwork } from '../mocks/index.js'
|
|
9
|
+
import { createComponents } from './utils.js'
|
|
10
|
+
import type { PubSubArgs, PubSubComponents } from './index.js'
|
|
11
|
+
import type { TestSetup } from '../index.js'
|
|
12
|
+
import type { PubSub } from '@libp2p/interface/pubsub'
|
|
13
|
+
|
|
14
|
+
const topic = 'foo'
|
|
15
|
+
const data = uint8ArrayFromString('bar')
|
|
16
|
+
|
|
17
|
+
export default (common: TestSetup<PubSub, PubSubArgs>): void => {
|
|
18
|
+
describe('pubsub api', () => {
|
|
19
|
+
let pubsub: PubSub
|
|
20
|
+
let components: PubSubComponents
|
|
21
|
+
|
|
22
|
+
// Create pubsub router
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
mockNetwork.reset()
|
|
25
|
+
components = await createComponents()
|
|
26
|
+
|
|
27
|
+
pubsub = components.pubsub = await common.setup({
|
|
28
|
+
components,
|
|
29
|
+
init: {
|
|
30
|
+
emitSelf: true
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
afterEach(async () => {
|
|
36
|
+
sinon.restore()
|
|
37
|
+
await stop(...Object.values(components))
|
|
38
|
+
await common.teardown()
|
|
39
|
+
mockNetwork.reset()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('can start correctly', async () => {
|
|
43
|
+
if (!isStartable(pubsub)) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
sinon.spy(components.registrar, 'register')
|
|
48
|
+
|
|
49
|
+
await start(...Object.values(components))
|
|
50
|
+
|
|
51
|
+
expect(pubsub.isStarted()).to.equal(true)
|
|
52
|
+
expect(components.registrar.register).to.have.property('callCount', 1)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('can stop correctly', async () => {
|
|
56
|
+
if (!isStartable(pubsub)) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
sinon.spy(components.registrar, 'unregister')
|
|
61
|
+
|
|
62
|
+
await start(...Object.values(components))
|
|
63
|
+
await stop(...Object.values(components))
|
|
64
|
+
|
|
65
|
+
expect(pubsub.isStarted()).to.equal(false)
|
|
66
|
+
expect(components.registrar.unregister).to.have.property('callCount', 1)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('can subscribe and unsubscribe correctly', async () => {
|
|
70
|
+
const handler = (): void => {
|
|
71
|
+
throw new Error('a message should not be received')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await start(...Object.values(components))
|
|
75
|
+
pubsub.subscribe(topic)
|
|
76
|
+
pubsub.addEventListener('message', handler)
|
|
77
|
+
|
|
78
|
+
await pWaitFor(() => {
|
|
79
|
+
const topics = pubsub.getTopics()
|
|
80
|
+
return topics.length === 1 && topics[0] === topic
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
pubsub.removeEventListener('message', handler)
|
|
84
|
+
pubsub.unsubscribe(topic)
|
|
85
|
+
|
|
86
|
+
await pWaitFor(() => pubsub.getTopics().length === 0)
|
|
87
|
+
|
|
88
|
+
// Publish to guarantee the handler is not called
|
|
89
|
+
await pubsub.publish(topic, data)
|
|
90
|
+
|
|
91
|
+
// handlers are called async
|
|
92
|
+
await delay(100)
|
|
93
|
+
|
|
94
|
+
await stop(...Object.values(components))
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('can subscribe and publish correctly', async () => {
|
|
98
|
+
const defer = pDefer()
|
|
99
|
+
|
|
100
|
+
await start(...Object.values(components))
|
|
101
|
+
|
|
102
|
+
pubsub.subscribe(topic)
|
|
103
|
+
pubsub.addEventListener('message', (evt) => {
|
|
104
|
+
expect(evt).to.have.nested.property('detail.topic', topic)
|
|
105
|
+
expect(evt).to.have.deep.nested.property('detail.data', data)
|
|
106
|
+
defer.resolve()
|
|
107
|
+
})
|
|
108
|
+
await pubsub.publish(topic, data)
|
|
109
|
+
await defer.promise
|
|
110
|
+
|
|
111
|
+
await stop(...Object.values(components))
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
}
|