// noble.mjs (quantum exposure report, C12): the ONE place the cryptographic dependencies are loaded. The tool needs // @noble/hashes 1.x (keccak-256, RIPEMD-160) and @noble/curves 1.x (secp256k1: public-key recovery from a signature, point decoding). // They are declared in package.json beside this file and resolved, in this order: from a `node_modules` reachable from this directory // (`npm install --ignore-scripts` here, or in a parent directory that already holds them), then from the directory named by // AERE_EXPUNERE_NODE_MODULES (the test sets it to the tool's own `node_modules` for the copies it runs, which have none), then from // the AERE anchor verifier's own `node_modules` when this directory lives in the AERE development tree. A candidate is USED only when it has the exports the tool // calls (secp256k1.Signature, secp256k1.ProjectivePoint.fromHex, secp256k1.CURVE.n, keccak_256, ripemd160): @noble/curves 2.x, for // one, has neither ProjectivePoint nor CURVE, and a parent `node_modules` holding it is passed over for the next candidate. When no // candidate fits, the error carries the code EAEREDEP, names what was found, and gives the command to run; the report tool and the // test exit with 2 ("could not run") and say so, instead of crashing inside an import. import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; const AICI = path.dirname(fileURLToPath(import.meta.url)); export class LipsaDependenta extends Error { constructor(sub, gasite = []) { super(`DEPENDENCY MISSING: ${sub} is not installed in a usable version${gasite.length ? ' (found without the exports this tool calls, so not used: ' + gasite.join('; ') + ')' : ''}; run \`npm install --ignore-scripts --no-audit --no-fund\` in ${AICI} (dependencies are declared in its package.json)`); this.code = 'EAEREDEP'; } } // the candidates outside this directory: the directory named by AERE_EXPUNERE_NODE_MODULES, then the AERE anchor verifier's node_modules const dinAfara = (sub) => [process.env.AERE_EXPUNERE_NODE_MODULES, path.join(AICI, '..', '..', '..', 'verificator-falcon', 'node_modules')].filter(Boolean).map((d) => pathToFileURL(path.join(d, sub)).href); async function incarca(sub, potrivit) { const incercari = [sub, sub.replace(/\.js$/, ''), pathToFileURL(path.join(AICI, 'node_modules', sub)).href, ...dinAfara(sub)]; const nepotrivite = []; for (const s of incercari) { let m; try { m = await import(s); } catch (e) { if (e && e.code && !/ERR_MODULE_NOT_FOUND|ERR_PACKAGE_PATH_NOT_EXPORTED|ERR_UNSUPPORTED_DIR_IMPORT/.test(e.code)) throw e; continue; } if (potrivit(m)) return m; nepotrivite.push(s.startsWith('file:') ? path.relative(AICI, fileURLToPath(s)) : s); } throw new LipsaDependenta(sub, [...new Set(nepotrivite)]); } const sha3 = await incarca('@noble/hashes/sha3.js', (m) => typeof m.keccak_256 === 'function'); const rmd = await incarca('@noble/hashes/ripemd160.js', (m) => typeof m.ripemd160 === 'function'); const curbaPotrivita = (m) => { const c = m.secp256k1; return !!c && typeof c.Signature === 'function' && !!c.ProjectivePoint && typeof c.ProjectivePoint.fromHex === 'function' && !!c.CURVE && typeof c.CURVE.n === 'bigint' && typeof c.getPublicKey === 'function'; }; const curves = await incarca('@noble/curves/secp256k1.js', curbaPotrivita); export const keccak_256 = sha3.keccak_256; export const ripemd160 = rmd.ripemd160; export const secp256k1 = curves.secp256k1;