@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,97 @@
|
|
|
1
|
+
import { UnexpectedPeerError } from '@libp2p/interface/errors'
|
|
2
|
+
import * as PeerIdFactory from '@libp2p/peer-id-factory'
|
|
3
|
+
import { expect } from 'aegir/chai'
|
|
4
|
+
import all from 'it-all'
|
|
5
|
+
import { pipe } from 'it-pipe'
|
|
6
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
7
|
+
import peers from '../peers.js'
|
|
8
|
+
import { createMaConnPair } from './utils/index.js'
|
|
9
|
+
import type { TestSetup } from '../index.js'
|
|
10
|
+
import type { ConnectionEncrypter } from '@libp2p/interface/connection-encrypter'
|
|
11
|
+
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
12
|
+
|
|
13
|
+
export default (common: TestSetup<ConnectionEncrypter>): void => {
|
|
14
|
+
describe('interface-connection-encrypter compliance tests', () => {
|
|
15
|
+
let crypto: ConnectionEncrypter
|
|
16
|
+
let localPeer: PeerId
|
|
17
|
+
let remotePeer: PeerId
|
|
18
|
+
let mitmPeer: PeerId
|
|
19
|
+
|
|
20
|
+
before(async () => {
|
|
21
|
+
[
|
|
22
|
+
crypto,
|
|
23
|
+
localPeer,
|
|
24
|
+
remotePeer,
|
|
25
|
+
mitmPeer
|
|
26
|
+
] = await Promise.all([
|
|
27
|
+
common.setup(),
|
|
28
|
+
PeerIdFactory.createFromJSON(peers[0]),
|
|
29
|
+
PeerIdFactory.createFromJSON(peers[1]),
|
|
30
|
+
PeerIdFactory.createFromJSON(peers[2])
|
|
31
|
+
])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
after(async () => {
|
|
35
|
+
await common.teardown()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('has a protocol string', () => {
|
|
39
|
+
expect(crypto.protocol).to.exist()
|
|
40
|
+
expect(crypto.protocol).to.be.a('string')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('it wraps the provided duplex connection', async () => {
|
|
44
|
+
const [localConn, remoteConn] = createMaConnPair()
|
|
45
|
+
|
|
46
|
+
const [
|
|
47
|
+
inboundResult,
|
|
48
|
+
outboundResult
|
|
49
|
+
] = await Promise.all([
|
|
50
|
+
crypto.secureInbound(remotePeer, localConn),
|
|
51
|
+
crypto.secureOutbound(localPeer, remoteConn, remotePeer)
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
// Echo server
|
|
55
|
+
void pipe(inboundResult.conn, inboundResult.conn)
|
|
56
|
+
|
|
57
|
+
// Send some data and collect the result
|
|
58
|
+
const input = uint8ArrayFromString('data to encrypt')
|
|
59
|
+
const result = await pipe(
|
|
60
|
+
[input],
|
|
61
|
+
outboundResult.conn,
|
|
62
|
+
async (source) => all(source)
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
expect(result).to.eql([input])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should return the remote peer id', async () => {
|
|
69
|
+
const [localConn, remoteConn] = createMaConnPair()
|
|
70
|
+
|
|
71
|
+
const [
|
|
72
|
+
inboundResult,
|
|
73
|
+
outboundResult
|
|
74
|
+
] = await Promise.all([
|
|
75
|
+
crypto.secureInbound(remotePeer, localConn),
|
|
76
|
+
crypto.secureOutbound(localPeer, remoteConn, remotePeer)
|
|
77
|
+
])
|
|
78
|
+
|
|
79
|
+
// Inbound should return the initiator (local) peer
|
|
80
|
+
expect(inboundResult.remotePeer.toBytes()).to.equalBytes(localPeer.toBytes())
|
|
81
|
+
// Outbound should return the receiver (remote) peer
|
|
82
|
+
expect(outboundResult.remotePeer.toBytes()).to.equalBytes(remotePeer.toBytes())
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('inbound connections should verify peer integrity if known', async () => {
|
|
86
|
+
const [localConn, remoteConn] = createMaConnPair()
|
|
87
|
+
|
|
88
|
+
await Promise.all([
|
|
89
|
+
crypto.secureInbound(remotePeer, localConn, mitmPeer),
|
|
90
|
+
crypto.secureOutbound(localPeer, remoteConn, remotePeer)
|
|
91
|
+
]).then(() => expect.fail(), (err) => {
|
|
92
|
+
expect(err).to.exist()
|
|
93
|
+
expect(err).to.have.property('code', UnexpectedPeerError.code)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { multiaddr } from '@multiformats/multiaddr'
|
|
2
|
+
import { duplexPair } from 'it-pair/duplex'
|
|
3
|
+
import type { MultiaddrConnection } from '@libp2p/interface/connection'
|
|
4
|
+
import type { Duplex, Source } from 'it-stream-types'
|
|
5
|
+
|
|
6
|
+
export function createMaConnPair (): [MultiaddrConnection, MultiaddrConnection] {
|
|
7
|
+
const [local, remote] = duplexPair<Uint8Array>()
|
|
8
|
+
|
|
9
|
+
function duplexToMaConn (duplex: Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>, Promise<void>>): MultiaddrConnection {
|
|
10
|
+
const output: MultiaddrConnection = {
|
|
11
|
+
...duplex,
|
|
12
|
+
close: async () => {},
|
|
13
|
+
abort: () => {},
|
|
14
|
+
remoteAddr: multiaddr('/ip4/127.0.0.1/tcp/4001'),
|
|
15
|
+
timeline: {
|
|
16
|
+
open: Date.now()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return output
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return [duplexToMaConn(local), duplexToMaConn(remote)]
|
|
24
|
+
}
|
package/src/index.ts
CHANGED
package/src/is-valid-tick.ts
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { UnexpectedPeerError } from '@libp2p/interface/errors'
|
|
2
|
+
import { peerIdFromBytes } from '@libp2p/peer-id'
|
|
3
|
+
import { multiaddr } from '@multiformats/multiaddr'
|
|
4
|
+
import { handshake } from 'it-handshake'
|
|
5
|
+
import map from 'it-map'
|
|
6
|
+
import { duplexPair } from 'it-pair/duplex'
|
|
7
|
+
import { pipe } from 'it-pipe'
|
|
8
|
+
import type { ConnectionEncrypter } from '@libp2p/interface/connection-encrypter'
|
|
9
|
+
import type { Transform, Source } from 'it-stream-types'
|
|
10
|
+
|
|
11
|
+
// A basic transform that does nothing to the data
|
|
12
|
+
const transform = <T>(): Transform<Source<T>, AsyncGenerator<T>> => {
|
|
13
|
+
return (source: Source<T>) => (async function * () {
|
|
14
|
+
for await (const chunk of source) {
|
|
15
|
+
yield chunk
|
|
16
|
+
}
|
|
17
|
+
})()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function mockConnectionEncrypter (): ConnectionEncrypter {
|
|
21
|
+
const encrypter: ConnectionEncrypter = {
|
|
22
|
+
protocol: 'insecure',
|
|
23
|
+
secureInbound: async (localPeer, duplex, expectedPeer) => {
|
|
24
|
+
// 1. Perform a basic handshake.
|
|
25
|
+
const shake = handshake<Uint8Array>(duplex)
|
|
26
|
+
shake.write(localPeer.toBytes())
|
|
27
|
+
const remoteId = await shake.read()
|
|
28
|
+
|
|
29
|
+
if (remoteId == null) {
|
|
30
|
+
throw new Error('Could not read remote ID')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const remotePeer = peerIdFromBytes(remoteId.slice())
|
|
34
|
+
shake.rest()
|
|
35
|
+
|
|
36
|
+
if (expectedPeer?.equals(remotePeer) === false) {
|
|
37
|
+
throw new UnexpectedPeerError()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. Create your encryption box/unbox wrapper
|
|
41
|
+
const wrapper = duplexPair<Uint8Array>()
|
|
42
|
+
const encrypt = transform<Uint8Array>() // Use transform iterables to modify data
|
|
43
|
+
const decrypt = transform<Uint8Array>()
|
|
44
|
+
|
|
45
|
+
void pipe(
|
|
46
|
+
wrapper[0], // We write to wrapper
|
|
47
|
+
encrypt, // The data is encrypted
|
|
48
|
+
shake.stream, // It goes to the remote peer
|
|
49
|
+
source => map(source, (list) => list.subarray()), // turn lists into arrays
|
|
50
|
+
decrypt, // Decrypt the incoming data
|
|
51
|
+
wrapper[0] // Pipe to the wrapper
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
conn: {
|
|
56
|
+
...wrapper[1],
|
|
57
|
+
close: async () => { },
|
|
58
|
+
localAddr: multiaddr('/ip4/127.0.0.1/tcp/4001'),
|
|
59
|
+
remoteAddr: multiaddr('/ip4/127.0.0.1/tcp/4002'),
|
|
60
|
+
timeline: {
|
|
61
|
+
open: Date.now()
|
|
62
|
+
},
|
|
63
|
+
conn: true
|
|
64
|
+
},
|
|
65
|
+
remotePeer,
|
|
66
|
+
remoteExtensions: {}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
secureOutbound: async (localPeer, duplex, remotePeer) => {
|
|
70
|
+
// 1. Perform a basic handshake.
|
|
71
|
+
const shake = handshake<Uint8Array>(duplex)
|
|
72
|
+
shake.write(localPeer.toBytes())
|
|
73
|
+
const remoteId = await shake.read()
|
|
74
|
+
|
|
75
|
+
if (remoteId == null) {
|
|
76
|
+
throw new Error('Could not read remote ID')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
shake.rest()
|
|
80
|
+
|
|
81
|
+
// 2. Create your encryption box/unbox wrapper
|
|
82
|
+
const wrapper = duplexPair<Uint8Array>()
|
|
83
|
+
const encrypt = transform<Uint8Array>()
|
|
84
|
+
const decrypt = transform<Uint8Array>()
|
|
85
|
+
|
|
86
|
+
void pipe(
|
|
87
|
+
wrapper[0], // We write to wrapper
|
|
88
|
+
encrypt, // The data is encrypted
|
|
89
|
+
shake.stream, // It goes to the remote peer
|
|
90
|
+
source => map(source, (list) => list.subarray()), // turn lists into arrays
|
|
91
|
+
decrypt, // Decrypt the incoming data
|
|
92
|
+
wrapper[0] // Pipe to the wrapper
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
conn: {
|
|
97
|
+
...wrapper[1],
|
|
98
|
+
close: async () => { },
|
|
99
|
+
localAddr: multiaddr('/ip4/127.0.0.1/tcp/4001'),
|
|
100
|
+
remoteAddr: multiaddr('/ip4/127.0.0.1/tcp/4002'),
|
|
101
|
+
timeline: {
|
|
102
|
+
open: Date.now()
|
|
103
|
+
},
|
|
104
|
+
conn: true
|
|
105
|
+
},
|
|
106
|
+
remotePeer: peerIdFromBytes(remoteId.slice()),
|
|
107
|
+
remoteExtensions: {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return encrypter
|
|
113
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ConnectionGater } from '@libp2p/interface/connection-gater'
|
|
2
|
+
|
|
3
|
+
export function mockConnectionGater (): ConnectionGater {
|
|
4
|
+
return {
|
|
5
|
+
denyDialPeer: async () => Promise.resolve(false),
|
|
6
|
+
denyDialMultiaddr: async () => Promise.resolve(false),
|
|
7
|
+
denyInboundConnection: async () => Promise.resolve(false),
|
|
8
|
+
denyOutboundConnection: async () => Promise.resolve(false),
|
|
9
|
+
denyInboundEncryptedConnection: async () => Promise.resolve(false),
|
|
10
|
+
denyOutboundEncryptedConnection: async () => Promise.resolve(false),
|
|
11
|
+
denyInboundUpgradedConnection: async () => Promise.resolve(false),
|
|
12
|
+
denyOutboundUpgradedConnection: async () => Promise.resolve(false),
|
|
13
|
+
denyInboundRelayReservation: async () => Promise.resolve(false),
|
|
14
|
+
denyOutboundRelayedConnection: async () => Promise.resolve(false),
|
|
15
|
+
denyInboundRelayedConnection: async () => Promise.resolve(false),
|
|
16
|
+
filterMultiaddrForPeer: async () => Promise.resolve(true)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
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, PendingDial } from '@libp2p/interface'
|
|
8
|
+
import type { Connection } from '@libp2p/interface/connection'
|
|
9
|
+
import type { EventEmitter } from '@libp2p/interface/events'
|
|
10
|
+
import type { PubSub } from '@libp2p/interface/pubsub'
|
|
11
|
+
import type { Startable } from '@libp2p/interface/startable'
|
|
12
|
+
import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager'
|
|
13
|
+
import type { Registrar } from '@libp2p/interface-internal/registrar'
|
|
14
|
+
|
|
15
|
+
export interface MockNetworkComponents {
|
|
16
|
+
peerId: PeerId
|
|
17
|
+
registrar: Registrar
|
|
18
|
+
connectionManager: ConnectionManager
|
|
19
|
+
events: EventEmitter<Libp2pEvents>
|
|
20
|
+
pubsub?: PubSub
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class MockNetwork {
|
|
24
|
+
private components: MockNetworkComponents[] = []
|
|
25
|
+
|
|
26
|
+
addNode (components: MockNetworkComponents): void {
|
|
27
|
+
this.components.push(components)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getNode (peerId: PeerId | Multiaddr []): MockNetworkComponents {
|
|
31
|
+
if (Array.isArray(peerId) && peerId.length > 0) {
|
|
32
|
+
peerId = peerIdFromString(peerId[0].getPeerId() ?? '')
|
|
33
|
+
} else if (isPeerId(peerId)) {
|
|
34
|
+
for (const components of this.components) {
|
|
35
|
+
if (peerId.equals(components.peerId)) {
|
|
36
|
+
return components
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
throw new CodeError('Peer not found', 'ERR_PEER_NOT_FOUND')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
reset (): void {
|
|
45
|
+
this.components = []
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const mockNetwork = new MockNetwork()
|
|
50
|
+
|
|
51
|
+
export interface MockConnectionManagerComponents {
|
|
52
|
+
peerId: PeerId
|
|
53
|
+
registrar: Registrar
|
|
54
|
+
events: EventEmitter<Libp2pEvents>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class MockConnectionManager implements ConnectionManager, Startable {
|
|
58
|
+
private connections: Connection[] = []
|
|
59
|
+
private readonly components: MockConnectionManagerComponents
|
|
60
|
+
private started = false
|
|
61
|
+
|
|
62
|
+
constructor (components: MockConnectionManagerComponents) {
|
|
63
|
+
this.components = components
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
isStarted (): boolean {
|
|
67
|
+
return this.started
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async start (): Promise<void> {
|
|
71
|
+
this.started = true
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async stop (): Promise<void> {
|
|
75
|
+
for (const connection of this.connections) {
|
|
76
|
+
await this.closeConnections(connection.remotePeer)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.started = false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getConnections (peerId?: PeerId): Connection[] {
|
|
83
|
+
if (peerId != null) {
|
|
84
|
+
return this.connections
|
|
85
|
+
.filter(c => c.remotePeer.toString() === peerId.toString())
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return this.connections
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getConnectionsMap (): PeerMap<Connection[]> {
|
|
92
|
+
const map = new PeerMap<Connection[]>()
|
|
93
|
+
|
|
94
|
+
for (const conn of this.connections) {
|
|
95
|
+
const conns: Connection[] = map.get(conn.remotePeer) ?? []
|
|
96
|
+
conns.push(conn)
|
|
97
|
+
|
|
98
|
+
map.set(conn.remotePeer, conns)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return map
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async openConnection (peerId: PeerId | Multiaddr | Multiaddr[]): Promise<Connection> {
|
|
105
|
+
if (this.components == null) {
|
|
106
|
+
throw new CodeError('Not initialized', 'ERR_NOT_INITIALIZED')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (isMultiaddr(peerId)) {
|
|
110
|
+
throw new CodeError('Dialing multiaddrs not supported', 'ERR_NOT_SUPPORTED')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let existingConnections: Connection[] = []
|
|
114
|
+
|
|
115
|
+
if (Array.isArray(peerId) && peerId.length > 0) {
|
|
116
|
+
existingConnections = this.getConnections(peerIdFromString(peerId[0].getPeerId() ?? ''))
|
|
117
|
+
} else if (isPeerId(peerId)) {
|
|
118
|
+
existingConnections = this.getConnections(peerId)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (existingConnections.length > 0) {
|
|
122
|
+
return existingConnections[0]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const componentsB = mockNetwork.getNode(peerId)
|
|
126
|
+
|
|
127
|
+
const [aToB, bToA] = connectionPair(this.components, componentsB)
|
|
128
|
+
|
|
129
|
+
// track connections
|
|
130
|
+
this.connections.push(aToB)
|
|
131
|
+
;(componentsB.connectionManager as MockConnectionManager).connections.push(bToA)
|
|
132
|
+
|
|
133
|
+
this.components.events.safeDispatchEvent('connection:open', {
|
|
134
|
+
detail: aToB
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
for (const protocol of this.components.registrar.getProtocols()) {
|
|
138
|
+
for (const topology of this.components.registrar.getTopologies(protocol)) {
|
|
139
|
+
topology.onConnect?.(componentsB.peerId, aToB)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.components.events.safeDispatchEvent('peer:connect', { detail: componentsB.peerId })
|
|
144
|
+
|
|
145
|
+
componentsB.events.safeDispatchEvent('connection:open', {
|
|
146
|
+
detail: bToA
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
for (const protocol of componentsB.registrar.getProtocols()) {
|
|
150
|
+
for (const topology of componentsB.registrar.getTopologies(protocol)) {
|
|
151
|
+
topology.onConnect?.(this.components.peerId, bToA)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
componentsB.events.safeDispatchEvent('peer:connect', { detail: this.components.peerId })
|
|
156
|
+
|
|
157
|
+
return aToB
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async closeConnections (peerId: PeerId): Promise<void> {
|
|
161
|
+
if (this.components == null) {
|
|
162
|
+
throw new CodeError('Not initialized', 'ERR_NOT_INITIALIZED')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const connections = this.getConnections(peerId)
|
|
166
|
+
|
|
167
|
+
if (connections.length === 0) {
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const componentsB = mockNetwork.getNode(peerId)
|
|
172
|
+
|
|
173
|
+
for (const protocol of this.components.registrar.getProtocols()) {
|
|
174
|
+
this.components.registrar.getTopologies(protocol).forEach(topology => {
|
|
175
|
+
topology.onDisconnect?.(componentsB.peerId)
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const conn of connections) {
|
|
180
|
+
await conn.close()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.connections = this.connections.filter(c => !c.remotePeer.equals(peerId))
|
|
184
|
+
|
|
185
|
+
if (this.connections.filter(c => !c.remotePeer.equals(peerId)).length === 0) {
|
|
186
|
+
componentsB.events.safeDispatchEvent('peer:disconnect', { detail: peerId })
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
await componentsB.connectionManager?.closeConnections(this.components.peerId)
|
|
190
|
+
|
|
191
|
+
if (componentsB.connectionManager?.getConnectionsMap().get(this.components.peerId) == null) {
|
|
192
|
+
componentsB.events.safeDispatchEvent('peer:disconnect', { detail: this.components.peerId })
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async acceptIncomingConnection (): Promise<boolean> {
|
|
197
|
+
return true
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
afterUpgradeInbound (): void {
|
|
201
|
+
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
getDialQueue (): PendingDial[] {
|
|
205
|
+
return []
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function mockConnectionManager (components: MockConnectionManagerComponents): ConnectionManager {
|
|
210
|
+
return new MockConnectionManager(components)
|
|
211
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { CodeError } from '@libp2p/interface/errors'
|
|
2
|
+
import { logger } from '@libp2p/logger'
|
|
3
|
+
import * as mss from '@libp2p/multistream-select'
|
|
4
|
+
import { peerIdFromString } from '@libp2p/peer-id'
|
|
5
|
+
import { duplexPair } from 'it-pair/duplex'
|
|
6
|
+
import { pipe } from 'it-pipe'
|
|
7
|
+
import { mockMultiaddrConnection } from './multiaddr-connection.js'
|
|
8
|
+
import { mockMuxer } from './muxer.js'
|
|
9
|
+
import { mockRegistrar } from './registrar.js'
|
|
10
|
+
import type { AbortOptions } from '@libp2p/interface'
|
|
11
|
+
import type { MultiaddrConnection, Connection, Stream, Direction, ConnectionTimeline, ConnectionStatus } from '@libp2p/interface/connection'
|
|
12
|
+
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
13
|
+
import type { StreamMuxer, StreamMuxerFactory } from '@libp2p/interface/stream-muxer'
|
|
14
|
+
import type { Registrar } from '@libp2p/interface-internal/registrar'
|
|
15
|
+
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
16
|
+
import type { Duplex, Source } from 'it-stream-types'
|
|
17
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
18
|
+
|
|
19
|
+
const log = logger('libp2p:mock-connection')
|
|
20
|
+
|
|
21
|
+
export interface MockConnectionOptions {
|
|
22
|
+
direction?: Direction
|
|
23
|
+
registrar?: Registrar
|
|
24
|
+
muxerFactory?: StreamMuxerFactory
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface MockConnectionInit {
|
|
28
|
+
remoteAddr: Multiaddr
|
|
29
|
+
remotePeer: PeerId
|
|
30
|
+
direction: Direction
|
|
31
|
+
maConn: MultiaddrConnection
|
|
32
|
+
muxer: StreamMuxer
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class MockConnection implements Connection {
|
|
36
|
+
public id: string
|
|
37
|
+
public remoteAddr: Multiaddr
|
|
38
|
+
public remotePeer: PeerId
|
|
39
|
+
public direction: Direction
|
|
40
|
+
public timeline: ConnectionTimeline
|
|
41
|
+
public multiplexer?: string
|
|
42
|
+
public encryption?: string
|
|
43
|
+
public status: ConnectionStatus
|
|
44
|
+
public streams: Stream[]
|
|
45
|
+
public tags: string[]
|
|
46
|
+
public transient: boolean
|
|
47
|
+
|
|
48
|
+
private readonly muxer: StreamMuxer
|
|
49
|
+
private readonly maConn: MultiaddrConnection
|
|
50
|
+
|
|
51
|
+
constructor (init: MockConnectionInit) {
|
|
52
|
+
const { remoteAddr, remotePeer, direction, maConn, muxer } = init
|
|
53
|
+
|
|
54
|
+
this.id = `mock-connection-${Math.random()}`
|
|
55
|
+
this.remoteAddr = remoteAddr
|
|
56
|
+
this.remotePeer = remotePeer
|
|
57
|
+
this.direction = direction
|
|
58
|
+
this.status = 'open'
|
|
59
|
+
this.direction = direction
|
|
60
|
+
this.timeline = maConn.timeline
|
|
61
|
+
this.multiplexer = 'test-multiplexer'
|
|
62
|
+
this.encryption = 'yes-yes-very-secure'
|
|
63
|
+
this.streams = []
|
|
64
|
+
this.tags = []
|
|
65
|
+
this.muxer = muxer
|
|
66
|
+
this.maConn = maConn
|
|
67
|
+
this.transient = false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async newStream (protocols: string | string[], options?: AbortOptions): Promise<Stream> {
|
|
71
|
+
if (!Array.isArray(protocols)) {
|
|
72
|
+
protocols = [protocols]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (protocols.length === 0) {
|
|
76
|
+
throw new Error('protocols must have a length')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (this.status !== 'open') {
|
|
80
|
+
throw new CodeError('connection must be open to create streams', 'ERR_CONNECTION_CLOSED')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const id = `${Math.random()}`
|
|
84
|
+
const stream = await this.muxer.newStream(id)
|
|
85
|
+
const result = await mss.select(stream, protocols, options)
|
|
86
|
+
|
|
87
|
+
stream.protocol = result.protocol
|
|
88
|
+
stream.direction = 'outbound'
|
|
89
|
+
stream.sink = result.stream.sink
|
|
90
|
+
stream.source = result.stream.source
|
|
91
|
+
|
|
92
|
+
this.streams.push(stream)
|
|
93
|
+
|
|
94
|
+
return stream
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
addStream (stream: Stream): void {
|
|
98
|
+
this.streams.push(stream)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
removeStream (id: string): void {
|
|
102
|
+
this.streams = this.streams.filter(stream => stream.id !== id)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async close (options?: AbortOptions): Promise<void> {
|
|
106
|
+
this.status = 'closing'
|
|
107
|
+
await Promise.all(
|
|
108
|
+
this.streams.map(async s => s.close(options))
|
|
109
|
+
)
|
|
110
|
+
await this.maConn.close()
|
|
111
|
+
this.status = 'closed'
|
|
112
|
+
this.timeline.close = Date.now()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
abort (err: Error): void {
|
|
116
|
+
this.status = 'closing'
|
|
117
|
+
this.streams.forEach(s => {
|
|
118
|
+
s.abort(err)
|
|
119
|
+
})
|
|
120
|
+
this.maConn.abort(err)
|
|
121
|
+
this.status = 'closed'
|
|
122
|
+
this.timeline.close = Date.now()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function mockConnection (maConn: MultiaddrConnection, opts: MockConnectionOptions = {}): Connection {
|
|
127
|
+
const remoteAddr = maConn.remoteAddr
|
|
128
|
+
const remotePeerIdStr = remoteAddr.getPeerId() ?? '12D3KooWCrhmFM1BCPGBkNzbPfDk4cjYmtAYSpZwUBC69Qg2kZyq'
|
|
129
|
+
|
|
130
|
+
if (remotePeerIdStr == null) {
|
|
131
|
+
throw new Error('Remote multiaddr must contain a peer id')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const remotePeer = peerIdFromString(remotePeerIdStr)
|
|
135
|
+
const direction = opts.direction ?? 'inbound'
|
|
136
|
+
const registrar = opts.registrar ?? mockRegistrar()
|
|
137
|
+
const muxerFactory = opts.muxerFactory ?? mockMuxer()
|
|
138
|
+
|
|
139
|
+
const muxer = muxerFactory.createStreamMuxer({
|
|
140
|
+
direction,
|
|
141
|
+
onIncomingStream: (muxedStream) => {
|
|
142
|
+
try {
|
|
143
|
+
mss.handle(muxedStream, registrar.getProtocols())
|
|
144
|
+
.then(({ stream, protocol }) => {
|
|
145
|
+
log('%s: incoming stream opened on %s', direction, protocol)
|
|
146
|
+
muxedStream.protocol = protocol
|
|
147
|
+
muxedStream.sink = stream.sink
|
|
148
|
+
muxedStream.source = stream.source
|
|
149
|
+
|
|
150
|
+
connection.addStream(muxedStream)
|
|
151
|
+
const { handler } = registrar.getHandler(protocol)
|
|
152
|
+
|
|
153
|
+
handler({ connection, stream: muxedStream })
|
|
154
|
+
}).catch(err => {
|
|
155
|
+
log.error(err)
|
|
156
|
+
})
|
|
157
|
+
} catch (err: any) {
|
|
158
|
+
log.error(err)
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
onStreamEnd: (muxedStream) => {
|
|
162
|
+
connection.removeStream(muxedStream.id)
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
void pipe(
|
|
167
|
+
maConn, muxer, maConn
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
const connection = new MockConnection({
|
|
171
|
+
remoteAddr,
|
|
172
|
+
remotePeer,
|
|
173
|
+
direction,
|
|
174
|
+
maConn,
|
|
175
|
+
muxer
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
return connection
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function mockStream (stream: Duplex<AsyncGenerator<Uint8ArrayList>, Source<Uint8ArrayList | Uint8Array>, Promise<void>>): Stream {
|
|
182
|
+
return {
|
|
183
|
+
...stream,
|
|
184
|
+
close: async () => {},
|
|
185
|
+
closeRead: async () => {},
|
|
186
|
+
closeWrite: async () => {},
|
|
187
|
+
abort: () => {},
|
|
188
|
+
direction: 'outbound',
|
|
189
|
+
protocol: '/foo/1.0.0',
|
|
190
|
+
timeline: {
|
|
191
|
+
open: Date.now()
|
|
192
|
+
},
|
|
193
|
+
metadata: {},
|
|
194
|
+
id: `stream-${Date.now()}`,
|
|
195
|
+
status: 'open',
|
|
196
|
+
readStatus: 'ready',
|
|
197
|
+
writeStatus: 'ready'
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface Peer {
|
|
202
|
+
peerId: PeerId
|
|
203
|
+
registrar: Registrar
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function multiaddrConnectionPair (a: { peerId: PeerId, registrar: Registrar }, b: { peerId: PeerId, registrar: Registrar }): [ MultiaddrConnection, MultiaddrConnection ] {
|
|
207
|
+
const [peerBtoPeerA, peerAtoPeerB] = duplexPair<Uint8Array>()
|
|
208
|
+
|
|
209
|
+
return [
|
|
210
|
+
mockMultiaddrConnection(peerAtoPeerB, b.peerId),
|
|
211
|
+
mockMultiaddrConnection(peerBtoPeerA, a.peerId)
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function connectionPair (a: { peerId: PeerId, registrar: Registrar }, b: { peerId: PeerId, registrar: Registrar }): [ Connection, Connection ] {
|
|
216
|
+
const [peerBtoPeerA, peerAtoPeerB] = multiaddrConnectionPair(a, b)
|
|
217
|
+
|
|
218
|
+
return [
|
|
219
|
+
mockConnection(peerBtoPeerA, {
|
|
220
|
+
registrar: a.registrar
|
|
221
|
+
}),
|
|
222
|
+
mockConnection(peerAtoPeerB, {
|
|
223
|
+
registrar: b.registrar
|
|
224
|
+
})
|
|
225
|
+
]
|
|
226
|
+
}
|