From dd79c5004429706ca932f9365a10679addc5fa07 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Sep 2024 10:50:16 +0000
Subject: [PATCH 1/4] deps: bump @libp2p/logger from 4.0.20 to 5.0.0
Bumps [@libp2p/logger](https://github.com/libp2p/js-libp2p) from 4.0.20 to 5.0.0.
- [Release notes](https://github.com/libp2p/js-libp2p/releases)
- [Changelog](https://github.com/libp2p/js-libp2p/blob/main/.release-please.json)
- [Commits](https://github.com/libp2p/js-libp2p/compare/logger-v4.0.20...utils-v5.0.0)
---
updated-dependencies:
- dependency-name: "@libp2p/logger"
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
packages/client/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/client/package.json b/packages/client/package.json
index 3149672..b11aa8e 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -46,7 +46,7 @@
},
"dependencies": {
"@libp2p/interface": "^1.1.1",
- "@libp2p/logger": "^4.0.4",
+ "@libp2p/logger": "^5.0.0",
"@libp2p/peer-id": "^4.0.4",
"@multiformats/multiaddr": "^12.1.3",
"any-signal": "^4.1.1",
From d240ac0c130168b27c860f5abe3f928ef4e067bd Mon Sep 17 00:00:00 2001
From: achingbrain
Date: Sun, 15 Sep 2024 12:21:54 +0100
Subject: [PATCH 2/4] chore: update deps
---
.github/dependabot.yml | 2 +-
README.md | 20 +--
package.json | 91 +------------
packages/client/README.md | 31 +++++
packages/client/package.json | 120 +++++++++++++++---
packages/client/src/client.ts | 57 +++++----
packages/client/src/errors.ts | 17 +++
packages/client/src/index.ts | 8 +-
packages/client/src/routings.ts | 36 +++---
packages/client/test/index.spec.ts | 61 ++++-----
packages/client/test/routings.spec.ts | 51 ++++----
packages/client/typedoc.json | 4 +-
packages/interop/package.json | 116 ++++++++++++++---
packages/interop/test/index.spec.ts | 23 ++--
packages/server/README.md | 14 +-
packages/server/package.json | 110 ++++++++++++++--
packages/server/src/constants.ts | 2 +
.../server/src/routes/routing/v1/ipns/get.ts | 23 +++-
.../server/src/routes/routing/v1/ipns/put.ts | 26 ++--
packages/server/test/index.spec.ts | 37 +++---
packages/server/typedoc.json | 4 +-
21 files changed, 547 insertions(+), 306 deletions(-)
create mode 100644 packages/client/src/errors.ts
create mode 100644 packages/server/src/constants.ts
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 0bc3b42..d401a77 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -5,7 +5,7 @@ updates:
schedule:
interval: daily
time: "10:00"
- open-pull-requests-limit: 10
+ open-pull-requests-limit: 20
commit-message:
prefix: "deps"
prefix-development: "deps(dev)"
diff --git a/README.md b/README.md
index 33d217c..91c58f4 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
+# helia-delegated-routing-v1-http-api
+
[](https://ipfs.tech)
[](https://discuss.ipfs.tech)
[](https://codecov.io/gh/ipfs/helia-delegated-routing-v1-http-api)
@@ -15,24 +17,24 @@
This repo contains a server implementation of the IPFS [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) along with a client that can be used to interact with any compliant server implementation.
-## Packages
+# Packages
-- [`/packages/client`](./packages/client) A Delegated Routing V1 HTTP API client
-- [`/packages/interop`](./packages/interop) Interop tests for the Delegated Routing V1 HTTP API server powered by Helia
-- [`/packages/server`](./packages/server) A Delegated Routing V1 HTTP API server powered by Helia
+- [`packages/client`](https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/main/packages/client) A Delegated Routing V1 HTTP API client
+- [`packages/interop`](https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/main/packages/interop) Interop tests for the Delegated Routing V1 HTTP API server powered by Helia
+- [`packages/server`](https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/main/packages/server) A Delegated Routing V1 HTTP API server powered by Helia
-## API Docs
+# API Docs
-
-## License
+# License
Licensed under either of
-- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / )
-- MIT ([LICENSE-MIT](LICENSE-MIT) / )
+- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/helia-delegated-routing-v1-http-api/blob/main/LICENSE-APACHE) / )
+- MIT ([LICENSE-MIT](https://github.com/ipfs/helia-delegated-routing-v1-http-api/blob/main/LICENSE-MIT) / )
-## Contribute
+# Contribute
Contributions welcome! Please check out [the issues](https://github.com/ipfs/helia-delegated-routing-v1-http-api/issues).
diff --git a/package.json b/package.json
index 39b63cf..610fe7e 100644
--- a/package.json
+++ b/package.json
@@ -37,95 +37,12 @@
},
"devDependencies": {
"aegir": "^44.1.1",
- "npm-run-all": "^4.1.5"
+ "npm-run-all": "^4.1.5",
+ "patch-package": "^8.0.0",
+ "rimraf": "^6.0.1"
},
"type": "module",
"workspaces": [
"packages/*"
- ],
- "release": {
- "branches": [
- "main"
- ],
- "plugins": [
- [
- "@semantic-release/commit-analyzer",
- {
- "preset": "conventionalcommits",
- "releaseRules": [
- {
- "breaking": true,
- "release": "major"
- },
- {
- "revert": true,
- "release": "patch"
- },
- {
- "type": "feat",
- "release": "minor"
- },
- {
- "type": "fix",
- "release": "patch"
- },
- {
- "type": "docs",
- "release": "patch"
- },
- {
- "type": "test",
- "release": "patch"
- },
- {
- "type": "deps",
- "release": "patch"
- },
- {
- "scope": "no-release",
- "release": false
- }
- ]
- }
- ],
- [
- "@semantic-release/release-notes-generator",
- {
- "preset": "conventionalcommits",
- "presetConfig": {
- "types": [
- {
- "type": "feat",
- "section": "Features"
- },
- {
- "type": "fix",
- "section": "Bug Fixes"
- },
- {
- "type": "chore",
- "section": "Trivial Changes"
- },
- {
- "type": "docs",
- "section": "Documentation"
- },
- {
- "type": "deps",
- "section": "Dependencies"
- },
- {
- "type": "test",
- "section": "Tests"
- }
- ]
- }
- }
- ],
- "@semantic-release/changelog",
- "@semantic-release/npm",
- "@semantic-release/github",
- "@semantic-release/git"
- ]
- }
+ ]
}
diff --git a/packages/client/README.md b/packages/client/README.md
index bfa616a..772aa39 100644
--- a/packages/client/README.md
+++ b/packages/client/README.md
@@ -4,6 +4,8 @@
+# @helia/delegated-routing-v1-http-api-client
+
[](https://ipfs.tech)
[](https://discuss.ipfs.tech)
[](https://codecov.io/gh/ipfs/helia-delegated-routing-v1-http-api)
@@ -50,3 +52,32 @@ const libp2p = await createLibp2p({
// later this will use the configured HTTP gateway
await libp2p.peerRouting.findPeer(peerIdFromString('QmFoo'))
```
+
+# Install
+
+```console
+$ npm i @helia/delegated-routing-v1-http-api-client
+```
+
+# API Docs
+
+-
+
+# License
+
+Licensed under either of
+
+- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/helia-delegated-routing-v1-http-api/blob/main/packages/client/LICENSE-APACHE) / )
+- MIT ([LICENSE-MIT](https://github.com/ipfs/helia-delegated-routing-v1-http-api/blob/main/packages/client/LICENSE-MIT) / )
+
+# Contribute
+
+Contributions welcome! Please check out [the issues](https://github.com/ipfs/helia-delegated-routing-v1-http-api/issues).
+
+Also see our [contributing document](https://github.com/ipfs/community/blob/master/CONTRIBUTING_JS.md) for more information on how we work, and about contributing in general.
+
+Please be aware that all interactions related to this repo are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
+
+Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
+
+[](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md)
diff --git a/packages/client/package.json b/packages/client/package.json
index b11aa8e..1a2494b 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -3,7 +3,7 @@
"version": "3.0.1",
"description": "A Delegated Routing V1 HTTP API client",
"license": "Apache-2.0 OR MIT",
- "homepage": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/master/packages/client#readme",
+ "homepage": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/main/packages/client#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/ipfs/helia-delegated-routing-v1-http-api.git"
@@ -11,6 +11,10 @@
"bugs": {
"url": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/issues"
},
+ "publishConfig": {
+ "access": "public",
+ "provenance": true
+ },
"keywords": [
"IPFS"
],
@@ -35,6 +39,91 @@
"sourceType": "module"
}
},
+ "release": {
+ "branches": [
+ "main"
+ ],
+ "plugins": [
+ [
+ "@semantic-release/commit-analyzer",
+ {
+ "preset": "conventionalcommits",
+ "releaseRules": [
+ {
+ "breaking": true,
+ "release": "major"
+ },
+ {
+ "revert": true,
+ "release": "patch"
+ },
+ {
+ "type": "feat",
+ "release": "minor"
+ },
+ {
+ "type": "fix",
+ "release": "patch"
+ },
+ {
+ "type": "docs",
+ "release": "patch"
+ },
+ {
+ "type": "test",
+ "release": "patch"
+ },
+ {
+ "type": "deps",
+ "release": "patch"
+ },
+ {
+ "scope": "no-release",
+ "release": false
+ }
+ ]
+ }
+ ],
+ [
+ "@semantic-release/release-notes-generator",
+ {
+ "preset": "conventionalcommits",
+ "presetConfig": {
+ "types": [
+ {
+ "type": "feat",
+ "section": "Features"
+ },
+ {
+ "type": "fix",
+ "section": "Bug Fixes"
+ },
+ {
+ "type": "chore",
+ "section": "Trivial Changes"
+ },
+ {
+ "type": "docs",
+ "section": "Documentation"
+ },
+ {
+ "type": "deps",
+ "section": "Dependencies"
+ },
+ {
+ "type": "test",
+ "section": "Tests"
+ }
+ ]
+ }
+ }
+ ],
+ "@semantic-release/changelog",
+ "@semantic-release/npm",
+ "@semantic-release/github",
+ "@semantic-release/git"
+ ]
+ },
"scripts": {
"clean": "aegir clean",
"lint": "aegir lint",
@@ -45,26 +134,25 @@
"release": "aegir release"
},
"dependencies": {
- "@libp2p/interface": "^1.1.1",
- "@libp2p/logger": "^5.0.0",
- "@libp2p/peer-id": "^4.0.4",
- "@multiformats/multiaddr": "^12.1.3",
+ "@libp2p/interface": "^2.0.1",
+ "@libp2p/logger": "^5.0.1",
+ "@libp2p/peer-id": "^5.0.1",
+ "@multiformats/multiaddr": "^12.3.1",
"any-signal": "^4.1.1",
- "browser-readablestream-to-it": "^2.0.3",
- "ipns": "^9.0.0",
- "it-first": "^3.0.3",
- "it-map": "^3.0.4",
- "it-ndjson": "^1.0.4",
- "multiformats": "^13.0.0",
- "p-defer": "^4.0.0",
+ "browser-readablestream-to-it": "^2.0.7",
+ "ipns": "^10.0.0",
+ "it-first": "^3.0.6",
+ "it-map": "^3.1.1",
+ "it-ndjson": "^1.0.7",
+ "multiformats": "^13.2.3",
+ "p-defer": "^4.0.1",
"p-queue": "^8.0.1",
- "uint8arrays": "^5.0.1"
+ "uint8arrays": "^5.1.0"
},
"devDependencies": {
- "@libp2p/peer-id-factory": "^4.0.4",
"aegir": "^44.1.1",
- "body-parser": "^1.20.2",
- "it-all": "^3.0.2"
+ "body-parser": "^1.20.3",
+ "it-all": "^3.0.6"
},
"sideEffects": false
}
diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts
index 706e05d..fb01200 100644
--- a/packages/client/src/client.ts
+++ b/packages/client/src/client.ts
@@ -1,14 +1,15 @@
-import { contentRoutingSymbol, peerRoutingSymbol, CodeError, setMaxListeners } from '@libp2p/interface'
+import { NotFoundError, contentRoutingSymbol, peerRoutingSymbol, setMaxListeners } from '@libp2p/interface'
import { logger } from '@libp2p/logger'
import { peerIdFromString } from '@libp2p/peer-id'
import { multiaddr } from '@multiformats/multiaddr'
import { anySignal } from 'any-signal'
import toIt from 'browser-readablestream-to-it'
-import { unmarshal, type IPNSRecord, marshal, peerIdToRoutingKey } from 'ipns'
+import { unmarshalIPNSRecord, type IPNSRecord, marshalIPNSRecord, multihashToIPNSRoutingKey } from 'ipns'
import { ipnsValidator } from 'ipns/validator'
import { parse as ndjson } from 'it-ndjson'
import defer from 'p-defer'
import PQueue from 'p-queue'
+import { BadResponseError, InvalidRequestError } from './errors.js'
import { DelegatedRoutingV1HttpApiClientContentRouting, DelegatedRoutingV1HttpApiClientPeerRouting } from './routings.js'
import type { DelegatedRoutingV1HttpApiClient, DelegatedRoutingV1HttpApiClientInit, GetIPNSOptions, PeerRecord } from './index.js'
import type { ContentRouting, PeerRouting, AbortOptions, PeerId } from '@libp2p/interface'
@@ -93,18 +94,18 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
if (res.status === 404) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
- // 404 (Not Found): must be returned if no matching records are found.
- throw new CodeError('No matching records found.', 'ERR_NOT_FOUND')
+ // 404 (Not Found): must be returned if no matching records are found
+ throw new NotFoundError('No matching records found')
}
if (res.status === 422) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
- // 422 (Unprocessable Entity): request does not conform to schema or semantic constraints.
- throw new CodeError('Request does not conform to schema or semantic constraints.', 'ERR_INVALID_REQUEST')
+ // 422 (Unprocessable Entity): request does not conform to schema or semantic constraints
+ throw new InvalidRequestError('Request does not conform to schema or semantic constraints')
}
if (res.body == null) {
- throw new CodeError('Routing response had no body', 'ERR_BAD_RESPONSE')
+ throw new BadResponseError('Routing response had no body')
}
const contentType = res.headers.get('Content-Type')
@@ -159,17 +160,17 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
if (res.status === 404) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
// 404 (Not Found): must be returned if no matching records are found.
- throw new CodeError('No matching records found.', 'ERR_NOT_FOUND')
+ throw new NotFoundError('No matching records found')
}
if (res.status === 422) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
- // 422 (Unprocessable Entity): request does not conform to schema or semantic constraints.
- throw new CodeError('Request does not conform to schema or semantic constraints.', 'ERR_INVALID_REQUEST')
+ // 422 (Unprocessable Entity): request does not conform to schema or semantic constraints
+ throw new InvalidRequestError('Request does not conform to schema or semantic constraints')
}
if (res.body == null) {
- throw new CodeError('Routing response had no body', 'ERR_BAD_RESPONSE')
+ throw new BadResponseError('Routing response had no body')
}
const contentType = res.headers.get('Content-Type')
@@ -199,8 +200,8 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
}
}
- async getIPNS (peerId: PeerId, options: GetIPNSOptions = {}): Promise {
- log('getIPNS starts: %c', peerId)
+ async getIPNS (libp2pKey: CID, options: GetIPNSOptions = {}): Promise {
+ log('getIPNS starts: %s', libp2pKey)
const timeoutSignal = AbortSignal.timeout(this.timeout)
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
@@ -214,7 +215,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
})
// https://specs.ipfs.tech/routing/http-routing-v1/
- const resource = `${this.clientUrl}routing/v1/ipns/${peerId.toCID().toString()}`
+ const resource = `${this.clientUrl}routing/v1/ipns/${libp2pKey}`
try {
await onStart.promise
@@ -226,28 +227,28 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
if (res.status === 404) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
- // 404 (Not Found): must be returned if no matching records are found.
- throw new CodeError('No matching records found.', 'ERR_NOT_FOUND')
+ // 404 (Not Found): must be returned if no matching records are found
+ throw new NotFoundError('No matching records found')
}
if (res.status === 422) {
// https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes
- // 422 (Unprocessable Entity): request does not conform to schema or semantic constraints.
- throw new CodeError('Request does not conform to schema or semantic constraints.', 'ERR_INVALID_REQUEST')
+ // 422 (Unprocessable Entity): request does not conform to schema or semantic constraints
+ throw new InvalidRequestError('Request does not conform to schema or semantic constraints')
}
if (res.body == null) {
- throw new CodeError('GET ipns response had no body', 'ERR_BAD_RESPONSE')
+ throw new BadResponseError('GET ipns response had no body')
}
const buf = await res.arrayBuffer()
const body = new Uint8Array(buf, 0, buf.byteLength)
if (options.validate !== false) {
- await ipnsValidator(peerIdToRoutingKey(peerId), body)
+ await ipnsValidator(multihashToIPNSRoutingKey(libp2pKey.multihash), body)
}
- return unmarshal(body)
+ return unmarshalIPNSRecord(body)
} catch (err: any) {
log.error('getIPNS GET %s error:', resource, err)
@@ -255,12 +256,12 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
} finally {
signal.clear()
onFinish.resolve()
- log('getIPNS finished: %c', peerId)
+ log('getIPNS finished: %s', libp2pKey)
}
}
- async putIPNS (peerId: PeerId, record: IPNSRecord, options: AbortOptions = {}): Promise {
- log('putIPNS starts: %c', peerId)
+ async putIPNS (libp2pKey: CID, record: IPNSRecord, options: AbortOptions = {}): Promise {
+ log('putIPNS starts: %c', libp2pKey)
const timeoutSignal = AbortSignal.timeout(this.timeout)
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
@@ -274,12 +275,12 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
})
// https://specs.ipfs.tech/routing/http-routing-v1/
- const resource = `${this.clientUrl}routing/v1/ipns/${peerId.toCID().toString()}`
+ const resource = `${this.clientUrl}routing/v1/ipns/${libp2pKey}`
try {
await onStart.promise
- const body = marshal(record)
+ const body = marshalIPNSRecord(record)
const getOptions = { method: 'PUT', headers: { 'Content-Type': 'application/vnd.ipfs.ipns-record' }, body, signal }
const res = await fetch(resource, getOptions)
@@ -287,7 +288,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
log('putIPNS PUT %s %d', resource, res.status)
if (res.status !== 200) {
- throw new CodeError('PUT ipns response had status other than 200', 'ERR_BAD_RESPONSE')
+ throw new BadResponseError('PUT ipns response had status other than 200')
}
} catch (err: any) {
log.error('putIPNS PUT %s error:', resource, err.stack)
@@ -296,7 +297,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
} finally {
signal.clear()
onFinish.resolve()
- log('putIPNS finished: %c', peerId)
+ log('putIPNS finished: %c', libp2pKey)
}
}
diff --git a/packages/client/src/errors.ts b/packages/client/src/errors.ts
new file mode 100644
index 0000000..c0dcbca
--- /dev/null
+++ b/packages/client/src/errors.ts
@@ -0,0 +1,17 @@
+export class InvalidRequestError extends Error {
+ static name = 'InvalidRequestError'
+
+ constructor (message = 'Invalid request') {
+ super(message)
+ this.name = 'InvalidRequestError'
+ }
+}
+
+export class BadResponseError extends Error {
+ static name = 'BadResponseError'
+
+ constructor (message = 'Bad response') {
+ super(message)
+ this.name = 'BadResponseError'
+ }
+}
diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts
index ca02c4e..7dd4bcf 100644
--- a/packages/client/src/index.ts
+++ b/packages/client/src/index.ts
@@ -102,14 +102,14 @@ export interface DelegatedRoutingV1HttpApiClient {
getPeers(peerId: PeerId, options?: AbortOptions): AsyncGenerator
/**
- * Returns a promise of a {@link IPNSRecord} for the given {@link PeerId}
+ * Returns a promise of a {@link IPNSRecord} for the given {@link MultihashDigest}
*/
- getIPNS(peerId: PeerId, options?: GetIPNSOptions): Promise
+ getIPNS(libp2pKey: CID, options?: GetIPNSOptions): Promise
/**
- * Publishes the given {@link IPNSRecord} for the provided {@link PeerId}
+ * Publishes the given {@link IPNSRecord} for the provided {@link MultihashDigest}
*/
- putIPNS(peerId: PeerId, record: IPNSRecord, options?: AbortOptions): Promise
+ putIPNS(libp2pKey: CID, record: IPNSRecord, options?: AbortOptions): Promise
/**
* Shut down any currently running HTTP requests and clear up any resources
diff --git a/packages/client/src/routings.ts b/packages/client/src/routings.ts
index 9e16d80..1f2061d 100644
--- a/packages/client/src/routings.ts
+++ b/packages/client/src/routings.ts
@@ -1,13 +1,11 @@
-import { type ContentRouting, type PeerRouting, type AbortOptions, type PeerId, type PeerInfo } from '@libp2p/interface'
-import { CodeError } from '@libp2p/interface'
-import { peerIdFromBytes } from '@libp2p/peer-id'
-import { marshal, unmarshal } from 'ipns'
+import { type ContentRouting, type PeerRouting, type AbortOptions, type PeerId, type PeerInfo, NotFoundError } from '@libp2p/interface'
+import { marshalIPNSRecord, multihashFromIPNSRoutingKey, unmarshalIPNSRecord } from 'ipns'
import first from 'it-first'
import map from 'it-map'
+import { CID } from 'multiformats/cid'
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import type { DelegatedRoutingV1HttpApiClient } from './index.js'
-import type { CID } from 'multiformats/cid'
const IPNS_PREFIX = uint8ArrayFromString('/ipns/')
@@ -15,10 +13,6 @@ function isIPNSKey (key: Uint8Array): boolean {
return uint8ArrayEquals(key.subarray(0, IPNS_PREFIX.byteLength), IPNS_PREFIX)
}
-const peerIdFromRoutingKey = (key: Uint8Array): PeerId => {
- return peerIdFromBytes(key.slice(IPNS_PREFIX.length))
-}
-
/**
* Wrapper class to convert [http-routing-v1 content events](https://specs.ipfs.tech/routing/http-routing-v1/#response-body) into returned values
*/
@@ -47,28 +41,30 @@ export class DelegatedRoutingV1HttpApiClientContentRouting implements ContentRou
return
}
- const peerId = peerIdFromRoutingKey(key)
- const record = unmarshal(value)
+ const digest = multihashFromIPNSRoutingKey(key)
+ const cid = CID.createV1(0x72, digest)
+ const record = unmarshalIPNSRecord(value)
- await this.client.putIPNS(peerId, record, options)
+ await this.client.putIPNS(cid, record, options)
}
async get (key: Uint8Array, options?: AbortOptions): Promise {
if (!isIPNSKey(key)) {
- throw new CodeError('Not found', 'ERR_NOT_FOUND')
+ throw new NotFoundError('Not found')
}
- const peerId = peerIdFromRoutingKey(key)
+ const digest = multihashFromIPNSRoutingKey(key)
+ const cid = CID.createV1(0x72, digest)
try {
- const record = await this.client.getIPNS(peerId, options)
+ const record = await this.client.getIPNS(cid, options)
- return marshal(record)
+ return marshalIPNSRecord(record)
} catch (err: any) {
- // ERR_BAD_RESPONSE is thrown when the response had no body, which means
+ // BadResponseError is thrown when the response had no body, which means
// the record couldn't be found
- if (err.code === 'ERR_BAD_RESPONSE') {
- throw new CodeError('Not found', 'ERR_NOT_FOUND')
+ if (err.name === 'BadResponseError') {
+ throw new NotFoundError('Not found')
}
throw err
@@ -96,7 +92,7 @@ export class DelegatedRoutingV1HttpApiClientPeerRouting implements PeerRouting {
}
}
- throw new CodeError('Not found', 'ERR_NOT_FOUND')
+ throw new NotFoundError('Not found')
}
async * getClosestPeers (key: Uint8Array, options: AbortOptions = {}): AsyncIterable {
diff --git a/packages/client/test/index.spec.ts b/packages/client/test/index.spec.ts
index 9c9fcd2..aa0be1e 100644
--- a/packages/client/test/index.spec.ts
+++ b/packages/client/test/index.spec.ts
@@ -1,10 +1,10 @@
/* eslint-env mocha */
-import { peerIdFromString } from '@libp2p/peer-id'
-import { createEd25519PeerId } from '@libp2p/peer-id-factory'
+import { generateKeyPair } from '@libp2p/crypto/keys'
+import { peerIdFromPrivateKey, peerIdFromString } from '@libp2p/peer-id'
import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
-import { create as createIpnsRecord, marshal as marshalIpnsRecord } from 'ipns'
+import { createIPNSRecord, marshalIPNSRecord } from 'ipns'
import all from 'it-all'
import { CID } from 'multiformats/cid'
import { createDelegatedRoutingV1HttpApiClient, type DelegatedRoutingV1HttpApiClient } from '../src/index.js'
@@ -33,16 +33,16 @@ describe('delegated-routing-v1-http-api-client', () => {
Protocol: 'transport-bitswap',
Schema: 'bitswap',
Metadata: 'gBI=',
- ID: (await createEd25519PeerId()).toString(),
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString(),
Addrs: ['/ip4/41.41.41.41/tcp/1234']
}, {
Protocol: 'transport-bitswap',
Schema: 'peer',
Metadata: 'gBI=',
- ID: (await createEd25519PeerId()).toString(),
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString(),
Addrs: ['/ip4/42.42.42.42/tcp/1234']
}, {
- ID: (await createEd25519PeerId()).toString(),
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString(),
Addrs: ['/ip4/43.43.43.43/tcp/1234']
}]
@@ -111,35 +111,35 @@ describe('delegated-routing-v1-http-api-client', () => {
})
it('should conform records to peer schema', async () => {
- const peerId = await createEd25519PeerId()
+ const privateKey = await generateKeyPair('Ed25519')
const records = [{
Protocol: 'transport-bitswap',
Schema: 'bitswap',
Metadata: 'gBI=',
- ID: peerId.toString(),
+ ID: privateKey.publicKey.toString(),
Addrs: ['/ip4/41.41.41.41/tcp/1234']
}, {
Protocol: 'transport-saddle',
Schema: 'horse-ride',
Metadata: 'gBI=',
- ID: peerId.toString(),
+ ID: privateKey.publicKey.toString(),
Addrs: ['/ip4/41.41.41.41/tcp/1234']
}, {
Protocols: ['transport-bitswap'],
Schema: 'peer',
Metadata: 'gBI=',
- ID: peerId.toString(),
+ ID: privateKey.publicKey.toString(),
Addrs: ['/ip4/42.42.42.42/tcp/1234']
}, {
Protocol: 'transport-bitswap',
Schema: 'peer',
Metadata: 'gBI=',
- ID: (await createEd25519PeerId()).toString(),
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString(),
Addrs: ['/ip4/42.42.42.42/tcp/1234']
}, {
Schema: 'peer',
- ID: (await createEd25519PeerId()).toString()
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString()
}]
const peers = [{
@@ -174,12 +174,12 @@ describe('delegated-routing-v1-http-api-client', () => {
}]
// load peer for the router to fetch
- await fetch(`${process.env.ECHO_SERVER}/add-peers/${peerId.toCID().toString()}`, {
+ await fetch(`${process.env.ECHO_SERVER}/add-peers/${privateKey.publicKey.toCID()}`, {
method: 'POST',
body: records.map(prov => JSON.stringify(prov)).join('\n')
})
- const peerRecords = await all(client.getPeers(peerId))
+ const peerRecords = await all(client.getPeers(peerIdFromPrivateKey(privateKey)))
expect(peerRecords.map(peerRecord => ({
...peerRecord,
ID: peerRecord.ID.toString(),
@@ -193,48 +193,49 @@ describe('delegated-routing-v1-http-api-client', () => {
it('should get ipns record', async () => {
const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn')
- const peerId = await createEd25519PeerId()
- const record = await createIpnsRecord(peerId, cid, 0, 1000)
+ const privateKey = await generateKeyPair('Ed25519')
+ const record = await createIPNSRecord(privateKey, cid, 0, 1000)
// load record for the router to fetch
- await fetch(`${process.env.ECHO_SERVER}/add-ipns/${peerId.toCID().toString()}`, {
+ await fetch(`${process.env.ECHO_SERVER}/add-ipns/${privateKey.publicKey.toCID()}`, {
method: 'POST',
headers: {
'Content-Type': 'application/vnd.ipfs.ipns-record'
},
- body: marshalIpnsRecord(record)
+ body: marshalIPNSRecord(record)
})
- const ipnsRecord = await client.getIPNS(peerId)
- expect(marshalIpnsRecord(ipnsRecord)).to.equalBytes(marshalIpnsRecord(record))
+ const ipnsRecord = await client.getIPNS(privateKey.publicKey.toCID())
+ expect(marshalIPNSRecord(ipnsRecord)).to.equalBytes(marshalIPNSRecord(record))
})
it('get ipns record fails with bad record', async () => {
const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn')
- const peerId = await createEd25519PeerId()
- const record = await createIpnsRecord(await createEd25519PeerId(), cid, 0, 1000)
+ const privateKey = await generateKeyPair('Ed25519')
+ const otherPrivateKey = await generateKeyPair('Ed25519')
+ const record = await createIPNSRecord(otherPrivateKey, cid, 0, 1000)
// load record for the router to fetch
- await fetch(`${process.env.ECHO_SERVER}/add-ipns/${peerId.toCID().toString()}`, {
+ await fetch(`${process.env.ECHO_SERVER}/add-ipns/${privateKey.publicKey.toCID()}`, {
method: 'POST',
headers: {
'Content-Type': 'application/vnd.ipfs.ipns-record'
},
- body: marshalIpnsRecord(record)
+ body: marshalIPNSRecord(record)
})
- await expect(client.getIPNS(peerId)).to.be.rejected()
+ await expect(client.getIPNS(privateKey.publicKey.toCID())).to.be.rejected()
})
it('should put ipns', async () => {
const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn')
- const peerId = await createEd25519PeerId()
- const record = await createIpnsRecord(peerId, cid, 0, 1000)
+ const privateKey = await generateKeyPair('Ed25519')
+ const record = await createIPNSRecord(privateKey, cid, 0, 1000)
- await client.putIPNS(peerId, record)
+ await client.putIPNS(privateKey.publicKey.toCID(), record)
// load record that our client just PUT to remote server
- const res = await fetch(`${process.env.ECHO_SERVER}/get-ipns/${peerId.toCID().toString()}`, {
+ const res = await fetch(`${process.env.ECHO_SERVER}/get-ipns/${privateKey.publicKey.toCID()}`, {
method: 'GET',
headers: {
Accept: 'application/vnd.ipfs.ipns-record'
@@ -242,6 +243,6 @@ describe('delegated-routing-v1-http-api-client', () => {
})
const receivedRecord = new Uint8Array(await res.arrayBuffer())
- expect(marshalIpnsRecord(record)).to.equalBytes(receivedRecord)
+ expect(marshalIPNSRecord(record)).to.equalBytes(receivedRecord)
})
})
diff --git a/packages/client/test/routings.spec.ts b/packages/client/test/routings.spec.ts
index e364861..354c576 100644
--- a/packages/client/test/routings.spec.ts
+++ b/packages/client/test/routings.spec.ts
@@ -1,10 +1,11 @@
/* eslint-disable max-nested-callbacks */
/* eslint-env mocha */
+import { generateKeyPair } from '@libp2p/crypto/keys'
import { contentRoutingSymbol, peerRoutingSymbol } from '@libp2p/interface'
-import { createEd25519PeerId } from '@libp2p/peer-id-factory'
+import { peerIdFromPrivateKey } from '@libp2p/peer-id'
import { expect } from 'aegir/chai'
-import { create as createIpnsRecord, marshal as marshalIpnsRecord } from 'ipns'
+import { createIPNSRecord, marshalIPNSRecord, multihashToIPNSRoutingKey } from 'ipns'
import all from 'it-all'
import { CID } from 'multiformats/cid'
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
@@ -51,16 +52,16 @@ describe('libp2p content-routing', () => {
Protocol: 'transport-bitswap',
Schema: 'bitswap',
Metadata: 'gBI=',
- ID: (await createEd25519PeerId()).toString(),
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString(),
Addrs: ['/ip4/41.41.41.41/tcp/1234']
}, {
Protocol: 'transport-bitswap',
Schema: 'peer',
Metadata: 'gBI=',
- ID: (await createEd25519PeerId()).toString(),
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString(),
Addrs: ['/ip4/42.42.42.42/tcp/1234']
}, {
- ID: (await createEd25519PeerId()).toString(),
+ ID: (await generateKeyPair('Ed25519')).publicKey.toString(),
Addrs: ['/ip4/43.43.43.43/tcp/1234']
}]
@@ -102,17 +103,14 @@ describe('libp2p content-routing', () => {
}
const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn')
- const peerId = await createEd25519PeerId()
- const record = await createIpnsRecord(peerId, cid, 0, 1000)
- const key = uint8ArrayConcat([
- uint8ArrayFromString('/ipns/'),
- peerId.toBytes()
- ])
+ const privateKey = await generateKeyPair('Ed25519')
+ const record = await createIPNSRecord(privateKey, cid, 0, 1000)
+ const key = multihashToIPNSRoutingKey(privateKey.publicKey.toMultihash())
- await routing.put(key, marshalIpnsRecord(record))
+ await routing.put(key, marshalIPNSRecord(record))
// load record that our client just PUT to remote server
- const res = await fetch(`${process.env.ECHO_SERVER}/get-ipns/${peerId.toCID().toString()}`, {
+ const res = await fetch(`${process.env.ECHO_SERVER}/get-ipns/${privateKey.publicKey.toCID()}`, {
method: 'GET',
headers: {
Accept: 'application/vnd.ipfs.ipns-record'
@@ -120,7 +118,7 @@ describe('libp2p content-routing', () => {
})
const receivedRecord = new Uint8Array(await res.arrayBuffer())
- expect(marshalIpnsRecord(record)).to.equalBytes(receivedRecord)
+ expect(marshalIPNSRecord(record)).to.equalBytes(receivedRecord)
})
it('should not put other records', async () => {
@@ -130,10 +128,10 @@ describe('libp2p content-routing', () => {
throw new Error('ContentRouting not found')
}
- const peerId = await createEd25519PeerId()
+ const privateKey = await generateKeyPair('Ed25519')
const key = uint8ArrayConcat([
uint8ArrayFromString('/an-unknown-key/'),
- peerId.toBytes()
+ privateKey.publicKey.toMultihash().bytes
])
await routing.put(key, Uint8Array.from([0, 1, 2, 3, 4]))
@@ -149,25 +147,25 @@ describe('libp2p content-routing', () => {
}
const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn')
- const peerId = await createEd25519PeerId()
- const record = await createIpnsRecord(peerId, cid, 0, 1000)
+ const privateKey = await generateKeyPair('Ed25519')
+ const record = await createIPNSRecord(privateKey, cid, 0, 1000)
// load record for the router to fetch
- await fetch(`${process.env.ECHO_SERVER}/add-ipns/${peerId.toCID().toString()}`, {
+ await fetch(`${process.env.ECHO_SERVER}/add-ipns/${privateKey.publicKey.toCID()}`, {
method: 'POST',
headers: {
'Content-Type': 'application/vnd.ipfs.ipns-record'
},
- body: marshalIpnsRecord(record)
+ body: marshalIPNSRecord(record)
})
const key = uint8ArrayConcat([
uint8ArrayFromString('/ipns/'),
- peerId.toBytes()
+ privateKey.publicKey.toMultihash().bytes
])
const value = await routing.get(key)
- expect(value).to.equalBytes(marshalIpnsRecord(record))
+ expect(value).to.equalBytes(marshalIPNSRecord(record))
})
it('should not get unknown records', async () => {
@@ -177,15 +175,15 @@ describe('libp2p content-routing', () => {
throw new Error('ContentRouting not found')
}
- const peerId = await createEd25519PeerId()
+ const privateKey = await generateKeyPair('Ed25519')
const key = uint8ArrayConcat([
uint8ArrayFromString('/am-unknown-key/'),
- peerId.toBytes()
+ privateKey.publicKey.toMultihash().bytes
])
await expect(routing.get(key)).to.eventually.be.rejected
- .with.property('code', 'ERR_NOT_FOUND')
+ .with.property('name', 'NotFoundError')
await expect(getServerCallCount()).to.eventually.equal(0)
})
@@ -218,7 +216,8 @@ describe('libp2p peer-routing', () => {
throw new Error('PeerRouting not found')
}
- const peerId = await createEd25519PeerId()
+ const privateKey = await generateKeyPair('Ed25519')
+ const peerId = peerIdFromPrivateKey(privateKey)
const records = [{
Protocol: 'transport-bitswap',
diff --git a/packages/client/typedoc.json b/packages/client/typedoc.json
index 627398b..f599dc7 100644
--- a/packages/client/typedoc.json
+++ b/packages/client/typedoc.json
@@ -1,7 +1,5 @@
{
"entryPoints": [
"./src/index.ts"
- ],
- "readme": "none",
- "includeVersion": true
+ ]
}
diff --git a/packages/interop/package.json b/packages/interop/package.json
index 93bd5c7..7cba6a0 100644
--- a/packages/interop/package.json
+++ b/packages/interop/package.json
@@ -3,7 +3,7 @@
"version": "0.0.0",
"description": "Interop tests for the Delegated Routing V1 HTTP API server powered by Helia",
"license": "Apache-2.0 OR MIT",
- "homepage": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/master/packages/interop#readme",
+ "homepage": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/main/packages/interop#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/ipfs/helia-delegated-routing-v1-http-api.git"
@@ -15,13 +15,6 @@
"IPFS"
],
"type": "module",
- "types": "./dist/src/index.d.ts",
- "files": [
- "src",
- "dist",
- "!dist/test",
- "!**/*.tsbuildinfo"
- ],
"eslintConfig": {
"extends": "ipfs",
"parserOptions": {
@@ -29,28 +22,113 @@
"sourceType": "module"
}
},
+ "release": {
+ "branches": [
+ "main"
+ ],
+ "plugins": [
+ [
+ "@semantic-release/commit-analyzer",
+ {
+ "preset": "conventionalcommits",
+ "releaseRules": [
+ {
+ "breaking": true,
+ "release": "major"
+ },
+ {
+ "revert": true,
+ "release": "patch"
+ },
+ {
+ "type": "feat",
+ "release": "minor"
+ },
+ {
+ "type": "fix",
+ "release": "patch"
+ },
+ {
+ "type": "docs",
+ "release": "patch"
+ },
+ {
+ "type": "test",
+ "release": "patch"
+ },
+ {
+ "type": "deps",
+ "release": "patch"
+ },
+ {
+ "scope": "no-release",
+ "release": false
+ }
+ ]
+ }
+ ],
+ [
+ "@semantic-release/release-notes-generator",
+ {
+ "preset": "conventionalcommits",
+ "presetConfig": {
+ "types": [
+ {
+ "type": "feat",
+ "section": "Features"
+ },
+ {
+ "type": "fix",
+ "section": "Bug Fixes"
+ },
+ {
+ "type": "chore",
+ "section": "Trivial Changes"
+ },
+ {
+ "type": "docs",
+ "section": "Documentation"
+ },
+ {
+ "type": "deps",
+ "section": "Dependencies"
+ },
+ {
+ "type": "test",
+ "section": "Tests"
+ }
+ ]
+ }
+ }
+ ],
+ "@semantic-release/changelog",
+ "@semantic-release/npm",
+ "@semantic-release/github",
+ "@semantic-release/git"
+ ]
+ },
"scripts": {
"clean": "aegir clean",
"lint": "aegir lint",
"dep-check": "aegir dep-check",
"build": "aegir build --bundle false",
"test": "aegir test -t node",
- "test:node": "aegir test -t node --cov"
+ "test:node": "aegir test -t node --cov",
+ "release": "aegir release"
},
"devDependencies": {
"@helia/delegated-routing-v1-http-api-client": "^3.0.0",
"@helia/delegated-routing-v1-http-api-server": "^3.0.0",
- "@helia/ipns": "^7.1.0",
- "@libp2p/identify": "^2.1.0",
- "@libp2p/interface": "^1.1.1",
- "@libp2p/kad-dht": "^12.0.3",
- "@libp2p/peer-id-factory": "^4.0.4",
+ "@helia/ipns": "^7.2.3-ec8bf66",
+ "@libp2p/identify": "^3.0.1",
+ "@libp2p/interface": "^2.0.1",
+ "@libp2p/kad-dht": "^13.0.1",
"aegir": "^44.1.1",
- "fastify": "^4.17.0",
- "helia": "^4.0.0",
- "ipns": "^9.0.0",
- "it-first": "^3.0.3",
- "multiformats": "^13.0.0"
+ "fastify": "^4.28.1",
+ "helia": "^4.2.6-ec8bf66",
+ "ipns": "^10.0.0",
+ "it-first": "^3.0.6",
+ "multiformats": "^13.2.3"
},
"private": true
}
diff --git a/packages/interop/test/index.spec.ts b/packages/interop/test/index.spec.ts
index 7521b16..62a09c9 100644
--- a/packages/interop/test/index.spec.ts
+++ b/packages/interop/test/index.spec.ts
@@ -3,9 +3,9 @@
import { createDelegatedRoutingV1HttpApiClient } from '@helia/delegated-routing-v1-http-api-client'
import { createDelegatedRoutingV1HttpApiServer } from '@helia/delegated-routing-v1-http-api-server'
import { ipns } from '@helia/ipns'
-import { createEd25519PeerId } from '@libp2p/peer-id-factory'
+import { generateKeyPair } from '@libp2p/crypto/keys'
import { expect } from 'aegir/chai'
-import { create as createIpnsRecord } from 'ipns'
+import { createIPNSRecord } from 'ipns'
import first from 'it-first'
import { CID } from 'multiformats/cid'
import * as raw from 'multiformats/codecs/raw'
@@ -88,29 +88,30 @@ describe('delegated-routing-v1-http-api interop', () => {
expect(result.ID.toString()).to.equal(network[2].libp2p.peerId.toString())
})
- it('should get an IPNS record', async () => {
+ it.skip('should get an IPNS record', async () => {
// publish a record using a remote host
const i = ipns(network[5])
const cid = CID.parse('bafybeiczsscdsbs7ffqz55asqdf3smv6klcw3gofszvwlyarci47bgf354')
- const peerId = await createEd25519PeerId()
- await i.publish(peerId, cid)
+ const privateKey = await generateKeyPair('Ed25519')
+ await i.publish(privateKey, cid)
// use client to resolve the published record
- const record = await client.getIPNS(peerId)
+ const record = await client.getIPNS(privateKey.publicKey.toCID())
expect(record.value).to.equal(`/ipfs/${cid.toString()}`)
})
- it('should put an IPNS record', async () => {
+ it.skip('should put an IPNS record', async () => {
// publish a record using the client
const cid = CID.parse('bafybeiczsscdsbs7ffqz55asqdf3smv6klcw3gofszvwlyarci47bgf354')
- const peerId = await createEd25519PeerId()
- const record = await createIpnsRecord(peerId, cid, 0, 1000 * 60 * 60 * 24)
+ const privateKey = await generateKeyPair('Ed25519')
+ const record = await createIPNSRecord(privateKey, cid, 0, 1000 * 60 * 60 * 24)
- await client.putIPNS(peerId, record)
+ await client.putIPNS(privateKey.publicKey.toCID(), record)
// resolve the record using a remote host
const i = ipns(network[8])
- const result = await i.resolve(peerId)
+ // @ts-expect-error helia needs to be updated to the latest libp2p deps
+ const result = await i.resolve(privateKey.publicKey.toCID())
expect(result.cid.toString()).to.equal(cid.toString())
})
})
diff --git a/packages/server/README.md b/packages/server/README.md
index 445afac..93c15c4 100644
--- a/packages/server/README.md
+++ b/packages/server/README.md
@@ -4,6 +4,8 @@
+# @helia/delegated-routing-v1-http-api-server
+
[](https://ipfs.tech)
[](https://discuss.ipfs.tech)
[](https://codecov.io/gh/ipfs/helia-delegated-routing-v1-http-api)
@@ -63,24 +65,24 @@ await server.listen({
// now make http requests
```
-## Install
+# Install
```console
$ npm i @helia/delegated-routing-v1-http-api-server
```
-## API Docs
+# API Docs
-
-## License
+# License
Licensed under either of
-- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / )
-- MIT ([LICENSE-MIT](LICENSE-MIT) / )
+- Apache 2.0, ([LICENSE-APACHE](https://github.com/ipfs/helia-delegated-routing-v1-http-api/blob/main/packages/server/LICENSE-APACHE) / )
+- MIT ([LICENSE-MIT](https://github.com/ipfs/helia-delegated-routing-v1-http-api/blob/main/packages/server/LICENSE-MIT) / )
-## Contribute
+# Contribute
Contributions welcome! Please check out [the issues](https://github.com/ipfs/helia-delegated-routing-v1-http-api/issues).
diff --git a/packages/server/package.json b/packages/server/package.json
index 27c38b9..efdc146 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -3,7 +3,7 @@
"version": "3.0.3",
"description": "A Delegated Routing V1 HTTP API server powered by Helia",
"license": "Apache-2.0 OR MIT",
- "homepage": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/master/packages/server#readme",
+ "homepage": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/tree/main/packages/server#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/ipfs/helia-delegated-routing-v1-http-api.git"
@@ -11,6 +11,10 @@
"bugs": {
"url": "https://github.com/ipfs/helia-delegated-routing-v1-http-api/issues"
},
+ "publishConfig": {
+ "access": "public",
+ "provenance": true
+ },
"keywords": [
"IPFS"
],
@@ -55,6 +59,91 @@
"sourceType": "module"
}
},
+ "release": {
+ "branches": [
+ "main"
+ ],
+ "plugins": [
+ [
+ "@semantic-release/commit-analyzer",
+ {
+ "preset": "conventionalcommits",
+ "releaseRules": [
+ {
+ "breaking": true,
+ "release": "major"
+ },
+ {
+ "revert": true,
+ "release": "patch"
+ },
+ {
+ "type": "feat",
+ "release": "minor"
+ },
+ {
+ "type": "fix",
+ "release": "patch"
+ },
+ {
+ "type": "docs",
+ "release": "patch"
+ },
+ {
+ "type": "test",
+ "release": "patch"
+ },
+ {
+ "type": "deps",
+ "release": "patch"
+ },
+ {
+ "scope": "no-release",
+ "release": false
+ }
+ ]
+ }
+ ],
+ [
+ "@semantic-release/release-notes-generator",
+ {
+ "preset": "conventionalcommits",
+ "presetConfig": {
+ "types": [
+ {
+ "type": "feat",
+ "section": "Features"
+ },
+ {
+ "type": "fix",
+ "section": "Bug Fixes"
+ },
+ {
+ "type": "chore",
+ "section": "Trivial Changes"
+ },
+ {
+ "type": "docs",
+ "section": "Documentation"
+ },
+ {
+ "type": "deps",
+ "section": "Dependencies"
+ },
+ {
+ "type": "test",
+ "section": "Tests"
+ }
+ ]
+ }
+ }
+ ],
+ "@semantic-release/changelog",
+ "@semantic-release/npm",
+ "@semantic-release/github",
+ "@semantic-release/git"
+ ]
+ },
"scripts": {
"clean": "aegir clean",
"lint": "aegir lint",
@@ -66,20 +155,19 @@
},
"dependencies": {
"@fastify/cors": "^9.0.1",
- "@helia/interface": "^4.0.0",
- "@libp2p/interface": "^1.1.1",
- "@libp2p/peer-id": "^4.0.4",
- "fastify": "^4.17.0",
- "ipns": "^9.0.0",
- "multiformats": "^13.0.0",
+ "@helia/interface": "^4.3.1-ec8bf66",
+ "@libp2p/interface": "^2.0.1",
+ "@libp2p/peer-id": "^5.0.1",
+ "fastify": "^4.28.1",
+ "ipns": "^10.0.0",
+ "multiformats": "^13.2.3",
"raw-body": "^3.0.0"
},
"devDependencies": {
- "@libp2p/peer-id-factory": "^4.0.4",
- "@multiformats/multiaddr": "^12.1.3",
- "@types/sinon": "^17.0.0",
+ "@multiformats/multiaddr": "^12.3.1",
+ "@types/sinon": "^17.0.3",
"aegir": "^44.1.1",
- "sinon": "^18.0.0",
+ "sinon": "^19.0.2",
"sinon-ts": "^2.0.0"
}
}
diff --git a/packages/server/src/constants.ts b/packages/server/src/constants.ts
new file mode 100644
index 0000000..ba83087
--- /dev/null
+++ b/packages/server/src/constants.ts
@@ -0,0 +1,2 @@
+// these values are from https://github.com/multiformats/multicodec/blob/master/table.csv
+export const LIBP2P_KEY_CODEC = 0x72
\ No newline at end of file
diff --git a/packages/server/src/routes/routing/v1/ipns/get.ts b/packages/server/src/routes/routing/v1/ipns/get.ts
index 450dcea..c7c8b41 100644
--- a/packages/server/src/routes/routing/v1/ipns/get.ts
+++ b/packages/server/src/routes/routing/v1/ipns/get.ts
@@ -1,9 +1,9 @@
+import { hasCode } from '@helia/utils'
import { setMaxListeners } from '@libp2p/interface'
-import { peerIdFromCID } from '@libp2p/peer-id'
-import { peerIdToRoutingKey } from 'ipns'
+import { multihashToIPNSRoutingKey } from 'ipns'
import { CID } from 'multiformats/cid'
+import { LIBP2P_KEY_CODEC } from '../../../../constants.js'
import type { Helia } from '@helia/interface'
-import type { PeerId } from '@libp2p/interface'
import type { FastifyInstance } from 'fastify'
interface Params {
@@ -27,7 +27,7 @@ export default function getIpnsV1 (fastify: FastifyInstance, helia: Helia): void
}
},
handler: async (request, reply) => {
- let peerId: PeerId
+ let cid: CID
const controller = new AbortController()
setMaxListeners(Infinity, controller.signal)
@@ -38,15 +38,24 @@ export default function getIpnsV1 (fastify: FastifyInstance, helia: Helia): void
try {
// PeerId must be encoded as a Libp2p-key CID.
const { name: cidStr } = request.params
- const peerCid = CID.parse(cidStr)
- peerId = peerIdFromCID(peerCid)
+ cid = CID.parse(cidStr)
} catch (err) {
fastify.log.error('could not parse CID from params', err)
return reply.code(422).type('text/html').send('Unprocessable Entity')
}
try {
- const rawRecord = await helia.routing.get(peerIdToRoutingKey(peerId), {
+ if (!hasCode(cid.multihash, 0x00) && !hasCode(cid.multihash, 0x12)) {
+ fastify.log.error('CID multihash had incorrect code %d', cid.multihash.code)
+ return reply.code(422).type('text/html').send('Unprocessable Entity')
+ }
+
+ if (cid.code !== LIBP2P_KEY_CODEC) {
+ fastify.log.error('CID had incorrect code %d', cid.code)
+ return reply.code(422).type('text/html').send('Unprocessable Entity')
+ }
+
+ const rawRecord = await helia.routing.get(multihashToIPNSRoutingKey(cid.multihash), {
signal: controller.signal
})
diff --git a/packages/server/src/routes/routing/v1/ipns/put.ts b/packages/server/src/routes/routing/v1/ipns/put.ts
index 4f7e144..b53472d 100644
--- a/packages/server/src/routes/routing/v1/ipns/put.ts
+++ b/packages/server/src/routes/routing/v1/ipns/put.ts
@@ -1,11 +1,11 @@
+import { hasCode } from '@helia/utils'
import { setMaxListeners } from '@libp2p/interface'
-import { peerIdFromCID } from '@libp2p/peer-id'
-import { peerIdToRoutingKey } from 'ipns'
+import { multihashToIPNSRoutingKey } from 'ipns'
import { ipnsValidator } from 'ipns/validator'
import { CID } from 'multiformats/cid'
import getRawBody from 'raw-body'
+import { LIBP2P_KEY_CODEC } from '../../../../constants.js'
import type { Helia } from '@helia/interface'
-import type { PeerId } from '@libp2p/interface'
import type { FastifyInstance } from 'fastify'
interface Params {
@@ -35,7 +35,7 @@ export default function putIpnsV1 (fastify: FastifyInstance, helia: Helia): void
}
},
handler: async (request, reply) => {
- let peerId: PeerId
+ let cid: CID
const controller = new AbortController()
setMaxListeners(Infinity, controller.signal)
@@ -46,18 +46,28 @@ export default function putIpnsV1 (fastify: FastifyInstance, helia: Helia): void
try {
// PeerId must be encoded as a Libp2p-key CID.
const { name: cidStr } = request.params
- const peerCid = CID.parse(cidStr)
- peerId = peerIdFromCID(peerCid)
+ cid = CID.parse(cidStr)
} catch (err) {
fastify.log.error('could not parse CID from params', err)
return reply.code(422).type('text/html').send('Unprocessable Entity')
}
+ if (!hasCode(cid.multihash, 0x00) && !hasCode(cid.multihash, 0x12)) {
+ fastify.log.error('CID multihash had incorrect code %d', cid.multihash.code)
+ return reply.code(422).type('text/html').send('Unprocessable Entity')
+ }
+
+ if (cid.code !== LIBP2P_KEY_CODEC) {
+ fastify.log.error('CID had incorrect code %d', cid.code)
+ return reply.code(422).type('text/html').send('Unprocessable Entity')
+ }
+
// @ts-expect-error request.body does not have a type
const body: Uint8Array = request.body
- await ipnsValidator(peerIdToRoutingKey(peerId), body)
+ const routingKey = multihashToIPNSRoutingKey(cid.multihash)
+ await ipnsValidator(routingKey, body)
- await helia.routing.put(peerIdToRoutingKey(peerId), body, {
+ await helia.routing.put(routingKey, body, {
signal: controller.signal
})
diff --git a/packages/server/test/index.spec.ts b/packages/server/test/index.spec.ts
index 8069e02..f27d1a9 100644
--- a/packages/server/test/index.spec.ts
+++ b/packages/server/test/index.spec.ts
@@ -1,9 +1,8 @@
/* eslint-env mocha */
-import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
-import { create as createIpnsRecord, marshal as marshalIpnsRecord, peerIdToRoutingKey } from 'ipns'
+import { createIPNSRecord, marshalIPNSRecord, multihashToIPNSRoutingKey } from 'ipns'
import { CID } from 'multiformats'
import { stubInterface } from 'sinon-ts'
import { createDelegatedRoutingV1HttpApiServer } from '../src/index.js'
@@ -11,6 +10,8 @@ import type { Helia } from '@helia/interface'
import type { PeerInfo } from '@libp2p/interface'
import type { FastifyInstance } from 'fastify'
import type { StubbedInstance } from 'sinon-ts'
+import { peerIdFromPrivateKey } from '@libp2p/peer-id'
+import { generateKeyPair } from '@libp2p/crypto/keys'
describe('delegated-routing-v1-http-api-server', () => {
let helia: StubbedInstance
@@ -90,13 +91,13 @@ describe('delegated-routing-v1-http-api-server', () => {
it('GET providers returns providers', async () => {
const provider1: PeerInfo = {
- id: await createEd25519PeerId(),
+ id: peerIdFromPrivateKey(await generateKeyPair('Ed25519')),
multiaddrs: [
multiaddr('/ip4/123.123.123.123/tcp/123')
]
}
const provider2: PeerInfo = {
- id: await createEd25519PeerId(),
+ id: peerIdFromPrivateKey(await generateKeyPair('Ed25519')),
multiaddrs: [
multiaddr('/ip4/123.123.123.123/tcp/123')
]
@@ -125,13 +126,13 @@ describe('delegated-routing-v1-http-api-server', () => {
it('GET providers returns provider stream', async () => {
const provider1: PeerInfo = {
- id: await createEd25519PeerId(),
+ id: peerIdFromPrivateKey(await generateKeyPair('Ed25519')),
multiaddrs: [
multiaddr('/ip4/123.123.123.123/tcp/123')
]
}
const provider2: PeerInfo = {
- id: await createEd25519PeerId(),
+ id: peerIdFromPrivateKey(await generateKeyPair('Ed25519')),
multiaddrs: [
multiaddr('/ip4/123.123.123.123/tcp/123')
]
@@ -165,7 +166,7 @@ describe('delegated-routing-v1-http-api-server', () => {
})
it('GET peers returns 422 if peer id is not cid', async () => {
- const res = await fetch(`${url}routing/v1/peers/${(await createEd25519PeerId()).toString()}`, {
+ const res = await fetch(`${url}routing/v1/peers/${peerIdFromPrivateKey(await generateKeyPair('Ed25519')).toString()}`, {
method: 'GET'
})
@@ -182,7 +183,7 @@ describe('delegated-routing-v1-http-api-server', () => {
it('GET peers returns peer records for get peers', async () => {
const peer: PeerInfo = {
- id: await createEd25519PeerId(),
+ id: peerIdFromPrivateKey(await generateKeyPair('Ed25519')),
multiaddrs: [
multiaddr('/ip4/123.123.123.123/tcp/123')
]
@@ -204,7 +205,7 @@ describe('delegated-routing-v1-http-api-server', () => {
})
it('GET ipns returns 422 if peer id is not cid', async () => {
- const res = await fetch(`${url}routing/v1/ipns/${(await createEd25519PeerId()).toString()}`, {
+ const res = await fetch(`${url}routing/v1/ipns/${peerIdFromPrivateKey(await generateKeyPair('Ed25519')).toString()}`, {
method: 'GET'
})
@@ -220,12 +221,13 @@ describe('delegated-routing-v1-http-api-server', () => {
})
it('GET ipns returns record', async () => {
- const peerId = await createEd25519PeerId()
+ const privateKey = await generateKeyPair('Ed25519')
+ const peerId = peerIdFromPrivateKey(privateKey)
const cid = CID.parse('bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4')
- const record = await createIpnsRecord(peerId, cid, 0, 1000)
+ const record = await createIPNSRecord(privateKey, cid, 0, 1000)
helia.routing.get = async function () {
- return marshalIpnsRecord(record)
+ return marshalIPNSRecord(record)
}
const res = await fetch(`${url}routing/v1/ipns/${peerId.toCID().toString()}`, {
@@ -237,14 +239,15 @@ describe('delegated-routing-v1-http-api-server', () => {
expect(res.status).to.equal(200)
const arrayBuffer = await res.arrayBuffer()
- expect(new Uint8Array(arrayBuffer)).to.equalBytes(marshalIpnsRecord(record))
+ expect(new Uint8Array(arrayBuffer)).to.equalBytes(marshalIPNSRecord(record))
})
it('PUT ipns puts record', async () => {
- const peerId = await createEd25519PeerId()
+ const privateKey = await generateKeyPair('Ed25519')
+ const peerId = peerIdFromPrivateKey(privateKey)
const cid = CID.parse('bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4')
- const record = await createIpnsRecord(peerId, cid, 0, 1000)
- const marshalledRecord = marshalIpnsRecord(record)
+ const record = await createIPNSRecord(privateKey, cid, 0, 1000)
+ const marshalledRecord = marshalIPNSRecord(record)
let putKey: Uint8Array = new Uint8Array()
let putValue: Uint8Array = new Uint8Array()
@@ -263,7 +266,7 @@ describe('delegated-routing-v1-http-api-server', () => {
})
expect(res.status).to.equal(200)
- expect(putKey).to.equalBytes(peerIdToRoutingKey(peerId))
+ expect(putKey).to.equalBytes(multihashToIPNSRoutingKey(privateKey.publicKey.toMultihash()))
expect(putValue).to.equalBytes(marshalledRecord)
})
})
diff --git a/packages/server/typedoc.json b/packages/server/typedoc.json
index bc4333f..1e561cb 100644
--- a/packages/server/typedoc.json
+++ b/packages/server/typedoc.json
@@ -2,7 +2,5 @@
"entryPoints": [
"./src/index.ts",
"./src/routes/index.ts"
- ],
- "readme": "none",
- "includeVersion": true
+ ]
}
From 5ed09a63d2a1be8d88ec92049d356f498c48783a Mon Sep 17 00:00:00 2001
From: achingbrain
Date: Sun, 15 Sep 2024 12:25:22 +0100
Subject: [PATCH 3/4] chore: update readme
---
packages/client/README.md | 15 +++++++++++++++
packages/server/README.md | 26 ++++++++++++++++++++------
2 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/packages/client/README.md b/packages/client/README.md
index 772aa39..2c7fe09 100644
--- a/packages/client/README.md
+++ b/packages/client/README.md
@@ -15,6 +15,21 @@
# About
+
+
A client implementation of the IPFS [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) that can be used to interact with any compliant server implementation.
## Example
diff --git a/packages/server/README.md b/packages/server/README.md
index 93c15c4..068684d 100644
--- a/packages/server/README.md
+++ b/packages/server/README.md
@@ -13,18 +13,33 @@
> A Delegated Routing V1 HTTP API server powered by Helia
-## About
+# About
+
+
Implements HTTP routes for a Fastify server that conform to the [Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/).
-### Example
+## Example
```typescript
import { createHelia } from 'helia'
-import { createRoutingV1HttpApiServer } from '@helia/routing-v1-http-api-server'
+import { createDelegatedRoutingV1HttpApiServer } from '@helia/delegated-routing-v1-http-api-server'
const helia = await createHelia()
-const server = await createRoutingV1HttpApiServer(helia, {
+const server = await createDelegatedRoutingV1HttpApiServer(helia, {
listen: {
// fastify listen options
}
@@ -34,9 +49,8 @@ const server = await createRoutingV1HttpApiServer(helia, {
```
Alternatively if you have a Fastify instance already you can add routes to it.
-,
-### Example
+## Example
```typescript
import fastify from 'fastify'
From 02e50de75f0474c54f7368caf90260a5349c273f Mon Sep 17 00:00:00 2001
From: achingbrain
Date: Sun, 15 Sep 2024 12:29:18 +0100
Subject: [PATCH 4/4] chore: linting and deps
---
packages/client/package.json | 1 +
packages/interop/package.json | 5 +++--
packages/server/package.json | 4 +++-
packages/server/src/constants.ts | 2 +-
packages/server/src/routes/routing/v1/ipns/get.ts | 8 +++++---
packages/server/test/index.spec.ts | 4 ++--
6 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/packages/client/package.json b/packages/client/package.json
index 1a2494b..41cc7a2 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -150,6 +150,7 @@
"uint8arrays": "^5.1.0"
},
"devDependencies": {
+ "@libp2p/crypto": "^5.0.1",
"aegir": "^44.1.1",
"body-parser": "^1.20.3",
"it-all": "^3.0.6"
diff --git a/packages/interop/package.json b/packages/interop/package.json
index 7cba6a0..1d42a2f 100644
--- a/packages/interop/package.json
+++ b/packages/interop/package.json
@@ -119,13 +119,14 @@
"devDependencies": {
"@helia/delegated-routing-v1-http-api-client": "^3.0.0",
"@helia/delegated-routing-v1-http-api-server": "^3.0.0",
- "@helia/ipns": "^7.2.3-ec8bf66",
+ "@helia/ipns": "next",
+ "@libp2p/crypto": "^5.0.1",
"@libp2p/identify": "^3.0.1",
"@libp2p/interface": "^2.0.1",
"@libp2p/kad-dht": "^13.0.1",
"aegir": "^44.1.1",
"fastify": "^4.28.1",
- "helia": "^4.2.6-ec8bf66",
+ "helia": "next",
"ipns": "^10.0.0",
"it-first": "^3.0.6",
"multiformats": "^13.2.3"
diff --git a/packages/server/package.json b/packages/server/package.json
index efdc146..d85b3a7 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -155,7 +155,8 @@
},
"dependencies": {
"@fastify/cors": "^9.0.1",
- "@helia/interface": "^4.3.1-ec8bf66",
+ "@helia/interface": "next",
+ "@helia/utils": "next",
"@libp2p/interface": "^2.0.1",
"@libp2p/peer-id": "^5.0.1",
"fastify": "^4.28.1",
@@ -164,6 +165,7 @@
"raw-body": "^3.0.0"
},
"devDependencies": {
+ "@libp2p/crypto": "^5.0.1",
"@multiformats/multiaddr": "^12.3.1",
"@types/sinon": "^17.0.3",
"aegir": "^44.1.1",
diff --git a/packages/server/src/constants.ts b/packages/server/src/constants.ts
index ba83087..da36f9a 100644
--- a/packages/server/src/constants.ts
+++ b/packages/server/src/constants.ts
@@ -1,2 +1,2 @@
// these values are from https://github.com/multiformats/multicodec/blob/master/table.csv
-export const LIBP2P_KEY_CODEC = 0x72
\ No newline at end of file
+export const LIBP2P_KEY_CODEC = 0x72
diff --git a/packages/server/src/routes/routing/v1/ipns/get.ts b/packages/server/src/routes/routing/v1/ipns/get.ts
index c7c8b41..c88f9bb 100644
--- a/packages/server/src/routes/routing/v1/ipns/get.ts
+++ b/packages/server/src/routes/routing/v1/ipns/get.ts
@@ -47,12 +47,12 @@ export default function getIpnsV1 (fastify: FastifyInstance, helia: Helia): void
try {
if (!hasCode(cid.multihash, 0x00) && !hasCode(cid.multihash, 0x12)) {
fastify.log.error('CID multihash had incorrect code %d', cid.multihash.code)
- return reply.code(422).type('text/html').send('Unprocessable Entity')
+ return await reply.code(422).type('text/html').send('Unprocessable Entity')
}
if (cid.code !== LIBP2P_KEY_CODEC) {
fastify.log.error('CID had incorrect code %d', cid.code)
- return reply.code(422).type('text/html').send('Unprocessable Entity')
+ return await reply.code(422).type('text/html').send('Unprocessable Entity')
}
const rawRecord = await helia.routing.get(multihashToIPNSRoutingKey(cid.multihash), {
@@ -64,7 +64,9 @@ export default function getIpnsV1 (fastify: FastifyInstance, helia: Helia): void
// one cannot simply send rawRecord https://github.com/fastify/fastify/issues/5118
.send(Buffer.from(rawRecord, 0, rawRecord.byteLength))
} catch (err: any) {
- if (err.code === 'ERR_NOT_FOUND' || err.errors?.[0].code === 'ERR_NOT_FOUND') {
+ if (err.code === 'ERR_NOT_FOUND' || err.errors?.[0].code === 'ERR_NOT_FOUND' ||
+ err.name === 'NotFoundError' || err.errors?.[0].name === 'NotFoundError'
+ ) {
return reply.code(404).send('Record not found')
}
diff --git a/packages/server/test/index.spec.ts b/packages/server/test/index.spec.ts
index f27d1a9..437df0e 100644
--- a/packages/server/test/index.spec.ts
+++ b/packages/server/test/index.spec.ts
@@ -1,5 +1,7 @@
/* eslint-env mocha */
+import { generateKeyPair } from '@libp2p/crypto/keys'
+import { peerIdFromPrivateKey } from '@libp2p/peer-id'
import { multiaddr } from '@multiformats/multiaddr'
import { expect } from 'aegir/chai'
import { createIPNSRecord, marshalIPNSRecord, multihashToIPNSRoutingKey } from 'ipns'
@@ -10,8 +12,6 @@ import type { Helia } from '@helia/interface'
import type { PeerInfo } from '@libp2p/interface'
import type { FastifyInstance } from 'fastify'
import type { StubbedInstance } from 'sinon-ts'
-import { peerIdFromPrivateKey } from '@libp2p/peer-id'
-import { generateKeyPair } from '@libp2p/crypto/keys'
describe('delegated-routing-v1-http-api-server', () => {
let helia: StubbedInstance