diff --git a/package.json b/package.json index d91508c..83eed64 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": "^2.0.0", + "@libp2p/interface-record": "^2.0.1", "@libp2p/logger": "^2.0.0", "@libp2p/peer-id": "^1.1.13", "@libp2p/utils": "^3.0.0", @@ -160,7 +160,7 @@ "it-map": "^1.0.6", "it-pipe": "^2.0.3", "multiformats": "^9.6.3", - "protons-runtime": "^2.0.2", + "protons-runtime": "^3.1.0", "uint8-varint": "^1.0.2", "uint8arraylist": "^2.1.0", "uint8arrays": "^3.0.0", @@ -171,7 +171,7 @@ "@libp2p/peer-id-factory": "^1.0.0", "@types/varint": "^6.0.0", "aegir": "^37.3.0", - "protons": "^4.0.1", + "protons": "^5.1.0", "sinon": "^14.0.0" } } diff --git a/src/envelope/envelope.ts b/src/envelope/envelope.ts index 6ee748e..2e56828 100644 --- a/src/envelope/envelope.ts +++ b/src/envelope/envelope.ts @@ -1,9 +1,9 @@ /* eslint-disable import/export */ /* eslint-disable @typescript-eslint/no-namespace */ -import { encodeMessage, decodeMessage, message, bytes } from 'protons-runtime' -import type { Codec } from 'protons-runtime' +import { encodeMessage, decodeMessage, message } from 'protons-runtime' import type { Uint8ArrayList } from 'uint8arraylist' +import type { Codec } from 'protons-runtime' export interface Envelope { publicKey: Uint8Array @@ -13,16 +13,102 @@ export interface Envelope { } export namespace Envelope { + let _codec: Codec + export const codec = (): Codec => { - return message({ - 1: { name: 'publicKey', codec: bytes }, - 2: { name: 'payloadType', codec: bytes }, - 3: { name: 'payload', codec: bytes }, - 5: { name: 'signature', codec: bytes } - }) + if (_codec == null) { + _codec = message((obj, writer, opts = {}) => { + if (opts.lengthDelimited !== false) { + writer.fork() + } + + if (obj.publicKey != null) { + writer.uint32(10) + writer.bytes(obj.publicKey) + } else { + throw new Error('Protocol error: required field "publicKey" was not found in object') + } + + if (obj.payloadType != null) { + writer.uint32(18) + writer.bytes(obj.payloadType) + } else { + throw new Error('Protocol error: required field "payloadType" was not found in object') + } + + if (obj.payload != null) { + writer.uint32(26) + writer.bytes(obj.payload) + } else { + throw new Error('Protocol error: required field "payload" was not found in object') + } + + if (obj.signature != null) { + writer.uint32(42) + writer.bytes(obj.signature) + } else { + throw new Error('Protocol error: required field "signature" was not found in object') + } + + if (opts.lengthDelimited !== false) { + writer.ldelim() + } + }, (reader, length) => { + const obj: any = { + publicKey: new Uint8Array(0), + payloadType: new Uint8Array(0), + payload: new Uint8Array(0), + signature: new Uint8Array(0) + } + + const end = length == null ? reader.len : reader.pos + length + + while (reader.pos < end) { + const tag = reader.uint32() + + switch (tag >>> 3) { + case 1: + obj.publicKey = reader.bytes() + break + case 2: + obj.payloadType = reader.bytes() + break + case 3: + obj.payload = reader.bytes() + break + case 5: + obj.signature = reader.bytes() + break + default: + reader.skipType(tag & 7) + break + } + } + + if (obj.publicKey == null) { + throw new Error('Protocol error: value for required field "publicKey" was not found in protobuf') + } + + if (obj.payloadType == null) { + throw new Error('Protocol error: value for required field "payloadType" was not found in protobuf') + } + + if (obj.payload == null) { + throw new Error('Protocol error: value for required field "payload" was not found in protobuf') + } + + if (obj.signature == null) { + throw new Error('Protocol error: value for required field "signature" was not found in protobuf') + } + + return obj + }) + } + + return _codec } - export const encode = (obj: Envelope): Uint8ArrayList => { + export const encode = (obj: Envelope): Uint8Array => { return encodeMessage(obj, Envelope.codec()) } diff --git a/src/envelope/index.ts b/src/envelope/index.ts index e2d54b4..fc10659 100644 --- a/src/envelope/index.ts +++ b/src/envelope/index.ts @@ -1,5 +1,6 @@ import errCode from 'err-code' import { fromString as uint8arraysFromString } from 'uint8arrays/from-string' +import { equals as uint8ArrayEquals } from 'uint8arrays/equals' import { unmarshalPrivateKey, unmarshalPublicKey } from '@libp2p/crypto/keys' import { codes } from '../errors.js' import { Envelope as Protobuf } from './envelope.js' @@ -12,7 +13,7 @@ import { unsigned } from 'uint8-varint' export interface EnvelopeInit { peerId: PeerId payloadType: Uint8Array - payload: Uint8Array | Uint8ArrayList + payload: Uint8Array signature: Uint8Array } @@ -73,9 +74,9 @@ export class RecordEnvelope implements Envelope { public peerId: PeerId public payloadType: Uint8Array - public payload: Uint8Array | Uint8ArrayList + public payload: Uint8Array public signature: Uint8Array - public marshaled?: Uint8ArrayList + public marshaled?: Uint8Array /** * The Envelope is responsible for keeping an arbitrary signed record @@ -93,7 +94,7 @@ export class RecordEnvelope implements Envelope { /** * Marshal the envelope content */ - marshal (): Uint8ArrayList { + marshal (): Uint8Array { if (this.peerId.publicKey == null) { throw new Error('Missing public key') } @@ -114,7 +115,7 @@ export class RecordEnvelope implements Envelope { * Verifies if the other Envelope is identical to this one */ equals (other: Envelope) { - return this.marshal().equals(other.marshal()) + return uint8ArrayEquals(this.marshal(), other.marshal()) } /** diff --git a/src/peer-record/index.ts b/src/peer-record/index.ts index d2297f0..2d565f9 100644 --- a/src/peer-record/index.ts +++ b/src/peer-record/index.ts @@ -48,7 +48,7 @@ export class PeerRecord { public seqNumber: bigint public domain = PeerRecord.DOMAIN public codec = PeerRecord.CODEC - private marshaled?: Uint8ArrayList + private marshaled?: Uint8Array 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 f7eebdb..21d4554 100644 --- a/src/peer-record/peer-record.ts +++ b/src/peer-record/peer-record.ts @@ -1,9 +1,9 @@ /* eslint-disable import/export */ /* eslint-disable @typescript-eslint/no-namespace */ -import { encodeMessage, decodeMessage, message, bytes, uint64 } from 'protons-runtime' -import type { Codec } from 'protons-runtime' +import { encodeMessage, decodeMessage, message } from 'protons-runtime' import type { Uint8ArrayList } from 'uint8arraylist' +import type { Codec } from 'protons-runtime' export interface PeerRecord { peerId: Uint8Array @@ -17,13 +17,57 @@ export namespace PeerRecord { } export namespace AddressInfo { + let _codec: Codec + export const codec = (): Codec => { - return message({ - 1: { name: 'multiaddr', codec: bytes } - }) + if (_codec == null) { + _codec = message((obj, writer, opts = {}) => { + if (opts.lengthDelimited !== false) { + writer.fork() + } + + if (obj.multiaddr != null) { + writer.uint32(10) + writer.bytes(obj.multiaddr) + } else { + throw new Error('Protocol error: required field "multiaddr" was not found in object') + } + + if (opts.lengthDelimited !== false) { + writer.ldelim() + } + }, (reader, length) => { + const obj: any = { + multiaddr: new Uint8Array(0) + } + + const end = length == null ? reader.len : reader.pos + length + + while (reader.pos < end) { + const tag = reader.uint32() + + switch (tag >>> 3) { + case 1: + obj.multiaddr = reader.bytes() + break + default: + reader.skipType(tag & 7) + break + } + } + + if (obj.multiaddr == null) { + throw new Error('Protocol error: value for required field "multiaddr" was not found in protobuf') + } + + return obj + }) + } + + return _codec } - export const encode = (obj: AddressInfo): Uint8ArrayList => { + export const encode = (obj: AddressInfo): Uint8Array => { return encodeMessage(obj, AddressInfo.codec()) } @@ -32,15 +76,85 @@ export namespace PeerRecord { } } + let _codec: Codec + export const codec = (): Codec => { - return message({ - 1: { name: 'peerId', codec: bytes }, - 2: { name: 'seq', codec: uint64 }, - 3: { name: 'addresses', codec: PeerRecord.AddressInfo.codec(), repeats: true } - }) + if (_codec == null) { + _codec = message((obj, writer, opts = {}) => { + if (opts.lengthDelimited !== false) { + writer.fork() + } + + if (obj.peerId != null) { + writer.uint32(10) + writer.bytes(obj.peerId) + } else { + throw new Error('Protocol error: required field "peerId" was not found in object') + } + + if (obj.seq != null) { + writer.uint32(16) + writer.uint64(obj.seq) + } else { + throw new Error('Protocol error: required field "seq" was not found in object') + } + + if (obj.addresses != null) { + for (const value of obj.addresses) { + writer.uint32(26) + PeerRecord.AddressInfo.codec().encode(value, writer) + } + } else { + throw new Error('Protocol error: required field "addresses" was not found in object') + } + + if (opts.lengthDelimited !== false) { + writer.ldelim() + } + }, (reader, length) => { + const obj: any = { + peerId: new Uint8Array(0), + seq: 0n, + addresses: [] + } + + const end = length == null ? reader.len : reader.pos + length + + while (reader.pos < end) { + const tag = reader.uint32() + + switch (tag >>> 3) { + case 1: + obj.peerId = reader.bytes() + break + case 2: + obj.seq = reader.uint64() + break + case 3: + obj.addresses.push(PeerRecord.AddressInfo.codec().decode(reader, reader.uint32())) + break + default: + reader.skipType(tag & 7) + break + } + } + + if (obj.peerId == null) { + throw new Error('Protocol error: value for required field "peerId" was not found in protobuf') + } + + if (obj.seq == null) { + throw new Error('Protocol error: value for required field "seq" was not found in protobuf') + } + + return obj + }) + } + + return _codec } - export const encode = (obj: PeerRecord): Uint8ArrayList => { + export const encode = (obj: PeerRecord): Uint8Array => { return encodeMessage(obj, PeerRecord.codec()) } diff --git a/test/envelope.spec.ts b/test/envelope.spec.ts index 6dee15e..fc243e1 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 new Uint8ArrayList(uint8arrayFromString(this.data)) + return uint8arrayFromString(this.data) } equals (other: Record) { - return this.marshal().equals(other.marshal()) + return uint8ArrayEquals(this.marshal(), other.marshal()) } }