From 42efbb57f188e2581f6f721b28bfdb6b58b37ae7 Mon Sep 17 00:00:00 2001 From: Nazar Hussain Date: Tue, 15 Jun 2021 00:07:58 +0200 Subject: [PATCH] :art: Update PeerId class use JSDoc ts types --- src/index.d.ts | 193 --------------------- src/index.js | 453 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 320 insertions(+), 326 deletions(-) delete mode 100644 src/index.d.ts diff --git a/src/index.d.ts b/src/index.d.ts deleted file mode 100644 index 0906fb3..0000000 --- a/src/index.d.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { PrivateKey, PublicKey, KeyType } from "libp2p-crypto"; -import CID from 'cids' - -declare namespace PeerId { - /** - * Options for PeerId creation. - */ - type CreateOptions = { - /** - * The number of bits to use. - */ - bits?: number; - /** - * The type of key to use. - */ - keyType?: KeyType; - }; - - /** - * PeerId JSON format. - */ - type JSONPeerId = { - /** - * String representation of PeerId. - */ - id: string; - /** - * Public key. - */ - pubKey?: string; - /** - * Private key. - */ - privKey?: string; - }; - - /** - * Checks if a value is an instance of PeerId. - * @param id The value to check. - */ - function isPeerId(id: any): id is PeerId - - /** - * Create a new PeerId. - * @param opts Options. - */ - function create(opts?: PeerId.CreateOptions): Promise; - - /** - * Create PeerId from hex string. - * @param str The input hex string. - */ - function createFromHexString(str: string): PeerId; - - /** - * Create PeerId from raw bytes. - * @param buf The raw bytes. - */ - function createFromBytes(buf: Uint8Array): PeerId; - - /** - * Create PeerId from base58-encoded string. - * @param str The base58-encoded string. - */ - function createFromB58String(str: string): PeerId; - - /** - * Create PeerId from CID. - * @param cid The CID. - */ - function createFromCID(cid: CID | Uint8Array | string | object): PeerId; - - /** - * Create PeerId from public key. - * @param key Public key, as Uint8Array or base64-encoded string. - */ - function createFromPubKey(key: Uint8Array | string): Promise; - - /** - * Create PeerId from private key. - * @param key Private key, as Uint8Array or base64-encoded string. - */ - function createFromPrivKey(key: Uint8Array | string): Promise; - - /** - * Create PeerId from PeerId JSON formatted object. - * @see {@link PeerId#toJSON} - * @param json PeerId in JSON format. - */ - function createFromJSON(json: JSONPeerId): Promise; - - /** - * Create PeerId from Protobuf bytes. - * @param buf Protobuf bytes, as Uint8Array or hex-encoded string. - */ - function createFromProtobuf(buf: Uint8Array | string): Promise; -} - -/** - * PeerId is an object representation of a peer identifier. - */ -declare class PeerId { - constructor(id: Uint8Array, privKey?: PrivateKey, pubKey?: PublicKey); - - /** - * Raw id. - */ - readonly id: Uint8Array; - - /** - * Private key. - */ - privKey: PrivateKey; - - /** - * Public key. - */ - pubKey: PublicKey; - - /** - * Return the protobuf version of the public key, matching go ipfs formatting. - */ - marshalPubKey(): Uint8Array; - - /** - * Return the protobuf version of the private key, matching go ipfs formatting. - */ - marshalPrivKey(): Uint8Array; - - /** - * Return the protobuf version of the peer-id. - * @param excludePriv Whether to exclude the private key information from the output. - */ - marshal(excludePriv?: boolean): Uint8Array; - - /** - * String representation. - */ - toPrint(): string; - - /** - * Return the jsonified version of the key. - * Matches the formatting of go-ipfs for its config file. - * @see {@link PeerId.createFromJSON} - */ - toJSON(): PeerId.JSONPeerId; - - /** - * Encode to hex. - */ - toHexString(): string; - - /** - * Return raw id bytes. - */ - toBytes(): Uint8Array; - - /** - * Encode to base58 string. - */ - toB58String(): string; - - /** - * Return self-describing string representation. - * Uses default format from RFC 0001: https://github.com/libp2p/specs/pull/209 - */ - toString(): string; - - /** - * Checks the equality of `this` peer against a given PeerId. - * @param id The other PeerId. - */ - equals(id: PeerId | Uint8Array): boolean; - - /** - * Checks the equality of `this` peer against a given PeerId. - * @deprecated Use {.equals} - * @param id The other PeerId. - */ - isEqual(id: PeerId | Uint8Array): boolean; - - /** - * Check if this PeerId instance is valid (privKey -> pubKey -> Id) - */ - isValid(): boolean; - - /** - * Check if the PeerId has an inline public key. - */ - hasInlinePublicKey(): boolean; -} - -export = PeerId; diff --git a/src/index.js b/src/index.js index 05c8122..df4f2ed 100644 --- a/src/index.js +++ b/src/index.js @@ -1,19 +1,49 @@ -/* - * Id is an object representation of a peer Id. a peer Id is a multihash - */ - 'use strict' const mh = require('multihashes') const CID = require('cids') +// TODO: Fix missing type +// @ts-ignore const cryptoKeys = require('libp2p-crypto/src/keys') +// TODO: Fix missing type +// @ts-ignore const withIs = require('class-is') const { PeerIdProto } = require('./proto') const uint8ArrayEquals = require('uint8arrays/equals') const uint8ArrayFromString = require('uint8arrays/from-string') const uint8ArrayToString = require('uint8arrays/to-string') +/** + * @typedef {import('libp2p-crypto').PrivateKey} PrivateKey + * @typedef {import('libp2p-crypto').PublicKey} PublicKey + * @typedef {import('libp2p-crypto').KeyType} KeyType + * @typedef {import('cids').CIDVersion} CIDVersion + */ + +/** + * PeerId JSON format. + * @typedef {Object} PeerIdJSON + * @property {string} id - String representation of PeerId. + * @property {string} [pubKey] - Public key. + * @property {string} [privKey] - Private key. + */ + +/** + * Options for PeerId creation. + * @typedef {Object} CreateOptions + * @property {number} [bits] - The number of bits to use. + * @property {KeyType} [keyType] - The type of key to use. + */ + class PeerId { + /** + * Create PeerId object + * @constructor + * + * @param {Uint8Array} id + * @param {PrivateKey} [privKey] + * @param {PublicKey} [pubKey] + */ constructor (id, privKey, pubKey) { if (!(id instanceof Uint8Array)) { throw new Error('invalid id provided') @@ -27,8 +57,16 @@ class PeerId { this._idB58String = mh.toB58String(this.id) this._privKey = privKey this._pubKey = pubKey + + /** + * @type {string | undefined} + */ + this._idCIDString } + /** + * @type {Uint8Array} + */ get id () { return this._id } @@ -37,6 +75,9 @@ class PeerId { throw new Error('Id is immutable') } + /** + * @type {PrivateKey | undefined} + */ get privKey () { return this._privKey } @@ -45,6 +86,9 @@ class PeerId { this._privKey = privKey } + /** + * @type {PublicKey | undefined} + */ get pubKey () { if (this._pubKey) { return this._pubKey @@ -71,21 +115,34 @@ class PeerId { this._pubKey = pubKey } - // Return the protobuf version of the public key, matching go ipfs formatting + /** + * Return the protobuf version of the public key, matching go ipfs formatting + * + * @returns {Uint8Array | undefined} + */ marshalPubKey () { if (this.pubKey) { return cryptoKeys.marshalPublicKey(this.pubKey) } } - // Return the protobuf version of the private key, matching go ipfs formatting + /** + * Return the protobuf version of the private key, matching go ipfs formatting + * + * @returns {Uint8Array | undefined} + */ marshalPrivKey () { if (this.privKey) { return cryptoKeys.marshalPrivateKey(this.privKey) } } - // Return the protobuf version of the peer-id + /** + * Return the protobuf version of the peer-id + * + * @param {boolean} [excludePriv] - Whether to exclude the private key information from the output. + * @returns {Uint8Array} + */ marshal (excludePriv) { return PeerIdProto.encode({ id: this.toBytes(), @@ -94,6 +151,11 @@ class PeerId { }).finish() } + /** + * String representation + * + * @returns {string} + */ toPrint () { let pid = this.toB58String() // All sha256 nodes start with Qm @@ -109,8 +171,11 @@ class PeerId { return '' } - // return the jsonified version of the key, matching the formatting - // of go-ipfs for its config file + /** + * The jsonified version of the key, matching the formatting of go-ipfs for its config file + * + * @returns {PeerIdJSON} + */ toJSON () { return { id: this.toB58String(), @@ -119,21 +184,39 @@ class PeerId { } } - // encode/decode functions + /** + * Encode to hex. + * + * @returns {string} + */ toHexString () { return mh.toHexString(this.id) } + /** + * Return raw id bytes + * + * @returns {Uint8Array} + */ toBytes () { return this.id } + /** + * Encode to base58 string. + * + * @returns {string} + */ toB58String () { return this._idB58String } - // return self-describing String representation - // in default format from RFC 0001: https://github.com/libp2p/specs/pull/209 + /** + * Self-describing String representation + * in default format from RFC 0001: https://github.com/libp2p/specs/pull/209 + * + * @returns {string} + */ toString () { if (!this._idCIDString) { const cid = new CID(1, 'libp2p-key', this.id, 'base32') @@ -143,7 +226,8 @@ class PeerId { enumerable: false }) } - return this._idCIDString + + return this._idCIDString ? this._idCIDString : '' } /** @@ -173,14 +257,17 @@ class PeerId { return this.equals(id) } - /* + /** * Check if this PeerId instance is valid (privKey -> pubKey -> Id) + * + * @returns {boolean} */ isValid () { // TODO: needs better checking return Boolean(this.privKey && this.privKey.public && this.privKey.public.bytes && + this.pubKey && this.pubKey.bytes instanceof Uint8Array && uint8ArrayEquals(this.privKey.public.bytes, this.pubKey.bytes)) } @@ -202,172 +289,272 @@ class PeerId { return false } -} - -const PeerIdWithIs = withIs(PeerId, { - className: 'PeerId', - symbolName: '@libp2p/js-peer-id/PeerId' -}) -exports = module.exports = PeerIdWithIs + /** + * Create a new PeerId. + * + * @param {CreateOptions} opts - Options + * @returns {Promise} + */ + static async create (opts) { + opts = opts || {} + opts.bits = opts.bits || 2048 + opts.keyType = opts.keyType || 'RSA' -const computeDigest = (pubKey) => { - if (pubKey.bytes.length <= 42) { - return mh.encode(pubKey.bytes, 'identity') - } else { - return pubKey.hash() + const key = await cryptoKeys.generateKeyPair(opts.keyType, opts.bits) + return computePeerId(key.public, key) } -} -const computePeerId = async (privKey, pubKey) => { - const digest = await computeDigest(pubKey) - return new PeerIdWithIs(digest, privKey, pubKey) -} - -// generation -exports.create = async (opts) => { - opts = opts || {} - opts.bits = opts.bits || 2048 - opts.keyType = opts.keyType || 'RSA' - - const key = await cryptoKeys.generateKeyPair(opts.keyType, opts.bits) - return computePeerId(key, key.public) -} + /** + * Create PeerId from raw bytes. + * + * @param {Uint8Array} buf - The raw bytes. + * @returns {PeerId} + */ + static createFromBytes (buf) { + return new PeerId(buf) + } -exports.createFromHexString = (str) => { - return new PeerIdWithIs(mh.fromHexString(str)) -} + /** + * Create PeerId from base58-encoded string. + * + * @param {string} str - The base58-encoded string. + * @returns {PeerId} + */ + static createFromB58String (str) { + return PeerId.createFromCID(str) // B58String is CIDv0 + } -exports.createFromBytes = (buf) => { - return new PeerIdWithIs(buf) -} + /** + * Create PeerId from hex string. + * + * @param {string} str - The hex string. + * @returns {PeerId} + */ + static createFromHexString (str) { + return new PeerId(mh.fromHexString(str)) + } -exports.createFromB58String = (str) => { - return exports.createFromCID(str) // B58String is CIDv0 -} + /** + * Create PeerId from CID. + * + * @param {CID | CIDVersion | Uint8Array | string} cid - The CID. + * @returns {PeerId} + */ + static createFromCID (cid) { + cid = CID.isCID(cid) ? cid : new CID(cid) + if (!validMulticodec(cid)) throw new Error('Supplied PeerID CID has invalid multicodec: ' + cid.codec) + return new PeerId(cid.multihash) + } -const validMulticodec = (cid) => { - // supported: 'libp2p-key' (CIDv1) and 'dag-pb' (CIDv0 converted to CIDv1) - return cid.codec === 'libp2p-key' || cid.codec === 'dag-pb' -} + /** + * Create PeerId from public key. + * + * @param {Uint8Array | string} key - Public key, as Uint8Array or base64-encoded string. + * @returns {Promise} + */ + static async createFromPubKey (key) { + let buf = key -exports.createFromCID = (cid) => { - cid = CID.isCID(cid) ? cid : new CID(cid) - if (!validMulticodec(cid)) throw new Error('Supplied PeerID CID has invalid multicodec: ' + cid.codec) - return new PeerIdWithIs(cid.multihash) -} + if (typeof buf === 'string') { + buf = uint8ArrayFromString(String(key), 'base64pad') + } -// Public Key input will be a Uint8Array -exports.createFromPubKey = async (key) => { - let buf = key + if (!(buf instanceof Uint8Array)) { + throw new Error('Supplied key is neither a base64 string nor a Uint8Array') + } - if (typeof buf === 'string') { - buf = uint8ArrayFromString(key, 'base64pad') + const pubKey = await cryptoKeys.unmarshalPublicKey(buf) + return computePeerId(pubKey) } - if (!(buf instanceof Uint8Array)) { - throw new Error('Supplied key is neither a base64 string nor a Uint8Array') - } + /** + * Create PeerId from private key. + * + * @param {Uint8Array | string} key - Private key, as Uint8Array or base64-encoded string. + * @returns {Promise} + */ + static async createFromPrivKey (key) { + if (typeof key === 'string') { + key = uint8ArrayFromString(key, 'base64pad') + } - const pubKey = await cryptoKeys.unmarshalPublicKey(buf) - return computePeerId(null, pubKey) -} + if (!(key instanceof Uint8Array)) { + throw new Error('Supplied key is neither a base64 string nor a Uint8Array') + } -// Private key input will be a string -exports.createFromPrivKey = async (key) => { - if (typeof key === 'string') { - key = uint8ArrayFromString(key, 'base64pad') + const privKey = await cryptoKeys.unmarshalPrivateKey(key) + return computePeerId(privKey.public, privKey) } - if (!(key instanceof Uint8Array)) { - throw new Error('Supplied key is neither a base64 string nor a Uint8Array') - } + /** + * Create PeerId from PeerId JSON formatted object. + * + * @param {PeerIdJSON} obj + * @returns {Promise} + */ + static async createFromJSON (obj) { + const id = mh.fromB58String(obj.id) + const rawPrivKey = obj.privKey && uint8ArrayFromString(obj.privKey, 'base64pad') + const rawPubKey = obj.pubKey && uint8ArrayFromString(obj.pubKey, 'base64pad') + const pub = rawPubKey && await cryptoKeys.unmarshalPublicKey(rawPubKey) + + if (!rawPrivKey) { + return new PeerId(id, undefined, pub) + } - const privKey = await cryptoKeys.unmarshalPrivateKey(key) - return computePeerId(privKey, privKey.public) -} + const privKey = await cryptoKeys.unmarshalPrivateKey(rawPrivKey) + const privDigest = await computeDigest(privKey.public) -exports.createFromJSON = async (obj) => { - const id = mh.fromB58String(obj.id) - const rawPrivKey = obj.privKey && uint8ArrayFromString(obj.privKey, 'base64pad') - const rawPubKey = obj.pubKey && uint8ArrayFromString(obj.pubKey, 'base64pad') - const pub = rawPubKey && await cryptoKeys.unmarshalPublicKey(rawPubKey) + /** + * @type {Uint8Array} + */ + let pubDigest = new Uint8Array() - if (!rawPrivKey) { - return new PeerIdWithIs(id, null, pub) - } + if (pub) { + pubDigest = await computeDigest(pub) + } - const privKey = await cryptoKeys.unmarshalPrivateKey(rawPrivKey) - const privDigest = await computeDigest(privKey.public) + if (pub && !uint8ArrayEquals(privDigest, pubDigest)) { + throw new Error('Public and private key do not match') + } - let pubDigest + if (id && !uint8ArrayEquals(privDigest, id)) { + throw new Error('Id and private key do not match') + } - if (pub) { - pubDigest = await computeDigest(pub) + return new PeerId(id, privKey, pub) } - if (pub && !uint8ArrayEquals(privDigest, pubDigest)) { - throw new Error('Public and private key do not match') - } + /** + * Create PeerId from Protobuf bytes. + * + * @param {Uint8Array | string} buf - Protobuf bytes, as Uint8Array or hex-encoded string. + * @returns {Promise} + */ + static async createFromProtobuf (buf) { + if (typeof buf === 'string') { + buf = uint8ArrayFromString(buf, 'base16') + } - if (id && !uint8ArrayEquals(privDigest, id)) { - throw new Error('Id and private key do not match') - } + let { id, privKey: privKeyArray, pubKey: pubKeyArray } = PeerIdProto.decode(buf) - return new PeerIdWithIs(id, privKey, pub) -} + /** + * @type {PrivateKey | false} + */ + const privKey = privKeyArray ? await cryptoKeys.unmarshalPrivateKey(privKeyArray) : false -exports.createFromProtobuf = async (buf) => { - if (typeof buf === 'string') { - buf = uint8ArrayFromString(buf, 'base16') - } + /** + * @type {PublicKey | false} + */ + const pubKey = pubKeyArray ? await cryptoKeys.unmarshalPublicKey(pubKeyArray) : false - let { id, privKey, pubKey } = PeerIdProto.decode(buf) + /** + * @type {Uint8Array} + */ + let pubDigest = new Uint8Array() - privKey = privKey ? await cryptoKeys.unmarshalPrivateKey(privKey) : false - pubKey = pubKey ? await cryptoKeys.unmarshalPublicKey(pubKey) : false + /** + * @type {Uint8Array} + */ + let privDigest = new Uint8Array() - let pubDigest - let privDigest + if (privKey) { + // @ts-ignore + privDigest = await computeDigest(privKey.public) + } - if (privKey) { - privDigest = await computeDigest(privKey.public) - } + if (pubKey) { + pubDigest = await computeDigest(pubKey) + } - if (pubKey) { - pubDigest = await computeDigest(pubKey) - } + if (privKey) { + if (pubKey) { + if (!uint8ArrayEquals(privDigest, pubDigest)) { + throw new Error('Public and private key do not match') + } + } + // @ts-ignore + return new PeerId(privDigest, privKey, privKey.public) + } - if (privKey) { + // TODO: val id and pubDigest if (pubKey) { - if (!uint8ArrayEquals(privDigest, pubDigest)) { - throw new Error('Public and private key do not match') - } + return new PeerId(pubDigest, undefined, pubKey) } - return new PeerIdWithIs(privDigest, privKey, privKey.public) - } - // TODO: val id and pubDigest + if (id) { + return new PeerId(id) + } - if (pubKey) { - return new PeerIdWithIs(pubDigest, null, pubKey) + throw new Error('Protobuf did not contain any usable key material') } +} + +/** + * @type {typeof PeerId & {isPeerId: (id: any)=> boolean}} + */ +module.exports = withIs(PeerId, { + className: 'PeerId', + symbolName: '@libp2p/js-peer-id/PeerId' +}) - if (id) { - return new PeerIdWithIs(id) +// TODO: withIs is creating "isPeerId" so not sure if its really needed any further. +/** + * Checks if a value is an instance of PeerId. + * + * @param {any} peerId - The value to check. + * @returns {boolean} + */ +module.exports.isPeerId = (peerId) => { + return Boolean(typeof peerId === 'object' && peerId._id && peerId._idB58String) +} + +/** + * Compute digest. + * + * @param {PublicKey} pubKey + * @returns {Promise} + */ +const computeDigest = async (pubKey) => { + if (pubKey.bytes.length <= 42) { + return Promise.resolve(mh.encode(pubKey.bytes, 'identity')) + } else { + return pubKey.hash() } +} - throw new Error('Protobuf did not contain any usable key material') +/** + * Compute PeerId. + * + * @param {PublicKey} pubKey + * @param {PrivateKey} [privKey] + * @returns {Promise} + */ +const computePeerId = async (pubKey, privKey) => { + const digest = await computeDigest(pubKey) + return new PeerId(digest, privKey, pubKey) } -exports.isPeerId = (peerId) => { - return Boolean(typeof peerId === 'object' && - peerId._id && - peerId._idB58String) +/** + * Create PeerId from CID. + * + * @param {CID | {codec: string}} cid - The CID. + * @returns {boolean} + */ +const validMulticodec = (cid) => { + // supported: 'libp2p-key' (CIDv1) and 'dag-pb' (CIDv0 converted to CIDv1) + return cid.codec === 'libp2p-key' || cid.codec === 'dag-pb' } -function toB64Opt (val) { +/** + * @param {Uint8Array | undefined} val + * @returns {string} + */ +const toB64Opt = (val) => { if (val) { return uint8ArrayToString(val, 'base64pad') } + + return '' }