Skip to content

Commit

Permalink
fix!: remove @libp2p/components (#229)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
achingbrain authored Oct 19, 2022
1 parent 3bc8ed4 commit dd47517
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 66 deletions.
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,33 +82,33 @@
"it-pb-stream": "^2.0.2",
"it-pipe": "^2.0.3",
"it-stream-types": "^1.0.4",
"protons-runtime": "^3.1.0",
"protons-runtime": "^4.0.1",
"uint8arraylist": "^2.3.2",
"uint8arrays": "^3.1.0"
"uint8arrays": "^4.0.2"
},
"devDependencies": {
"@libp2p/daemon-client": "^3.0.1",
"@libp2p/daemon-server": "^3.0.1",
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.3",
"@libp2p/interface-connection-encrypter-compliance-tests": "^3.0.0",
"@libp2p/interop": "^3.0.1",
"@libp2p/mplex": "^5.0.0",
"@libp2p/mplex": "^7.0.0",
"@libp2p/peer-id-factory": "^1.0.8",
"@libp2p/tcp": "^4.1.0",
"@libp2p/tcp": "^5.0.1",
"@multiformats/multiaddr": "^11.0.3",
"aegir": "^37.3.0",
"benchmark": "^2.1.4",
"execa": "^6.1.0",
"go-libp2p": "^0.0.6",
"iso-random-stream": "^2.0.2",
"libp2p": "0.39.4",
"libp2p": "^0.40.0",
"mkdirp": "^1.0.4",
"p-defer": "^4.0.0",
"protons": "^5.1.0",
"protons": "^6.0.0",
"sinon": "^14.0.0",
"util": "^0.12.4"
},
"browser": {
"./dist/src/alloc-unsafe.js": "./dist/src/alloc-unsafe-browser.js",
"util": false
}
}
}
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import type { ConnectionEncrypter } from '@libp2p/interface-connection-encrypter'
import { Noise } from './noise.js'
import type { NoiseInit } from './noise.js'
export * from './crypto.js'
export * from './crypto/stablelib.js'
export * from './noise.js'

export function noise (init: NoiseInit = {}): () => ConnectionEncrypter {
return () => new Noise(init)
}
20 changes: 14 additions & 6 deletions src/noise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ interface HandshakeParams {
remotePeer?: PeerId
}

export interface NoiseInit {
/**
* x25519 private key, reuse for faster handshakes
*/
staticNoiseKey?: bytes
extensions?: NoiseExtensions
crypto?: ICryptoInterface
prologueBytes?: Uint8Array
}

export class Noise implements INoiseConnection {
public protocol = '/noise'
public crypto: ICryptoInterface
Expand All @@ -32,12 +42,10 @@ export class Noise implements INoiseConnection {
private readonly staticKeys: KeyPair
private readonly extensions?: NoiseExtensions

/**
* @param {bytes} staticNoiseKey - x25519 private key, reuse for faster handshakes
* @param {NoiseExtensions} extensions
*/
constructor (staticNoiseKey?: bytes, extensions?: NoiseExtensions, crypto: ICryptoInterface = stablelib, prologueBytes?: Uint8Array) {
this.crypto = crypto
constructor (init: NoiseInit = {}) {
const { staticNoiseKey, extensions, crypto, prologueBytes } = init

this.crypto = crypto ?? stablelib
this.extensions = extensions

if (staticNoiseKey) {
Expand Down
50 changes: 20 additions & 30 deletions src/proto/payload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint-disable import/export */
/* eslint-disable complexity */
/* eslint-disable @typescript-eslint/no-namespace */
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */

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

export const codec = (): Codec<NoiseExtensions> => {
if (_codec == null) {
_codec = message<NoiseExtensions>((obj, writer, opts = {}) => {
_codec = message<NoiseExtensions>((obj, w, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
w.fork()
}

if (obj.webtransportCerthashes != null) {
for (const value of obj.webtransportCerthashes) {
writer.uint32(10)
writer.bytes(value)
w.uint32(10)
w.bytes(value)
}
} else {
throw new Error('Protocol error: required field "webtransportCerthashes" was not found in object')
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
w.ldelim()
}
}, (reader, length) => {
const obj: any = {
Expand Down Expand Up @@ -78,32 +78,30 @@ export namespace NoiseHandshakePayload {

export const codec = (): Codec<NoiseHandshakePayload> => {
if (_codec == null) {
_codec = message<NoiseHandshakePayload>((obj, writer, opts = {}) => {
_codec = message<NoiseHandshakePayload>((obj, w, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
w.fork()
}

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

if (obj.identitySig != null) {
writer.uint32(18)
writer.bytes(obj.identitySig)
} else {
throw new Error('Protocol error: required field "identitySig" was not found in object')
if (opts.writeDefaults === true || (obj.identitySig != null && obj.identitySig.byteLength > 0)) {
w.uint32(18)
w.bytes(obj.identitySig)
}

if (obj.extensions != null) {
writer.uint32(34)
NoiseExtensions.codec().encode(obj.extensions, writer)
w.uint32(34)
NoiseExtensions.codec().encode(obj.extensions, w, {
writeDefaults: false
})
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
w.ldelim()
}
}, (reader, length) => {
const obj: any = {
Expand Down Expand Up @@ -132,14 +130,6 @@ export namespace NoiseHandshakePayload {
}
}

if (obj.identityKey == null) {
throw new Error('Protocol error: value for required field "identityKey" was not found in protobuf')
}

if (obj.identitySig == null) {
throw new Error('Protocol error: value for required field "identitySig" was not found in protobuf')
}

return obj
})
}
Expand Down
2 changes: 1 addition & 1 deletion test/compliance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import tests from '@libp2p/interface-connection-encrypter-compliance-tests'
import { Noise } from '../src/index.js'
import { Noise } from '../src/noise.js'

describe('spec compliance tests', function () {
tests({
Expand Down
2 changes: 1 addition & 1 deletion test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'aegir/chai'
import { Noise } from '../src/index.js'
import { Noise } from '../src/noise.js'

describe('Index', () => {
it('should expose class with tag and required functions', () => {
Expand Down
13 changes: 6 additions & 7 deletions test/interop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import type { SpawnOptions, Daemon, DaemonFactory } from '@libp2p/interop'
import { createServer } from '@libp2p/daemon-server'
import { createClient } from '@libp2p/daemon-client'
import { createLibp2p, Libp2pOptions } from 'libp2p'
import { TCP } from '@libp2p/tcp'
import { tcp } from '@libp2p/tcp'
import { multiaddr } from '@multiformats/multiaddr'
import { path as p2pd } from 'go-libp2p'
import { execa } from 'execa'
import pDefer from 'p-defer'
import { logger } from '@libp2p/logger'
import { Mplex } from '@libp2p/mplex'
import { mplex } from '@libp2p/mplex'
import fs from 'fs'
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
import type { PeerId } from '@libp2p/interface-peer-id'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { Noise } from '../src/index.js'
import { noise } from '../src/index.js'

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

const node = await createLibp2p(opts)
Expand Down
24 changes: 12 additions & 12 deletions test/noise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { stablelib } from '../src/crypto/stablelib.js'
import { decode0, decode2, encode1, uint16BEDecode, uint16BEEncode } from '../src/encoder.js'
import { XX } from '../src/handshakes/xx.js'
import { XXHandshake } from '../src/handshake-xx.js'
import { Noise } from '../src/index.js'
import { Noise } from '../src/noise.js'
import { createHandshakePayload, getHandshakePayload, getPayload, signPayload } from '../src/utils.js'
import { createPeerIdsFromFixtures } from './fixtures/peer.js'
import { getKeyPairFromPeerId } from './utils.js'
Expand All @@ -32,8 +32,8 @@ describe('Noise', () => {

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

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

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

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

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

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
const [outbound, inbound] = await Promise.all([
Expand Down Expand Up @@ -166,10 +166,10 @@ describe('Noise', () => {
try {
const certhashInit = Buffer.from('certhash data from init')
const staticKeysInitiator = stablelib.generateX25519KeyPair()
const noiseInit = new Noise(staticKeysInitiator.privateKey, { webtransportCerthashes: [certhashInit] })
const noiseInit = new Noise({ staticNoiseKey: staticKeysInitiator.privateKey, extensions: { webtransportCerthashes: [certhashInit] } })
const staticKeysResponder = stablelib.generateX25519KeyPair()
const certhashResp = Buffer.from('certhash data from respon')
const noiseResp = new Noise(staticKeysResponder.privateKey, { webtransportCerthashes: [certhashResp] })
const noiseResp = new Noise({ staticNoiseKey: staticKeysResponder.privateKey, extensions: { webtransportCerthashes: [certhashResp] } })

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

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

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

0 comments on commit dd47517

Please sign in to comment.