Skip to content

Commit dd47517

Browse files
authored
fix!: remove @libp2p/components (#229)
* fix!: remove @libp2p/components `@libp2p/components` is a choke-point for our dependency graph as it depends on every interface, meaning when one interface revs a major `@libp2p/components` major has to change too which means every module depending on it also needs a major. Switch instead to constructor injection of simple objects that let modules declare their dependencies on interfaces directly instead of indirectly via `@libp2p/components` Refs libp2p/js-libp2p-components#6 BREAKING CHANGE: modules no longer implement `Initializable` instead switching to constructor injection * chore: update deps
1 parent 3bc8ed4 commit dd47517

8 files changed

+69
-66
lines changed

package.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -82,33 +82,33 @@
8282
"it-pb-stream": "^2.0.2",
8383
"it-pipe": "^2.0.3",
8484
"it-stream-types": "^1.0.4",
85-
"protons-runtime": "^3.1.0",
85+
"protons-runtime": "^4.0.1",
8686
"uint8arraylist": "^2.3.2",
87-
"uint8arrays": "^3.1.0"
87+
"uint8arrays": "^4.0.2"
8888
},
8989
"devDependencies": {
9090
"@libp2p/daemon-client": "^3.0.1",
9191
"@libp2p/daemon-server": "^3.0.1",
92-
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.3",
92+
"@libp2p/interface-connection-encrypter-compliance-tests": "^3.0.0",
9393
"@libp2p/interop": "^3.0.1",
94-
"@libp2p/mplex": "^5.0.0",
94+
"@libp2p/mplex": "^7.0.0",
9595
"@libp2p/peer-id-factory": "^1.0.8",
96-
"@libp2p/tcp": "^4.1.0",
96+
"@libp2p/tcp": "^5.0.1",
9797
"@multiformats/multiaddr": "^11.0.3",
9898
"aegir": "^37.3.0",
9999
"benchmark": "^2.1.4",
100100
"execa": "^6.1.0",
101101
"go-libp2p": "^0.0.6",
102102
"iso-random-stream": "^2.0.2",
103-
"libp2p": "0.39.4",
103+
"libp2p": "^0.40.0",
104104
"mkdirp": "^1.0.4",
105105
"p-defer": "^4.0.0",
106-
"protons": "^5.1.0",
106+
"protons": "^6.0.0",
107107
"sinon": "^14.0.0",
108108
"util": "^0.12.4"
109109
},
110110
"browser": {
111111
"./dist/src/alloc-unsafe.js": "./dist/src/alloc-unsafe-browser.js",
112112
"util": false
113113
}
114-
}
114+
}

src/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import type { ConnectionEncrypter } from '@libp2p/interface-connection-encrypter'
2+
import { Noise } from './noise.js'
3+
import type { NoiseInit } from './noise.js'
14
export * from './crypto.js'
25
export * from './crypto/stablelib.js'
3-
export * from './noise.js'
6+
7+
export function noise (init: NoiseInit = {}): () => ConnectionEncrypter {
8+
return () => new Noise(init)
9+
}

src/noise.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ interface HandshakeParams {
2424
remotePeer?: PeerId
2525
}
2626

27+
export interface NoiseInit {
28+
/**
29+
* x25519 private key, reuse for faster handshakes
30+
*/
31+
staticNoiseKey?: bytes
32+
extensions?: NoiseExtensions
33+
crypto?: ICryptoInterface
34+
prologueBytes?: Uint8Array
35+
}
36+
2737
export class Noise implements INoiseConnection {
2838
public protocol = '/noise'
2939
public crypto: ICryptoInterface
@@ -32,12 +42,10 @@ export class Noise implements INoiseConnection {
3242
private readonly staticKeys: KeyPair
3343
private readonly extensions?: NoiseExtensions
3444

35-
/**
36-
* @param {bytes} staticNoiseKey - x25519 private key, reuse for faster handshakes
37-
* @param {NoiseExtensions} extensions
38-
*/
39-
constructor (staticNoiseKey?: bytes, extensions?: NoiseExtensions, crypto: ICryptoInterface = stablelib, prologueBytes?: Uint8Array) {
40-
this.crypto = crypto
45+
constructor (init: NoiseInit = {}) {
46+
const { staticNoiseKey, extensions, crypto, prologueBytes } = init
47+
48+
this.crypto = crypto ?? stablelib
4149
this.extensions = extensions
4250

4351
if (staticNoiseKey) {

src/proto/payload.ts

+20-30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/* eslint-disable import/export */
2+
/* eslint-disable complexity */
23
/* eslint-disable @typescript-eslint/no-namespace */
4+
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
35

46
import { encodeMessage, decodeMessage, message } from 'protons-runtime'
57
import type { Uint8ArrayList } from 'uint8arraylist'
@@ -14,22 +16,20 @@ export namespace NoiseExtensions {
1416

1517
export const codec = (): Codec<NoiseExtensions> => {
1618
if (_codec == null) {
17-
_codec = message<NoiseExtensions>((obj, writer, opts = {}) => {
19+
_codec = message<NoiseExtensions>((obj, w, opts = {}) => {
1820
if (opts.lengthDelimited !== false) {
19-
writer.fork()
21+
w.fork()
2022
}
2123

2224
if (obj.webtransportCerthashes != null) {
2325
for (const value of obj.webtransportCerthashes) {
24-
writer.uint32(10)
25-
writer.bytes(value)
26+
w.uint32(10)
27+
w.bytes(value)
2628
}
27-
} else {
28-
throw new Error('Protocol error: required field "webtransportCerthashes" was not found in object')
2929
}
3030

3131
if (opts.lengthDelimited !== false) {
32-
writer.ldelim()
32+
w.ldelim()
3333
}
3434
}, (reader, length) => {
3535
const obj: any = {
@@ -78,32 +78,30 @@ export namespace NoiseHandshakePayload {
7878

7979
export const codec = (): Codec<NoiseHandshakePayload> => {
8080
if (_codec == null) {
81-
_codec = message<NoiseHandshakePayload>((obj, writer, opts = {}) => {
81+
_codec = message<NoiseHandshakePayload>((obj, w, opts = {}) => {
8282
if (opts.lengthDelimited !== false) {
83-
writer.fork()
83+
w.fork()
8484
}
8585

86-
if (obj.identityKey != null) {
87-
writer.uint32(10)
88-
writer.bytes(obj.identityKey)
89-
} else {
90-
throw new Error('Protocol error: required field "identityKey" was not found in object')
86+
if (opts.writeDefaults === true || (obj.identityKey != null && obj.identityKey.byteLength > 0)) {
87+
w.uint32(10)
88+
w.bytes(obj.identityKey)
9189
}
9290

93-
if (obj.identitySig != null) {
94-
writer.uint32(18)
95-
writer.bytes(obj.identitySig)
96-
} else {
97-
throw new Error('Protocol error: required field "identitySig" was not found in object')
91+
if (opts.writeDefaults === true || (obj.identitySig != null && obj.identitySig.byteLength > 0)) {
92+
w.uint32(18)
93+
w.bytes(obj.identitySig)
9894
}
9995

10096
if (obj.extensions != null) {
101-
writer.uint32(34)
102-
NoiseExtensions.codec().encode(obj.extensions, writer)
97+
w.uint32(34)
98+
NoiseExtensions.codec().encode(obj.extensions, w, {
99+
writeDefaults: false
100+
})
103101
}
104102

105103
if (opts.lengthDelimited !== false) {
106-
writer.ldelim()
104+
w.ldelim()
107105
}
108106
}, (reader, length) => {
109107
const obj: any = {
@@ -132,14 +130,6 @@ export namespace NoiseHandshakePayload {
132130
}
133131
}
134132

135-
if (obj.identityKey == null) {
136-
throw new Error('Protocol error: value for required field "identityKey" was not found in protobuf')
137-
}
138-
139-
if (obj.identitySig == null) {
140-
throw new Error('Protocol error: value for required field "identitySig" was not found in protobuf')
141-
}
142-
143133
return obj
144134
})
145135
}

test/compliance.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import tests from '@libp2p/interface-connection-encrypter-compliance-tests'
2-
import { Noise } from '../src/index.js'
2+
import { Noise } from '../src/noise.js'
33

44
describe('spec compliance tests', function () {
55
tests({

test/index.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from 'aegir/chai'
2-
import { Noise } from '../src/index.js'
2+
import { Noise } from '../src/noise.js'
33

44
describe('Index', () => {
55
it('should expose class with tag and required functions', () => {

test/interop.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import type { SpawnOptions, Daemon, DaemonFactory } from '@libp2p/interop'
33
import { createServer } from '@libp2p/daemon-server'
44
import { createClient } from '@libp2p/daemon-client'
55
import { createLibp2p, Libp2pOptions } from 'libp2p'
6-
import { TCP } from '@libp2p/tcp'
6+
import { tcp } from '@libp2p/tcp'
77
import { multiaddr } from '@multiformats/multiaddr'
88
import { path as p2pd } from 'go-libp2p'
99
import { execa } from 'execa'
1010
import pDefer from 'p-defer'
1111
import { logger } from '@libp2p/logger'
12-
import { Mplex } from '@libp2p/mplex'
12+
import { mplex } from '@libp2p/mplex'
1313
import fs from 'fs'
1414
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
1515
import type { PeerId } from '@libp2p/interface-peer-id'
1616
import { peerIdFromKeys } from '@libp2p/peer-id'
17-
import { Noise } from '../src/index.js'
17+
import { noise } from '../src/index.js'
1818

1919
async function createGoPeer (options: SpawnOptions): Promise<Daemon> {
2020
const controlPort = Math.floor(Math.random() * (50000 - 10000 + 1)) + 10000
@@ -76,10 +76,9 @@ async function createJsPeer (options: SpawnOptions): Promise<Daemon> {
7676
addresses: {
7777
listen: ['/ip4/0.0.0.0/tcp/0']
7878
},
79-
transports: [new TCP()],
80-
streamMuxers: [new Mplex()],
81-
// @ts-expect-error libp2p options is still referencing the old connection encrypter interface https://github.com/libp2p/js-libp2p/pull/1402
82-
connectionEncryption: [new Noise()]
79+
transports: [tcp()],
80+
streamMuxers: [mplex()],
81+
connectionEncryption: [noise()]
8382
}
8483

8584
const node = await createLibp2p(opts)

test/noise.spec.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { stablelib } from '../src/crypto/stablelib.js'
1313
import { decode0, decode2, encode1, uint16BEDecode, uint16BEEncode } from '../src/encoder.js'
1414
import { XX } from '../src/handshakes/xx.js'
1515
import { XXHandshake } from '../src/handshake-xx.js'
16-
import { Noise } from '../src/index.js'
16+
import { Noise } from '../src/noise.js'
1717
import { createHandshakePayload, getHandshakePayload, getPayload, signPayload } from '../src/utils.js'
1818
import { createPeerIdsFromFixtures } from './fixtures/peer.js'
1919
import { getKeyPairFromPeerId } from './utils.js'
@@ -32,8 +32,8 @@ describe('Noise', () => {
3232

3333
it('should communicate through encrypted streams without noise pipes', async () => {
3434
try {
35-
const noiseInit = new Noise(undefined, undefined)
36-
const noiseResp = new Noise(undefined, undefined)
35+
const noiseInit = new Noise({ staticNoiseKey: undefined, extensions: undefined })
36+
const noiseResp = new Noise({ staticNoiseKey: undefined, extensions: undefined })
3737

3838
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
3939
const [outbound, inbound] = await Promise.all([
@@ -53,7 +53,7 @@ describe('Noise', () => {
5353
})
5454

5555
it('should test that secureOutbound is spec compliant', async () => {
56-
const noiseInit = new Noise(undefined, undefined)
56+
const noiseInit = new Noise({ staticNoiseKey: undefined })
5757
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
5858

5959
const [outbound, { wrapped, handshake }] = await Promise.all([
@@ -108,8 +108,8 @@ describe('Noise', () => {
108108
it('should test large payloads', async function () {
109109
this.timeout(10000)
110110
try {
111-
const noiseInit = new Noise(undefined, undefined)
112-
const noiseResp = new Noise(undefined, undefined)
111+
const noiseInit = new Noise({ staticNoiseKey: undefined })
112+
const noiseResp = new Noise({ staticNoiseKey: undefined })
113113

114114
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
115115
const [outbound, inbound] = await Promise.all([
@@ -133,9 +133,9 @@ describe('Noise', () => {
133133
it('should working without remote peer provided in incoming connection', async () => {
134134
try {
135135
const staticKeysInitiator = stablelib.generateX25519KeyPair()
136-
const noiseInit = new Noise(staticKeysInitiator.privateKey)
136+
const noiseInit = new Noise({ staticNoiseKey: staticKeysInitiator.privateKey })
137137
const staticKeysResponder = stablelib.generateX25519KeyPair()
138-
const noiseResp = new Noise(staticKeysResponder.privateKey)
138+
const noiseResp = new Noise({ staticNoiseKey: staticKeysResponder.privateKey })
139139

140140
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
141141
const [outbound, inbound] = await Promise.all([
@@ -166,10 +166,10 @@ describe('Noise', () => {
166166
try {
167167
const certhashInit = Buffer.from('certhash data from init')
168168
const staticKeysInitiator = stablelib.generateX25519KeyPair()
169-
const noiseInit = new Noise(staticKeysInitiator.privateKey, { webtransportCerthashes: [certhashInit] })
169+
const noiseInit = new Noise({ staticNoiseKey: staticKeysInitiator.privateKey, extensions: { webtransportCerthashes: [certhashInit] } })
170170
const staticKeysResponder = stablelib.generateX25519KeyPair()
171171
const certhashResp = Buffer.from('certhash data from respon')
172-
const noiseResp = new Noise(staticKeysResponder.privateKey, { webtransportCerthashes: [certhashResp] })
172+
const noiseResp = new Noise({ staticNoiseKey: staticKeysResponder.privateKey, extensions: { webtransportCerthashes: [certhashResp] } })
173173

174174
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
175175
const [outbound, inbound] = await Promise.all([
@@ -187,8 +187,8 @@ describe('Noise', () => {
187187

188188
it('should accept a prologue', async () => {
189189
try {
190-
const noiseInit = new Noise(undefined, undefined, stablelib, Buffer.from('Some prologue'))
191-
const noiseResp = new Noise(undefined, undefined, stablelib, Buffer.from('Some prologue'))
190+
const noiseInit = new Noise({ staticNoiseKey: undefined, crypto: stablelib, prologueBytes: Buffer.from('Some prologue') })
191+
const noiseResp = new Noise({ staticNoiseKey: undefined, crypto: stablelib, prologueBytes: Buffer.from('Some prologue') })
192192

193193
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
194194
const [outbound, inbound] = await Promise.all([

0 commit comments

Comments
 (0)