Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update to new multiformats #149

Merged
merged 10 commits into from
Jul 6, 2021
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
- [`createFromPrivKey(privKey)`](#createfromprivkeyprivkey)
- [`createFromJSON(obj)`](#createfromjsonobj)
- [`createFromProtobuf(buf)`](#createfromprotobufbuf)
- [`parse(str)`](#parsestr)
- [Export](#export)
- [`toHexString()`](#tohexstring)
- [`toBytes()`](#tobytes)
Expand Down Expand Up @@ -169,7 +170,7 @@ Returns `PeerId`.

### `createFromCID(cid)`

- `cid: CID|String|Buffer` - The multihash encoded as [CID](https://github.com/ipld/js-cid) (object, `String` or `Buffer`)
- `cid: CID` - The multihash encoded as [CID](https://github.com/multiformats/js-multiformats/blob/master/src/cid.js) object

Creates a Peer ID from a CID representation of the key's multihash ([RFC 0001](https://github.com/libp2p/specs/blob/master/RFC/0001-text-peerid-cid.md)).

Expand Down Expand Up @@ -209,6 +210,10 @@ Returns `Promise<PeerId>`.

`buf` is a protocol-buffers encoded PeerId (see `marshal()`)

### `parse(str)`

Attempts to create a PeerId from a base58btc encoded string or a CID encoded as a string.

## Export

### `toHexString()`
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"bin": "src/bin.js",
"scripts": {
"lint": "aegir lint",
"prepare": "npm run build",
"build": "npm run build:proto && npm run build:proto-types && aegir build --no-types",
"build:proto": "pbjs -t static-module -w commonjs -r libp2p-peer-id --force-number --no-verify --no-delimited --no-create --no-beautify --no-defaults --lint eslint-disable -o src/proto.js ./src/proto.proto",
"build:proto-types": "pbts -o src/proto.d.ts src/proto.js",
Expand Down Expand Up @@ -41,14 +42,14 @@
"@types/dirty-chai": "^2.0.2",
"@types/mocha": "^8.2.0",
"aegir": "^33.0.0",
"multihashes": "^4.0.2",
"util": "^0.12.3"
},
"dependencies": {
"cids": "^1.1.5",
"class-is": "^1.1.0",
"libp2p-crypto": "^0.19.0",
"minimist": "^1.2.5",
"multihashes": "^4.0.2",
"multiformats": "^9.0.0",
"protobufjs": "^6.10.2",
"uint8arrays": "^2.0.5"
},
Expand Down
10 changes: 8 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PrivateKey, PublicKey, KeyType } from "libp2p-crypto";
import CID from 'cids'
import { CID } from 'multiformats/cid'

declare namespace PeerId {
/**
Expand Down Expand Up @@ -68,7 +68,7 @@ declare namespace PeerId {
* Create PeerId from CID.
* @param cid The CID.
*/
function createFromCID(cid: CID | Uint8Array | string | object): PeerId;
function createFromCID(cid: CID): PeerId;

/**
* Create PeerId from public key.
Expand All @@ -94,6 +94,12 @@ declare namespace PeerId {
* @param buf Protobuf bytes, as Uint8Array or hex-encoded string.
*/
function createFromProtobuf(buf: Uint8Array | string): Promise<PeerId>;

/**
* Parse a PeerId from a string.
* @param str encoded public key string.
*/
function parse(str: string): PeerId;
}

/**
Expand Down
76 changes: 57 additions & 19 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@

'use strict'

const mh = require('multihashes')
const CID = require('cids')
const { CID } = require('multiformats/cid')
const { base58btc } = require('multiformats/bases/base58')
const { base16 } = require('multiformats/bases/base16')
const Digest = require('multiformats/hashes/digest')
const cryptoKeys = require('libp2p-crypto/src/keys')
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')
const { identity } = require('multiformats/hashes/identity')

// these values are from https://github.com/multiformats/multicodec/blob/master/table.csv
const DAG_PB_CODE = 0x70
const LIBP2P_KEY_CODE = 0x72

class PeerId {
constructor (id, privKey, pubKey) {
Expand All @@ -24,7 +31,7 @@ class PeerId {
}

this._id = id
this._idB58String = mh.toB58String(this.id)
this._idB58String = base58btc.encode(this.id).substring(1)
this._privKey = privKey
this._pubKey = pubKey
}
Expand Down Expand Up @@ -55,9 +62,9 @@ class PeerId {
}

try {
const decoded = mh.decode(this.id)
const decoded = Digest.decode(this.id)

if (decoded.name === 'identity') {
if (decoded.code === identity.code) {
this._pubKey = cryptoKeys.unmarshalPublicKey(decoded.digest)
}
} catch (_) {
Expand Down Expand Up @@ -121,7 +128,7 @@ class PeerId {

// encode/decode functions
toHexString () {
return mh.toHexString(this.id)
return base16.encode(this.id).substring(1)
}

toBytes () {
Expand All @@ -136,10 +143,10 @@ class PeerId {
// in default format from RFC 0001: https://github.com/libp2p/specs/pull/209
toString () {
if (!this._idCIDString) {
const cid = new CID(1, 'libp2p-key', this.id, 'base32')
const cid = CID.createV1(LIBP2P_KEY_CODE, Digest.decode(this.id))

Object.defineProperty(this, '_idCIDString', {
value: cid.toBaseEncodedString('base32'),
value: cid.toString(),
enumerable: false
})
}
Expand Down Expand Up @@ -192,8 +199,9 @@ class PeerId {
*/
hasInlinePublicKey () {
try {
const decoded = mh.decode(this.id)
if (decoded.name === 'identity') {
const decoded = Digest.decode(this.id)

if (decoded.code === identity.code) {
return true
}
} catch (_) {
Expand All @@ -213,7 +221,7 @@ exports = module.exports = PeerIdWithIs

const computeDigest = (pubKey) => {
if (pubKey.bytes.length <= 42) {
return mh.encode(pubKey.bytes, 'identity')
return Digest.create(identity.code, pubKey.bytes).bytes
} else {
return pubKey.hash()
}
Expand All @@ -235,26 +243,46 @@ exports.create = async (opts) => {
}

exports.createFromHexString = (str) => {
return new PeerIdWithIs(mh.fromHexString(str))
return new PeerIdWithIs(base16.decode('f' + str))
}

exports.createFromBytes = (buf) => {
return new PeerIdWithIs(buf)
try {
const cid = CID.decode(buf)

if (!validMulticodec(cid)) {
throw new Error('Supplied PeerID CID is invalid')
}

return exports.createFromCID(cid)
} catch {
const digest = Digest.decode(buf)

if (digest.code !== identity.code) {
throw new Error('Supplied PeerID CID is invalid')
}

return new PeerIdWithIs(buf)
}
}

exports.createFromB58String = (str) => {
return exports.createFromCID(str) // B58String is CIDv0
return exports.createFromBytes(base58btc.decode('z' + str))
}

const validMulticodec = (cid) => {
// supported: 'libp2p-key' (CIDv1) and 'dag-pb' (CIDv0 converted to CIDv1)
return cid.codec === 'libp2p-key' || cid.codec === 'dag-pb'
return cid.code === LIBP2P_KEY_CODE || cid.code === DAG_PB_CODE
}

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)
cid = CID.asCID(cid)

if (!cid || !validMulticodec(cid)) {
throw new Error('Supplied PeerID CID is invalid')
}

return new PeerIdWithIs(cid.multihash.bytes)
}

// Public Key input will be a Uint8Array
Expand Down Expand Up @@ -288,7 +316,7 @@ exports.createFromPrivKey = async (key) => {
}

exports.createFromJSON = async (obj) => {
const id = mh.fromB58String(obj.id)
const id = base58btc.decode('z' + 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)
Expand Down Expand Up @@ -360,6 +388,16 @@ exports.createFromProtobuf = async (buf) => {
throw new Error('Protobuf did not contain any usable key material')
}

exports.parse = (str) => {
if (str.charAt(0) === '1') {
// base58btc encoded public key
return exports.createFromBytes(base58btc.decode(`z${str}`))
}

// try to parse it as a regular base58btc multihash or base32 encoded CID
return exports.createFromCID(CID.parse(str))
}

exports.isPeerId = (peerId) => {
return Boolean(typeof peerId === 'object' &&
peerId._id &&
Expand Down
Loading