diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 4017f2f..bef1a0c 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -12,7 +12,8 @@ "@lumeweb/libportal": "0.2.0-develop.17", "@lumeweb/node-library-preset": "0.2.7", "@noble/curves": "^1.1.0", - "@noble/hashes": "^1.3.1" + "@noble/hashes": "^1.3.1", + "binconv": "^0.2.0" }, "devDependencies": { "@semantic-release/changelog": "^6.0.3", @@ -3814,6 +3815,11 @@ "node": ">=8" } }, + "node_modules/binconv": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/binconv/-/binconv-0.2.0.tgz", + "integrity": "sha512-FAVbv8Fe1lsk1XQeWaYzfIQ9EnBgLJxjlqVA5XzbusP1YCzFgXUfIfmuanNmfM7i0XsNCNnYOnnq7NRLVKRgdQ==" + }, "node_modules/bottleneck": { "version": "2.19.5", "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", diff --git a/package.json b/package.json index 0c4b801..4dcfe4d 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "@lumeweb/libportal": "0.2.0-develop.17", "@lumeweb/node-library-preset": "0.2.7", "@noble/curves": "^1.1.0", - "@noble/hashes": "^1.3.1" + "@noble/hashes": "^1.3.1", + "binconv": "^0.2.0" }, "publishConfig": { "access": "public" diff --git a/src/download.ts b/src/download.ts index 8aaf9e6..342a3b2 100644 --- a/src/download.ts +++ b/src/download.ts @@ -1,6 +1,12 @@ import { getActivePortals } from "#portal.js"; import { ErrTuple } from "#types.js"; import { decodeCid, getVerifiableStream } from "@lumeweb/libportal"; +import { + readableStreamToUint8Array, + uint8ArrayToReadableStream, +} from "binconv"; +import { equalBytes } from "@noble/curves/abstract/utils"; +import { blake3 } from "@noble/hashes/blake3"; const NO_PORTALS_ERROR = [null, "no active portals"] as ErrTuple; @@ -37,3 +43,40 @@ export async function downloadObject(cid: string): Promise { return NO_PORTALS_ERROR; } + +export async function downloadSmallObject(cid: string): Promise { + const activePortals = getActivePortals(); + + if (!activePortals.length) { + return NO_PORTALS_ERROR; + } + + for (const portal of activePortals) { + if (!(await portal.isLoggedIn())) { + try { + await portal.register(); + } catch {} + + await portal.login(); + } + + let stream; + + try { + stream = await portal.downloadFile(cid); + } catch { + continue; + } + + const CID = decodeCid(cid); + const data = await readableStreamToUint8Array(stream); + + if (!equalBytes(blake3(data), CID.hash)) { + return [null, "cid verification failed"]; + } + + return [uint8ArrayToReadableStream(data), null]; + } + + return NO_PORTALS_ERROR; +}