diff --git a/.aegir.js b/.aegir.js index 06748160..7af58ff9 100644 --- a/.aegir.js +++ b/.aegir.js @@ -1,4 +1,4 @@ module.exports = { - bundlesize: { maxSize: '170kB' } + bundlesize: { maxSize: '190kB' } } diff --git a/package.json b/package.json index b8142fa5..3cd4efb6 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,11 @@ "main": "src/index.js", "scripts": { "lint": "aegir lint", + "build": "npm run build:proto && npm run build:proto-types && aegir build", + "build:proto": "pbjs -t static-module -w commonjs --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/message/dht.js ./src/message/dht.proto", + "build:proto-types": "pbts -o src/message/dht.d.ts src/message/dht.js", "test": "aegir test -t node", "test:node": "aegir test -t node", - "prepare": "aegir build --no-bundle", "docs": "aegir docs", "release": "aegir release --docs -t node", "release-minor": "aegir release --type minor --docs -t node", @@ -17,9 +19,6 @@ "coverage-publish": "aegir-coverage publish", "sim": "node test/simulation/index.js" }, - "pre-push": [ - "lint" - ], "files": [ "src", "dist" @@ -36,8 +35,7 @@ "url": "https://github.com/libp2p/js-libp2p-kad-dht/issues" }, "engines": { - "node": ">=12.0.0", - "npm": ">=6.0.0" + "node": ">=14.0.0" }, "eslintConfig": { "extends": "ipfs" @@ -53,13 +51,13 @@ "heap": "~0.2.6", "interface-datastore": "^3.0.4", "it-first": "^1.0.4", - "it-length-prefixed": "^3.1.0", + "it-length-prefixed": "^5.0.2", "it-pipe": "^1.1.0", "k-bucket": "^5.0.0", "libp2p-crypto": "^0.19.0", - "libp2p-interfaces": "^0.8.2", + "libp2p-interfaces": "^0.10.0", "libp2p-record": "^0.10.0", - "multiaddr": "^8.1.2", + "multiaddr": "^9.0.0", "multihashing-async": "^2.1.0", "p-filter": "^2.1.0", "p-map": "^4.0.0", @@ -68,9 +66,9 @@ "p-times": "^3.0.0", "peer-id": "^0.14.2", "promise-to-callback": "^1.0.0", - "protons": "^2.0.0", + "protobufjs": "^6.10.2", "streaming-iterables": "^5.0.4", - "uint8arrays": "^2.0.5", + "uint8arrays": "^2.1.4", "varint": "^6.0.0", "xor-distance": "^2.0.0" }, @@ -78,13 +76,13 @@ "@types/debug": "^4.1.5", "aegir": "^30.3.0", "async-iterator-all": "^1.0.0", - "chai": "^4.2.0", + "chai": "^4.3.0", "chai-checkmark": "^1.0.1", "datastore-level": "^4.0.0", "delay": "^5.0.0", "dirty-chai": "^2.0.1", "it-pair": "^1.0.0", - "libp2p": "^0.30.7", + "libp2p": "libp2p/js-libp2p#chore/config-ts", "lodash": "^4.17.11", "lodash.random": "^3.2.0", "lodash.range": "^3.2.0", @@ -92,7 +90,7 @@ "p-each-series": "^2.1.0", "p-map-series": "^2.1.0", "p-retry": "^4.2.0", - "sinon": "^9.0.0" + "sinon": "^10.0.0" }, "contributors": [ "Vasco Santos ", diff --git a/src/content-routing/index.js b/src/content-routing/index.js index 678a1609..117901a4 100644 --- a/src/content-routing/index.js +++ b/src/content-routing/index.js @@ -12,7 +12,7 @@ const utils = require('../utils') /** * @typedef {import('cids')} CID * @typedef {import('peer-id')} PeerId - * @typedef {import('multiaddr')} Multiaddr + * @typedef {import('multiaddr').Multiaddr} Multiaddr */ /** diff --git a/src/index.js b/src/index.js index 3c2d5edd..ab6057b3 100644 --- a/src/index.js +++ b/src/index.js @@ -30,7 +30,7 @@ const Record = libp2pRecord.Record * @typedef {import('libp2p/src/dialer')} Dialer * @typedef {import('libp2p/src/registrar')} Registrar * @typedef {import('cids')} CID - * @typedef {import('multiaddr')} Multiaddr + * @typedef {import('multiaddr').Multiaddr} Multiaddr * @typedef {object} PeerData * @property {PeerId} id * @property {Multiaddr[]} multiaddrs diff --git a/src/message/dht.d.ts b/src/message/dht.d.ts new file mode 100644 index 00000000..98b8f883 --- /dev/null +++ b/src/message/dht.d.ts @@ -0,0 +1,264 @@ +import * as $protobuf from "protobufjs"; +/** Properties of a Record. */ +export interface IRecord { + + /** Record key */ + key?: (Uint8Array|null); + + /** Record value */ + value?: (Uint8Array|null); + + /** Record author */ + author?: (Uint8Array|null); + + /** Record signature */ + signature?: (Uint8Array|null); + + /** Record timeReceived */ + timeReceived?: (string|null); +} + +/** Represents a Record. */ +export class Record implements IRecord { + + /** + * Constructs a new Record. + * @param [p] Properties to set + */ + constructor(p?: IRecord); + + /** Record key. */ + public key: Uint8Array; + + /** Record value. */ + public value: Uint8Array; + + /** Record author. */ + public author: Uint8Array; + + /** Record signature. */ + public signature: Uint8Array; + + /** Record timeReceived. */ + public timeReceived: string; + + /** + * Encodes the specified Record message. Does not implicitly {@link Record.verify|verify} messages. + * @param m Record message or plain object to encode + * @param [w] Writer to encode to + * @returns Writer + */ + public static encode(m: IRecord, w?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Record message from the specified reader or buffer. + * @param r Reader or buffer to decode from + * @param [l] Message length if known beforehand + * @returns Record + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Record; + + /** + * Creates a Record message from a plain object. Also converts values to their respective internal types. + * @param d Plain object + * @returns Record + */ + public static fromObject(d: { [k: string]: any }): Record; + + /** + * Creates a plain object from a Record message. Also converts values to other types if specified. + * @param m Record + * @param [o] Conversion options + * @returns Plain object + */ + public static toObject(m: Record, o?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Record to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; +} + +/** Properties of a Message. */ +export interface IMessage { + + /** Message type */ + type?: (Message.MessageType|null); + + /** Message clusterLevelRaw */ + clusterLevelRaw?: (number|null); + + /** Message key */ + key?: (Uint8Array|null); + + /** Message record */ + record?: (Uint8Array|null); + + /** Message closerPeers */ + closerPeers?: (Message.IPeer[]|null); + + /** Message providerPeers */ + providerPeers?: (Message.IPeer[]|null); +} + +/** Represents a Message. */ +export class Message implements IMessage { + + /** + * Constructs a new Message. + * @param [p] Properties to set + */ + constructor(p?: IMessage); + + /** Message type. */ + public type: Message.MessageType; + + /** Message clusterLevelRaw. */ + public clusterLevelRaw: number; + + /** Message key. */ + public key: Uint8Array; + + /** Message record. */ + public record: Uint8Array; + + /** Message closerPeers. */ + public closerPeers: Message.IPeer[]; + + /** Message providerPeers. */ + public providerPeers: Message.IPeer[]; + + /** + * Encodes the specified Message message. Does not implicitly {@link Message.verify|verify} messages. + * @param m Message message or plain object to encode + * @param [w] Writer to encode to + * @returns Writer + */ + public static encode(m: IMessage, w?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Message message from the specified reader or buffer. + * @param r Reader or buffer to decode from + * @param [l] Message length if known beforehand + * @returns Message + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Message; + + /** + * Creates a Message message from a plain object. Also converts values to their respective internal types. + * @param d Plain object + * @returns Message + */ + public static fromObject(d: { [k: string]: any }): Message; + + /** + * Creates a plain object from a Message message. Also converts values to other types if specified. + * @param m Message + * @param [o] Conversion options + * @returns Plain object + */ + public static toObject(m: Message, o?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Message to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; +} + +export namespace Message { + + /** MessageType enum. */ + enum MessageType { + PUT_VALUE = 0, + GET_VALUE = 1, + ADD_PROVIDER = 2, + GET_PROVIDERS = 3, + FIND_NODE = 4, + PING = 5 + } + + /** ConnectionType enum. */ + enum ConnectionType { + NOT_CONNECTED = 0, + CONNECTED = 1, + CAN_CONNECT = 2, + CANNOT_CONNECT = 3 + } + + /** Properties of a Peer. */ + interface IPeer { + + /** Peer id */ + id?: (Uint8Array|null); + + /** Peer addrs */ + addrs?: (Uint8Array[]|null); + + /** Peer connection */ + connection?: (Message.ConnectionType|null); + } + + /** Represents a Peer. */ + class Peer implements IPeer { + + /** + * Constructs a new Peer. + * @param [p] Properties to set + */ + constructor(p?: Message.IPeer); + + /** Peer id. */ + public id: Uint8Array; + + /** Peer addrs. */ + public addrs: Uint8Array[]; + + /** Peer connection. */ + public connection: Message.ConnectionType; + + /** + * Encodes the specified Peer message. Does not implicitly {@link Message.Peer.verify|verify} messages. + * @param m Peer message or plain object to encode + * @param [w] Writer to encode to + * @returns Writer + */ + public static encode(m: Message.IPeer, w?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a Peer message from the specified reader or buffer. + * @param r Reader or buffer to decode from + * @param [l] Message length if known beforehand + * @returns Peer + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(r: ($protobuf.Reader|Uint8Array), l?: number): Message.Peer; + + /** + * Creates a Peer message from a plain object. Also converts values to their respective internal types. + * @param d Plain object + * @returns Peer + */ + public static fromObject(d: { [k: string]: any }): Message.Peer; + + /** + * Creates a plain object from a Peer message. Also converts values to other types if specified. + * @param m Peer + * @param [o] Conversion options + * @returns Plain object + */ + public static toObject(m: Message.Peer, o?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this Peer to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } +} diff --git a/src/message/dht.js b/src/message/dht.js new file mode 100644 index 00000000..7c5a50d5 --- /dev/null +++ b/src/message/dht.js @@ -0,0 +1,830 @@ +/*eslint-disable*/ +"use strict"; + +var $protobuf = require("protobufjs/minimal"); + +// Common aliases +var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; + +// Exported root namespace +var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); + +$root.Record = (function() { + + /** + * Properties of a Record. + * @exports IRecord + * @interface IRecord + * @property {Uint8Array|null} [key] Record key + * @property {Uint8Array|null} [value] Record value + * @property {Uint8Array|null} [author] Record author + * @property {Uint8Array|null} [signature] Record signature + * @property {string|null} [timeReceived] Record timeReceived + */ + + /** + * Constructs a new Record. + * @exports Record + * @classdesc Represents a Record. + * @implements IRecord + * @constructor + * @param {IRecord=} [p] Properties to set + */ + function Record(p) { + if (p) + for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) + if (p[ks[i]] != null) + this[ks[i]] = p[ks[i]]; + } + + /** + * Record key. + * @member {Uint8Array} key + * @memberof Record + * @instance + */ + Record.prototype.key = $util.newBuffer([]); + + /** + * Record value. + * @member {Uint8Array} value + * @memberof Record + * @instance + */ + Record.prototype.value = $util.newBuffer([]); + + /** + * Record author. + * @member {Uint8Array} author + * @memberof Record + * @instance + */ + Record.prototype.author = $util.newBuffer([]); + + /** + * Record signature. + * @member {Uint8Array} signature + * @memberof Record + * @instance + */ + Record.prototype.signature = $util.newBuffer([]); + + /** + * Record timeReceived. + * @member {string} timeReceived + * @memberof Record + * @instance + */ + Record.prototype.timeReceived = ""; + + /** + * Encodes the specified Record message. Does not implicitly {@link Record.verify|verify} messages. + * @function encode + * @memberof Record + * @static + * @param {IRecord} m Record message or plain object to encode + * @param {$protobuf.Writer} [w] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Record.encode = function encode(m, w) { + if (!w) + w = $Writer.create(); + if (m.key != null && Object.hasOwnProperty.call(m, "key")) + w.uint32(10).bytes(m.key); + if (m.value != null && Object.hasOwnProperty.call(m, "value")) + w.uint32(18).bytes(m.value); + if (m.author != null && Object.hasOwnProperty.call(m, "author")) + w.uint32(26).bytes(m.author); + if (m.signature != null && Object.hasOwnProperty.call(m, "signature")) + w.uint32(34).bytes(m.signature); + if (m.timeReceived != null && Object.hasOwnProperty.call(m, "timeReceived")) + w.uint32(42).string(m.timeReceived); + return w; + }; + + /** + * Decodes a Record message from the specified reader or buffer. + * @function decode + * @memberof Record + * @static + * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from + * @param {number} [l] Message length if known beforehand + * @returns {Record} Record + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Record.decode = function decode(r, l) { + if (!(r instanceof $Reader)) + r = $Reader.create(r); + var c = l === undefined ? r.len : r.pos + l, m = new $root.Record(); + while (r.pos < c) { + var t = r.uint32(); + switch (t >>> 3) { + case 1: + m.key = r.bytes(); + break; + case 2: + m.value = r.bytes(); + break; + case 3: + m.author = r.bytes(); + break; + case 4: + m.signature = r.bytes(); + break; + case 5: + m.timeReceived = r.string(); + break; + default: + r.skipType(t & 7); + break; + } + } + return m; + }; + + /** + * Creates a Record message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof Record + * @static + * @param {Object.} d Plain object + * @returns {Record} Record + */ + Record.fromObject = function fromObject(d) { + if (d instanceof $root.Record) + return d; + var m = new $root.Record(); + if (d.key != null) { + if (typeof d.key === "string") + $util.base64.decode(d.key, m.key = $util.newBuffer($util.base64.length(d.key)), 0); + else if (d.key.length) + m.key = d.key; + } + if (d.value != null) { + if (typeof d.value === "string") + $util.base64.decode(d.value, m.value = $util.newBuffer($util.base64.length(d.value)), 0); + else if (d.value.length) + m.value = d.value; + } + if (d.author != null) { + if (typeof d.author === "string") + $util.base64.decode(d.author, m.author = $util.newBuffer($util.base64.length(d.author)), 0); + else if (d.author.length) + m.author = d.author; + } + if (d.signature != null) { + if (typeof d.signature === "string") + $util.base64.decode(d.signature, m.signature = $util.newBuffer($util.base64.length(d.signature)), 0); + else if (d.signature.length) + m.signature = d.signature; + } + if (d.timeReceived != null) { + m.timeReceived = String(d.timeReceived); + } + return m; + }; + + /** + * Creates a plain object from a Record message. Also converts values to other types if specified. + * @function toObject + * @memberof Record + * @static + * @param {Record} m Record + * @param {$protobuf.IConversionOptions} [o] Conversion options + * @returns {Object.} Plain object + */ + Record.toObject = function toObject(m, o) { + if (!o) + o = {}; + var d = {}; + if (o.defaults) { + if (o.bytes === String) + d.key = ""; + else { + d.key = []; + if (o.bytes !== Array) + d.key = $util.newBuffer(d.key); + } + if (o.bytes === String) + d.value = ""; + else { + d.value = []; + if (o.bytes !== Array) + d.value = $util.newBuffer(d.value); + } + if (o.bytes === String) + d.author = ""; + else { + d.author = []; + if (o.bytes !== Array) + d.author = $util.newBuffer(d.author); + } + if (o.bytes === String) + d.signature = ""; + else { + d.signature = []; + if (o.bytes !== Array) + d.signature = $util.newBuffer(d.signature); + } + d.timeReceived = ""; + } + if (m.key != null && m.hasOwnProperty("key")) { + d.key = o.bytes === String ? $util.base64.encode(m.key, 0, m.key.length) : o.bytes === Array ? Array.prototype.slice.call(m.key) : m.key; + } + if (m.value != null && m.hasOwnProperty("value")) { + d.value = o.bytes === String ? $util.base64.encode(m.value, 0, m.value.length) : o.bytes === Array ? Array.prototype.slice.call(m.value) : m.value; + } + if (m.author != null && m.hasOwnProperty("author")) { + d.author = o.bytes === String ? $util.base64.encode(m.author, 0, m.author.length) : o.bytes === Array ? Array.prototype.slice.call(m.author) : m.author; + } + if (m.signature != null && m.hasOwnProperty("signature")) { + d.signature = o.bytes === String ? $util.base64.encode(m.signature, 0, m.signature.length) : o.bytes === Array ? Array.prototype.slice.call(m.signature) : m.signature; + } + if (m.timeReceived != null && m.hasOwnProperty("timeReceived")) { + d.timeReceived = m.timeReceived; + } + return d; + }; + + /** + * Converts this Record to JSON. + * @function toJSON + * @memberof Record + * @instance + * @returns {Object.} JSON object + */ + Record.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Record; +})(); + +$root.Message = (function() { + + /** + * Properties of a Message. + * @exports IMessage + * @interface IMessage + * @property {Message.MessageType|null} [type] Message type + * @property {number|null} [clusterLevelRaw] Message clusterLevelRaw + * @property {Uint8Array|null} [key] Message key + * @property {Uint8Array|null} [record] Message record + * @property {Array.|null} [closerPeers] Message closerPeers + * @property {Array.|null} [providerPeers] Message providerPeers + */ + + /** + * Constructs a new Message. + * @exports Message + * @classdesc Represents a Message. + * @implements IMessage + * @constructor + * @param {IMessage=} [p] Properties to set + */ + function Message(p) { + this.closerPeers = []; + this.providerPeers = []; + if (p) + for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) + if (p[ks[i]] != null) + this[ks[i]] = p[ks[i]]; + } + + /** + * Message type. + * @member {Message.MessageType} type + * @memberof Message + * @instance + */ + Message.prototype.type = 0; + + /** + * Message clusterLevelRaw. + * @member {number} clusterLevelRaw + * @memberof Message + * @instance + */ + Message.prototype.clusterLevelRaw = 0; + + /** + * Message key. + * @member {Uint8Array} key + * @memberof Message + * @instance + */ + Message.prototype.key = $util.newBuffer([]); + + /** + * Message record. + * @member {Uint8Array} record + * @memberof Message + * @instance + */ + Message.prototype.record = $util.newBuffer([]); + + /** + * Message closerPeers. + * @member {Array.} closerPeers + * @memberof Message + * @instance + */ + Message.prototype.closerPeers = $util.emptyArray; + + /** + * Message providerPeers. + * @member {Array.} providerPeers + * @memberof Message + * @instance + */ + Message.prototype.providerPeers = $util.emptyArray; + + /** + * Encodes the specified Message message. Does not implicitly {@link Message.verify|verify} messages. + * @function encode + * @memberof Message + * @static + * @param {IMessage} m Message message or plain object to encode + * @param {$protobuf.Writer} [w] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Message.encode = function encode(m, w) { + if (!w) + w = $Writer.create(); + if (m.type != null && Object.hasOwnProperty.call(m, "type")) + w.uint32(8).int32(m.type); + if (m.key != null && Object.hasOwnProperty.call(m, "key")) + w.uint32(18).bytes(m.key); + if (m.record != null && Object.hasOwnProperty.call(m, "record")) + w.uint32(26).bytes(m.record); + if (m.closerPeers != null && m.closerPeers.length) { + for (var i = 0; i < m.closerPeers.length; ++i) + $root.Message.Peer.encode(m.closerPeers[i], w.uint32(66).fork()).ldelim(); + } + if (m.providerPeers != null && m.providerPeers.length) { + for (var i = 0; i < m.providerPeers.length; ++i) + $root.Message.Peer.encode(m.providerPeers[i], w.uint32(74).fork()).ldelim(); + } + if (m.clusterLevelRaw != null && Object.hasOwnProperty.call(m, "clusterLevelRaw")) + w.uint32(80).int32(m.clusterLevelRaw); + return w; + }; + + /** + * Decodes a Message message from the specified reader or buffer. + * @function decode + * @memberof Message + * @static + * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from + * @param {number} [l] Message length if known beforehand + * @returns {Message} Message + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Message.decode = function decode(r, l) { + if (!(r instanceof $Reader)) + r = $Reader.create(r); + var c = l === undefined ? r.len : r.pos + l, m = new $root.Message(); + while (r.pos < c) { + var t = r.uint32(); + switch (t >>> 3) { + case 1: + m.type = r.int32(); + break; + case 10: + m.clusterLevelRaw = r.int32(); + break; + case 2: + m.key = r.bytes(); + break; + case 3: + m.record = r.bytes(); + break; + case 8: + if (!(m.closerPeers && m.closerPeers.length)) + m.closerPeers = []; + m.closerPeers.push($root.Message.Peer.decode(r, r.uint32())); + break; + case 9: + if (!(m.providerPeers && m.providerPeers.length)) + m.providerPeers = []; + m.providerPeers.push($root.Message.Peer.decode(r, r.uint32())); + break; + default: + r.skipType(t & 7); + break; + } + } + return m; + }; + + /** + * Creates a Message message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof Message + * @static + * @param {Object.} d Plain object + * @returns {Message} Message + */ + Message.fromObject = function fromObject(d) { + if (d instanceof $root.Message) + return d; + var m = new $root.Message(); + switch (d.type) { + case "PUT_VALUE": + case 0: + m.type = 0; + break; + case "GET_VALUE": + case 1: + m.type = 1; + break; + case "ADD_PROVIDER": + case 2: + m.type = 2; + break; + case "GET_PROVIDERS": + case 3: + m.type = 3; + break; + case "FIND_NODE": + case 4: + m.type = 4; + break; + case "PING": + case 5: + m.type = 5; + break; + } + if (d.clusterLevelRaw != null) { + m.clusterLevelRaw = d.clusterLevelRaw | 0; + } + if (d.key != null) { + if (typeof d.key === "string") + $util.base64.decode(d.key, m.key = $util.newBuffer($util.base64.length(d.key)), 0); + else if (d.key.length) + m.key = d.key; + } + if (d.record != null) { + if (typeof d.record === "string") + $util.base64.decode(d.record, m.record = $util.newBuffer($util.base64.length(d.record)), 0); + else if (d.record.length) + m.record = d.record; + } + if (d.closerPeers) { + if (!Array.isArray(d.closerPeers)) + throw TypeError(".Message.closerPeers: array expected"); + m.closerPeers = []; + for (var i = 0; i < d.closerPeers.length; ++i) { + if (typeof d.closerPeers[i] !== "object") + throw TypeError(".Message.closerPeers: object expected"); + m.closerPeers[i] = $root.Message.Peer.fromObject(d.closerPeers[i]); + } + } + if (d.providerPeers) { + if (!Array.isArray(d.providerPeers)) + throw TypeError(".Message.providerPeers: array expected"); + m.providerPeers = []; + for (var i = 0; i < d.providerPeers.length; ++i) { + if (typeof d.providerPeers[i] !== "object") + throw TypeError(".Message.providerPeers: object expected"); + m.providerPeers[i] = $root.Message.Peer.fromObject(d.providerPeers[i]); + } + } + return m; + }; + + /** + * Creates a plain object from a Message message. Also converts values to other types if specified. + * @function toObject + * @memberof Message + * @static + * @param {Message} m Message + * @param {$protobuf.IConversionOptions} [o] Conversion options + * @returns {Object.} Plain object + */ + Message.toObject = function toObject(m, o) { + if (!o) + o = {}; + var d = {}; + if (o.arrays || o.defaults) { + d.closerPeers = []; + d.providerPeers = []; + } + if (o.defaults) { + d.type = o.enums === String ? "PUT_VALUE" : 0; + if (o.bytes === String) + d.key = ""; + else { + d.key = []; + if (o.bytes !== Array) + d.key = $util.newBuffer(d.key); + } + if (o.bytes === String) + d.record = ""; + else { + d.record = []; + if (o.bytes !== Array) + d.record = $util.newBuffer(d.record); + } + d.clusterLevelRaw = 0; + } + if (m.type != null && m.hasOwnProperty("type")) { + d.type = o.enums === String ? $root.Message.MessageType[m.type] : m.type; + } + if (m.key != null && m.hasOwnProperty("key")) { + d.key = o.bytes === String ? $util.base64.encode(m.key, 0, m.key.length) : o.bytes === Array ? Array.prototype.slice.call(m.key) : m.key; + } + if (m.record != null && m.hasOwnProperty("record")) { + d.record = o.bytes === String ? $util.base64.encode(m.record, 0, m.record.length) : o.bytes === Array ? Array.prototype.slice.call(m.record) : m.record; + } + if (m.closerPeers && m.closerPeers.length) { + d.closerPeers = []; + for (var j = 0; j < m.closerPeers.length; ++j) { + d.closerPeers[j] = $root.Message.Peer.toObject(m.closerPeers[j], o); + } + } + if (m.providerPeers && m.providerPeers.length) { + d.providerPeers = []; + for (var j = 0; j < m.providerPeers.length; ++j) { + d.providerPeers[j] = $root.Message.Peer.toObject(m.providerPeers[j], o); + } + } + if (m.clusterLevelRaw != null && m.hasOwnProperty("clusterLevelRaw")) { + d.clusterLevelRaw = m.clusterLevelRaw; + } + return d; + }; + + /** + * Converts this Message to JSON. + * @function toJSON + * @memberof Message + * @instance + * @returns {Object.} JSON object + */ + Message.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * MessageType enum. + * @name Message.MessageType + * @enum {number} + * @property {number} PUT_VALUE=0 PUT_VALUE value + * @property {number} GET_VALUE=1 GET_VALUE value + * @property {number} ADD_PROVIDER=2 ADD_PROVIDER value + * @property {number} GET_PROVIDERS=3 GET_PROVIDERS value + * @property {number} FIND_NODE=4 FIND_NODE value + * @property {number} PING=5 PING value + */ + Message.MessageType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "PUT_VALUE"] = 0; + values[valuesById[1] = "GET_VALUE"] = 1; + values[valuesById[2] = "ADD_PROVIDER"] = 2; + values[valuesById[3] = "GET_PROVIDERS"] = 3; + values[valuesById[4] = "FIND_NODE"] = 4; + values[valuesById[5] = "PING"] = 5; + return values; + })(); + + /** + * ConnectionType enum. + * @name Message.ConnectionType + * @enum {number} + * @property {number} NOT_CONNECTED=0 NOT_CONNECTED value + * @property {number} CONNECTED=1 CONNECTED value + * @property {number} CAN_CONNECT=2 CAN_CONNECT value + * @property {number} CANNOT_CONNECT=3 CANNOT_CONNECT value + */ + Message.ConnectionType = (function() { + var valuesById = {}, values = Object.create(valuesById); + values[valuesById[0] = "NOT_CONNECTED"] = 0; + values[valuesById[1] = "CONNECTED"] = 1; + values[valuesById[2] = "CAN_CONNECT"] = 2; + values[valuesById[3] = "CANNOT_CONNECT"] = 3; + return values; + })(); + + Message.Peer = (function() { + + /** + * Properties of a Peer. + * @memberof Message + * @interface IPeer + * @property {Uint8Array|null} [id] Peer id + * @property {Array.|null} [addrs] Peer addrs + * @property {Message.ConnectionType|null} [connection] Peer connection + */ + + /** + * Constructs a new Peer. + * @memberof Message + * @classdesc Represents a Peer. + * @implements IPeer + * @constructor + * @param {Message.IPeer=} [p] Properties to set + */ + function Peer(p) { + this.addrs = []; + if (p) + for (var ks = Object.keys(p), i = 0; i < ks.length; ++i) + if (p[ks[i]] != null) + this[ks[i]] = p[ks[i]]; + } + + /** + * Peer id. + * @member {Uint8Array} id + * @memberof Message.Peer + * @instance + */ + Peer.prototype.id = $util.newBuffer([]); + + /** + * Peer addrs. + * @member {Array.} addrs + * @memberof Message.Peer + * @instance + */ + Peer.prototype.addrs = $util.emptyArray; + + /** + * Peer connection. + * @member {Message.ConnectionType} connection + * @memberof Message.Peer + * @instance + */ + Peer.prototype.connection = 0; + + /** + * Encodes the specified Peer message. Does not implicitly {@link Message.Peer.verify|verify} messages. + * @function encode + * @memberof Message.Peer + * @static + * @param {Message.IPeer} m Peer message or plain object to encode + * @param {$protobuf.Writer} [w] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + Peer.encode = function encode(m, w) { + if (!w) + w = $Writer.create(); + if (m.id != null && Object.hasOwnProperty.call(m, "id")) + w.uint32(10).bytes(m.id); + if (m.addrs != null && m.addrs.length) { + for (var i = 0; i < m.addrs.length; ++i) + w.uint32(18).bytes(m.addrs[i]); + } + if (m.connection != null && Object.hasOwnProperty.call(m, "connection")) + w.uint32(24).int32(m.connection); + return w; + }; + + /** + * Decodes a Peer message from the specified reader or buffer. + * @function decode + * @memberof Message.Peer + * @static + * @param {$protobuf.Reader|Uint8Array} r Reader or buffer to decode from + * @param {number} [l] Message length if known beforehand + * @returns {Message.Peer} Peer + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + Peer.decode = function decode(r, l) { + if (!(r instanceof $Reader)) + r = $Reader.create(r); + var c = l === undefined ? r.len : r.pos + l, m = new $root.Message.Peer(); + while (r.pos < c) { + var t = r.uint32(); + switch (t >>> 3) { + case 1: + m.id = r.bytes(); + break; + case 2: + if (!(m.addrs && m.addrs.length)) + m.addrs = []; + m.addrs.push(r.bytes()); + break; + case 3: + m.connection = r.int32(); + break; + default: + r.skipType(t & 7); + break; + } + } + return m; + }; + + /** + * Creates a Peer message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof Message.Peer + * @static + * @param {Object.} d Plain object + * @returns {Message.Peer} Peer + */ + Peer.fromObject = function fromObject(d) { + if (d instanceof $root.Message.Peer) + return d; + var m = new $root.Message.Peer(); + if (d.id != null) { + if (typeof d.id === "string") + $util.base64.decode(d.id, m.id = $util.newBuffer($util.base64.length(d.id)), 0); + else if (d.id.length) + m.id = d.id; + } + if (d.addrs) { + if (!Array.isArray(d.addrs)) + throw TypeError(".Message.Peer.addrs: array expected"); + m.addrs = []; + for (var i = 0; i < d.addrs.length; ++i) { + if (typeof d.addrs[i] === "string") + $util.base64.decode(d.addrs[i], m.addrs[i] = $util.newBuffer($util.base64.length(d.addrs[i])), 0); + else if (d.addrs[i].length) + m.addrs[i] = d.addrs[i]; + } + } + switch (d.connection) { + case "NOT_CONNECTED": + case 0: + m.connection = 0; + break; + case "CONNECTED": + case 1: + m.connection = 1; + break; + case "CAN_CONNECT": + case 2: + m.connection = 2; + break; + case "CANNOT_CONNECT": + case 3: + m.connection = 3; + break; + } + return m; + }; + + /** + * Creates a plain object from a Peer message. Also converts values to other types if specified. + * @function toObject + * @memberof Message.Peer + * @static + * @param {Message.Peer} m Peer + * @param {$protobuf.IConversionOptions} [o] Conversion options + * @returns {Object.} Plain object + */ + Peer.toObject = function toObject(m, o) { + if (!o) + o = {}; + var d = {}; + if (o.arrays || o.defaults) { + d.addrs = []; + } + if (o.defaults) { + if (o.bytes === String) + d.id = ""; + else { + d.id = []; + if (o.bytes !== Array) + d.id = $util.newBuffer(d.id); + } + d.connection = o.enums === String ? "NOT_CONNECTED" : 0; + } + if (m.id != null && m.hasOwnProperty("id")) { + d.id = o.bytes === String ? $util.base64.encode(m.id, 0, m.id.length) : o.bytes === Array ? Array.prototype.slice.call(m.id) : m.id; + } + if (m.addrs && m.addrs.length) { + d.addrs = []; + for (var j = 0; j < m.addrs.length; ++j) { + d.addrs[j] = o.bytes === String ? $util.base64.encode(m.addrs[j], 0, m.addrs[j].length) : o.bytes === Array ? Array.prototype.slice.call(m.addrs[j]) : m.addrs[j]; + } + } + if (m.connection != null && m.hasOwnProperty("connection")) { + d.connection = o.enums === String ? $root.Message.ConnectionType[m.connection] : m.connection; + } + return d; + }; + + /** + * Converts this Peer to JSON. + * @function toJSON + * @memberof Message.Peer + * @instance + * @returns {Object.} JSON object + */ + Peer.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return Peer; + })(); + + return Message; +})(); + +module.exports = $root; diff --git a/src/message/dht.proto.js b/src/message/dht.proto similarity index 95% rename from src/message/dht.proto.js rename to src/message/dht.proto index c74ada90..160735b9 100644 --- a/src/message/dht.proto.js +++ b/src/message/dht.proto @@ -1,6 +1,5 @@ -'use strict' - -module.exports = `// can't use, because protocol-buffers doesn't support imports +syntax = "proto3"; +// can't use, because protocol-buffers doesn't support imports // so we have to duplicate for now :( // import "record.proto"; @@ -73,4 +72,4 @@ message Message { // Used to return Providers // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS repeated Peer providerPeers = 9; -}` +} diff --git a/src/message/index.js b/src/message/index.js index 4d43216b..ea0ac438 100644 --- a/src/message/index.js +++ b/src/message/index.js @@ -1,14 +1,12 @@ 'use strict' const PeerId = require('peer-id') -const multiaddr = require('multiaddr') -// @ts-ignore -const protons = require('protons') +const { Multiaddr } = require('multiaddr') const { Record } = require('libp2p-record') -const pbm = protons(require('./dht.proto')) +const Proto = require('./dht') -const MESSAGE_TYPE = pbm.Message.MessageType -const CONNECTION_TYPE = pbm.Message.ConnectionType +const MESSAGE_TYPE = Proto.Message.MessageType +const CONNECTION_TYPE = Proto.Message.ConnectionType /** * @typedef {0|1|2|3|4} ConnectionType @@ -26,7 +24,7 @@ const CONNECTION_TYPE = pbm.Message.ConnectionType */ class Message { /** - * @param {MESSAGE_TYPE} type + * @param {import('./dht').Message.MessageType} type * @param {Uint8Array} key * @param {number} level */ @@ -43,8 +41,8 @@ class Message { this.closerPeers = [] /** @type {PeerData[]} */ this.providerPeers = [] - /** @type {import('libp2p-record').Record | null} */ - this.record = null + /** @type {import('libp2p-record').Record | undefined} */ + this.record = undefined } /** @@ -86,7 +84,7 @@ class Message { } } - return pbm.Message.encode(obj) + return Proto.Message.encode(obj).finish() } /** @@ -95,14 +93,14 @@ class Message { * @param {Uint8Array} raw */ static deserialize (raw) { - const dec = pbm.Message.decode(raw) + const dec = Proto.Message.decode(raw) const msg = new Message(dec.type, dec.key, dec.clusterLevelRaw) msg.closerPeers = dec.closerPeers.map(fromPbPeer) msg.providerPeers = dec.providerPeers.map(fromPbPeer) - if (dec.record) { + if (dec.record && dec.record.length) { msg.record = Record.deserialize(dec.record) } @@ -128,12 +126,13 @@ function toPbPeer (peer) { } /** - * @param {PBPeer} peer + * @param {import('./dht').Message.IPeer} peer */ function fromPbPeer (peer) { return { + // @ts-ignore id is optional on protobuf, but it will exist? id: new PeerId(peer.id), - multiaddrs: peer.addrs.map((a) => multiaddr(a)) + multiaddrs: (peer.addrs || []).map((a) => new Multiaddr(a)) } } diff --git a/src/peer-routing/index.js b/src/peer-routing/index.js index 09b04854..c83df6aa 100644 --- a/src/peer-routing/index.js +++ b/src/peer-routing/index.js @@ -14,7 +14,7 @@ const Query = require('../query') const utils = require('../utils') /** - * @typedef {import('multiaddr')} Multiaddr + * @typedef {import('multiaddr').Multiaddr} Multiaddr */ /** diff --git a/src/rpc/handlers/index.js b/src/rpc/handlers/index.js index a676887a..8b655eaf 100644 --- a/src/rpc/handlers/index.js +++ b/src/rpc/handlers/index.js @@ -22,6 +22,7 @@ module.exports = (dht) => { * @param {number} type */ function getMessageHandler (type) { + // @ts-ignore ts does not aknowledge number as an index type return handlers[type] } diff --git a/test/kad-dht.spec.js b/test/kad-dht.spec.js index 6ae483b9..33346160 100644 --- a/test/kad-dht.spec.js +++ b/test/kad-dht.spec.js @@ -6,7 +6,7 @@ chai.use(require('dirty-chai')) chai.use(require('chai-checkmark')) const expect = chai.expect const sinon = require('sinon') -const multiaddr = require('multiaddr') +const { Multiaddr } = require('multiaddr') const { Record } = require('libp2p-record') const errcode = require('err-code') const uint8ArrayEquals = require('uint8arrays/equals') @@ -158,12 +158,14 @@ describe('KadDHT', () => { await dht.start() await dht.start() + + await dht.start() }) it('should not fail to stop when was not started', async () => { const [dht] = await tdht.spawn(1) - dht.stop() + await dht.stop() }) }) @@ -757,7 +759,7 @@ describe('KadDHT', () => { const dhts = await tdht.spawn(2) const ids = dhts.map((d) => d.peerId) - dhts[0].peerStore.addressBook.add(dhts[1].peerId, [multiaddr('/ip4/160.1.1.1/tcp/80')]) + dhts[0].peerStore.addressBook.add(dhts[1].peerId, [new Multiaddr('/ip4/160.1.1.1/tcp/80')]) const key = await dhts[0].getPublicKey(ids[1]) expect(key).to.eql(dhts[1].peerId.pubKey) @@ -777,7 +779,7 @@ describe('KadDHT', () => { await tdht.connect(dhts[0], dhts[1]) - dhts[0].peerStore.addressBook.add(dhts[1].peerId, [multiaddr('/ip4/160.1.1.1/tcp/80')]) + dhts[0].peerStore.addressBook.add(dhts[1].peerId, [new Multiaddr('/ip4/160.1.1.1/tcp/80')]) const key = await dhts[0].getPublicKey(ids[1]) expect(uint8ArrayEquals(key, dhts[1].peerId.pubKey)).to.eql(true) diff --git a/test/message.spec.js b/test/message.spec.js index 5f268fb3..d01449ef 100644 --- a/test/message.spec.js +++ b/test/message.spec.js @@ -5,7 +5,7 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect const PeerId = require('peer-id') -const multiaddr = require('multiaddr') +const { Multiaddr } = require('multiaddr') const range = require('lodash.range') const random = require('lodash.random') const { Record } = require('libp2p-record') @@ -35,16 +35,16 @@ describe('Message', () => { const closer = peers.slice(0, 5).map((p) => ({ id: p, multiaddrs: [ - multiaddr(`/ip4/198.176.1.${random(198)}/tcp/1234`), - multiaddr(`/ip4/100.176.1.${random(198)}`) + new Multiaddr(`/ip4/198.176.1.${random(198)}/tcp/1234`), + new Multiaddr(`/ip4/100.176.1.${random(198)}`) ] })) const provider = peers.slice(0, 5).map((p) => ({ id: p, multiaddrs: [ - multiaddr(`/ip4/98.176.1.${random(198)}/tcp/1234`), - multiaddr(`/ip4/10.176.1.${random(198)}`) + new Multiaddr(`/ip4/98.176.1.${random(198)}/tcp/1234`), + new Multiaddr(`/ip4/10.176.1.${random(198)}`) ] })) diff --git a/test/rpc/handlers/add-provider.spec.js b/test/rpc/handlers/add-provider.spec.js index 4bfbca13..1f5687f6 100644 --- a/test/rpc/handlers/add-provider.spec.js +++ b/test/rpc/handlers/add-provider.spec.js @@ -5,7 +5,7 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect -const multiaddr = require('multiaddr') +const { Multiaddr } = require('multiaddr') const uint8ArrayFromString = require('uint8arrays/from-string') const Message = require('../../../src/message') @@ -64,8 +64,8 @@ describe('rpc - handlers - AddProvider', () => { const cid = values[0].cid const msg = new Message(Message.TYPES.ADD_PROVIDER, cid.bytes, 0) - const ma1 = multiaddr('/ip4/127.0.0.1/tcp/1234') - const ma2 = multiaddr('/ip4/127.0.0.1/tcp/2345') + const ma1 = new Multiaddr('/ip4/127.0.0.1/tcp/1234') + const ma2 = new Multiaddr('/ip4/127.0.0.1/tcp/2345') msg.providerPeers = [ { diff --git a/test/utils/test-dht.js b/test/utils/test-dht.js index 04df038d..0ff9c054 100644 --- a/test/utils/test-dht.js +++ b/test/utils/test-dht.js @@ -3,7 +3,7 @@ const PeerStore = require('libp2p/src/peer-store') const pRetry = require('p-retry') const delay = require('delay') -const multiaddr = require('multiaddr') +const { Multiaddr } = require('multiaddr') const KadDHT = require('../../src') const { PROTOCOL_DHT } = require('../../src/constants') @@ -79,7 +79,7 @@ class TestDHT { const dht = new KadDHT({ libp2p: { - multiaddrs: [multiaddr('/ip4/0.0.0.0/tcp/4002')] + multiaddrs: [new Multiaddr('/ip4/0.0.0.0/tcp/4002')] }, dialer: { connectToPeer: (peer) => connectToPeer(dht, peer) diff --git a/tsconfig.json b/tsconfig.json index 3de2a3b0..0c9d2b04 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,5 +5,8 @@ }, "include": [ "src" + ], + "exclude": [ + "src/message/dht.js" // exclude generated file ] }