File size: 1,934 Bytes
93c7565 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | const crypto = require("crypto");
const IPFS_GATEWAYS = [
"https://ipfs.io/ipfs/",
"https://cloudflare-ipfs.com/ipfs/",
"https://gateway.pinata.cloud/ipfs/",
"https://dweb.link/ipfs/",
"https://w3s.link/ipfs/"
];
const FETCH_TIMEOUT_MS = 5000;
const BACKOFF_MS = [500, 1000, 2000]; // 0.5s → 1s → 2s
async function fetchWithTimeout(url, timeoutMs) {
const controller = new AbortController();
const t = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch(url, {
signal: controller.signal,
headers: { "Accept": "application/octet-stream" }
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const buf = Buffer.from(await res.arrayBuffer());
return buf;
} finally {
clearTimeout(t);
}
}
function sha256(buffer) {
return crypto.createHash("sha256").update(buffer).digest("hex");
}
async function resolveCID(cid) {
const results = [];
for (const gateway of IPFS_GATEWAYS) {
try {
const data = await fetchWithTimeout(`${gateway}${cid}`, FETCH_TIMEOUT_MS);
results.push({
gateway,
ok: true,
hash: sha256(data),
});
} catch (err) {
results.push({
gateway,
ok: false,
error: err.message,
});
}
}
return results;
}
function evaluateResolution(results, expectedHash) {
const successes = results.filter(r => r.ok);
const failures = results.filter(r => !r.ok);
if (successes.length === 0) {
return { status: "NETWORK_UNAVAILABLE" };
}
const mismatches = successes.filter(r => r.hash !== expectedHash);
if (mismatches.length >= 2) {
return {
status: "HASH_MISMATCH",
evidence: mismatches
};
}
return {
status: "CONSISTENT",
observedHash: successes[0].hash,
failures
};
}
module.exports = {
IPFS_GATEWAYS,
FETCH_TIMEOUT_MS,
BACKOFF_MS,
resolveCID,
evaluateResolution,
sha256
}; |