@libp2p/interface-compliance-tests 3.0.6 → 3.0.7-05abd49f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -3
- package/dist/src/connection/index.d.ts +5 -0
- package/dist/src/connection/index.d.ts.map +1 -0
- package/dist/src/connection/index.js +151 -0
- package/dist/src/connection/index.js.map +1 -0
- package/dist/src/connection-encryption/index.d.ts +5 -0
- package/dist/src/connection-encryption/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/index.js +71 -0
- package/dist/src/connection-encryption/index.js.map +1 -0
- package/dist/src/connection-encryption/utils/index.d.ts +3 -0
- package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/utils/index.js +18 -0
- package/dist/src/connection-encryption/utils/index.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/mocks/connection-encrypter.d.ts +3 -0
- package/dist/src/mocks/connection-encrypter.d.ts.map +1 -0
- package/dist/src/mocks/connection-encrypter.js +98 -0
- package/dist/src/mocks/connection-encrypter.js.map +1 -0
- package/dist/src/mocks/connection-gater.d.ts +3 -0
- package/dist/src/mocks/connection-gater.d.ts.map +1 -0
- package/dist/src/mocks/connection-gater.js +17 -0
- package/dist/src/mocks/connection-gater.js.map +1 -0
- package/dist/src/mocks/connection-manager.d.ts +27 -0
- package/dist/src/mocks/connection-manager.d.ts.map +1 -0
- package/dist/src/mocks/connection-manager.js +145 -0
- package/dist/src/mocks/connection-manager.js.map +1 -0
- package/dist/src/mocks/connection.d.ts +32 -0
- package/dist/src/mocks/connection.d.ts.map +1 -0
- package/dist/src/mocks/connection.js +162 -0
- package/dist/src/mocks/connection.js.map +1 -0
- package/dist/src/mocks/duplex.d.ts +3 -0
- package/dist/src/mocks/duplex.d.ts.map +1 -0
- package/dist/src/mocks/duplex.js +9 -0
- package/dist/src/mocks/duplex.js.map +1 -0
- package/dist/src/mocks/index.d.ts +13 -0
- package/dist/src/mocks/index.d.ts.map +1 -0
- package/dist/src/mocks/index.js +11 -0
- package/dist/src/mocks/index.js.map +1 -0
- package/dist/src/mocks/metrics.d.ts +3 -0
- package/dist/src/mocks/metrics.d.ts.map +1 -0
- package/dist/src/mocks/metrics.js +115 -0
- package/dist/src/mocks/metrics.js.map +1 -0
- package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
- package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
- package/dist/src/mocks/multiaddr-connection.js +51 -0
- package/dist/src/mocks/multiaddr-connection.js.map +1 -0
- package/dist/src/mocks/muxer.d.ts +8 -0
- package/dist/src/mocks/muxer.d.ts.map +1 -0
- package/dist/src/mocks/muxer.js +341 -0
- package/dist/src/mocks/muxer.js.map +1 -0
- package/dist/src/mocks/peer-discovery.d.ts +22 -0
- package/dist/src/mocks/peer-discovery.d.ts.map +1 -0
- package/dist/src/mocks/peer-discovery.js +47 -0
- package/dist/src/mocks/peer-discovery.js.map +1 -0
- package/dist/src/mocks/registrar.d.ts +18 -0
- package/dist/src/mocks/registrar.d.ts.map +1 -0
- package/dist/src/mocks/registrar.js +66 -0
- package/dist/src/mocks/registrar.js.map +1 -0
- package/dist/src/mocks/upgrader.d.ts +10 -0
- package/dist/src/mocks/upgrader.d.ts.map +1 -0
- package/dist/src/mocks/upgrader.js +31 -0
- package/dist/src/mocks/upgrader.js.map +1 -0
- package/dist/src/peer-discovery/index.d.ts +5 -0
- package/dist/src/peer-discovery/index.d.ts.map +1 -0
- package/dist/src/peer-discovery/index.js +66 -0
- package/dist/src/peer-discovery/index.js.map +1 -0
- package/dist/src/stream-muxer/base-test.d.ts +5 -0
- package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/base-test.js +153 -0
- package/dist/src/stream-muxer/base-test.js.map +1 -0
- package/dist/src/stream-muxer/close-test.d.ts +5 -0
- package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/close-test.js +269 -0
- package/dist/src/stream-muxer/close-test.js.map +1 -0
- package/dist/src/stream-muxer/index.d.ts +5 -0
- package/dist/src/stream-muxer/index.d.ts.map +1 -0
- package/dist/src/stream-muxer/index.js +13 -0
- package/dist/src/stream-muxer/index.js.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.js +11 -0
- package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
- package/dist/src/stream-muxer/spawner.d.ts +4 -0
- package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
- package/dist/src/stream-muxer/spawner.js +36 -0
- package/dist/src/stream-muxer/spawner.js.map +1 -0
- package/dist/src/stream-muxer/stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/stress-test.js +23 -0
- package/dist/src/stream-muxer/stress-test.js.map +1 -0
- package/dist/src/transport/dial-test.d.ts +5 -0
- package/dist/src/transport/dial-test.d.ts.map +1 -0
- package/dist/src/transport/dial-test.js +98 -0
- package/dist/src/transport/dial-test.js.map +1 -0
- package/dist/src/transport/filter-test.d.ts +5 -0
- package/dist/src/transport/filter-test.d.ts.map +1 -0
- package/dist/src/transport/filter-test.js +18 -0
- package/dist/src/transport/filter-test.js.map +1 -0
- package/dist/src/transport/index.d.ts +15 -0
- package/dist/src/transport/index.d.ts.map +1 -0
- package/dist/src/transport/index.js +11 -0
- package/dist/src/transport/index.js.map +1 -0
- package/dist/src/transport/listen-test.d.ts +5 -0
- package/dist/src/transport/listen-test.d.ts.map +1 -0
- package/dist/src/transport/listen-test.js +152 -0
- package/dist/src/transport/listen-test.js.map +1 -0
- package/package.json +66 -95
- package/src/connection/index.ts +184 -0
- package/src/connection-encryption/index.ts +97 -0
- package/src/connection-encryption/utils/index.ts +23 -0
- package/src/index.ts +1 -1
- package/src/mocks/connection-encrypter.ts +113 -0
- package/src/mocks/connection-gater.ts +18 -0
- package/src/mocks/connection-manager.ts +209 -0
- package/src/mocks/connection.ts +218 -0
- package/src/mocks/duplex.ts +10 -0
- package/src/mocks/index.ts +12 -0
- package/src/mocks/metrics.ts +162 -0
- package/src/mocks/multiaddr-connection.ts +67 -0
- package/src/mocks/muxer.ts +447 -0
- package/src/mocks/peer-discovery.ts +60 -0
- package/src/mocks/registrar.ts +88 -0
- package/src/mocks/upgrader.ts +49 -0
- package/src/peer-discovery/index.ts +90 -0
- package/src/stream-muxer/base-test.ts +196 -0
- package/src/stream-muxer/close-test.ts +346 -0
- package/src/stream-muxer/index.ts +15 -0
- package/src/stream-muxer/mega-stress-test.ts +14 -0
- package/src/stream-muxer/spawner.ts +54 -0
- package/src/stream-muxer/stress-test.ts +27 -0
- package/src/transport/dial-test.ts +124 -0
- package/src/transport/filter-test.ts +25 -0
- package/src/transport/index.ts +25 -0
- package/src/transport/listen-test.ts +191 -0
- package/dist/typedoc-urls.json +0 -3
package/package.json
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/interface-compliance-tests",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.7-05abd49f",
|
|
4
4
|
"description": "Compliance tests for JS libp2p interfaces",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
|
-
"homepage": "https://github.com/libp2p/js-libp2p
|
|
6
|
+
"homepage": "https://github.com/libp2p/js-libp2p/tree/master/packages/interface-compliance-tests#readme",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/libp2p/js-libp2p
|
|
9
|
+
"url": "git+https://github.com/libp2p/js-libp2p.git"
|
|
10
10
|
},
|
|
11
11
|
"bugs": {
|
|
12
|
-
"url": "https://github.com/libp2p/js-libp2p
|
|
12
|
+
"url": "https://github.com/libp2p/js-libp2p/issues"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
15
|
"interface",
|
|
16
16
|
"libp2p"
|
|
17
17
|
],
|
|
18
|
-
"engines": {
|
|
19
|
-
"node": ">=16.0.0",
|
|
20
|
-
"npm": ">=7.0.0"
|
|
21
|
-
},
|
|
22
18
|
"type": "module",
|
|
23
19
|
"types": "./dist/src/index.d.ts",
|
|
24
20
|
"typesVersions": {
|
|
@@ -48,13 +44,37 @@
|
|
|
48
44
|
"types": "./dist/src/index.d.ts",
|
|
49
45
|
"import": "./dist/src/index.js"
|
|
50
46
|
},
|
|
47
|
+
"./connection": {
|
|
48
|
+
"types": "./dist/src/connection/index.d.ts",
|
|
49
|
+
"import": "./dist/src/connection/index.js"
|
|
50
|
+
},
|
|
51
|
+
"./connection-encryption": {
|
|
52
|
+
"types": "./dist/src/connection-encryption/index.d.ts",
|
|
53
|
+
"import": "./dist/src/connection-encryption/index.js"
|
|
54
|
+
},
|
|
51
55
|
"./is-valid-tick": {
|
|
52
56
|
"types": "./dist/src/is-valid-tick.d.ts",
|
|
53
57
|
"import": "./dist/src/is-valid-tick.js"
|
|
54
58
|
},
|
|
59
|
+
"./mocks": {
|
|
60
|
+
"types": "./dist/src/mocks/index.d.ts",
|
|
61
|
+
"import": "./dist/src/mocks/index.js"
|
|
62
|
+
},
|
|
63
|
+
"./peer-discovery": {
|
|
64
|
+
"types": "./dist/src/peer-discovery/index.d.ts",
|
|
65
|
+
"import": "./dist/src/peer-discovery/index.js"
|
|
66
|
+
},
|
|
55
67
|
"./peers": {
|
|
56
68
|
"types": "./dist/src/peers.d.ts",
|
|
57
69
|
"import": "./dist/src/peers.js"
|
|
70
|
+
},
|
|
71
|
+
"./stream-muxer": {
|
|
72
|
+
"types": "./dist/src/stream-muxer/index.d.ts",
|
|
73
|
+
"import": "./dist/src/stream-muxer/index.js"
|
|
74
|
+
},
|
|
75
|
+
"./transport": {
|
|
76
|
+
"types": "./dist/src/transport/index.d.ts",
|
|
77
|
+
"import": "./dist/src/transport/index.js"
|
|
58
78
|
}
|
|
59
79
|
},
|
|
60
80
|
"eslintConfig": {
|
|
@@ -63,100 +83,51 @@
|
|
|
63
83
|
"sourceType": "module"
|
|
64
84
|
}
|
|
65
85
|
},
|
|
66
|
-
"release": {
|
|
67
|
-
"branches": [
|
|
68
|
-
"master"
|
|
69
|
-
],
|
|
70
|
-
"plugins": [
|
|
71
|
-
[
|
|
72
|
-
"@semantic-release/commit-analyzer",
|
|
73
|
-
{
|
|
74
|
-
"preset": "conventionalcommits",
|
|
75
|
-
"releaseRules": [
|
|
76
|
-
{
|
|
77
|
-
"breaking": true,
|
|
78
|
-
"release": "major"
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"revert": true,
|
|
82
|
-
"release": "patch"
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"type": "feat",
|
|
86
|
-
"release": "minor"
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"type": "fix",
|
|
90
|
-
"release": "patch"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"type": "docs",
|
|
94
|
-
"release": "patch"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"type": "test",
|
|
98
|
-
"release": "patch"
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"type": "deps",
|
|
102
|
-
"release": "patch"
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"scope": "no-release",
|
|
106
|
-
"release": false
|
|
107
|
-
}
|
|
108
|
-
]
|
|
109
|
-
}
|
|
110
|
-
],
|
|
111
|
-
[
|
|
112
|
-
"@semantic-release/release-notes-generator",
|
|
113
|
-
{
|
|
114
|
-
"preset": "conventionalcommits",
|
|
115
|
-
"presetConfig": {
|
|
116
|
-
"types": [
|
|
117
|
-
{
|
|
118
|
-
"type": "feat",
|
|
119
|
-
"section": "Features"
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
"type": "fix",
|
|
123
|
-
"section": "Bug Fixes"
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
"type": "chore",
|
|
127
|
-
"section": "Trivial Changes"
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"type": "docs",
|
|
131
|
-
"section": "Documentation"
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
"type": "deps",
|
|
135
|
-
"section": "Dependencies"
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
"type": "test",
|
|
139
|
-
"section": "Tests"
|
|
140
|
-
}
|
|
141
|
-
]
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
],
|
|
145
|
-
"@semantic-release/changelog",
|
|
146
|
-
"@semantic-release/npm",
|
|
147
|
-
"@semantic-release/github",
|
|
148
|
-
"@semantic-release/git"
|
|
149
|
-
]
|
|
150
|
-
},
|
|
151
86
|
"scripts": {
|
|
152
87
|
"clean": "aegir clean",
|
|
153
88
|
"lint": "aegir lint",
|
|
154
89
|
"dep-check": "aegir dep-check",
|
|
155
90
|
"build": "aegir build",
|
|
156
|
-
"
|
|
91
|
+
"test": "aegir test",
|
|
92
|
+
"test:chrome": "aegir test -t browser --cov",
|
|
93
|
+
"test:chrome-webworker": "aegir test -t webworker",
|
|
94
|
+
"test:firefox": "aegir test -t browser -- --browser firefox",
|
|
95
|
+
"test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
|
|
96
|
+
"test:node": "aegir test -t node --cov",
|
|
97
|
+
"test:electron-main": "aegir test -t electron-main"
|
|
157
98
|
},
|
|
158
99
|
"dependencies": {
|
|
159
|
-
"
|
|
100
|
+
"@libp2p/interface": "0.0.1-05abd49f",
|
|
101
|
+
"@libp2p/interface-internal": "0.0.1-05abd49f",
|
|
102
|
+
"@libp2p/logger": "2.1.1-05abd49f",
|
|
103
|
+
"@libp2p/multistream-select": "3.1.9-05abd49f",
|
|
104
|
+
"@libp2p/peer-collections": "3.0.2-05abd49f",
|
|
105
|
+
"@libp2p/peer-id": "2.0.3-05abd49f",
|
|
106
|
+
"@libp2p/peer-id-factory": "2.0.3-05abd49f",
|
|
107
|
+
"@multiformats/multiaddr": "^12.1.3",
|
|
108
|
+
"abortable-iterator": "^5.0.1",
|
|
109
|
+
"any-signal": "^4.1.1",
|
|
110
|
+
"delay": "^6.0.0",
|
|
111
|
+
"it-all": "^3.0.2",
|
|
112
|
+
"it-drain": "^3.0.2",
|
|
113
|
+
"it-handshake": "^4.1.3",
|
|
114
|
+
"it-map": "^3.0.3",
|
|
115
|
+
"it-ndjson": "^1.0.3",
|
|
116
|
+
"it-pair": "^2.0.6",
|
|
117
|
+
"it-pipe": "^3.0.1",
|
|
118
|
+
"it-pushable": "^3.1.3",
|
|
119
|
+
"it-stream-types": "^2.0.1",
|
|
120
|
+
"merge-options": "^3.0.4",
|
|
121
|
+
"p-defer": "^4.0.0",
|
|
122
|
+
"p-limit": "^4.0.0",
|
|
123
|
+
"p-wait-for": "^5.0.2",
|
|
124
|
+
"sinon": "^15.1.2",
|
|
125
|
+
"ts-sinon": "^2.0.2",
|
|
126
|
+
"uint8arraylist": "^2.4.3",
|
|
127
|
+
"uint8arrays": "^4.0.4"
|
|
128
|
+
},
|
|
129
|
+
"devDependencies": {
|
|
130
|
+
"aegir": "^39.0.10"
|
|
160
131
|
},
|
|
161
132
|
"typedoc": {
|
|
162
133
|
"entryPoint": "./src/index.ts"
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { expect } from 'aegir/chai'
|
|
2
|
+
import sinon from 'sinon'
|
|
3
|
+
import { stubInterface } from 'ts-sinon'
|
|
4
|
+
import type { TestSetup } from '../index.js'
|
|
5
|
+
import type { Connection, Stream } from '@libp2p/interface/connection'
|
|
6
|
+
|
|
7
|
+
export default (test: TestSetup<Connection>): void => {
|
|
8
|
+
describe('connection', () => {
|
|
9
|
+
describe('open connection', () => {
|
|
10
|
+
let connection: Connection
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
connection = await test.setup()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
await connection.close()
|
|
18
|
+
await test.teardown()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should have properties set', () => {
|
|
22
|
+
expect(connection.id).to.exist()
|
|
23
|
+
expect(connection.remotePeer).to.exist()
|
|
24
|
+
expect(connection.remoteAddr).to.exist()
|
|
25
|
+
expect(connection.stat.status).to.equal('OPEN')
|
|
26
|
+
expect(connection.stat.timeline.open).to.exist()
|
|
27
|
+
expect(connection.stat.timeline.close).to.not.exist()
|
|
28
|
+
expect(connection.stat.direction).to.exist()
|
|
29
|
+
expect(connection.streams).to.eql([])
|
|
30
|
+
expect(connection.tags).to.eql([])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should get the metadata of an open connection', () => {
|
|
34
|
+
const stat = connection.stat
|
|
35
|
+
|
|
36
|
+
expect(stat.status).to.equal('OPEN')
|
|
37
|
+
expect(stat.direction).to.exist()
|
|
38
|
+
expect(stat.timeline.open).to.exist()
|
|
39
|
+
expect(stat.timeline.close).to.not.exist()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should return an empty array of streams', () => {
|
|
43
|
+
const streams = connection.streams
|
|
44
|
+
|
|
45
|
+
expect(streams).to.eql([])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('should be able to create a new stream', async () => {
|
|
49
|
+
expect(connection.streams).to.be.empty()
|
|
50
|
+
|
|
51
|
+
const protocolToUse = '/echo/0.0.1'
|
|
52
|
+
const stream = await connection.newStream([protocolToUse])
|
|
53
|
+
|
|
54
|
+
expect(stream).to.have.nested.property('stat.protocol', protocolToUse)
|
|
55
|
+
|
|
56
|
+
const connStreams = connection.streams
|
|
57
|
+
|
|
58
|
+
expect(stream).to.exist()
|
|
59
|
+
expect(connStreams).to.exist()
|
|
60
|
+
expect(connStreams).to.have.lengthOf(1)
|
|
61
|
+
expect(connStreams[0]).to.equal(stream)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe('close connection', () => {
|
|
66
|
+
let connection: Connection
|
|
67
|
+
let timelineProxy
|
|
68
|
+
const proxyHandler = {
|
|
69
|
+
set () {
|
|
70
|
+
// @ts-expect-error - TS fails to infer here
|
|
71
|
+
return Reflect.set(...arguments)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
beforeEach(async () => {
|
|
76
|
+
timelineProxy = new Proxy({
|
|
77
|
+
open: Date.now() - 10,
|
|
78
|
+
upgraded: Date.now()
|
|
79
|
+
}, proxyHandler)
|
|
80
|
+
|
|
81
|
+
connection = await test.setup()
|
|
82
|
+
connection.stat.timeline = timelineProxy
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
afterEach(async () => {
|
|
86
|
+
await test.teardown()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should be able to close the connection after being created', async () => {
|
|
90
|
+
expect(connection.stat.timeline.close).to.not.exist()
|
|
91
|
+
await connection.close()
|
|
92
|
+
|
|
93
|
+
expect(connection.stat.timeline.close).to.exist()
|
|
94
|
+
expect(connection.stat.status).to.equal('CLOSED')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should be able to close the connection after opening a stream', async () => {
|
|
98
|
+
// Open stream
|
|
99
|
+
const protocol = '/echo/0.0.1'
|
|
100
|
+
await connection.newStream([protocol])
|
|
101
|
+
|
|
102
|
+
// Close connection
|
|
103
|
+
expect(connection.stat.timeline.close).to.not.exist()
|
|
104
|
+
await connection.close()
|
|
105
|
+
|
|
106
|
+
expect(connection.stat.timeline.close).to.exist()
|
|
107
|
+
expect(connection.stat.status).to.equal('CLOSED')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should properly track streams', async () => {
|
|
111
|
+
// Open stream
|
|
112
|
+
const protocol = '/echo/0.0.1'
|
|
113
|
+
const stream = await connection.newStream([protocol])
|
|
114
|
+
expect(stream).to.have.nested.property('stat.protocol', protocol)
|
|
115
|
+
|
|
116
|
+
// Close stream
|
|
117
|
+
stream.close()
|
|
118
|
+
|
|
119
|
+
expect(connection.streams.filter(s => s.id === stream.id)).to.be.empty()
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('should track outbound streams', async () => {
|
|
123
|
+
// Open stream
|
|
124
|
+
const protocol = '/echo/0.0.1'
|
|
125
|
+
const stream = await connection.newStream(protocol)
|
|
126
|
+
expect(stream).to.have.nested.property('stat.direction', 'outbound')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it.skip('should track inbound streams', async () => {
|
|
130
|
+
// Add an remotely opened stream
|
|
131
|
+
const stream = stubInterface<Stream>()
|
|
132
|
+
connection.addStream(stream)
|
|
133
|
+
expect(stream).to.have.property('direction', 'inbound')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should support a proxy on the timeline', async () => {
|
|
137
|
+
sinon.spy(proxyHandler, 'set')
|
|
138
|
+
expect(connection.stat.timeline.close).to.not.exist()
|
|
139
|
+
|
|
140
|
+
await connection.close()
|
|
141
|
+
// @ts-expect-error - fails to infer callCount
|
|
142
|
+
expect(proxyHandler.set.callCount).to.equal(1)
|
|
143
|
+
// @ts-expect-error - fails to infer getCall
|
|
144
|
+
const [obj, key, value] = proxyHandler.set.getCall(0).args
|
|
145
|
+
expect(obj).to.eql(connection.stat.timeline)
|
|
146
|
+
expect(key).to.equal('close')
|
|
147
|
+
expect(value).to.be.a('number').that.equals(connection.stat.timeline.close)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should fail to create a new stream if the connection is closing', async () => {
|
|
151
|
+
expect(connection.stat.timeline.close).to.not.exist()
|
|
152
|
+
const p = connection.close()
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const protocol = '/echo/0.0.1'
|
|
156
|
+
await connection.newStream([protocol])
|
|
157
|
+
} catch (err: any) {
|
|
158
|
+
expect(err).to.exist()
|
|
159
|
+
return
|
|
160
|
+
} finally {
|
|
161
|
+
await p
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
throw new Error('should fail to create a new stream if the connection is closing')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('should fail to create a new stream if the connection is closed', async () => {
|
|
168
|
+
expect(connection.stat.timeline.close).to.not.exist()
|
|
169
|
+
await connection.close()
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const protocol = '/echo/0.0.1'
|
|
173
|
+
await connection.newStream([protocol])
|
|
174
|
+
} catch (err: any) {
|
|
175
|
+
expect(err).to.exist()
|
|
176
|
+
expect(err.code).to.equal('ERR_CONNECTION_CLOSED')
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
throw new Error('should fail to create a new stream if the connection is closing')
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
}
|
|
@@ -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,23 @@
|
|
|
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
|
+
remoteAddr: multiaddr('/ip4/127.0.0.1/tcp/4001'),
|
|
14
|
+
timeline: {
|
|
15
|
+
open: Date.now()
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return output
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return [duplexToMaConn(local), duplexToMaConn(remote)]
|
|
23
|
+
}
|
package/src/index.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
|
+
}
|