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
};