Skip to content

Commit

Permalink
fix(hasher): Introducing Blake3-multihash (#395)
Browse files Browse the repository at this point in the history
* feat(blake3): ➕ Adding blake3-multihash

Signed-off-by: Nishant Arora <[email protected]>

* feat(blake3): 👔 Adding blake3 hasher

Signed-off-by: Nishant Arora <[email protected]>

* fix: ♻️ Addressing comments

Signed-off-by: Nishant Arora <[email protected]>

* fix: 🔀 Marking dep as peerDep

Signed-off-by: Nishant Arora <[email protected]>

---------

Signed-off-by: Nishant Arora <[email protected]>
  • Loading branch information
whizzzkid authored Nov 30, 2023
1 parent 5760e1d commit 5a37a5e
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 10 deletions.
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,29 @@ import 'ipld-explorer-components/dist/components/loader/Loader.css'

**NOTE:** PRs adding an old IPLDFormat codec would need the old `blockcodec-to-ipld-format` tool which has many out of date deps. We will only accept PRs for adding BlockCodec interface codecs.

To add another codec you will need to:
To add another codec you will need to update all locations containing the comment `// #WhenAddingNewCodec`:

1. Add a dependency on the codec to this package
1. Add the codec in the switch statement in src/lib/codec-importer.ts
1. Add a unit test to src/lib/resolve-ipld-path.test.js and ensure that calling `resolveIpldPath` returns the expected results
* If the default `resolveFn` in src/lib/get-codec-for-cid.ts doesn't resolve your paths correctly, you will need to add a resolver method for your codec to the `codecResolverMap` in src/lib/get-codec-for-cid.ts
1. Add a dependency on the codec to this package (if it's not already in multiformats or other package)
1. Add the codec in the switch statement in [./src/lib/codec-importer.ts](./src/lib/codec-importer.ts)
1. Update [./src/lib/get-codec-name-from-code.ts](./src/lib/get-codec-name-from-code.ts) to return the codec name for your codec
1. Add a unit test to [./src/lib/resolve-ipld-path.test.js](./src/lib/resolve-ipld-path.test.js) and ensure that calling `resolveIpldPath` returns the expected results
* If the default `resolveFn` in [./src/lib/get-codec-for-cid.ts](./src/lib/get-codec-for-cid.ts) doesn't resolve your paths correctly, you will need to add a resolver method for your codec to the `codecResolverMap` in [./src/lib/get-codec-for-cid.ts](./src/lib/get-codec-for-cid.ts)

see https://github.com/ipfs/ipld-explorer-components/pull/360#discussion_r1206251817 for history.

### Adding another hasher

To add another hasher you will need to update all locations containing the comment `// #WhenAddingNewHasher`:

1. Add a dependency on the hasher to this package (if it's not already in multiformats or other package)
1. Add the hasher in the switch statement in [./src/lib/get-codec-for-cid.ts](./src/lib/get-codec-for-cid.ts)
1. Update [./src/lib/hash-importer.ts](./src/lib/hash-importer.ts)
- Update `SupportedHashers` to include your hasher type
- Update `getHasherForCode` to return your hasher
1. Update the hasher codes used by the `hashers` property passed to Helia init in [./src/lib/init-helia.ts](./src/lib/init-helia.ts)

see https://github.com/ipfs/ipld-explorer-components/pull/395 for an example.

### Redux-bundler requirements

These components use [redux-bundler](https://reduxbundler.com/) and your app will need to use a redux-bundler provider in order to propogate the properties and selectors. You can find a basic example of this in ./dev/devPage.jsx.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@
},
"peerDependencies": {
"@loadable/component": "^5.14.1",
"blake3-multihash": "^0.0.4",
"i18next": "^21.6.16",
"i18next-browser-languagedetector": "^6.1.0",
"i18next-http-backend": "^1.2.1",
Expand Down
6 changes: 6 additions & 0 deletions src/components/StartExploringPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ const StartExploringPage = ({ t, embed, runTour = false, joyrideCallback }) => (
*/}
<ExploreSuggestion name='Ceramic LogEntry for sgb.chat Ambassador proposal' cid='bagcqcerarvdwmhvk73mze3e2n6yvpt5h7fh3eae7n6y3hizsflz5grpyeczq' type='dag-jose' />
</li>
<li>
<ExploreSuggestion name='hello world (blake3)' cid='bagaaihraf4oq2kddg6o5ewlu6aol6xab75xkwbgzx2dlot7cdun7iirve23a' type='dag-json' />
</li>
<li>
<ExploreSuggestion name='hello world' cid='baguqeerasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea' type='dag-json' />
</li>
Expand All @@ -68,6 +71,9 @@ const StartExploringPage = ({ t, embed, runTour = false, joyrideCallback }) => (
<li>
<ExploreSuggestion name='Raw Block for "hello"' cid='bafkreibm6jg3ux5qumhcn2b3flc3tyu6dmlb4xa7u5bf44yegnrjhc4yeq' type='raw' />
</li>
<li>
<ExploreSuggestion name='Raw Block for "hello" (blake3)' cid='bafkr4ihkr4ld3m4gqkjf4reryxsy2s5tkbxprqkow6fin2iiyvreuzzab4' type='raw' />
</li>
</ul>
</div>
<div className='pt2-l'>
Expand Down
1 change: 1 addition & 0 deletions src/lib/codec-importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default async function codecImporter<T extends CodecDataTypes = CodecData
codecName = getCodecNameFromCode(codeOrName)
}

// #WhenAddingNewCodec
switch (codecName) {
case 'dag-cbor':
return import('@ipld/dag-cbor')
Expand Down
1 change: 1 addition & 0 deletions src/lib/get-codec-for-cid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ interface CodecResolverFn {
(node: PBNode | unknown, path: string): Promise<ResolveType<PBNode | PBLink>>
}

// #WhenAddingNewCodec (maybe)
const codecResolverMap: Record<string, CodecResolverFn> = {
'dag-pb': async (node, path) => {
if (!isPBNode(node)) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/get-codec-name-from-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Converts supported codec codes from https://github.com/multiformats/multicodec/blob/master/table.csv to their names.
*/
export default function getCodecNameFromCode (code: number): string {
// #WhenAddingNewCodec
switch (code) {
case 113:
return 'dag-cbor'
Expand Down
17 changes: 14 additions & 3 deletions src/lib/hash-importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import * as sha3 from '@multiformats/sha3'
import { type Hasher, from } from 'multiformats/hashes/hasher'
import * as sha2 from 'multiformats/hashes/sha2'

// #WhenAddingNewHasher
export type SupportedHashers = typeof sha2.sha256 |
typeof sha2.sha512 |
Hasher<'keccak-256', 27> |
Hasher<'sha1', 17> |
Hasher<'blake2b-512', 45632> |
Hasher<'sha3-512', 20>
Hasher<'sha3-512', 20> |
Hasher<'blake3', 30>

export async function getHashersForCodes (code: number, ...codes: number[]): Promise<SupportedHashers[]> {
return Promise.all(codes.map(getHasherForCode))
Expand All @@ -26,6 +28,7 @@ function getBoundHasher <T extends SupportedHashers> (hasher: T): T {
}

export async function getHasherForCode (code: number): Promise<SupportedHashers> {
// #WhenAddingNewHasher
switch (code) {
case sha2.sha256.code:
return getBoundHasher(sha2.sha256)
Expand All @@ -36,7 +39,7 @@ export async function getHasherForCode (code: number): Promise<SupportedHashers>
return getBoundHasher(from({
name: 'sha1',
code,
encode: async (data: Uint8Array) => {
encode: async (data: Uint8Array): Promise<Uint8Array> => {
const crypto = globalThis.crypto ?? (await import('crypto')).webcrypto
const hashBuffer = await crypto.subtle.digest('SHA-1', data)
return new Uint8Array(hashBuffer)
Expand All @@ -46,7 +49,15 @@ export async function getHasherForCode (code: number): Promise<SupportedHashers>
return getBoundHasher(sha3.sha3512)
case sha3.keccak256.code: // keccak-256
return getBoundHasher(sha3.keccak256)

case 30:
return getBoundHasher(from({
name: 'blake3',
code,
encode: async (data: Uint8Array): Promise<Uint8Array> => {
const { digest } = await import('blake3-multihash')
return (await digest(data)).digest
}
}))
default:
throw new Error(`unknown multihasher code '${code}'`)
}
Expand Down
7 changes: 5 additions & 2 deletions src/lib/init-helia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export default async function initHelia (kuboGatewayOptions: KuboGatewayOptions)
trustlessGateway(),
trustlessGateway({ gateways: [`${kuboGatewayOptions.protocol ?? 'http'}://${kuboGatewayOptions.host}:${kuboGatewayOptions.port}`] })
],
hashers: await getHashersForCodes(17, 18, 19, 20, 27),
// #WhenAddingNewHasher
hashers: await getHashersForCodes(17, 18, 19, 20, 27, 30),
datastore,
blockstore,
// @ts-expect-error - libp2p types are borked right now
Expand All @@ -71,7 +72,9 @@ export default async function initHelia (kuboGatewayOptions: KuboGatewayOptions)
await Promise.allSettled([
addDagNodeToHelia(helia, 'dag-json', { hello: 'world' }), // baguqeerasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea
addDagNodeToHelia(helia, 'dag-cbor', { hello: 'world' }, 27), // bafyrwigbexamue2ba3hmtai7hwlcmd6ekiqsduyf5avv7oz6ln3radvjde
addDagNodeToHelia(helia, 'json', { hello: 'world' }, 20) // bagaaifcavabu6fzheerrmtxbbwv7jjhc3kaldmm7lbnvfopyrthcvod4m6ygpj3unrcggkzhvcwv5wnhc5ufkgzlsji7agnmofovc2g4a3ui7ja
addDagNodeToHelia(helia, 'json', { hello: 'world' }, 20), // bagaaifcavabu6fzheerrmtxbbwv7jjhc3kaldmm7lbnvfopyrthcvod4m6ygpj3unrcggkzhvcwv5wnhc5ufkgzlsji7agnmofovc2g4a3ui7ja
addDagNodeToHelia(helia, 'json', { hello: 'world' }, 30), // bagaaihraf4oq2kddg6o5ewlu6aol6xab75xkwbgzx2dlot7cdun7iirve23a
addDagNodeToHelia(helia, 'raw', (new TextEncoder()).encode('hello'), 30) // bafkr4ihkr4ld3m4gqkjf4reryxsy2s5tkbxprqkow6fin2iiyvreuzzab4
])

return helia
Expand Down
1 change: 1 addition & 0 deletions src/lib/resolve-ipld-path.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { addDagNodeToHelia } from './helpers'
import resolveIpldPath, { findLinkPath } from './resolve-ipld-path'
import { createHeliaMock } from '../../test/unit/heliaMock'

// #WhenAddingNewCodec
describe('resolveIpldPath', () => {
/**
* @type {import('@helia/interface').Helia}
Expand Down

0 comments on commit 5a37a5e

Please sign in to comment.