#!/usr/bin/env node // verifica.mjs: the reader's side of a verifiable dataset, without the data (roadmap point 20). Everything is judged against a // COMMITMENT (a version's JSON) and prints VALID / INVALID with the reasons; exit 0 VALID, 1 INVALID, 2 could not measure. // node verifica.mjs raspuns --angajament a query answer: correct, complete, in order // node verifica.mjs inregistrare --angajament one record with its proof // node verifica.mjs consistenta --vechi --nou the newer version only appended // node verifica.mjs disponibilitate --url --angajament [--esantioane 16] [--seed ] // random records fetched and verified (retrievability) // node verifica.mjs angajament --plic [--verify-proof ] [--rpc URL --chain 28001] // the AIP-23 envelope is this commitment's; with --rpc, // its notarization under a post-quantum certified anchor import fs from 'node:fs'; import path from 'node:path'; import { spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import { verificaRaspuns, verificaInregistrare, verificaConsistentaVersiuni, verificaPlicAngajament, esantioneaza, hashAngajament } from './baza.mjs'; const AICI = path.dirname(fileURLToPath(import.meta.url)); const a = process.argv.slice(2); const cmd = a[0]; const opt = (k, d = null) => { const i = a.indexOf(k); return i >= 0 && i + 1 < a.length ? a[i + 1] : d; }; const citeste = (f) => JSON.parse(fs.readFileSync(f, 'utf8')); const iesi = (verdict, motive = [], extra = '') => { console.log(`${verdict}${extra ? ' ' + extra : ''}${motive.length ? '\n ' + motive.join('\n ') : ''}`); process.exitCode = verdict === 'VALID' ? 0 : verdict === 'INVALID' ? 1 : 2; }; const ruleaza = (verifyProof, envFile, { rpc, chain }) => new Promise((ok) => { const args = [verifyProof, envFile, '--json', ...(rpc ? ['--rpc', rpc] : []), ...(chain ? ['--chain', String(chain)] : [])]; const c = spawn(process.execPath, args); let out = ''; c.stdout.on('data', (d) => { out += d; }); c.stderr.on('data', () => {}); const ceas = setTimeout(() => c.kill(), 300000); c.on('error', () => { clearTimeout(ceas); ok(null); }); c.on('close', () => { clearTimeout(ceas); try { ok(JSON.parse(out)); } catch { ok(null); } }); }); async function main() { if (cmd === 'raspuns') { const r = verificaRaspuns(citeste(a[1]), citeste(opt('--angajament'))); return iesi(r.verdict, r.motive, `(${r.rows} rows)`); } if (cmd === 'inregistrare') { const r = verificaInregistrare(citeste(a[1]), citeste(opt('--angajament'))); return iesi(r.ok ? 'VALID' : 'INVALID', r.motive); } if (cmd === 'consistenta') { const r = verificaConsistentaVersiuni(citeste(a[1]), citeste(opt('--vechi')), citeste(opt('--nou'))); return iesi(r.ok ? 'VALID' : 'INVALID', r.motive); } if (cmd === 'disponibilitate') { const url = opt('--url'); const c = citeste(opt('--angajament')); if (!url) throw new Error('--url is required'); const ia = async (u) => { const r = await fetch(u, { headers: { accept: 'application/json' }, signal: AbortSignal.timeout(20000) }); if (!r.ok) return null; return r.json(); }; const r = await esantioneaza({ url, commitment: c, k: Number(opt('--esantioane', 16)), seed: opt('--seed', null), ia }); return iesi(r.ok ? 'VALID' : 'INVALID', r.motive, `(${r.verificate} of ${r.cerute} sampled records verified, seed ${r.seed}${r.lipsa.length ? '; not served: ' + r.lipsa.join(',') : ''})`); } if (cmd === 'angajament') { const c = citeste(a[1]); const env = citeste(opt('--plic')); if (!verificaPlicAngajament(env, c)) return iesi('INVALID', ['the envelope is not this commitment\'s (sha256/name differ, or not an aere-proof-of-data attestation)']); const vp = opt('--verify-proof', process.env.AERE_VERIFY_PROOF || path.resolve(AICI, '..', '..', 'verificator-falcon', 'verify-proof.mjs')); if (!fs.existsSync(vp)) return iesi('UNMEASURED', ['the AIP-23 verifier is not at ' + vp + ' (AERE_VERIFY_PROOF or --verify-proof)'], `commitment ${hashAngajament(c)} is the envelope's`); const v = await ruleaza(vp, opt('--plic'), { rpc: opt('--rpc'), chain: opt('--chain') }); if (!v) return iesi('UNMEASURED', ['the verifier gave no JSON']); const fin = (v.levels || []).find((l) => l.level === 'finality'); const extra = `envelope ${v.verdict}; finality ${fin ? fin.state + ': ' + String(fin.detail).slice(0, 160) : 'absent'}`; if (v.verdict !== 'VALID') return iesi('INVALID', [], extra); if (opt('--rpc') && !(fin && fin.state === 'PASSED')) return iesi(fin && fin.state === 'FAILED' ? 'INVALID' : 'UNMEASURED', [], extra); return iesi('VALID', [], extra); } console.log('usage: verifica.mjs raspuns|inregistrare|consistenta|disponibilitate|angajament ... (see the header)'); process.exitCode = 2; } try { await main(); } catch (e) { iesi('UNMEASURED', [String(e.message).slice(0, 200)]); }