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

Use gateway endpoint to fetch files #2040

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
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
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@
"tsx": "^4.17.0",
"typescript": "^5.5.4"
},
"packageManager": "yarn@4.4.1"
"packageManager": "yarn@4.4.1",
"resolutions": {
"protons-runtime": "5.5.0"
}
}
1 change: 1 addition & 0 deletions packages/toolkit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
},
"dependencies": {
"@dappnode/types": "workspace:^0.1.0",
"@helia/verified-fetch": "1.4.3",
"@ipld/car": "^5.3.2",
"esm": "^3.2.25",
"ethers": "^6.10.0",
Expand Down
45 changes: 19 additions & 26 deletions packages/toolkit/src/repository/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { getReleaseSignatureStatus, serializeIpfsDirectory } from "./releaseSign
import { isEnsDomain } from "../isEnsDomain.js";
import { dappnodeRegistry } from "./params.js";
import { ethers } from "ethers";
import { createVerifiedFetch } from "@helia/verified-fetch";

const source = "ipfs" as const;

Expand All @@ -42,6 +43,7 @@ const source = "ipfs" as const;
export class DappnodeRepository extends ApmRepository {
protected ipfs: KuboRPCClient;
protected timeout: number;
protected ipfsGatewayUrl: string;

/**
* Constructs an instance of DappnodeRepository
Expand All @@ -51,6 +53,7 @@ export class DappnodeRepository extends ApmRepository {
constructor(ipfsUrl: string, ethersProvider: ethers.AbstractProvider, timeout?: number) {
super(ethersProvider);
this.timeout = timeout || 30 * 1000;
this.ipfsGatewayUrl = ipfsUrl;
this.ipfs = create({ url: ipfsUrl, timeout: this.timeout });
}

Expand Down Expand Up @@ -231,9 +234,9 @@ export class DappnodeRepository extends ApmRepository {
}

// Process matched entries. If multiple files are allowed, and more than one file matches, we parse all of them.
const { maxSize: maxLength, format } = fileConfig;
const { format } = fileConfig;
const contents = await Promise.all(
matchingEntries.map((entry) => this.writeFileToMemory(entry.cid.toString(), maxLength))
matchingEntries.map((entry) => this.getVerifiedContentFromGateway(entry.cid.toString()))
);

// If multiple files are allowed, we return an array of parsed assets.
Expand All @@ -246,34 +249,22 @@ export class DappnodeRepository extends ApmRepository {
* This function is intended for small files.
*
* @param hash - The content identifier (CID) of the file to download.
* @param maxLength - The maximum length of the file in bytes. If the downloaded file exceeds this length, an error is thrown.
* @returns The downloaded file content as a UTF8 string.
* @throws Error when the maximum size is exceeded.
* @see catString
* @see catCarReaderToMemory
*/
public async writeFileToMemory(hash: string, maxLength?: number): Promise<string> {
const chunks = [];
const { carReader, root } = await this.getAndVerifyContentFromGateway(hash);
const content = await this.unpackCarReader(carReader, root);
for await (const chunk of content) chunks.push(chunk);

// Concatenate the chunks into a single Uint8Array
let totalLength = 0;
chunks.forEach((chunk) => (totalLength += chunk.length));
const buffer = new Uint8Array(totalLength);
let offset = 0;
chunks.forEach((chunk) => {
buffer.set(chunk, offset);
offset += chunk.length;
private async getVerifiedContentFromGateway(hash: string): Promise<string> {
// TODO: Add max leght?
const cid = CID.parse(this.sanitizeIpfsPath(hash));

const customGatewayFetch = await createVerifiedFetch({
gateways: [this.ipfsGatewayUrl]
});

if (maxLength && buffer.length >= maxLength) throw Error(`Maximum size ${maxLength} bytes exceeded`);
const response = await customGatewayFetch(cid);

// Convert the Uint8Array to a string
// TODO: This assumes the data is UTF-8 encoded. If it's not, you will need a more complex conversion. Research which encoding is used by IPFS.
const decoder = new TextDecoder("utf-8");
return decoder.decode(buffer);
// Log the full response body (assuming it's text-based)
const content = await response.text();

return content;
}

/**
Expand Down Expand Up @@ -379,7 +370,9 @@ export class DappnodeRepository extends ApmRepository {

public async list(hash: string): Promise<IPFSEntry[]> {
const files: IPFSEntry[] = [];
const dagGet = await this.ipfs.dag.get(CID.parse(this.sanitizeIpfsPath(hash)), { timeout: this.timeout });
const cid = CID.parse(this.sanitizeIpfsPath(hash));

const dagGet = await this.ipfs.dag.get(cid, { timeout: this.timeout });
if (dagGet.value.Links)
for (const link of dagGet.value.Links)
files.push({
Expand Down
Loading
Loading