Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make local peer id optional #440

Merged
merged 2 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Noise } from './noise.js'
import type { NoiseInit } from './noise.js'
import type { NoiseExtensions } from './proto/payload.js'
import type { ComponentLogger, ConnectionEncrypter, Metrics } from '@libp2p/interface'
import type { ComponentLogger, ConnectionEncrypter, Metrics, PeerId } from '@libp2p/interface'
export type { ICryptoInterface } from './crypto.js'
export { pureJsCrypto } from './crypto/js.js'

export interface NoiseComponents {
peerId: PeerId
logger: ComponentLogger
metrics?: Metrics
}
Expand Down
40 changes: 37 additions & 3 deletions src/noise.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
import { type MultiaddrConnection, type SecuredConnection, type PeerId, CodeError, type PrivateKey, serviceCapabilities } from '@libp2p/interface'
import { type MultiaddrConnection, type SecuredConnection, type PeerId, CodeError, type PrivateKey, serviceCapabilities, isPeerId } from '@libp2p/interface'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { decode } from 'it-length-prefixed'
import { lpStream, type LengthPrefixedStream } from 'it-length-prefixed-stream'
Expand Down Expand Up @@ -72,7 +72,11 @@ export class Noise implements INoiseConnection {
* @param connection - streaming iterable duplex that will be encrypted
* @param remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer.
*/
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>> {
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>>
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>>
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (...args: any[]): Promise<SecuredConnection<Stream, NoiseExtensions>> {
const { localPeer, connection, remotePeer } = this.parseArgs<Stream>(args)

const wrappedConnection = lpStream(
connection,
{
Expand Down Expand Up @@ -113,7 +117,11 @@ export class Noise implements INoiseConnection {
* @param connection - streaming iterable duplex that will be encrypted.
* @param remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades.
*/
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>> {
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>>
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>>
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (...args: any[]): Promise<SecuredConnection<Stream, NoiseExtensions>> {
const { localPeer, connection, remotePeer } = this.parseArgs<Stream>(args)

const wrappedConnection = lpStream(
connection,
{
Expand Down Expand Up @@ -226,4 +234,30 @@ export class Noise implements INoiseConnection {

return user
}

/**
* Detect call signature in `libp2p@1.x.x` or `libp2p@2.x.x` style.
*
* TODO: remove this after `libp2p@2.x.x` is released and only support the
* newer style
*/
private parseArgs <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (args: any[]): { localPeer: PeerId, connection: Stream, remotePeer?: PeerId } {
// if the first argument is a peer id, we're using the libp2p@1.x.x style
if (isPeerId(args[0])) {
return {
localPeer: args[0],
connection: args[1],
remotePeer: args[2]
}
} else {
// handle upcoming changes in libp2p@2.x.x where the first argument is the
// connection and the second is optionally the remote peer
// @see https://github.com/libp2p/js-libp2p/pull/2304
return {
localPeer: this.components.peerId,
connection: args[0],
remotePeer: args[1]
}
}
}
}
9 changes: 7 additions & 2 deletions test/compliance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import tests from '@libp2p/interface-compliance-tests/connection-encryption'
import { defaultLogger } from '@libp2p/logger'
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { Noise } from '../src/noise.js'
import type { PeerId } from '@libp2p/interface'

describe('spec compliance tests', function () {
tests({
async setup () {
return new Noise({ logger: defaultLogger() })
async setup (opts: { peerId?: PeerId }) {
return new Noise({
peerId: opts?.peerId ?? await createEd25519PeerId(),
logger: defaultLogger()
})
},
async teardown () {}
})
Expand Down
19 changes: 15 additions & 4 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defaultLogger } from '@libp2p/logger'
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { expect } from 'aegir/chai'
import { lpStream } from 'it-length-prefixed-stream'
import { duplexPair } from 'it-pair/duplex'
Expand All @@ -18,8 +19,11 @@ function createCounterSpy (): ReturnType<typeof sinon.spy> {
}

describe('Index', () => {
it('should expose class with tag and required functions', () => {
const noiseInstance = noise()({ logger: defaultLogger() })
it('should expose class with tag and required functions', async () => {
const noiseInstance = noise()({
peerId: await createEd25519PeerId(),
logger: defaultLogger()
})
expect(noiseInstance.protocol).to.equal('/noise')
expect(typeof (noiseInstance.secureInbound)).to.equal('function')
expect(typeof (noiseInstance.secureOutbound)).to.equal('function')
Expand All @@ -35,8 +39,15 @@ describe('Index', () => {
return counter
}
}
const noiseInit = new Noise({ logger: defaultLogger(), metrics: metrics as any as Metrics })
const noiseResp = new Noise({ logger: defaultLogger() })
const noiseInit = new Noise({
peerId: await createEd25519PeerId(),
logger: defaultLogger(),
metrics: metrics as any as Metrics
})
const noiseResp = new Noise({
peerId: await createEd25519PeerId(),
logger: defaultLogger()
})

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array | Uint8ArrayList>()
const [outbound, inbound] = await Promise.all([
Expand Down
50 changes: 40 additions & 10 deletions test/noise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@ describe('Noise', () => {

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

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array | Uint8ArrayList>()
const [outbound, inbound] = await Promise.all([
Expand All @@ -51,8 +57,14 @@ describe('Noise', () => {
it('should test large payloads', async function () {
this.timeout(10000)
try {
const noiseInit = new Noise({ logger: defaultLogger() }, { staticNoiseKey: undefined })
const noiseResp = new Noise({ logger: defaultLogger() }, { staticNoiseKey: undefined })
const noiseInit = new Noise({
peerId: localPeer,
logger: defaultLogger()
}, { staticNoiseKey: undefined })
const noiseResp = new Noise({
peerId: remotePeer,
logger: defaultLogger()
}, { staticNoiseKey: undefined })

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array | Uint8ArrayList>()
const [outbound, inbound] = await Promise.all([
Expand All @@ -76,9 +88,15 @@ describe('Noise', () => {
it('should working without remote peer provided in incoming connection', async () => {
try {
const staticKeysInitiator = pureJsCrypto.generateX25519KeyPair()
const noiseInit = new Noise({ logger: defaultLogger() }, { staticNoiseKey: staticKeysInitiator.privateKey })
const noiseInit = new Noise({
peerId: localPeer,
logger: defaultLogger()
}, { staticNoiseKey: staticKeysInitiator.privateKey })
const staticKeysResponder = pureJsCrypto.generateX25519KeyPair()
const noiseResp = new Noise({ logger: defaultLogger() }, { staticNoiseKey: staticKeysResponder.privateKey })
const noiseResp = new Noise({
peerId: remotePeer,
logger: defaultLogger()
}, { staticNoiseKey: staticKeysResponder.privateKey })

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array | Uint8ArrayList>()
const [outbound, inbound] = await Promise.all([
Expand Down Expand Up @@ -109,10 +127,16 @@ describe('Noise', () => {
try {
const certhashInit = Buffer.from('certhash data from init')
const staticKeysInitiator = pureJsCrypto.generateX25519KeyPair()
const noiseInit = new Noise({ logger: defaultLogger() }, { staticNoiseKey: staticKeysInitiator.privateKey, extensions: { webtransportCerthashes: [certhashInit] } })
const noiseInit = new Noise({
peerId: localPeer,
logger: defaultLogger()
}, { staticNoiseKey: staticKeysInitiator.privateKey, extensions: { webtransportCerthashes: [certhashInit] } })
const staticKeysResponder = pureJsCrypto.generateX25519KeyPair()
const certhashResp = Buffer.from('certhash data from respon')
const noiseResp = new Noise({ logger: defaultLogger() }, { staticNoiseKey: staticKeysResponder.privateKey, extensions: { webtransportCerthashes: [certhashResp] } })
const noiseResp = new Noise({
peerId: remotePeer,
logger: defaultLogger()
}, { staticNoiseKey: staticKeysResponder.privateKey, extensions: { webtransportCerthashes: [certhashResp] } })

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

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

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