From d11ad9230c37a3b642b5ab7bd0ceacb9eaca4c22 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Tue, 23 Apr 2024 07:27:49 +0100 Subject: [PATCH 1/3] feat: add static http gateway routing Adds a routing implementation that returns a static list of gateways as providers for CIDs. It's expected that these gateways are able to fetch content on our behalf. --- packages/routers/package.json | 2 + packages/routers/src/http-gateway-routing.ts | 90 +++++++++++++++++++ packages/routers/src/index.ts | 1 + .../routers/test/http-gateway-routing.spec.ts | 23 +++++ 4 files changed, 116 insertions(+) create mode 100644 packages/routers/src/http-gateway-routing.ts create mode 100644 packages/routers/test/http-gateway-routing.spec.ts diff --git a/packages/routers/package.json b/packages/routers/package.json index e58a2d03..f4722721 100644 --- a/packages/routers/package.json +++ b/packages/routers/package.json @@ -56,6 +56,7 @@ "@helia/delegated-routing-v1-http-api-client": "^3.0.0", "@helia/interface": "^4.2.0", "@libp2p/interface": "^1.1.4", + "@multiformats/uri-to-multiaddr": "^8.0.0", "ipns": "^9.0.0", "it-first": "^3.0.4", "it-map": "^3.0.5", @@ -66,6 +67,7 @@ "@libp2p/peer-id": "^4.0.7", "@libp2p/peer-id-factory": "^4.0.7", "aegir": "^42.2.5", + "it-all": "^3.0.4", "it-drain": "^3.0.5", "sinon-ts": "^2.0.0" }, diff --git a/packages/routers/src/http-gateway-routing.ts b/packages/routers/src/http-gateway-routing.ts new file mode 100644 index 00000000..9a8ecc93 --- /dev/null +++ b/packages/routers/src/http-gateway-routing.ts @@ -0,0 +1,90 @@ +import { uriToMultiaddr } from '@multiformats/uri-to-multiaddr' +import { CID } from 'multiformats/cid' +import { identity } from 'multiformats/hashes/identity' +import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import { toString as uint8ArrayToString } from 'uint8arrays/to-string' +import type { Provider, Routing, RoutingOptions } from '@helia/interface' +import type { PeerId, PeerInfo } from '@libp2p/interface' +import type { MultihashDigest, Version } from 'multiformats' + +export interface HTTPGatwayRouterInit { + gateways?: Array +} + +// these values are from https://github.com/multiformats/multicodec/blob/master/table.csv +const LIBP2P_KEY_CODE = 0x72 +const inspect = Symbol.for('nodejs.util.inspect.custom') + +class URLPeerId { + readonly type = 'url' + readonly multihash: MultihashDigest + readonly privateKey?: Uint8Array + readonly publicKey?: Uint8Array + readonly url: string + + constructor (url: URL) { + this.url = url.toString() + this.multihash = identity.digest(uint8ArrayFromString(this.url)) + } + + [inspect] (): string { + return `PeerId(${this.url})` + } + + toString (): string { + return this.toCID().toString() + } + + toCID (): CID { + return CID.createV1(LIBP2P_KEY_CODE, this.multihash) + } + + toBytes (): Uint8Array { + return this.toCID().bytes + } + + equals (other?: PeerId | Uint8Array | string): boolean { + if (other == null) { + return false + } + + if (other instanceof Uint8Array) { + other = uint8ArrayToString(other) + } + + return other.toString() === this.toString() + } +} + +function toPeerInfo (url: string | URL): PeerInfo { + url = url.toString() + + return { + id: new URLPeerId(new URL(url)), + multiaddrs: [ + uriToMultiaddr(url) + ] + } +} + +class HTTPGatwayRouter implements Partial { + private readonly gateways: PeerInfo[] + + constructor (init: HTTPGatwayRouterInit = {}) { + this.gateways = (init.gateways ?? []).map(url => toPeerInfo(url)) + } + + async * findProviders (cid: CID, options?: RoutingOptions | undefined): AsyncIterable { + yield * this.gateways.map(info => ({ + ...info, + protocols: ['transport-ipfs-gateway-http'] + })) + } +} + +/** + * Returns a static list of HTTP Gateways as providers + */ +export function httpGatewayRouting (init: HTTPGatwayRouterInit = {}): Partial { + return new HTTPGatwayRouter(init) +} diff --git a/packages/routers/src/index.ts b/packages/routers/src/index.ts index c82e8cbd..df504b99 100644 --- a/packages/routers/src/index.ts +++ b/packages/routers/src/index.ts @@ -4,4 +4,5 @@ * Abstraction layer over different content and peer routing mechanisms. */ export { delegatedHTTPRouting } from './delegated-http-routing.js' +export { httpGatewayRouting } from './http-gateway-routing.js' export { libp2pRouting } from './libp2p-routing.js' diff --git a/packages/routers/test/http-gateway-routing.spec.ts b/packages/routers/test/http-gateway-routing.spec.ts new file mode 100644 index 00000000..8c01f20b --- /dev/null +++ b/packages/routers/test/http-gateway-routing.spec.ts @@ -0,0 +1,23 @@ +import { expect } from 'aegir/chai' +import all from 'it-all' +import { CID } from 'multiformats' +import { httpGatewayRouting } from '../src/http-gateway-routing.js' + +describe('http-gateway-routing', () => { + it('should find providers', async () => { + const gateway = 'https://example.com' + const routing = httpGatewayRouting({ + gateways: [ + gateway + ] + }) + + const cid = CID.parse('bafyreidykglsfhoixmivffc5uwhcgshx4j465xwqntbmu43nb2dzqwfvae') + + const providers = await all(routing.findProviders?.(cid) ?? []) + + expect(providers).to.have.lengthOf(1) + expect(providers).to.have.nested.property('[0].protocols').that.includes('transport-ipfs-gateway-http') + expect(providers[0].multiaddrs.map(ma => ma.toString())).to.include('/dns4/example.com/tcp/443/https') + }) +}) From e55c77c0b5d976a49f9a36f71a4deab7b91bcf56 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Wed, 24 Apr 2024 07:23:20 +0100 Subject: [PATCH 2/3] chore: work with isPeerId --- packages/routers/src/http-gateway-routing.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/routers/src/http-gateway-routing.ts b/packages/routers/src/http-gateway-routing.ts index 9a8ecc93..3bca44a4 100644 --- a/packages/routers/src/http-gateway-routing.ts +++ b/packages/routers/src/http-gateway-routing.ts @@ -4,7 +4,7 @@ import { identity } from 'multiformats/hashes/identity' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import type { Provider, Routing, RoutingOptions } from '@helia/interface' -import type { PeerId, PeerInfo } from '@libp2p/interface' +import { peerIdSymbol, type PeerId, type PeerInfo } from '@libp2p/interface' import type { MultihashDigest, Version } from 'multiformats' export interface HTTPGatwayRouterInit { @@ -31,6 +31,8 @@ class URLPeerId { return `PeerId(${this.url})` } + readonly [peerIdSymbol] = true + toString (): string { return this.toCID().toString() } From e206e0e7ee3c4bd0c2f3c3e8265e35c0e838db6d Mon Sep 17 00:00:00 2001 From: achingbrain Date: Wed, 24 Apr 2024 15:25:41 +0100 Subject: [PATCH 3/3] chore: implement peer id --- packages/routers/src/http-gateway-routing.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/routers/src/http-gateway-routing.ts b/packages/routers/src/http-gateway-routing.ts index 3bca44a4..081d912d 100644 --- a/packages/routers/src/http-gateway-routing.ts +++ b/packages/routers/src/http-gateway-routing.ts @@ -1,10 +1,11 @@ +import { peerIdSymbol } from '@libp2p/interface' import { uriToMultiaddr } from '@multiformats/uri-to-multiaddr' import { CID } from 'multiformats/cid' import { identity } from 'multiformats/hashes/identity' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import type { Provider, Routing, RoutingOptions } from '@helia/interface' -import { peerIdSymbol, type PeerId, type PeerInfo } from '@libp2p/interface' +import type { PeerId, PeerInfo } from '@libp2p/interface' import type { MultihashDigest, Version } from 'multiformats' export interface HTTPGatwayRouterInit { @@ -12,10 +13,10 @@ export interface HTTPGatwayRouterInit { } // these values are from https://github.com/multiformats/multicodec/blob/master/table.csv -const LIBP2P_KEY_CODE = 0x72 +const TRANSPORT_IPFS_GATEWAY_HTTP_CODE = 0x0920 const inspect = Symbol.for('nodejs.util.inspect.custom') -class URLPeerId { +class URLPeerId implements PeerId { readonly type = 'url' readonly multihash: MultihashDigest readonly privateKey?: Uint8Array @@ -38,7 +39,7 @@ class URLPeerId { } toCID (): CID { - return CID.createV1(LIBP2P_KEY_CODE, this.multihash) + return CID.createV1(TRANSPORT_IPFS_GATEWAY_HTTP_CODE, this.multihash) } toBytes (): Uint8Array {