Multiformats

From Organic Design wiki

Multiformats is a collection of protocols which aim to future-proof systems, today. They do this mainly by allowing data to be self-describable. This allows interoperability, protocol agility, and helps us avoid lock in.

Concepts

Multihash

Multiaddr

Multicodec

Multibase

CID

A CID is a content identifier and is created by assigning some content with a code that specifies the content type.

Peer ID

A peer ID the hash or CID of an encoded public key.

Varints

A lot of the multi* specs use variable size integers.

Code Examples

Peer ID

JS

In this example we will be using the noble-ed25519 for key generation and the js-multiformat libraries.


Install the libraries:

npm i @noble/ed25519 multiformats


Import the required libraries:

const ed = require("@noble/ed25519");
const CID = require("multiformats/cid").CID;
const identity = require("multiformats/hashes/identity").identity;
const base58 = require("multiformats/bases/base58").base58btc;
const base32 = require("multiformats/bases/base32").base32;

Generate a keypair using noble-ed25519

const privateKey = ed.utils.randomPrivateKey();
const publicKey = await ed.getPublicKey(privateKey);

Now we would need to encode the public key, however since we are using ed25519 we do not need any specific encoding before hashing. This public key is less than (or equal to) 42 bytes long so we will not actually hash it, if it were over 42 bytes we would need to hash it with SHA256 as per the specification. Just because we don't need to hash it doesn't mean we can skip converting it into a multihash first, this conversion without hashing can be done using the identity hash.

const encoded = identity.encode(publicKey);
const hash = await identity.digest(encoded);

You can generate a legacy CID from here by converting the hash to base 58 as per the specification.

const rawBase58 = base58.encode(hash.bytes);

This however has an issue: this is in a multibase format, the legacy CIDs are implicit so we will need to drop the first character of this string ('z') as per the multibase specification.

const legacyCid = rawBase58.substring(1);

You should note that this string should start with a '1' if identity was used or 'Qm' if SHA256 was used.

if (legacyCid.slice(0, 2) !== "Qm" && legacyCid.slice(0, 1) !== "1")
	throw new Error("lecacy cid is in the wrong format");

After that distraction of generating a legacy CID we will generate one in the new format. Note that we use the code '0x72' (11410) which is the identifier for 'libp2p-key' content as per the specification table. The first parameter '1' refers to CID version.

const cid = CID.create(1, 0x72, hash);

Now that we have a CID we just need to convert it into a string, we use base32 since that is what the specification says is the default.

const identifier = cid.toString(base32);

Now that we have generated the CID string we can output it and check it using the inspector.

console.log(identifier);

Note that the multihash adds 2 more bytes to the public key, multiformat has added an additional 2 bytes for the encoding and multibase has added 'b' to it (1 byte in ascii).


You can convert a CID back to a hash in a similar way.

const parsedCid = CID.parse(identifier);

if (parsedCid.code !== 0x72)
	throw new Error("cid is the wrong type");

if (parsedCid.toString() !== cid.toString())
	throw new Error("cid did not convert back");

if (JSON.stringify(parsedCid.multihash) !== JSON.stringify(hash))
	throw new Error("cid did not provide the correct hash");

const parsedHash = parsedCid.multihash.digest;

if (parsedHash.toString() !== encoded.toString())
	throw new Error("hash did not provide the correct digest");

See Also