Skip to content

Commit 59b27ab

Browse files
committed
fix: remove node buffers
Replaces node buffers with browser-friendly `Uint8Array`s. Previously we didn't want to do this because there's no equivalent to node's `Buffer.allocUnsafe` which doesn't set all entries of allocated buffers to 0. The thing is we only actually use that in one place so to address this I've isolated the use of `Buffer.allocUnsafe` to the `alloc-unsafe.ts` file and used the `browser` field in package.json to override it for browser use. Running the benchmark suite in this module shows the performance is comparable to or even slightly better than master (I think due to not having to convert `Uint8Array`s to `Buffer`s any more): Before: ```console $ node benchmark.js handshake x 59.95 ops/sec ±11.20% (75 runs sampled) handshake x 54.68 ops/sec ±10.81% (68 runs sampled) handshake x 50.42 ops/sec ±11.55% (65 runs sampled) handshake x 53.41 ops/sec ±11.84% (68 runs sampled) handshake x 50.25 ops/sec ±11.80% (66 runs sampled) ``` After: ```console $ node ./benchmark.js Initializing handshake benchmark Init complete, running benchmark handshake x 61.48 ops/sec ±11.71% (76 runs sampled) handshake x 59.43 ops/sec ±11.13% (73 runs sampled) handshake x 56.09 ops/sec ±12.02% (71 runs sampled) handshake x 60.05 ops/sec ±11.69% (74 runs sampled) handshake x 59.66 ops/sec ±10.59% (74 runs sampled) ```
1 parent 61251ed commit 59b27ab

26 files changed

+1353
-635
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@chainsafe/libp2p-noise",
3-
"version": "4.1.1",
3+
"version": "5.0.0",
44
"main": "dist/src/index.js",
55
"types": "dist/src/index.d.ts",
66
"files": [
@@ -33,6 +33,7 @@
3333
"proto:gen": "pbjs -t static-module -r libp2p-noise -o ./src/proto/payload.js ./src/proto/payload.proto && pbts -o ./src/proto/payload.d.ts ./src/proto/payload.js && yarn run lint --fix"
3434
},
3535
"browser": {
36+
"./dist/src/alloc-unsafe.js": "./dist/src/alloc-unsafe-browser.js",
3637
"util": false
3738
},
3839
"devDependencies": {
@@ -52,11 +53,12 @@
5253
"@stablelib/hkdf": "^1.0.1",
5354
"@stablelib/sha256": "^1.0.1",
5455
"@stablelib/x25519": "^1.0.1",
56+
"bl": "^5.0.0",
5557
"debug": "^4.3.1",
5658
"it-buffer": "^0.1.3",
5759
"it-length-prefixed": "^5.0.3",
5860
"it-pair": "^1.0.0",
59-
"it-pb-rpc": "^0.1.11",
61+
"it-pb-rpc": "^0.1.12",
6062
"it-pipe": "^1.1.0",
6163
"peer-id": "^0.16.0",
6264
"protobufjs": "^6.11.2",

src/@types/basic.d.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { Buffer } from 'buffer'
2-
3-
export type bytes = Buffer
4-
export type bytes32 = Buffer
5-
export type bytes16 = Buffer
1+
export type bytes = Uint8Array
2+
export type bytes32 = Uint8Array
3+
export type bytes16 = Uint8Array
64

75
export type uint32 = number
86
export type uint64 = number

src/@types/handshake-interface.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import PeerId from 'peer-id'
55
export interface IHandshake {
66
session: NoiseSession
77
remotePeer: PeerId
8-
remoteEarlyData: Buffer
8+
remoteEarlyData: bytes
99
encrypt: (plaintext: bytes, session: NoiseSession) => bytes
1010
decrypt: (ciphertext: bytes, session: NoiseSession) => {plaintext: bytes, valid: boolean}
1111
}

src/@types/it-length-prefixed/index.d.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
declare module 'it-length-prefixed' {
2-
import BufferList from 'bl'
3-
import { Buffer } from 'buffer'
2+
import BufferList from 'bl/BufferList'
43

54
interface LengthDecoderFunction {
6-
(data: Buffer | BufferList): number
5+
(data: Uint8Array | BufferList): number
76
bytes: number
87
}
98

109
interface LengthEncoderFunction {
11-
(value: number, target: Buffer, offset: number): number|Buffer
10+
(value: number, target: Uint8Array, offset: number): number|Uint8Array
1211
bytes: number
1312
}
1413

1514
interface Encoder {
16-
(options?: Partial<{lengthEncoder: LengthEncoderFunction}>): AsyncGenerator<BufferList, Buffer>
17-
single: (chunk: Buffer, options?: Partial<{lengthEncoder: LengthEncoderFunction}>) => BufferList
15+
(options?: Partial<{lengthEncoder: LengthEncoderFunction}>): AsyncGenerator<BufferList, Uint8Array>
16+
single: (chunk: Uint8Array, options?: Partial<{lengthEncoder: LengthEncoderFunction}>) => BufferList
1817
MIN_POOL_SIZE: number
1918
DEFAULT_POOL_SIZE: number
2019
}

src/@types/libp2p.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ export interface INoiseConnection {
1414

1515
export interface SecureOutbound {
1616
conn: any
17-
remoteEarlyData: Buffer
17+
remoteEarlyData: Uint8Array
1818
remotePeer: PeerId
1919
}

src/alloc-unsafe-browser.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
export default (size: number) => {
3+
return new Uint8Array(size)
4+
}

src/alloc-unsafe.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
export default (size: number) => {
3+
return Buffer.allocUnsafe(size)
4+
}

src/crypto.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Buffer } from 'buffer'
21
import { IHandshake } from './@types/handshake-interface'
32
import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from './constants'
43

@@ -10,15 +9,13 @@ interface IReturnEncryptionWrapper {
109
export function encryptStream (handshake: IHandshake): IReturnEncryptionWrapper {
1110
return async function * (source) {
1211
for await (const chunk of source) {
13-
const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length)
14-
15-
for (let i = 0; i < chunkBuffer.length; i += NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG) {
12+
for (let i = 0; i < chunk.length; i += NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG) {
1613
let end = i + NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG
17-
if (end > chunkBuffer.length) {
18-
end = chunkBuffer.length
14+
if (end > chunk.length) {
15+
end = chunk.length
1916
}
2017

21-
const data = handshake.encrypt(chunkBuffer.slice(i, end), handshake.session)
18+
const data = handshake.encrypt(chunk.slice(i, end), handshake.session)
2219
yield data
2320
}
2421
}
@@ -29,16 +26,13 @@ export function encryptStream (handshake: IHandshake): IReturnEncryptionWrapper
2926
export function decryptStream (handshake: IHandshake): IReturnEncryptionWrapper {
3027
return async function * (source) {
3128
for await (const chunk of source) {
32-
const chunkBuffer = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.length)
33-
34-
for (let i = 0; i < chunkBuffer.length; i += NOISE_MSG_MAX_LENGTH_BYTES) {
29+
for (let i = 0; i < chunk.length; i += NOISE_MSG_MAX_LENGTH_BYTES) {
3530
let end = i + NOISE_MSG_MAX_LENGTH_BYTES
36-
if (end > chunkBuffer.length) {
37-
end = chunkBuffer.length
31+
if (end > chunk.length) {
32+
end = chunk.length
3833
}
3934

40-
const chunk = chunkBuffer.slice(i, end)
41-
const { plaintext: decrypted, valid } = await handshake.decrypt(chunk, handshake.session)
35+
const { plaintext: decrypted, valid } = await handshake.decrypt(chunk.slice(i, end), handshake.session)
4236
if (!valid) {
4337
throw new Error('Failed to validate decrypted chunk')
4438
}

src/encoder.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,39 @@
1-
import { Buffer } from 'buffer'
21
import { bytes } from './@types/basic'
32
import { MessageBuffer } from './@types/handshake'
4-
import BufferList from 'bl'
3+
import BufferList from 'bl/BufferList'
4+
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
5+
import allocUnsafe from './alloc-unsafe'
56

6-
export const uint16BEEncode = (value: number, target: Buffer, offset: number): Buffer => {
7-
target = target || Buffer.allocUnsafe(2)
8-
target.writeUInt16BE(value, offset)
7+
export const uint16BEEncode = (value: number, target: Uint8Array, offset: number): Uint8Array => {
8+
target = target || allocUnsafe(2)
9+
new DataView(target.buffer, target.byteOffset, target.byteLength).setUint16(offset, value, false)
910
return target
1011
}
1112
uint16BEEncode.bytes = 2
1213

13-
export const uint16BEDecode = (data: Buffer | BufferList): number => {
14+
export const uint16BEDecode = (data: Uint8Array | BufferList): number => {
1415
if (data.length < 2) throw RangeError('Could not decode int16BE')
15-
return data.readUInt16BE(0)
16+
17+
if (data instanceof BufferList) {
18+
return data.readUInt16BE(0)
19+
}
20+
21+
return new DataView(data.buffer, data.byteOffset, data.byteLength).getUint16(0, false)
1622
}
1723
uint16BEDecode.bytes = 2
1824

1925
// Note: IK and XX encoder usage is opposite (XX uses in stages encode0 where IK uses encode1)
2026

2127
export function encode0 (message: MessageBuffer): bytes {
22-
return Buffer.concat([message.ne, message.ciphertext])
28+
return Buffer.from(uint8ArrayConcat([message.ne, message.ciphertext], message.ne.length + message.ciphertext.length))
2329
}
2430

2531
export function encode1 (message: MessageBuffer): bytes {
26-
return Buffer.concat([message.ne, message.ns, message.ciphertext])
32+
return Buffer.from(uint8ArrayConcat([message.ne, message.ns, message.ciphertext], message.ne.length + message.ns.length + message.ciphertext.length))
2733
}
2834

2935
export function encode2 (message: MessageBuffer): bytes {
30-
return Buffer.concat([message.ns, message.ciphertext])
36+
return Buffer.from(uint8ArrayConcat([message.ns, message.ciphertext], message.ns.length + message.ciphertext.length))
3137
}
3238

3339
export function decode0 (input: bytes): MessageBuffer {

src/errors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import BufferList from 'bl'
22

33
export class FailedIKError extends Error {
4-
public initialMsg: string|BufferList|Buffer
4+
public initialMsg: string|BufferList|Uint8Array
55

6-
constructor (initialMsg: string|BufferList|Buffer, message?: string) {
6+
constructor (initialMsg: string|BufferList|Uint8Array, message?: string) {
77
super(message)
88

99
this.initialMsg = initialMsg

0 commit comments

Comments
 (0)