From 878f0ca0945d535c578c0285b33532bfefd80fbb Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Wed, 3 Aug 2022 08:43:10 +0100 Subject: [PATCH] deps!: update interface deps to use byte lists (#16) To support no-copy operations, update interfaces to use `Uint8ArrayList`s instead of `Uint8Array`s. --- package.json | 10 ++++--- src/envelope/envelope.ts | 5 ++-- src/envelope/index.ts | 53 ++++++++++++++++------------------ src/peer-record/index.ts | 5 ++-- src/peer-record/peer-record.ts | 9 +++--- test/envelope.spec.ts | 8 ++--- 6 files changed, 46 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index 1f34f0d..baf4696 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "dependencies": { "@libp2p/crypto": "^1.0.0", "@libp2p/interface-peer-id": "^1.0.2", - "@libp2p/interface-record": "^1.0.1", + "@libp2p/interface-record": "^2.0.0", "@libp2p/logger": "^2.0.0", "@libp2p/peer-id": "^1.1.13", "@libp2p/utils": "^3.0.0", @@ -160,16 +160,18 @@ "it-map": "^1.0.6", "it-pipe": "^2.0.3", "multiformats": "^9.6.3", - "protons-runtime": "^1.0.4", + "protons-runtime": "^2.0.2", + "uint8-varint": "^1.0.2", + "uint8arraylist": "^2.1.0", "uint8arrays": "^3.0.0", "varint": "^6.0.0" }, "devDependencies": { - "@libp2p/interface-record-compliance-tests": "^1.0.0", + "@libp2p/interface-record-compliance-tests": "^2.0.0", "@libp2p/peer-id-factory": "^1.0.0", "@types/varint": "^6.0.0", "aegir": "^37.3.0", - "protons": "^3.0.4", + "protons": "^4.0.1", "sinon": "^14.0.0" } } diff --git a/src/envelope/envelope.ts b/src/envelope/envelope.ts index 43ef968..6ee748e 100644 --- a/src/envelope/envelope.ts +++ b/src/envelope/envelope.ts @@ -3,6 +3,7 @@ import { encodeMessage, decodeMessage, message, bytes } from 'protons-runtime' import type { Codec } from 'protons-runtime' +import type { Uint8ArrayList } from 'uint8arraylist' export interface Envelope { publicKey: Uint8Array @@ -21,11 +22,11 @@ export namespace Envelope { }) } - export const encode = (obj: Envelope): Uint8Array => { + export const encode = (obj: Envelope): Uint8ArrayList => { return encodeMessage(obj, Envelope.codec()) } - export const decode = (buf: Uint8Array): Envelope => { + export const decode = (buf: Uint8Array | Uint8ArrayList): Envelope => { return decodeMessage(buf, Envelope.codec()) } } diff --git a/src/envelope/index.ts b/src/envelope/index.ts index 77b6031..e2d54b4 100644 --- a/src/envelope/index.ts +++ b/src/envelope/index.ts @@ -1,19 +1,18 @@ import errCode from 'err-code' -import { concat as uint8arraysConcat } from 'uint8arrays/concat' import { fromString as uint8arraysFromString } from 'uint8arrays/from-string' import { unmarshalPrivateKey, unmarshalPublicKey } from '@libp2p/crypto/keys' -import varint from 'varint' -import { equals as uint8arraysEquals } from 'uint8arrays/equals' import { codes } from '../errors.js' import { Envelope as Protobuf } from './envelope.js' import { peerIdFromKeys } from '@libp2p/peer-id' import type { PeerId } from '@libp2p/interface-peer-id' import type { Record, Envelope } from '@libp2p/interface-record' +import { Uint8ArrayList } from 'uint8arraylist' +import { unsigned } from 'uint8-varint' export interface EnvelopeInit { peerId: PeerId payloadType: Uint8Array - payload: Uint8Array + payload: Uint8Array | Uint8ArrayList signature: Uint8Array } @@ -21,7 +20,7 @@ export class RecordEnvelope implements Envelope { /** * Unmarshal a serialized Envelope protobuf message */ - static createFromProtobuf = async (data: Uint8Array) => { + static createFromProtobuf = async (data: Uint8Array | Uint8ArrayList) => { const envelopeData = Protobuf.decode(data) const peerId = await peerIdFromKeys(envelopeData.publicKey) @@ -38,18 +37,16 @@ export class RecordEnvelope implements Envelope { * and signs it with the given peerId's private key */ static seal = async (record: Record, peerId: PeerId) => { - const domain = record.domain - const payloadType = record.codec - const payload = record.marshal() - - const signData = formatSignaturePayload(domain, payloadType, payload) - if (peerId.privateKey == null) { throw new Error('Missing private key') } + const domain = record.domain + const payloadType = record.codec + const payload = record.marshal() + const signData = formatSignaturePayload(domain, payloadType, payload) const key = await unmarshalPrivateKey(peerId.privateKey) - const signature = await key.sign(signData) + const signature = await key.sign(signData.subarray()) return new RecordEnvelope({ peerId, @@ -63,7 +60,7 @@ export class RecordEnvelope implements Envelope { * Open and certify a given marshalled envelope. * Data is unmarshalled and the signature validated for the given domain. */ - static openAndCertify = async (data: Uint8Array, domain: string) => { + static openAndCertify = async (data: Uint8Array | Uint8ArrayList, domain: string) => { const envelope = await RecordEnvelope.createFromProtobuf(data) const valid = await envelope.validate(domain) @@ -76,9 +73,9 @@ export class RecordEnvelope implements Envelope { public peerId: PeerId public payloadType: Uint8Array - public payload: Uint8Array + public payload: Uint8Array | Uint8ArrayList public signature: Uint8Array - public marshaled?: Uint8Array + public marshaled?: Uint8ArrayList /** * The Envelope is responsible for keeping an arbitrary signed record @@ -96,7 +93,7 @@ export class RecordEnvelope implements Envelope { /** * Marshal the envelope content */ - marshal () { + marshal (): Uint8ArrayList { if (this.peerId.publicKey == null) { throw new Error('Missing public key') } @@ -105,7 +102,7 @@ export class RecordEnvelope implements Envelope { this.marshaled = Protobuf.encode({ publicKey: this.peerId.publicKey, payloadType: this.payloadType, - payload: this.payload, + payload: this.payload.subarray(), signature: this.signature }) } @@ -117,7 +114,7 @@ export class RecordEnvelope implements Envelope { * Verifies if the other Envelope is identical to this one */ equals (other: Envelope) { - return uint8arraysEquals(this.marshal(), other.marshal()) + return this.marshal().equals(other.marshal()) } /** @@ -132,14 +129,14 @@ export class RecordEnvelope implements Envelope { const key = unmarshalPublicKey(this.peerId.publicKey) - return await key.verify(signData, this.signature) + return await key.verify(signData.subarray(), this.signature) } } /** * Helper function that prepares a Uint8Array to sign or verify a signature */ -const formatSignaturePayload = (domain: string, payloadType: Uint8Array, payload: Uint8Array) => { +const formatSignaturePayload = (domain: string, payloadType: Uint8Array, payload: Uint8Array | Uint8ArrayList): Uint8ArrayList => { // When signing, a peer will prepare a Uint8Array by concatenating the following: // - The length of the domain separation string string in bytes // - The domain separation string, encoded as UTF-8 @@ -149,16 +146,16 @@ const formatSignaturePayload = (domain: string, payloadType: Uint8Array, payload // - The value of the payload field const domainUint8Array = uint8arraysFromString(domain) - const domainLength = varint.encode(domainUint8Array.byteLength) - const payloadTypeLength = varint.encode(payloadType.length) - const payloadLength = varint.encode(payload.length) + const domainLength = unsigned.encode(domainUint8Array.byteLength) + const payloadTypeLength = unsigned.encode(payloadType.length) + const payloadLength = unsigned.encode(payload.length) - return uint8arraysConcat([ - new Uint8Array(domainLength), + return new Uint8ArrayList( + domainLength, domainUint8Array, - new Uint8Array(payloadTypeLength), + payloadTypeLength, payloadType, - new Uint8Array(payloadLength), + payloadLength, payload - ]) + ) } diff --git a/src/peer-record/index.ts b/src/peer-record/index.ts index 41b079f..d2297f0 100644 --- a/src/peer-record/index.ts +++ b/src/peer-record/index.ts @@ -7,6 +7,7 @@ import { ENVELOPE_DOMAIN_PEER_RECORD, ENVELOPE_PAYLOAD_TYPE_PEER_RECORD } from './consts.js' +import type { Uint8ArrayList } from 'uint8arraylist' export interface PeerRecordInit { peerId: PeerId @@ -30,7 +31,7 @@ export class PeerRecord { /** * Unmarshal Peer Record Protobuf */ - static createFromProtobuf = (buf: Uint8Array): PeerRecord => { + static createFromProtobuf = (buf: Uint8Array | Uint8ArrayList): PeerRecord => { const peerRecord = Protobuf.decode(buf) const peerId = peerIdFromBytes(peerRecord.peerId) const multiaddrs = (peerRecord.addresses ?? []).map((a) => new Multiaddr(a.multiaddr)) @@ -47,7 +48,7 @@ export class PeerRecord { public seqNumber: bigint public domain = PeerRecord.DOMAIN public codec = PeerRecord.CODEC - private marshaled?: Uint8Array + private marshaled?: Uint8ArrayList constructor (init: PeerRecordInit) { const { peerId, multiaddrs, seqNumber } = init diff --git a/src/peer-record/peer-record.ts b/src/peer-record/peer-record.ts index 946ce34..f7eebdb 100644 --- a/src/peer-record/peer-record.ts +++ b/src/peer-record/peer-record.ts @@ -3,6 +3,7 @@ import { encodeMessage, decodeMessage, message, bytes, uint64 } from 'protons-runtime' import type { Codec } from 'protons-runtime' +import type { Uint8ArrayList } from 'uint8arraylist' export interface PeerRecord { peerId: Uint8Array @@ -22,11 +23,11 @@ export namespace PeerRecord { }) } - export const encode = (obj: AddressInfo): Uint8Array => { + export const encode = (obj: AddressInfo): Uint8ArrayList => { return encodeMessage(obj, AddressInfo.codec()) } - export const decode = (buf: Uint8Array): AddressInfo => { + export const decode = (buf: Uint8Array | Uint8ArrayList): AddressInfo => { return decodeMessage(buf, AddressInfo.codec()) } } @@ -39,11 +40,11 @@ export namespace PeerRecord { }) } - export const encode = (obj: PeerRecord): Uint8Array => { + export const encode = (obj: PeerRecord): Uint8ArrayList => { return encodeMessage(obj, PeerRecord.codec()) } - export const decode = (buf: Uint8Array): PeerRecord => { + export const decode = (buf: Uint8Array | Uint8ArrayList): PeerRecord => { return decodeMessage(buf, PeerRecord.codec()) } } diff --git a/test/envelope.spec.ts b/test/envelope.spec.ts index f876f8d..6dee15e 100644 --- a/test/envelope.spec.ts +++ b/test/envelope.spec.ts @@ -1,11 +1,11 @@ import { expect } from 'aegir/chai' import { fromString as uint8arrayFromString } from 'uint8arrays/from-string' -import { equals as uint8arrayEquals } from 'uint8arrays/equals' import { RecordEnvelope } from '../src/envelope/index.js' import { codes as ErrorCodes } from '../src/errors.js' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import type { Record } from '@libp2p/interface-record' import type { PeerId } from '@libp2p/interface-peer-id' +import { Uint8ArrayList } from 'uint8arraylist' const domain = 'libp2p-testing' const codec = uint8arrayFromString('/libp2p/testdata') @@ -22,11 +22,11 @@ class TestRecord implements Record { } marshal () { - return uint8arrayFromString(this.data) + return new Uint8ArrayList(uint8arrayFromString(this.data)) } equals (other: Record) { - return uint8arrayEquals(this.marshal(), other.marshal()) + return this.marshal().equals(other.marshal()) } } @@ -54,7 +54,7 @@ describe('Envelope', () => { expect(envelope).to.exist() expect(envelope.peerId.equals(peerId)).to.eql(true) expect(envelope.payloadType).to.equalBytes(payloadType) - expect(envelope.payload).to.equalBytes(payload) + expect(envelope.payload.subarray()).to.equalBytes(payload.subarray()) expect(envelope.signature).to.equalBytes(signature) })