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

feat(hasher): add blake3 #395

Merged
merged 7 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading