Converting an IPFS Hash to 32 Bytes

What is an IPFS hash anyway? Take for example, ‘hello world’. When we add it to IPFS it generates this hash:

Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD

Breeeaak it down one time. [Qm] - two bytes Q is the function code, the m is the digest length (e.g the length of the rest of the hash). This is called a multihash. SO 'Q' is the SHA-256 algorithm and 'm' means a length of 32 bytes.

But thats not all. The hash is encoded using base58.

Base 58

Like base64 but doesn't use any non-alphanumeric characters. Why? Because it is intended for humans. Similar looking letters are also removed, such as 0/O, I/l. So this format is used on IPFS hashes as well as Bitcoin et al.

Ok, so what is my point? Well, Say we want to store this hash in Solidity using 32 bytes. You can see we have an extra couple of bytes. In current builds, IPFS use 'Qm'. It depends on the protocol used for encryption, so beware, it may not always be 'Qm'.

Using bs58 we can:

  • decode the base58
  • remove the prefixed two bytes
  • convert to hex
  • reverse the process

My first crack at the decode function was this:

'Qm' + bs58.encode(Buffer.from(short.slice(2), 'hex'))

But the 'Qm' part is incorrect. The actual way to do it is prefix with hex '1220', then encode:

const shorten = (hash) => '0x' + bs58.decode(hash).slice(2).toString('hex')

const lengthen = (short) => bs58.encode(Buffer.from('1220' + short.slice(2), 'hex'))

So in this way we can store and recreate hashes in Solidity.

References