// Proba B3 milestone 3, PE LANT: jurnalul de audit al unei desfasurari -> capul lui atestat -> notarizat pe AereNotary de pe // testnetul public 28001 -> verify-proof ii da finalitate POST-CUANTICA (ancora certificata -> radacina de stare -> dovada Merkle a // lui firstSeen). Cu cheia de DEZVOLTATOR a testnetului (valoare de test, niciodata tiparita), niciodata o cheie de mainnet. // node proba-notarizare-testnet.mjs --key-file // Cazuri, fiecare cu perechea lui: // A inainte de notarizare, verify-proof pe cap: finalitatea NU e post-cuantica, absenta e DOVEDITA ("not notarized") // B notarize-head: tranzactie reusita, firstSeen > 0, plicul iesit declara notarizarea // C verify-proof pe plicul notarizat: finality PASSED post-quantum, cu timpul = firstSeen (asteapta ancora certificata de dupa) // D CONTROL: o intrare din jurnal manipulata -> verify-log rupt la seq-ul ei, attest-head refuza // E CONTROL: capul altui jurnal (o intrare in plus) -> verify-proof: NEnotarizat, nu post-cuantic // F CONTROL: plic cu statement schimbat si statementHash vechi -> notarize-head REFUZA, fara tranzactie // G CONTROL: RPC-ul mainnetului (2800) fara AERE_CONFIRM_MAINNET=yes -> REFUZ inainte de cheie, cod 3 import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const AICI = path.dirname(fileURLToPath(import.meta.url)); const SIDECAR = path.join(AICI, 'sidecar.mjs'); const VERIFY = process.env.AERE_VERIFY_PROOF ? path.resolve(process.env.AERE_VERIFY_PROOF) : path.resolve(AICI, '..', '..', 'verificator-falcon', 'verify-proof.mjs'); const RPC = 'https://testnet-rpc.aere.network'; const arg = (n) => { const i = process.argv.indexOf('--' + n); return i > 0 ? process.argv[i + 1] : null; }; const KEY = arg('key-file'); if (!KEY || !fs.existsSync(KEY)) { console.error('cer --key-file '); process.exitCode = 2; } const T = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-b3-notar-')); let ok = 0, rau = 0; const linii = []; const cere = (c, ce) => { linii.push((c ? 'OK ' : 'RAU ') + ce); c ? ok++ : rau++; }; const taie = (s) => String(s ?? '').replace(/(0x)?[0-9a-fA-F]{40,}/g, (m) => (m.length === 66 ? m.slice(0, 12) + '..' : '')).slice(0, 220); function run(script, args, env = {}) { try { return { cod: 0, out: execFileSync(process.execPath, [script, ...args], { encoding: 'utf8', env: { ...process.env, ...env }, stdio: ['ignore', 'pipe', 'pipe'] }) }; } catch (e) { return { cod: e.status ?? 2, out: String(e.stdout || '') + String(e.stderr || '') }; } } const verifica = (f) => { const r = run(VERIFY, [f, '--rpc', RPC, '--chain', '28001', '--json']); try { return JSON.parse(r.out); } catch { return { verdict: '?', levels: [], brut: r.out }; } }; const fin = (v) => (v.levels || []).find((l) => l.level === 'finality') || {}; const dormi = (ms) => new Promise((r) => setTimeout(r, ms)); if (KEY && fs.existsSync(KEY)) try { const log = path.join(T, 'audit.log'); const creazaJurnal = (logf, n) => { for (let i = 1; i <= n; i++) { const f = path.join(T, `artefact-${i}.bin`); fs.writeFileSync(f, `desfasurarea ${i} a serviciului de proba, ${T}`); const r = run(SIDECAR, ['record', '--kind', 'deployment', '--name', 'serviciu-proba', '--version', `1.0.${i}`, '--content-file', f, '--log', logf, '--host', 'proba-b3']); if (r.cod !== 0) throw new Error('record a cazut: ' + taie(r.out)); } }; creazaJurnal(log, 3); cere(run(SIDECAR, ['verify-log', '--log', log]).cod === 0, 'jurnalul de 3 desfasurari e intreg'); const head = path.join(T, 'cap.json'); cere(run(SIDECAR, ['attest-head', '--log', log, '--out', head, '--host', 'proba-b3']).cod === 0 && fs.existsSync(head), 'capul lantului atestat (aere-audit-head)'); // A: absenta DOVEDITA inainte const vA = verifica(head); const fA = fin(vA); cere(fA.state !== 'PASSED' && /not notarized|ABSENT|no record/i.test(String(fA.detail) + fA.state), `A inainte de notarizare: finality ${fA.state} (${taie(fA.detail)})`); // B: notarizarea const notar = path.join(T, 'cap-notarizat.json'); const rB = run(SIDECAR, ['notarize-head', '--head', head, '--rpc', RPC, '--key-file', KEY, '--out', notar]); const pB = fs.existsSync(notar) ? JSON.parse(fs.readFileSync(notar, 'utf8')) : null; cere(rB.cod === 0 && pB && pB.notarization && pB.notarization.firstSeen > 0 && pB.notarization.chainId === 28001, `B notarize-head: ${taie(rB.out.trim().split('\n').pop())}`); // C: finalitatea post-cuantica apare dupa urmatoarea ancora certificata (asteptare marginita, 5 minute) let vC = null, fC = {}, incercari = 0; for (const t0 = Date.now(); Date.now() - t0 < 300000; incercari++) { vC = verifica(notar); fC = fin(vC); if (fC.state === 'PASSED' && /post-quantum/.test(fC.detail || '')) break; await dormi(20000); } const timp = pB && pB.notarization ? String(pB.notarization.firstSeen) : 'X'; cere(vC.verdict === 'VALID' && fC.state === 'PASSED' && /post-quantum/.test(fC.detail || '') && String(fC.detail).includes(timp), `C verify-proof: finality ${fC.state} post-quantum cu firstSeen ${timp} (dupa ${incercari} reincercari): ${taie(fC.detail)}`); // D: jurnal manipulat const logD = path.join(T, 'audit-manipulat.log'); fs.copyFileSync(log, logD); const ld = fs.readFileSync(logD, 'utf8').split('\n'); const o = JSON.parse(ld[1]); o.proof.statement.name = 'serviciu-strain'; ld[1] = JSON.stringify(o); fs.writeFileSync(logD, ld.join('\n')); const rD = run(SIDECAR, ['verify-log', '--log', logD]); const rD2 = run(SIDECAR, ['attest-head', '--log', logD, '--out', path.join(T, 'x.json')]); cere(rD.cod === 1 && /seq 1/.test(rD.out) && rD2.cod === 1, `D CONTROL: intrare manipulata -> ${taie(rD.out.trim())}; attest-head refuza`); // E: capul altui jurnal const logE = path.join(T, 'audit-altul.log'); fs.copyFileSync(log, logE); creazaJurnal(logE, 1); const headE = path.join(T, 'cap-altul.json'); run(SIDECAR, ['attest-head', '--log', logE, '--out', headE, '--host', 'proba-b3']); const fE = fin(verifica(headE)); cere(fE.state !== 'PASSED', `E CONTROL: capul unui jurnal cu o intrare in plus NU e notarizat: finality ${fE.state} (${taie(fE.detail)})`); // F: plic manipulat const pF = JSON.parse(fs.readFileSync(head, 'utf8')); pF.statement.count = pF.statement.count + 1; const headF = path.join(T, 'cap-manipulat.json'); fs.writeFileSync(headF, JSON.stringify(pF)); const rF = run(SIDECAR, ['notarize-head', '--head', headF, '--rpc', RPC, '--key-file', KEY]); cere(rF.cod === 1 && /modified attestation/.test(rF.out) && !/notarized on chain/.test(rF.out), `F CONTROL: statement schimbat -> notarize-head refuza fara tranzactie (${taie(rF.out.trim())})`); // G: garda de mainnet const rG = run(SIDECAR, ['notarize-head', '--head', head, '--rpc', 'https://rpc.aere.network', '--key-file', KEY], { AERE_CONFIRM_MAINNET: '' }); cere(rG.cod === 3 && /2800/.test(rG.out), `G CONTROL: RPC de mainnet fara DA -> cod ${rG.cod}, refuzat (${taie(rG.out.trim())})`); // pastreaza dovada: capul notarizat si jurnalul lui, fara nimic secret const dov = path.join(AICI, 'dovezi-notarizare-testnet'); fs.mkdirSync(dov, { recursive: true }); if (pB) fs.writeFileSync(path.join(dov, 'cap-notarizat-28001.json'), JSON.stringify(pB, null, 1) + '\n'); fs.copyFileSync(log, path.join(dov, 'audit.log')); } catch (e) { linii.push('RAU proba nu a putut rula: ' + taie(e.message)); rau++; } finally { try { fs.rmSync(T, { recursive: true, force: true }); } catch {} } for (const l of linii) console.log(l); console.log(`B3 notarizare pe testnet: ${ok}/${ok + rau} cum trebuia`); if (!process.exitCode) process.exitCode = rau ? 1 : 0;