feat(ipns): add CID support for publish, republishRecord, and resolve#852
feat(ipns): add CID support for publish, republishRecord, and resolve#852Harshdev098 wants to merge 1 commit intoipfs:mainfrom Harshdev098:patch-1
Conversation
tabcat
left a comment
There was a problem hiding this comment.
Did not mean to include publish in the request for changes.
The original issue had to do with usage of the delegated routing client's IPNS methods.
The routing key inputs for those methods are not compatible with these methods in @helia/ipns which caused some issues for me.
I'd be interested in knowing why they take so many different types. It may be good to look at the commit history.
I know the delegated router needs the libp2p key as a CID because that is how it is encoded in the url. But these methods only need the multihash.
| // Handle value conversion to string path, with CID support for IPNS names | ||
| let valuePath: string | ||
| if (CID.asCID(value)) { | ||
| const cid = value as CID | ||
| if (cid.code === LIBP2P_KEY_CODEC) { | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(cid.multihash.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS value') | ||
| } | ||
| valuePath = `/ipns/${cid.toString(base36)}` | ||
| } else { | ||
| valuePath = `/ipfs/${cid.toString()}` | ||
| } | ||
| } else if (typeof value === 'string') { | ||
| valuePath = value | ||
| } else if (isPublicKey(value)) { | ||
| const publicKey = value as PublicKey | ||
| const mh = publicKey.toMultihash() | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(mh.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS value') | ||
| } | ||
| const cid = CID.create(1, LIBP2P_KEY_CODEC, mh) | ||
| valuePath = `/ipns/${cid.toString(base36)}` | ||
| } else { | ||
| // Must be MultihashDigest<0x00 | 0x12> | ||
| const mh = value as MultihashDigest<0x00 | 0x12> | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(mh.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS value') | ||
| } | ||
| const cid = CID.create(1, LIBP2P_KEY_CODEC, mh) | ||
| valuePath = `/ipns/${cid.toString(base36)}` | ||
| } | ||
|
|
||
| // convert ttl from milliseconds to nanoseconds as createIPNSRecord expects | ||
| const ttlNs = options.ttl != null ? BigInt(options.ttl) * 1_000_000n : DEFAULT_TTL_NS | ||
| const record = await createIPNSRecord(key, value, sequenceNumber, options.lifetime ?? DEFAULT_LIFETIME_MS, { ...options, ttlNs }) |
There was a problem hiding this comment.
valuePath is never being used and createIPNSRecord already accepts a CID type.
Publish already supported a CID as a value. I don't remember why a change was requested for this now. This hunk can be removed.
|
|
||
| async resolve (key: PublicKey | MultihashDigest<0x00 | 0x12>, options: ResolveOptions = {}): Promise<IPNSResolveResult> { | ||
| const digest = isPublicKey(key) ? key.toMultihash() : key | ||
| async resolve (key: CID | PublicKey | MultihashDigest<0x00 | 0x12>, options: ResolveOptions = {}): Promise<IPNSResolveResult> { |
There was a problem hiding this comment.
Narrow the CID type by specifying the generic types for the code and multihash code. Should match the type from https://github.com/ipfs/helia-delegated-routing-v1-http-api/blob/00aceb97918530d7c17b8256bfb1104d34f19080/packages/client/src/client.ts#L246
| if (CID.asCID(key)) { | ||
| const cid = key as CID | ||
| if (cid.code !== LIBP2P_KEY_CODEC) { | ||
| throw new Error('CID must use libp2p-key codec (0x72) for IPNS names') | ||
| } | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(cid.multihash.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS key') | ||
| } | ||
| digest = cid.multihash as MultihashDigest<0x00 | 0x12> | ||
| } else if (isPublicKey(key)) { | ||
| const publicKey = key as PublicKey | ||
| const mh = publicKey.toMultihash() as MultihashDigest<0x00 | 0x12> | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(mh.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS key') | ||
| } | ||
| digest = mh | ||
| } else { | ||
| const mh = key as MultihashDigest<0x00 | 0x12> | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(mh.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS key') | ||
| } | ||
| digest = mh | ||
| } | ||
|
|
There was a problem hiding this comment.
I would leave this out and simply normalize the key parameter into a digest like before.
Check if key is CID then return key.multihash and it fits the type of MultihashDigest<0x00 | 0x12>
| async republishRecord (key: MultihashDigest<0x00 | 0x12> | string, record: IPNSRecord, options: RepublishRecordOptions = {}): Promise<void> { | ||
| async republishRecord (key: CID | MultihashDigest<0x00 | 0x12> | string, record: IPNSRecord, options: RepublishRecordOptions = {}): Promise<void> { | ||
| let mh: MultihashDigest<0x00 | 0x12> | undefined | ||
| try { | ||
| mh = extractPublicKeyFromIPNSRecord(record)?.toMultihash() // embedded public key take precedence, if present | ||
| if (mh == null) { | ||
| // if no public key is embedded in the record, use the key that was passed in | ||
| if (typeof key === 'string') { | ||
| if (CID.asCID(key)) { | ||
| const cid = key as CID | ||
| if (cid.code !== LIBP2P_KEY_CODEC) { | ||
| throw new Error('CID must use libp2p-key codec (0x72) for IPNS names') | ||
| } | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(cid.multihash.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS key') | ||
| } | ||
| mh = cid.multihash as MultihashDigest<0x00 | 0x12> | ||
| } else if (typeof key === 'string') { | ||
| let parsedKey = key | ||
| if (key.startsWith(IPNS_STRING_PREFIX)) { | ||
| // remove the /ipns/ prefix from the key | ||
| key = key.slice(IPNS_STRING_PREFIX.length) | ||
| parsedKey = key.slice(IPNS_STRING_PREFIX.length) | ||
| } | ||
| // Convert string key to MultihashDigest | ||
| // Preferentially try to parse as CID | ||
| try { | ||
| mh = peerIdFromString(key).toMultihash() | ||
| } catch (err: any) { | ||
| throw new Error(`Invalid string key: ${err.message}`) | ||
| const cid = CID.parse(parsedKey) | ||
| if (cid.code === LIBP2P_KEY_CODEC) { | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(cid.multihash.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS key') | ||
| } | ||
| mh = cid.multihash as MultihashDigest<0x00 | 0x12> | ||
| } else { | ||
| throw new Error('Invalid CID codec for IPNS name') | ||
| } | ||
| } catch (err) { | ||
| try { | ||
| mh = peerIdFromString(parsedKey).toMultihash() as MultihashDigest<0x00 | 0x12> | ||
| } catch (e: any) { | ||
| throw new Error(`Invalid string key: ${e.message}`) | ||
| } | ||
| } | ||
| } else { | ||
| mh = key | ||
| const digest = key as MultihashDigest<0x00 | 0x12> | ||
| if (![IDENTITY_CODEC, SHA2_256_CODEC].includes(digest.code)) { | ||
| throw new Error('Unsupported multihash code for IPNS key') | ||
| } | ||
| mh = digest |
There was a problem hiding this comment.
The only change needed was to normalize key to multihash if it was a CID.
|
Closing in favour of #861 @Harshdev098 thanks for taking the time to open this. In future please read the issues carefully and add tests to your PRs - it's not possible to accept untested code as there is nothing to prevent a future regression. |
Fixes: #801
Added CID encoded IPNS key in
publish,republishRecordandresolvemethods