Skip to content

Commit

Permalink
chore: update to new multiformats (#149)
Browse files Browse the repository at this point in the history
Replaces multihashes with the new multiformats module

BREAKING CHANGE: uses the CID class from the new multiformats module

Co-authored-by: Vasco Santos <vasco.santos@moxy.studio>
  • Loading branch information
achingbrain and vasco-santos committed Jul 6, 2021
1 parent 173b3b3 commit 69d7a01
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 53 deletions.
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

0 comments on commit 69d7a01

Please sign in to comment.