1757 lines
205 KiB
JavaScript
1757 lines
205 KiB
JavaScript
// Proba martorului gazduit al agentilor (martor-agenti.mjs, martor-agenti-serviciu.mjs; B3, 2026-10-19). Fluxul intreg, cu chei nascute la
|
|
// rulare (agenti, martor, oameni) si un NOTAR-MOCK local (numit asa in fiecare rand): doi agenti, capete succesive, un cap care sare, un cap
|
|
// mai vechi si unul care nu continua ultimul (409), echivocarea prinsa, pastrata si intoarsa, refuzurile numite ale cererii (continutul
|
|
// registrului, semnatura, cheia, identitatea, sesiunea, momentul), limitele de ritm, capetele de jurnal semnate de martor cu dovada de
|
|
// includere si de consistenta, repornirea pe aceeasi stare, chitanta verificata FARA martor (local) si ca ancora (cu raspunsul verificatorului
|
|
// AIP-23 in forma lui), antedatarea de dupa cap prinsa cu ancora si nu fara ea, linia de comanda, usa gateway-ului (daca e in arbore), si plicul
|
|
// martorului judecat de verificatorul AIP-23 de referinta (AERE_VERIFY_PROOF; fara el randurile acelea sunt sarite pe nume si iesirea e 2).
|
|
// Sectiunea 9 (2026-10-19): cate un rand pentru fiecare din cele 18 constatari ale reviziei adversariale B3 (numele incep cu "9.R<n>"), fiecare
|
|
// scris asa incat pe forma de dinainte de reparatie sa iasa RAU (nu sa opreasca proba): martorul fixat, lista intreaga a capetelor, cioburile
|
|
// si bugetul jurnalului, echivocarea pe drumul "deja vazut", argumentele, iesirea lui submit, vocabularul felului, plafonul dovezilor, paginarea,
|
|
// cota consumata prin retrimitere, limitatoarele, scrierile durabile si erorile publice. Un rand "9.P<n>" e controlul unui paznic NOU pus de
|
|
// reparatia lui R<n> (forma veche putea sa treaca de el din alt motiv); 9.R6 se masoara in controlul negativ, 9.R14 si 9.R15 in
|
|
// cloud-gateway/proba-desfasoara-martor-agenti.sh.
|
|
// Sectiunea 10 (2026-10-19, a doua revizie adversariala, 15 constatari confirmate): randurile "10.R2-<nn>", la fel scrise sa iasa RAU pe forma
|
|
// de dinainte (cf56bcda6): lista de capete semnata de martor (--witness-url fata de un proxy ostil, un impostor si o pagina veche reluata), dovezile
|
|
// de echivocare legate de ciobul capului contrazis, limitatorul la plafonul de chei, o lista cu elemente stricate, ancorele simple sub un martor
|
|
// fixat, arborele incremental si memoria unei stari cu un cap de jurnal pe frunza, rotirea care asteapta notarizarea, ciobul nascut intreg, ruta
|
|
// ciobului inghetat, lacatul cu pornirea masinii, codurile de iesire, bugetul citit inainte de cota agentului, Retry-After prin usa, si jumatatea
|
|
// "radacina" a paznicului de deschidere. Randurile "10.P2-<nn>" sunt controalele paznicilor noi; doua randuri 10.R2-15 si 10.P2-13 trec si pe
|
|
// forma veche (acolo comportamentul era bun, lipsea proba care sa-l poata inrosi).
|
|
// Sectiunea 11 (2026-10-19, a treia revizie adversariala, 5 constatari confirmate): randurile "11.R3-<nn>", RAU pe forma de dinainte (c34b34f1b):
|
|
// restul unei stergeri de ciob intrerupte (redenumit intai; un rest vechi, fara ciob.json sau frunze.ndjson, sters), dovada care traieste cel mai
|
|
// mult pe sesiune, steagurile date fara valoare, chitanta ultimului cap tinut de martor sub --witness-url, codurile lui receipt si submit.
|
|
// Sectiunea 12 (2026-10-19, a patra revizie adversariala, 6 constatari confirmate): randurile "12.R4-<nn>", RAU pe forma de dinainte (c5183bcb8),
|
|
// pe regula de proiectare a lui --witness-url (marturia martorului): notarizarea intarziata, un proxy care strica GET-ul unui cap, fiecare cap tinut
|
|
// cu chitanta lui, submit pe drumul principal, steagurile --flag=valoare si cele necunoscute, departajarea a doua dovezi si marcajul .de-sters.
|
|
// Sectiunea 13 (2026-10-19, a cincea revizie adversariala, 6 constatari confirmate): randurile "13.R5-<nn>", RAU pe forma de dinainte (b55038c2a):
|
|
// verify ca EVALUARE COMPLETA cu un singur verdict (un NEMASURAT nu mai ascunde o vina dovedita; o lista a altui martor, o ancora simpla, o pana a
|
|
// notarului sau a RPC-ului nu mai fac un FAILED), tipurile judecate inainte de folosire (nicio exceptie), si asteptarea pe 429 cu Retry-After.
|
|
// Sectiunea 14 (2026-10-19, verificarea tintita a reparatiilor R5, constatarile N1..N3): randurile "14.N<n>", RAU pe forma de dinainte (786aa0038):
|
|
// o chitanta fara --rpc e o dovada NEMASURATA [rpc] judecata la locul ei (nu o iesire care ascunde ramura aratata de lista semnata), `receipt` scrie
|
|
// numai chitanta capului cerut, a registrului dat, cu semnatura martorului verificata, si regula (c) pe drumul "nimic nou" al lui submit.
|
|
// Fiecare afirmatie cu perechea ei negativa.
|
|
// node proba-martor-agenti.mjs offline (notar-mock): iesire 0 toate cum trebuia, 1 una rosie, 2 o parte nemasurata
|
|
// node proba-martor-agenti.mjs --testnet si notarizarea REALA pe testnetul public 28001 (cheie de proba: AERE_TESTNET_KEY_FILE, sau una
|
|
// nascuta si alimentata din robinet), plicul VALID cu finalitate post-cuantica, verify --anchors
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import net from 'node:net';
|
|
import http from 'node:http';
|
|
import crypto from 'node:crypto';
|
|
import { spawn, spawnSync } from 'node:child_process';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import { definePolicy } from './agent-policy.mjs';
|
|
import { newAgentIdentity, openLedger, resumeLedger, verifyLedger, agentIdFromKey, sessionId } from './agent-ledger.mjs';
|
|
import { NOTARI } from './agent-ancora.mjs';
|
|
import * as M from './martor-agenti.mjs';
|
|
import * as S from './martor-agenti-serviciu.mjs';
|
|
import * as PK from '../proof-kinds/proof-kinds.mjs';
|
|
|
|
const { porneste, tur, notarizatorReal } = S;
|
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
|
const TESTNET = process.argv.includes('--testnet');
|
|
let ok = 0, rau = 0, sarit = 0;
|
|
const cer = (c, ce, d = '') => { console.log(` ${c ? 'OK ' : 'RAU'} ${ce}${c || !d ? '' : ' -- ' + String(d).slice(0, 300)}`); c ? ok++ : rau++; };
|
|
const sari = (ce, dece) => { console.log(` -- ${ce} (SARIT, NEMASURAT: ${dece})`); sarit++; };
|
|
// un rand care nu are voie sa opreasca proba: pe forma veche a componentei o functie lipsa sau o forma de raspuns alta da RAU, numit
|
|
const rand = async (ce, f) => { let r; try { r = await f(); } catch (e) { return cer(false, ce, 'EXCEPTIE: ' + (e && e.message)); } return cer(!!r[0], ce, r[1]); };
|
|
const clon = (o) => JSON.parse(JSON.stringify(o));
|
|
const sha = (s) => '0x' + crypto.createHash('sha256').update(Buffer.from(s, 'utf8')).digest('hex');
|
|
const iso = (ms) => new Date(ms).toISOString();
|
|
const T = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-martor-agenti-'));
|
|
const DEV_VERIFY = path.resolve(AICI, '..', '..', 'verificator-falcon', 'verify-proof.mjs');
|
|
const VERIFY = process.env.AERE_VERIFY_PROOF !== undefined ? process.env.AERE_VERIFY_PROOF : (fs.existsSync(DEV_VERIFY) ? DEV_VERIFY : '');
|
|
const resemneaza = (env, cheie) => { env.statementHash = sha(JSON.stringify(env.statement)); env.signature.signature = crypto.sign(null, Buffer.from(JSON.stringify(env.statement), 'utf8'), cheie).toString('base64'); return env; };
|
|
const hx = () => crypto.randomBytes(32).toString('hex');
|
|
// un registru "de forma" (numai seq si hash, cat cere martorul): pentru probele de volum, unde intrarile nu se judeca
|
|
const registruBrut = (id, n, policyHash = '0x' + hx()) => ({ agentId: id.agentId, session: sessionId(id.agentId, policyHash), policyHash, entries: Array.from({ length: n }, (_, seq) => ({ seq, hash: hx() })) });
|
|
// fisierul frunzelor ciobului k (starea de dinainte de cioburi le tinea la radacina: proba se poate rula si pe forma veche a componentei)
|
|
const frunzeCiob = (dir, k = 0) => { const f = path.join(dir, 'cioburi', String(k), 'frunze.ndjson'); return fs.existsSync(f) || !fs.existsSync(path.join(dir, 'frunze.ndjson')) ? f : path.join(dir, 'frunze.ndjson'); };
|
|
// `submit` si `verify --witness-url` vorbesc cu un martor din ACEST proces: un spawnSync ar bloca bucla care il serveste, deci copilul ruleaza asincron
|
|
const ruleazaAsincron = (args, env = {}) => new Promise((g) => { const c = spawn(process.execPath, args, { stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env, ...env } }); let stdout = '', stderr = ''; c.stdout.on('data', (x) => { stdout += x; }); c.stderr.on('data', (x) => { stderr += x; }); c.on('close', (status) => g({ status, stdout, stderr })); });
|
|
|
|
// ------------------------------------------------------------------------------------------------ actorii si registrele
|
|
const acum = Math.floor(Date.now() / 1000);
|
|
const BAZA = acum - 3000; // intrarile 0..2 ale lui A la BAZA..BAZA+10; capul seq 2 trimis la BAZA+1200
|
|
const pol = (id) => definePolicy({ agentId: id.agentId, spend: { amount: '1000', windowSeconds: 3600 }, recipients: ['0xbbbb'] });
|
|
const registru = (id, p, momente) => { let t = momente[0]; const L = openLedger({ identity: id, policy: p.policy, policyHash: p.policyHash, now: () => t }); for (const m of momente) { t = m; L.record({ kind: 'payment', to: '0xbbbb', amount: '10' }); } return L; };
|
|
const A = newAgentIdentity(), B = newAgentIdentity(), C = newAgentIdentity(), D = newAgentIdentity(), E = newAgentIdentity();
|
|
const pA = pol(A), pB = pol(B), pC = pol(C), pD = pol(D), pE = pol(E);
|
|
const LA = registru(A, pA, [BAZA, BAZA + 5, BAZA + 10]);
|
|
const LA2 = registru(A, pA, [BAZA, BAZA + 6, BAZA + 11, BAZA + 12]); // alta RAMURA a aceluiasi agent, aceeasi sesiune
|
|
const LB = registru(B, pB, [BAZA + 20, BAZA + 25]), LC = registru(C, pC, [BAZA + 30, BAZA + 31]), LD = registru(D, pD, [BAZA + 40]), LE = registru(E, pE, [BAZA + 50]);
|
|
let wMs = (BAZA + 1) * 1000; // ceasul martorului (injectat)
|
|
const DIR = path.join(T, 'stare');
|
|
const LIMITE = { postPeClient: { cereri: 40, ms: 600_000 }, postPeAgent: { cereri: 12, ms: 600_000 }, getPeClient: { cereri: 2000, ms: 600_000 } };
|
|
let m = M.deschideMartor(DIR, { ceas: () => wMs });
|
|
let srv = await porneste(m, { limite: LIMITE });
|
|
const U = () => `http://127.0.0.1:${srv.port}`;
|
|
async function postLa(baza, corp, client = 'proba-principal', ct = 'application/json', antete = {}) {
|
|
const r = await fetch(baza + '/v1/agents/witness', { method: 'POST', headers: { 'content-type': ct, 'x-real-ip': client, ...antete }, body: typeof corp === 'string' ? corp : JSON.stringify(corp) });
|
|
let j = null; try { j = await r.json(); } catch { j = null; } return { cod: r.status, j, antete: r.headers };
|
|
}
|
|
const post = (corp, client, ct) => postLa(U(), corp, client, ct);
|
|
const get = async (cale, baza = U(), client = 'proba-citire') => { const r = await fetch(baza + cale, { headers: { 'x-real-ip': client } }); let j = null; try { j = await r.json(); } catch { j = null; } return { cod: r.status, j }; };
|
|
const cerere = (L, id, o = {}) => M.cerereMartor(L.export(), id, { createdAt: iso(wMs), ...o });
|
|
// raspunsul verificatorului AIP-23 in forma lui, pentru un plic dat (fara retea)
|
|
const verdict = (h, { fin = 'PASSED', t, sig = 'PASSED' } = {}) => ({ verdict: fin === 'PASSED' && sig === 'PASSED' ? 'VALID' : 'PARTIAL', statementHash: h, levels: [
|
|
{ level: 'form', state: 'PASSED', detail: 'kind=' + M.CHECKPOINT_KIND + '-attestation' }, { level: 'integrity', state: 'PASSED', detail: 'sha256(statement) == statementHash' },
|
|
{ level: 'signature', state: sig, detail: 'ml-dsa-65: verifies over the canonical statement text' },
|
|
{ level: 'finality', state: fin, detail: fin === 'PASSED' ? `post-quantum: the state of block 9, the parent of anchor 10 whose post-quantum certificate verified here, records this statementHash in AereNotary ${NOTARI[28001]} as first seen at ${iso(t * 1000)} (${t}); first seen in block 5` : 'notarized after the anchor; the next anchor will cover it' }] });
|
|
const stub = (f) => async (_vp, fis) => f(JSON.parse(fs.readFileSync(fis, 'utf8')));
|
|
const notarMock = (firstSeen) => async (env) => ({ ok: true, notarization: { chainId: 28001, notary: NOTARI[28001], firstSeen, txHash: '0x' + 'ab'.repeat(32), block: 123456, mock: 'local notary mock (offline probe)', statementHashSeen: env.statementHash } });
|
|
// un verificator AIP-23 inlocuit, in forma raspunsului lui, pentru linia de comanda; momentul il da proba (STUB_F), nu plicul
|
|
const F5 = path.join(T, 'cli'); fs.mkdirSync(F5);
|
|
const fStub = path.join(F5, 'verify-stub.mjs');
|
|
fs.writeFileSync(fStub, `import fs from 'node:fs'; const e = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); const t = Number(process.env.STUB_F);
|
|
console.log(JSON.stringify({ verdict: 'VALID', statementHash: e.statementHash, levels: [{ level: 'form', state: 'PASSED', detail: 'x' }, { level: 'integrity', state: 'PASSED', detail: 'x' }, { level: 'signature', state: 'PASSED', detail: 'x' },
|
|
{ level: 'finality', state: 'PASSED', detail: 'post-quantum: records this statementHash as first seen at ' + new Date(t * 1000).toISOString() + ' (' + t + ')' }] }));\n`);
|
|
const scrie = (n, o) => { const f = path.join(F5, n); fs.writeFileSync(f, JSON.stringify(o)); return f; };
|
|
const CLI = path.join(AICI, 'agent-cli.mjs');
|
|
// sectiunea 10: servere locale (proxy ostil, impostor) si paginile semnate de o cheie de martor data
|
|
const serverLocal = (f) => new Promise((g) => { const s = http.createServer((req, res) => { Promise.resolve(f(req, res, new URL(req.url, 'http://x'))).catch(() => { try { res.writeHead(500); res.end('{}'); } catch { /* deja raspuns */ } }); }); s.listen(0, '127.0.0.1', () => g({ url: `http://127.0.0.1:${s.address().port}`, close: () => new Promise((h) => s.close(() => h())) })); });
|
|
const trimite = (res, cod, o, antete = {}) => { const b = Buffer.from(JSON.stringify(o)); res.writeHead(cod, { 'content-type': 'application/json', 'content-length': b.length, ...antete }); res.end(b); };
|
|
// o pagina de capete semnata de o cheie de martor data (impostorul isi semneaza cinstit propriile pagini: cheia martorului fixat nu o are)
|
|
const KIND_PAGINA = M.PAGINA_KIND || 'aere-agent-witness-heads';
|
|
const paginaSemnataDe = (k, st) => { const text = JSON.stringify(st); return { v: 1, kind: KIND_PAGINA + '-attestation', statement: st, statementHash: sha(text), signature: { scheme: 'ml-dsa-65', publicKey: k.publicKeyPem, signature: crypto.sign(null, Buffer.from(text, 'utf8'), k.privateKey).toString('base64') } }; };
|
|
const paginaGoala = (k, agentId, session, from, heads = []) => ({ v: 1, kind: KIND_PAGINA, witness: k.witnessId, log: 'impostor', agentId, session, from, limit: 100, count: heads.length, latest: null, heads, next: null, equivocations: [], at: iso(Date.now()) });
|
|
|
|
|
|
try {
|
|
// ---------------------------------------------------------------------------------------------- 1. cererea si judecata ei
|
|
const c0 = cerere(LA, A, { seq: 0 });
|
|
const v0 = M.verificaCapSemnat(c0, { acumS: wMs / 1000 });
|
|
cer(v0.ok && c0.kind === 'aere-agent-ledger-head-attestation' && c0.statement.hash === LA.export().entries[0].hash && c0.statement.previous === null && c0.signature.scheme === 'ml-dsa-65',
|
|
'1. cererea: capul registrului (seq, hash, sesiune, politica) semnat ML-DSA-65 de agent, ca plic AIP-23; martorul il primeste', JSON.stringify(v0).slice(0, 200));
|
|
const cu = (f) => { const x = clon(c0); f(x); return M.verificaCapSemnat(x, { acumS: wMs / 1000 }); };
|
|
const rS = cu((x) => { x.statement.createdAt = iso(wMs + 1000); x.statementHash = sha(JSON.stringify(x.statement)); });
|
|
cer(!rS.ok && rS.rule === 'semnatura', '1. CONTROL: declaratia schimbata si hash-ul refacut, semnatura veche -> refuz "semnatura"', JSON.stringify(rS));
|
|
const rI = cu((x) => { x.statement.seq = 1; });
|
|
cer(!rI.ok && rI.rule === 'integritate', '1. CONTROL: declaratia schimbata fara hash refacut -> refuz "integritate"', JSON.stringify(rI));
|
|
const ed = crypto.generateKeyPairSync('ed25519');
|
|
const rK = cu((x) => { const aid = agentIdFromKey(ed.publicKey); x.statement.agentId = aid; x.statement.session = sessionId(aid, x.statement.policyHash); x.signature.publicKey = ed.publicKey.export({ type: 'spki', format: 'pem' }); resemneaza(x, ed.privateKey); });
|
|
cer(!rK.ok && rK.rule === 'cheie', '1. CONTROL: un cap semnat cu o cheie Ed25519 (id si sesiune derivate din ea) -> refuz "cheie"', JSON.stringify(rK));
|
|
const rId = cu((x) => { x.statement.agentId = B.agentId; x.statement.session = sessionId(B.agentId, x.statement.policyHash); resemneaza(x, A.privateKey); });
|
|
cer(!rId.ok && rId.rule === 'identitate', '1. CONTROL: cheia lui A semneaza un cap cu id-ul lui B -> refuz "identitate"', JSON.stringify(rId));
|
|
const rSe = cu((x) => { x.statement.session = 'ab'.repeat(16); resemneaza(x, A.privateKey); });
|
|
cer(!rSe.ok && rSe.rule === 'sesiune', '1. CONTROL: o sesiune care nu e cea derivata din agent si politica -> refuz "sesiune"', JSON.stringify(rSe));
|
|
const rC1 = cu((x) => { x.entries = LA.export().entries; });
|
|
const rC2 = cu((x) => { x.statement.action = { kind: 'payment', amount: '10' }; resemneaza(x, A.privateKey); });
|
|
cer(!rC1.ok && rC1.rule === 'continut' && !rC2.ok && rC2.rule === 'continut', '1. CONTROL: intrarile registrului langa cap, sau o actiune in declaratie (semnata) -> refuz "continut": martorul nu primeste niciodata continutul registrului', JSON.stringify([rC1, rC2]));
|
|
const rT = M.verificaCapSemnat(cerere(LA, A, { seq: 0, createdAt: iso(wMs - 400_000) }), { acumS: wMs / 1000 });
|
|
cer(!rT.ok && rT.rule === 'timp', '1. CONTROL: un cap declarat cu 400 s inaintea ceasului martorului -> refuz "timp"', JSON.stringify(rT));
|
|
// forma: un predecesor care nu e inaintea capului, sau un moment fara "T" si "Z", semnate cinstit (judecata fara ceas: numai forma ii poate prinde)
|
|
const rF1 = (() => { const x = clon(c0); x.statement.seq = 1; x.statement.previous = { seq: 1, hash: x.statement.hash }; return M.verificaCapSemnat(resemneaza(x, A.privateKey)); })();
|
|
const rF2 = (() => { const x = clon(c0); x.statement.createdAt = x.statement.createdAt.replace('T', ' ').replace('Z', ''); return M.verificaCapSemnat(resemneaza(x, A.privateKey)); })();
|
|
cer(!rF1.ok && rF1.rule === 'forma' && !rF2.ok && rF2.rule === 'forma', '1. CONTROL: forma capului: un predecesor care nu e inaintea capului, sau un moment fara "T" si "Z" (semnate cinstit) -> refuz "forma"', JSON.stringify([rF1, rF2]).slice(0, 200));
|
|
let arunca = null; try { M.cerereMartor(LA.export(), A, { seq: 2, previous: { seq: 0, hash: LA2.export().entries[0].hash } }); } catch (e) { arunca = e.message; }
|
|
cer(/another branch/.test(String(arunca)), '1. CONTROL: agentul care ar numi ca predecesor un cap pe care registrul lui nu il are e oprit inainte de trimitere (alta ramura)', arunca);
|
|
|
|
// ---------------------------------------------------------------------------------------------- 2. primirea, consistenta, echivocarea
|
|
const r0 = await post(c0);
|
|
cer(r0.cod === 202 && r0.j.status === 'pending' && r0.j.id === c0.statementHash && r0.j.leafIndex === 0 && r0.j.sandbox === M.SANDBOX,
|
|
'2. POST: primul cap al lui A (previous null) -> 202 pending, pozitia 0 in jurnal, "sandbox: testnet 28001; mainnet: at activation"', JSON.stringify(r0.j).slice(0, 300));
|
|
const pr = r0.j.promise;
|
|
cer(pr && pr.statement.kind === 'aere-proof-of-time' && pr.statement.subjectHash === c0.statementHash && pr.statement.source === m.witnessId && Date.parse(pr.statement.at) === wMs && pr.signature.scheme === 'ml-dsa-65',
|
|
'2. promisiunea de la primire: plic AIP-23 `time` (capul a existat la momentul primirii, dupa martor), semnat ML-DSA-65 de martor', JSON.stringify(pr).slice(0, 200));
|
|
const r0b = await post(c0);
|
|
cer(r0b.cod === 200 && r0b.j.id === c0.statementHash && r0b.j.status === 'pending', '2. acelasi cap trimis din nou -> 200, aceeasi intrare (fara o a doua frunza)', JSON.stringify(r0b.j).slice(0, 200));
|
|
wMs = (BAZA + 1200) * 1000; // capul seq 2 se trimite la 20 de minute dupa ultima intrare (asta e fereastra pe care o inchide ancora)
|
|
const c2 = cerere(LA, A, { seq: 2, previous: 0 });
|
|
const r2 = await post(c2);
|
|
cer(r2.cod === 202 && r2.j.leafIndex === 1, '2. un cap care SARE (seq 0 -> seq 2), numind ca predecesor ultimul cap martorizat -> 202', JSON.stringify(r2.j).slice(0, 200));
|
|
const rb0 = await post(cerere(LB, B, { seq: 0 })), rb1 = await post(cerere(LB, B, { seq: 1, previous: 0 }));
|
|
cer(rb0.cod === 202 && rb1.cod === 202 && rb1.j.leafIndex === 3, '2. al doilea agent (B): capete succesive 0 si 1 -> 202, 202 (jurnalul e comun, capetele sunt pe agent si politica)', JSON.stringify([rb0.j, rb1.j]).slice(0, 200));
|
|
const rV = await post(cerere(LA, A, { seq: 1, previous: 0 }));
|
|
cer(rV.cod === 409 && rV.j.error === 'stale_head' && rV.j.latest.seq === 2, '2. CONTROL: un cap mai VECHI decat ultimul martorizat (seq 1 dupa seq 2) -> 409 stale_head, cu ultimul cap', JSON.stringify(rV.j).slice(0, 200));
|
|
LA.record({ kind: 'payment', to: '0xbbbb', amount: '10' }, { at: undefined }); // seq 3 al lui A, la ceasul registrului (BAZA + 10)
|
|
const rN = await post(cerere(LA, A, { seq: 3, previous: 0 }));
|
|
cer(rN.cod === 409 && rN.j.error === 'not_successor' && rN.j.latest.seq === 2, '2. CONTROL: un cap care nu numeste ca predecesor ultimul cap martorizat -> 409 not_successor, cu ultimul cap (ca 409-ul din C2SP tlog-witness)', JSON.stringify(rN.j).slice(0, 200));
|
|
const rPrim = await post(cerere(LC, C, { seq: 1, previous: 0 }));
|
|
cer(rPrim.cod === 409 && rPrim.j.error === 'not_successor' && rPrim.j.latest === null, '2. CONTROL: primul cap al unui agent care numeste un predecesor nemartorizat -> 409 not_successor (latest null)', JSON.stringify(rPrim.j).slice(0, 200));
|
|
const rE1 = await post(cerere(LA2, A, { seq: 2, previous: 0 }));
|
|
const vE1 = rE1.j && rE1.j.proof ? M.verificaEchivocare(rE1.j.proof) : { ok: false };
|
|
cer(rE1.cod === 409 && rE1.j.error === 'equivocation' && vE1.ok && vE1.seq === 2, '2. ECHIVOCARE: alta ramura a lui A trimite seq 2 cu alt hash -> 409, cu dovada (doua capete semnate de agent, aceeasi pozitie), verificabila de oricine', JSON.stringify(rE1.j).slice(0, 300));
|
|
const rE2 = await post(cerere(LA2, A, { seq: 3, previous: 2 }));
|
|
const vE2 = rE2.j && rE2.j.proof ? M.verificaEchivocare(rE2.j.proof) : { ok: false };
|
|
cer(rE2.cod === 409 && rE2.j.error === 'equivocation' && vE2.ok && vE2.seq === 2, '2. ECHIVOCARE prin predecesor: seq 3 care numeste ca previous un hash al pozitiei 2 pe care agentul a semnat-o altfel -> 409, cu dovada', JSON.stringify(rE2.j).slice(0, 300));
|
|
await rand('2. GET heads?session: capetele lui A (0, 2) in ordine; o singura dovada PASTRATA pentru agent si politica (a doua e intoarsa, nepastrata), ca id, iar dovada intreaga pe ruta ei', async () => {
|
|
const hA = await get(`/v1/agents/witness/${A.agentId}/heads?session=${LA.export().session}`);
|
|
const eq = hA.j && Array.isArray(hA.j.equivocations) ? hA.j.equivocations : [];
|
|
const dv = eq.length ? await get('/v1/agents/witness/equivocation/' + eq[0].id) : { cod: 0 };
|
|
return [hA.cod === 200 && Array.isArray(hA.j.heads) && hA.j.heads.map((x) => x.seq).join(',') === '0,2' && eq.length === 1 && eq[0].a === undefined && rE1.j.kept === true && rE2.j.kept === false
|
|
&& dv.cod === 200 && M.verificaEchivocare(dv.j.proof).ok, JSON.stringify([hA.j, dv.cod]).slice(0, 300)];
|
|
});
|
|
const falsa = clon(rE1.j.proof); falsa.b.statement.hash = falsa.a.statement.hash;
|
|
const edDov = clon(rE1.j.proof); edDov.a.signature.publicKey = ed.publicKey.export({ type: 'spki', format: 'pem' });
|
|
cer(!M.verificaEchivocare(falsa).ok && !M.verificaEchivocare(edDov).ok && M.verificaEchivocare(edDov).rule === 'cheie', '2. CONTROL: o dovada cu un cap atins, sau cu o cheie Ed25519 in locul cheii agentului -> refuzata', JSON.stringify([M.verificaEchivocare(falsa), M.verificaEchivocare(edDov)]));
|
|
const rCt = await post({ ...cerere(LE, E, { seq: 0 }), ledger: LE.export() }, 'proba-e');
|
|
const rMare = await post('{"x":"' + 'a'.repeat(20000) + '"}', 'proba-e');
|
|
const rTip = await post(cerere(LE, E, { seq: 0 }), 'proba-e', 'text/plain');
|
|
const sigE = cerere(LE, E, { seq: 0 }); sigE.statement.createdAt = iso(wMs + 2000); sigE.statementHash = sha(JSON.stringify(sigE.statement));
|
|
const rSig = await post(sigE, 'proba-e');
|
|
cer(rCt.cod === 400 && rCt.j.rule === 'continut' && rMare.cod === 413 && rTip.cod === 415 && rSig.cod === 403 && rSig.j.rule === 'semnatura',
|
|
'2. CONTROL prin HTTP: registrul in cerere -> 400 "continut"; un corp de 20 KB -> 413; alt tip de continut -> 415; semnatura care nu verifica -> 403 "semnatura"', JSON.stringify([rCt.cod, rCt.j && rCt.j.rule, rMare.cod, rTip.cod, rSig.cod, rSig.j && rSig.j.rule]));
|
|
const rD0 = await post(cerere(LD, D, { seq: 0 }), 'proba-d'); // capul lui D in jurnalul probei (pozitia 4)
|
|
// limita pe agent: pe un martor separat (jurnalul probei ramane cel numarat mai jos), capete NOI succesive ale aceluiasi agent
|
|
const mQ = M.deschideMartor(fs.mkdtempSync(path.join(T, 'q-')), { ceas: () => wMs }); const srvQ = await porneste(mQ, { limite: LIMITE });
|
|
const UQ = `http://127.0.0.1:${srvQ.port}`;
|
|
const Q_ = newAgentIdentity(); const LQ = registruBrut(Q_, LIMITE.postPeAgent.cereri + 1); const coduriQ = [];
|
|
for (let s = 0; s <= LIMITE.postPeAgent.cereri; s++) coduriQ.push((await postLa(UQ, M.cerereMartor(LQ, Q_, { seq: s, previous: s ? s - 1 : null, createdAt: iso(wMs) }), 'proba-q')).cod);
|
|
cer(rD0.cod === 202 && coduriQ.slice(0, -1).every((c) => c === 202) && coduriQ[coduriQ.length - 1] === 429, `2. CONTROL ritm pe agent: ${LIMITE.postPeAgent.cereri} capete NOI ale aceluiasi agent trec, al ${LIMITE.postPeAgent.cereri + 1}-lea -> 429`, coduriQ.join(','));
|
|
const F_ = newAgentIdentity(); const LF = registru(F_, pol(F_), [BAZA + 60]); const cF = cerere(LF, F_, { seq: 0 });
|
|
const falsF = clon(cF); falsF.signature.signature = Buffer.from('x'.repeat(64)).toString('base64');
|
|
const coduriFals = []; for (let i = 0; i < LIMITE.postPeAgent.cereri + 2; i++) coduriFals.push((await postLa(UQ, falsF, 'proba-strain-' + i)).cod);
|
|
const rF = { cod: (await postLa(UQ, cF, 'proba-f')).cod }; await srvQ.close(); mQ.inchide();
|
|
// un agent care echivoca in bucla: fiecare refuz intoarce dovada lui, dar martorul pastreaza UNA pe agent si politica ("o dovada ajunge")
|
|
const G_ = newAgentIdentity(); const pG = pol(G_); const mG = M.deschideMartor(fs.mkdtempSync(path.join(T, 'g-')), { ceas: () => wMs });
|
|
const g0 = mG.primeste(cerere(registru(G_, pG, [BAZA + 70]), G_, { seq: 0 })); const echG = [];
|
|
for (let i = 0; i < 10; i++) echG.push(mG.primeste(cerere(registru(G_, pG, [BAZA + 70]), G_, { seq: 0 })));
|
|
await rand('2. CONTROL: zece echivocari ale aceluiasi agent -> zece 409 cu dovada fiecare, dar martorul pastreaza UNA (o dovada ajunge; discul nu se umple)', async () => {
|
|
const s = mG.capeteAgent(G_.agentId).sessions[0]; const pastrate = typeof s.equivocations === 'number' ? s.equivocations : s.equivocations.length; mG.inchide();
|
|
return [g0.cod === 202 && echG.every((r) => r.cod === 409 && r.corp.error === 'equivocation' && M.verificaEchivocare(r.corp.proof).ok) && pastrate === 1 && echG.filter((r) => r.corp.kept).length === 1, JSON.stringify([g0.cod, echG.map((r) => r.cod).join(','), pastrate])];
|
|
});
|
|
cer(coduriFals.every((c) => c === 403) && rF.cod === 202, `2. CONTROL: ${LIMITE.postPeAgent.cereri + 2} cereri cu semnatura rea care numesc agentul F (de la straini) -> 403, si NU consuma cota lui F: capul lui adevarat -> 202`, coduriFals.join(',') + ' / ' + rF.cod);
|
|
const coduriF = []; for (let i = 0; i < LIMITE.postPeClient.cereri + 1; i++) coduriF.push((await post('{}', 'proba-inundatie')).cod);
|
|
cer(coduriF.slice(0, -1).every((c) => c === 400) && coduriF[coduriF.length - 1] === 429, `2. CONTROL ritm pe client: ${LIMITE.postPeClient.cereri} cereri ale aceluiasi client sunt judecate, a ${LIMITE.postPeClient.cereri + 1}-a -> 429 inainte de corp`, coduriF.slice(-3).join(','));
|
|
|
|
// ---------------------------------------------------------------------------------------------- 3. capul de jurnal, chitanta, consistenta
|
|
const g2a = await get('/v1/agents/witness/' + c2.statementHash);
|
|
cer(g2a.cod === 200 && g2a.j.status === 'pending' && !g2a.j.receipt, '3. inainte de un cap de jurnal: GET -> pending, fara chitanta', JSON.stringify(g2a.j).slice(0, 200));
|
|
const t1 = await tur(m, { notarizeaza: null });
|
|
const g2b = await get('/v1/agents/witness/' + c2.statementHash);
|
|
const plicNenot = g2b.j && g2b.j.receipt;
|
|
cer(t1.built === 0 && g2b.j.status === 'checkpointed' && plicNenot && plicNenot.statement.kind === M.CHECKPOINT_KIND && plicNenot.statement.treeSize === 5 && !plicNenot.notarization && plicNenot.witnessReceipt.leafIndex === 1,
|
|
'3. capul de jurnal 0 (5 capete) semnat de martor; chitanta lui A seq 2: pozitia 1, dovada de includere, inca nenotarizata (checkpointed)', JSON.stringify(g2b.j).slice(0, 300));
|
|
const lc = M.verificaChitanta(plicNenot, { ledger: LA.export() });
|
|
cer(lc.ok && lc.head.seq === 2 && lc.checkpoint.treeSize === 5, '3. chitanta verificata FARA martor: semnatura martorului, capul semnat de agent, frunza refacuta, includerea RFC 9162 pana la radacina, capul in registrul lui A', JSON.stringify(lc).slice(0, 200));
|
|
const chit = (f) => { const x = clon(plicNenot); f(x); return M.verificaChitanta(x, { ledger: LA.export() }); };
|
|
const kc = [['includere', chit((x) => { x.witnessReceipt.path[0] = '0x' + '00'.repeat(32); }), 'un element al drumului schimbat'],
|
|
['includere', chit((x) => { x.witnessReceipt.leafIndex = 2; }), 'alta pozitie'],
|
|
['frunza', chit((x) => { x.witnessReceipt.receivedAtMs += 1000; }), 'alt moment de primire'],
|
|
['semnatura', chit((x) => { x.witnessReceipt.head.statement.createdAt = iso(wMs + 3000); x.witnessReceipt.head.statementHash = sha(JSON.stringify(x.witnessReceipt.head.statement)); }), 'capul primit schimbat (hash refacut, semnatura agentului veche)'],
|
|
['punct-semnatura', chit((x) => { x.statement.treeSize = 4; x.statementHash = sha(JSON.stringify(x.statement)); }), 'capul de jurnal schimbat (hash refacut, semnatura martorului veche)'],
|
|
['punct-integritate', chit((x) => { x.statement.treeSize = 4; }), 'capul de jurnal schimbat fara hash refacut'],
|
|
['martor', chit((x) => { const k = crypto.generateKeyPairSync('ml-dsa-65'); x.signature.publicKey = k.publicKey.export({ type: 'spki', format: 'pem' }); x.signature.signature = crypto.sign(null, Buffer.from(JSON.stringify(x.statement)), k.privateKey).toString('base64'); }), 'capul de jurnal semnat de alta cheie decat a martorului numit'],
|
|
['promisiune', chit((x) => { x.witnessReceipt.promise.statement.at = iso(wMs + 5000); }), 'promisiunea cu alt moment'],
|
|
['registru', M.verificaChitanta(plicNenot, { ledger: LA2.export() }), 'judecata pe alta ramura a agentului'],
|
|
['chitanta-forma', chit((x) => { delete x.witnessReceipt.leafHash; }), 'chitanta fara leafHash'],
|
|
['punct-forma', chit((x) => { x.statement.treeSize = 0; x.statementHash = sha(JSON.stringify(x.statement)); }), 'capul de jurnal cu marimea 0 (hash refacut)']];
|
|
for (const [regula, r, ce] of kc) cer(!r.ok && r.rule === regula, `3. CONTROL chitanta: ${ce} -> refuz "${regula}"`, JSON.stringify(r).slice(0, 200));
|
|
// capul primit, cu sesiunea sau integritatea lui atinse, nu trece de chitanta: aceleasi garzi ca la primire
|
|
const rcSes = chit((x) => { x.witnessReceipt.head.statement.session = 'cd'.repeat(16); x.witnessReceipt.head.statementHash = sha(JSON.stringify(x.witnessReceipt.head.statement)); });
|
|
const rcInt = chit((x) => { x.witnessReceipt.head.statement.seq = 7; });
|
|
cer(!rcSes.ok && rcSes.rule === 'sesiune' && !rcInt.ok && rcInt.rule === 'integritate', '3. CONTROL chitanta: capul primit cu alta sesiune -> "sesiune"; capul primit atins fara hash refacut -> "integritate"', JSON.stringify([rcSes, rcInt]).slice(0, 200));
|
|
// martorul nu e crezut nici pe propriul moment de primire: un jurnal facut de mana cu o frunza primita la 400 s dupa momentul declarat de cap
|
|
const km = M.cheiaMartorului(fs.mkdtempSync(path.join(T, 'km-'))); const cR = cerere(LE, E, { seq: 0 });
|
|
const rcvR = Date.parse(cR.statement.createdAt) + 400_000; const fr = M.frunzaMartor(cR.statementHash, rcvR);
|
|
const pR = M.capDeJurnal(km, { treeSize: 1, root: fr, log: 'proba', chainId: 28001, maxMergeDelay: 1800 });
|
|
const rPrimire = M.verificaChitanta({ ...pR, witnessReceipt: { format: M.RECEIPT_FORMAT, head: cR, receivedAtMs: rcvR, leafIndex: 0, treeSize: 1, leafHash: fr, path: [] } });
|
|
cer(!rPrimire.ok && rPrimire.rule === 'primire', '3. CONTROL: o chitanta a unui martor care a scris in frunza o primire la 400 s de momentul capului -> refuz "primire" (regula martorului, verificata de verificator)', JSON.stringify(rPrimire));
|
|
// notarizatorul real refuza orice lant decat 28001 INAINTE de cheie (un ethers fals care raspunde 2800 si nu poate face nicio cheie)
|
|
const ethFals = (lant) => ({ JsonRpcProvider: class { async getNetwork() { return { chainId: BigInt(lant) }; } }, parseEther: () => 0n,
|
|
Wallet: class { static createRandom() { throw new Error('the test never gets this far'); } constructor() { throw new Error('the test never gets this far'); } } });
|
|
const kfx = path.join(T, 'nu-trebuie-creata.key');
|
|
const n2800 = await notarizatorReal({ rpc: 'x', cheieFisier: kfx, ethers: ethFals(2800) })(plicNenot);
|
|
cer(!n2800.ok && /REFUSED.*testnet 28001 only/.test(n2800.motiv) && !fs.existsSync(kfx), '3. CONTROL: notarizatorul pe un RPC care serveste lantul 2800 -> REFUZ inainte de cheie (nicio cheie creata): sandbox 28001, mainnet la activare', JSON.stringify(n2800));
|
|
// notarizarea: NOTAR-MOCK local (raspunde ca notarizeEnvelope, cu firstSeen la 60 s dupa primire)
|
|
const F = BAZA + 1260;
|
|
const mock = notarMock(F);
|
|
const t2 = await tur(m, { notarizeaza: mock });
|
|
const g2c = await get('/v1/agents/witness/' + c2.statementHash);
|
|
const plic = g2c.j && g2c.j.receipt;
|
|
cer(t2.notarized === 0 && g2c.j.status === 'notarized' && plic.notarization && plic.notarization.firstSeen === F && plic.notarization.statementHashSeen === plic.statementHash,
|
|
'3. NOTAR-MOCK local: ultimul cap de jurnal notarizat (notarul a primit chiar statementHash-ul lui); GET -> notarized, chitanta cu notarizarea', JSON.stringify(g2c.j).slice(0, 300));
|
|
wMs = (BAZA + 1300) * 1000;
|
|
LA.record({ kind: 'payment', to: '0xbbbb', amount: '1' }, { at: undefined });
|
|
const t0LA = LA.export().entries.length;
|
|
const c4 = cerere(LA, A, { seq: t0LA - 1, previous: 2 });
|
|
const r4 = await post(c4);
|
|
const t3 = await tur(m, { notarizeaza: mock });
|
|
const cp = await get('/v1/agents/witness/checkpoint?from=5');
|
|
const p0 = plic.statement, p1 = cp.j && cp.j.latest && cp.j.latest.statement;
|
|
const cons = cp.j && cp.j.consistency;
|
|
cer(r4.cod === 202 && t3.built === 1 && p1 && p1.treeSize === 6 && p1.previous === plic.statementHash && cons && M.verifyConsistency({ firstSize: 5, secondSize: 6, firstRoot: p0.root, secondRoot: p1.root, path: cons.path }),
|
|
'3. al doilea cap de jurnal (6 capete) numeste capul de dinainte si e CONSISTENT cu el (dovada RFC 9162, 2.1.4): jurnalul e numai adaugat', JSON.stringify(cp.j).slice(0, 300));
|
|
const consRea = cons ? { firstSize: 5, secondSize: 6, firstRoot: '0x' + '11'.repeat(32), secondRoot: p1.root, path: cons.path } : null;
|
|
cer(!!consRea && !M.verifyConsistency(consRea), '3. CONTROL: consistenta fata de o radacina veche falsa -> nu verifica');
|
|
// repornirea pe aceeasi stare: frunzele, capetele, echivocarile si capetele de jurnal raman; un cap nou continua
|
|
await srv.close(); m.inchide();
|
|
const m2 = M.deschideMartor(DIR, { ceas: () => wMs });
|
|
const s2 = m2.sanatate();
|
|
LA.record({ kind: 'payment', to: '0xbbbb', amount: '1' }, { at: undefined });
|
|
const rR = m2.primeste(cerere(LA, A, { seq: LA.export().entries.length - 1, previous: t0LA - 1 }));
|
|
cer(s2.leaves === 6 && s2.checkpoints === 2 && s2.equivocations === 1 && s2.witness === m.witnessId && rR.cod === 202 && rR.corp.leafIndex === 6,
|
|
'3. repornire: aceeasi cheie de martor, 6 frunze, 2 capete de jurnal, 1 echivocare pastrata; capul urmator al lui A continua jurnalul (pozitia 6)', JSON.stringify([s2, rR.cod]).slice(0, 300));
|
|
let refuzConf = null; try { M.deschideMartor(DIR, { log: 'alt-jurnal', ceas: () => wMs }); } catch (e) { refuzConf = e.message; }
|
|
cer(/never changes its name/.test(String(refuzConf)), '3. CONTROL: aceeasi stare deschisa sub alt nume de jurnal -> refuz (un jurnal nu isi schimba numele, lantul, martorul sau intarzierea)', refuzConf);
|
|
m2.inchide();
|
|
// un proces oprit in mijlocul scrierii unei frunze lasa un rand taiat la coada: la deschidere randul se taie (nu a primit niciodata raspuns)
|
|
await rand('3. CONTROL: un rand de frunza taiat la coada (proces oprit in mijlocul scrierii) -> taiat la deschidere, jurnalul ramane cel raspuns', async () => {
|
|
const fFr = frunzeCiob(DIR); const marime = fs.statSync(fFr).size;
|
|
fs.appendFileSync(fFr, '{"i":7,"h":"0x');
|
|
const m3 = M.deschideMartor(DIR, { ceas: () => wMs }); const s3 = m3.sanatate(); m3.inchide();
|
|
return [s3.leaves === 7 && fs.statSync(fFr).size === marime, JSON.stringify([s3.leaves, fs.statSync(fFr).size, marime])];
|
|
});
|
|
fs.writeFileSync(path.join(DIR, '.lacat'), String(process.ppid));
|
|
let refuzLacat = null; try { M.deschideMartor(DIR, { ceas: () => wMs }); } catch (e) { refuzLacat = e.message; }
|
|
cer(/held by process/.test(String(refuzLacat)), '3. CONTROL: starea tinuta de alt proces viu (lacatul) -> refuz', refuzLacat);
|
|
fs.rmSync(path.join(DIR, '.lacat'), { force: true });
|
|
m = M.deschideMartor(DIR, { ceas: () => wMs }); srv = await porneste(m, { limite: LIMITE });
|
|
|
|
// ---------------------------------------------------------------------------------------------- 4. chitanta ca ancora a registrului
|
|
// registrul lui A pana la capul seq 2, apoi o continuare CINSTITA (dupa primire) si una ANTEDATATA (in fereastra dintre intrarea 2 si cap)
|
|
const pana2 = { ...LA.export(), entries: LA.export().entries.slice(0, 3) };
|
|
let tc = BAZA + 1210; const cinstit = resumeLedger({ identity: A, policy: pA.policy, policyHash: pA.policyHash, ledger: pana2, now: () => tc }); cinstit.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
tc = BAZA + 100; const antedatat = resumeLedger({ identity: A, policy: pA.policy, policyHash: pA.policyHash, ledger: pana2, now: () => tc }); antedatat.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
const ruleazaBun = stub((e) => verdict(e.statementHash, { t: F }));
|
|
const an = await M.ancoreDinChitante(cinstit.export(), [g2c.j], { verifyProof: 'stub', rpc: 'stub', ruleaza: ruleazaBun, martori: [m.witnessId] });
|
|
cer(an.stare === 'OK' && an.anchors.length === 1 && an.anchors[0].seq === 2 && an.anchors[0].at === BAZA + 1200 && an.anchors[0].firstSeen === F,
|
|
'4. chitanta -> ancora {seq 2, hash, at = momentul semnat de agent}, cu firstSeen-ul certificat (raspunsul GET al martorului primit ca atare)', JSON.stringify(an).slice(0, 300));
|
|
const vC = verifyLedger(cinstit.export(), { policy: pA.policy, anchors: an.anchors });
|
|
cer(vC.ok && vC.time.lowerBound === 'witnessed', '4. registrul cinstit cu ancora martorului: ok, marginea de jos "witnessed"', JSON.stringify(vC).slice(0, 200));
|
|
const vA0 = verifyLedger(antedatat.export(), { policy: pA.policy });
|
|
cer(vA0.ok, '4. MARTORUL MASURII: fara ancora, intrarea antedatata (intre ultima intrare si capul trimis) trece', JSON.stringify(vA0).slice(0, 200));
|
|
const vA = verifyLedger(antedatat.export(), { policy: pA.policy, anchors: an.anchors });
|
|
cer(!vA.ok && /backdated/.test(vA.error || ''), '4. cu ancora martorului, intrarea antedatata e prinsa ("backdated"): ea contrazice capul semnat de agent', JSON.stringify(vA).slice(0, 200));
|
|
const k4 = [
|
|
['FAILED', 'lant', stub(() => verdict('0x' + '22'.repeat(32), { t: F })), 'verificatorul a judecat alt statementHash'],
|
|
['NEMASURAT', null, stub((e) => verdict(e.statementHash, { fin: 'PENDING', t: F })), 'finalitate in asteptare'],
|
|
['FAILED', 'lant', stub((e) => verdict(e.statementHash, { fin: 'FAILED', t: F })), 'finalitate FAILED (radacina nu e pe lant)'],
|
|
['NEMASURAT', null, async () => null, 'verificatorul nu raspunde'],
|
|
['FAILED', 'lant', stub((e) => verdict(e.statementHash, { sig: 'FAILED', t: F })), 'semnatura martorului FAILED la verificatorul AIP-23'],
|
|
['NEMASURAT', null, stub((e) => verdict(e.statementHash, { sig: 'UNMEASURED', t: F })), 'semnatura martorului UNMEASURED la verificatorul AIP-23 (nu PASSED)'],
|
|
['FAILED', 'intarziere', stub((e) => verdict(e.statementHash, { t: BAZA + 1200 + 4000 })), 'capul de jurnal vazut de lant la 4000 s dupa primire (peste intarzierea maxima)'],
|
|
['FAILED', 'primire', stub((e) => verdict(e.statementHash, { t: BAZA + 1200 - 400 })), 'lantul a vazut capul de jurnal INAINTE de primirea pe care o scrie martorul']];
|
|
for (const [stare, regula, r, ce] of k4) {
|
|
await rand(`4. CONTROL: ${ce} -> ${stare}${regula ? ' "' + regula + '"' : ''}, nicio ancora`, async () => {
|
|
const x = await M.ancoreDinChitante(cinstit.export(), [g2c.j], { verifyProof: 'stub', rpc: 'stub', ruleaza: r });
|
|
return [x.stare === stare && !x.anchors.length && (!regula || x.randuri[0].rule === regula), JSON.stringify(x).slice(0, 250)];
|
|
});
|
|
}
|
|
const xR = await M.ancoreDinChitante(LA2.export(), [g2c.j], { verifyProof: 'stub', rpc: 'stub', ruleaza: ruleazaBun });
|
|
const xL = await M.ancoreDinChitante(cinstit.export(), [g2c.j], { verifyProof: 'stub', rpc: 'stub', ruleaza: ruleazaBun, chain: 2800 });
|
|
const xN = await M.ancoreDinChitante(cinstit.export(), [g2b.j], { verifyProof: 'stub', rpc: 'stub', ruleaza: ruleazaBun });
|
|
const g2cLant = clon(g2c.j); g2cLant.receipt.notarization.chainId = 2800;
|
|
const xNl = await M.ancoreDinChitante(cinstit.export(), [g2cLant], { verifyProof: 'stub', rpc: 'stub', ruleaza: ruleazaBun });
|
|
cer(xR.stare === 'FAILED' && xR.randuri[0].rule === 'registru' && xL.stare === 'FAILED' && xL.randuri[0].rule === 'lant' && xN.stare === 'NEMASURAT' && xNl.stare === 'FAILED' && xNl.randuri[0].rule === 'lant',
|
|
'4. CONTROL: chitanta pe alta ramura -> FAILED "registru"; verificatorul cere lantul 2800 pentru un cap de jurnal de pe 28001 -> FAILED "lant"; o notarizare pe alt lant decat cel al capului de jurnal -> FAILED "lant"; chitanta inca nenotarizata -> NEMASURAT', JSON.stringify([xR.randuri, xL.randuri, xN.randuri, xNl.randuri]).slice(0, 300));
|
|
|
|
// ---------------------------------------------------------------------------------------------- 5. linia de comanda
|
|
const fPol = scrie('policy.json', pA), fCin = scrie('cinstit.json', cinstit.export()), fAnt = scrie('antedatat.json', antedatat.export()), fChit = scrie('receipt.json', g2c.j), fPend = scrie('pending.json', g2a.j);
|
|
const cli = (args, env = {}) => spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8', env: { ...process.env, AERE_VERIFY_PROOF: '', STUB_F: String(F), ...env } });
|
|
const v1 = cli(['verify', '--ledger', fCin, '--policy', fPol, '--anchors', fChit, '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub, '--witness', m.witnessId]);
|
|
const v2 = cli(['verify', '--ledger', fAnt, '--policy', fPol, '--anchors', fChit, '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub, '--witness', m.witnessId]);
|
|
const v3 = cli(['verify', '--ledger', fCin, '--policy', fPol, '--anchors', fChit, '--rpc', 'http://127.0.0.1:9']);
|
|
const v4 = cli(['verify', '--ledger', fCin, '--policy', fPol, '--anchors', fPend, '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub]);
|
|
cer(v1.status === 0 && /"lowerBound": "witnessed"/.test(v1.stdout), '5. agent-cli verify --anchors <raspunsul martorului> --witness <martorul>: registrul cinstit -> 0, marginea de jos "witnessed"', v1.stdout + v1.stderr);
|
|
cer(v2.status === 1 && /backdated/.test(v2.stdout), '5. agent-cli verify: registrul antedatat cu aceeasi chitanta -> 1, "backdated"', v2.stdout.slice(0, 200) + v2.stderr);
|
|
cer(v3.status === 2 && /NOT MEASURED/.test(v3.stderr) && v4.status === 2 && /no receipt yet/.test(v4.stderr), '5. CONTROL: fara verificatorul AIP-23 -> 2 NOT MEASURED, pe nume; un raspuns pending (fara chitanta) -> 2, pe nume', `${v3.status} ${v3.stderr} | ${v4.status} ${v4.stderr}`);
|
|
const fEch = scrie('echivocare.json', rE1.j.proof), fEchRea = scrie('echivocare-rea.json', falsa);
|
|
const e1 = spawnSync(process.execPath, [path.join(AICI, 'martor-agenti.mjs'), 'verify-equivocation', '--proof', fEch], { encoding: 'utf8' });
|
|
const e2 = spawnSync(process.execPath, [path.join(AICI, 'martor-agenti.mjs'), 'verify-equivocation', '--proof', fEchRea], { encoding: 'utf8' });
|
|
const e3 = spawnSync(process.execPath, [path.join(AICI, 'martor-agenti.mjs'), 'verify-receipt', '--receipt', fChit, '--ledger', fCin], { encoding: 'utf8' });
|
|
cer(e1.status === 0 && e2.status === 1 && e3.status === 0, '5. martor-agenti.mjs verify-equivocation (0 pe dovada martorului, 1 pe cea atinsa) si verify-receipt (0, numai local)', `${e1.status} ${e2.status} ${e3.status} ${e3.stderr}`);
|
|
const fKey = path.join(F5, 'agent.key.pem'); fs.writeFileSync(fKey, B.privateKey.export({ type: 'pkcs8', format: 'pem' }));
|
|
LB.record({ kind: 'payment', to: '0xbbbb', amount: '1' }, { at: undefined }); const fLB = scrie('lb.json', LB.export());
|
|
wMs = Date.now(); // `submit` scrie capul la ceasul masinii; ceasul martorului il urmeaza
|
|
const sb = await ruleazaAsincron([path.join(AICI, 'martor-agenti.mjs'), 'submit', '--key', fKey, '--ledger', fLB, '--url', U()]);
|
|
const sbj = (() => { try { return JSON.parse(sb.stdout); } catch { return null; } })();
|
|
cer(sb.status === 0 && sbj && sbj.status === 'pending' && sbj.seq === 2, '5. martor-agenti.mjs submit: ia ultimul cap martorizat al lui B ca predecesor, trimite seq 2 -> 202', sb.stdout.slice(0, 200) + sb.stderr);
|
|
const sbAlt = await ruleazaAsincron([path.join(AICI, 'martor-agenti.mjs'), 'submit', '--key', fKey, '--ledger', scrie('lb-ramura.json', registru(B, pB, [acum - 10, acum - 5, acum - 3, acum - 1]).export()), '--url', U()]);
|
|
cer(sbAlt.status === 1 && /another branch/.test(sbAlt.stderr), '5. CONTROL: submit cu o alta ramura a lui B se opreste inainte de trimitere (registrul nu are capul martorizat)', sbAlt.stdout + sbAlt.stderr);
|
|
// 9.R7: submit fara nimic nou (martorul tine deja chiar ultima intrare): 0, fara POST; un --seq mai vechi decat ultimul cap tinut: 1, numit
|
|
await rand('9.R7 submit fara nimic nou (martorul tine deja ultima intrare a registrului) -> 0 "nothing new", fara nicio frunza noua; --seq mai vechi decat ultimul cap tinut -> 1 "newer head"', async () => {
|
|
const inainte = m.sanatate().leaves;
|
|
const sb2 = await ruleazaAsincron([path.join(AICI, 'martor-agenti.mjs'), 'submit', '--key', fKey, '--ledger', fLB, '--url', U()]);
|
|
const sb3 = await ruleazaAsincron([path.join(AICI, 'martor-agenti.mjs'), 'submit', '--key', fKey, '--ledger', fLB, '--url', U(), '--seq', '1']);
|
|
return [sb2.status === 0 && /nothing new/.test(sb2.stderr) && m.sanatate().leaves === inainte && sb3.status === 1 && /newer head/.test(sb3.stderr), `${sb2.status} ${sb2.stderr.trim()} | ${sb3.status} ${sb3.stderr.trim()}`];
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------------------------- 6. usa gateway-ului (daca e in arbore)
|
|
// AERE_GATEWAY: controlul negativ ruleaza proba dintr-o copie si ii da gateway-ul arborelui (nu il planteaza)
|
|
const GW = process.env.AERE_GATEWAY || path.resolve(AICI, '..', '..', 'cloud-gateway', 'gateway.mjs');
|
|
// gateway-ul Aere Cloud nu face parte din copia publicata a componentei: acolo randul e o INFORMATIE (nu un NEMASURAT), ca la conformanta
|
|
if (!fs.existsSync(GW)) console.log(` (6. usa gateway-ului Aere Cloud: gateway.mjs nu face parte din aceasta copie (${GW}); se masoara in depozitul de dezvoltare)`);
|
|
else {
|
|
const portLiber = () => new Promise((g) => { const s = net.createServer(); s.listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => g(p)); }); });
|
|
const pornesteGw = async (env) => {
|
|
const port = await portLiber(); const d = fs.mkdtempSync(path.join(T, 'gw-'));
|
|
const c = spawn(process.execPath, [GW], { env: { ...process.env, PORT: String(port), DIR_METRICA: d, RPC_URL: 'http://127.0.0.1:9', ...env }, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
let out = ''; c.stdout.on('data', (x) => { out += x; }); c.stderr.on('data', (x) => { out += x; });
|
|
for (let i = 0; i < 100 && !/aere-cloud-gateway pe/.test(out); i++) await new Promise((g) => setTimeout(g, 100));
|
|
return { c, port, out: () => out };
|
|
};
|
|
const gw1 = await pornesteGw({ AERE_AGENTS_WITNESS_PORT: String(srv.port) });
|
|
const gw0 = await pornesteGw({ AERE_AGENTS_WITNESS_PORT: '' });
|
|
try {
|
|
LE.record({ kind: 'payment', to: '0xbbbb', amount: '1' }, { at: undefined }); wMs = Date.now();
|
|
await rand('6. gateway cu AERE_AGENTS_WITNESS_PORT: POST /v1/agents/witness si GET heads ajung la martor, fara cheie API', async () => {
|
|
const viaGw = await fetch(`http://127.0.0.1:${gw1.port}/v1/agents/witness`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(M.cerereMartor(LE.export(), E, { seq: 0, createdAt: iso(wMs) })) });
|
|
const jg = await viaGw.json().catch(() => null);
|
|
const hg = await get(`/v1/agents/witness/${E.agentId}/heads`, `http://127.0.0.1:${gw1.port}`);
|
|
return [viaGw.status === 202 && jg && jg.sandbox === M.SANDBOX && hg.cod === 200 && hg.j.sessions[0].count === 1, JSON.stringify([viaGw.status, jg, hg.cod]).slice(0, 300) + gw1.out().slice(0, 200)];
|
|
});
|
|
await rand('6. CONTROL: gateway fara variabila (usa nearmata): aceeasi ruta raspunde ca azi, 401 fara cheie', async () => {
|
|
const fara = await fetch(`http://127.0.0.1:${gw0.port}/v1/agents/witness`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' });
|
|
const jf = await fara.json().catch(() => null);
|
|
return [fara.status === 401 && jf && jf.error === 'missing_api_key', JSON.stringify([fara.status, jf]) + gw0.out().slice(0, 200)];
|
|
});
|
|
// 9.R13 (CORS): o pagina straina poate trimite POST-uri JSON din browserele vizitatorilor (nginx raspunde CORS deschis pe /v1/); un browser
|
|
// trimite intotdeauna Origin, un agent nu: usa refuza un POST cu Origin, inainte sa ajunga la martor
|
|
await rand('9.R13 gateway: un POST la /v1/agents/witness care poarta antetul Origin (un browser, pe drumul CORS) -> 403 inainte de martor; GET-ul cu Origin trece', async () => {
|
|
const inainte = m.sanatate().leaves;
|
|
LE.record({ kind: 'payment', to: '0xbbbb', amount: '1' }, { at: undefined }); wMs = Date.now();
|
|
const rO = await fetch(`http://127.0.0.1:${gw1.port}/v1/agents/witness`, { method: 'POST', headers: { 'content-type': 'application/json', origin: 'https://pagina-straina.example' }, body: JSON.stringify(M.cerereMartor(LE.export(), E, { seq: 1, previous: 0, createdAt: iso(wMs) })) });
|
|
const jO = await rO.json().catch(() => null);
|
|
const gO = await fetch(`http://127.0.0.1:${gw1.port}/v1/agents/witness/health`, { headers: { origin: 'https://pagina-straina.example' } });
|
|
return [rO.status === 403 && jO && jO.error === 'browser_origin_refused' && m.sanatate().leaves === inainte && gO.status === 200, JSON.stringify([rO.status, jO, gO.status])];
|
|
});
|
|
} finally { gw1.c.kill(); gw0.c.kill(); }
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------- 7. verificatorul AIP-23 de referinta
|
|
if (!VERIFY || !fs.existsSync(VERIFY)) { sari('7. plicul martorului VALID la verificatorul AIP-23 de referinta', 'AERE_VERIFY_PROOF nu numeste verify-proof.mjs'); sari('7. chitanta cu notarizarea NOTAR-MOCK, offline: PARTIAL la verificatorul AIP-23', 'idem'); sari('7. CONTROL: plicul atins INVALID la verificatorul AIP-23', 'idem'); }
|
|
else {
|
|
const vp = (o, ...a) => { const f = path.join(T, 'aip23-' + crypto.randomBytes(4).toString('hex') + '.json'); fs.writeFileSync(f, JSON.stringify(o)); const r = spawnSync(process.execPath, [VERIFY, f, '--json', ...a], { encoding: 'utf8' }); try { return { cod: r.status, ...JSON.parse(r.stdout) }; } catch { return { cod: r.status, verdict: '?', err: r.stderr.slice(0, 200) }; } };
|
|
const n1 = vp(plicNenot); const sig = n1.levels && n1.levels.find((x) => x.level === 'signature');
|
|
cer(n1.verdict === 'VALID' && n1.cod === 0 && sig && sig.state === 'PASSED', '7. capul de jurnal (chitanta checkpointed) la verificatorul AIP-23 de referinta: VALID, semnatura ML-DSA-65 a martorului PASSED, finalitatea ABSENT (nenotarizat)', JSON.stringify(n1).slice(0, 300));
|
|
const n2 = vp(plic, '--offline'); const fin = n2.levels && n2.levels.find((x) => x.level === 'finality');
|
|
cer(n2.verdict === 'PARTIAL' && fin && fin.state === 'UNMEASURED', '7. chitanta cu notarizarea NOTAR-MOCK, offline: PARTIAL, finalitatea UNMEASURED pe nume (o pretentie de notarizare nu e niciodata VALID fara lant)', JSON.stringify(n2).slice(0, 300));
|
|
const atins = clon(plicNenot); atins.statement.treeSize = 7;
|
|
const n3 = vp(atins); const reSemnat = clon(plicNenot); reSemnat.statement.treeSize = 7; reSemnat.statementHash = sha(JSON.stringify(reSemnat.statement)); const n4 = vp(reSemnat);
|
|
cer(n3.verdict === 'INVALID' && n4.verdict === 'INVALID' && n4.levels.find((x) => x.level === 'signature').state === 'FAILED', '7. CONTROL: capul de jurnal atins -> INVALID (integritate); cu hash refacut -> INVALID (semnatura martorului)', JSON.stringify([n3.verdict, n4.verdict]));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------- 9. revizia adversariala B3 (2026-10-19)
|
|
await revizia({ m, U, plicNenot, g2c, F, fPol, cinstit });
|
|
await revizia2();
|
|
await revizia3();
|
|
await revizia4();
|
|
await revizia5();
|
|
await revizia6();
|
|
|
|
// ---------------------------------------------------------------------------------------------- 8. testnetul 28001 (numai cu --testnet)
|
|
if (!TESTNET) console.log(' (8. notarizarea reala pe testnetul 28001 nu e ceruta: node proba-martor-agenti.mjs --testnet)');
|
|
else await testnet();
|
|
} finally { try { await srv.close(); } catch { /* deja inchis */ } try { m.inchide(); } catch { /* idem */ } fs.rmSync(T, { recursive: true, force: true }); }
|
|
|
|
async function revizia({ m: mPrincipal, plicNenot, g2c }) {
|
|
// ---- 9.R1 / 9.R17: martorul FIXAT. Un al doilea martor, COERENT cu el insusi (cheia lui, ceasul lui), pe care il poate rula orice agent:
|
|
// ceasul la F - 2100, capul declarat la F - 2400 (|C - R| = 300), notarizat de agent la F. Fara fixare chitanta lui e primita; marginea e a lantului.
|
|
const acumR = Math.floor(Date.now() / 1000); const FR = acumR + 60;
|
|
const A1 = newAgentIdentity(); const pA1 = pol(A1); const T0 = acumR - 3000;
|
|
const L1 = registru(A1, pA1, [T0, T0 + 5, T0 + 10]), L1b = registru(A1, pA1, [T0, T0 + 6, T0 + 11]); // doua ramuri ale aceluiasi agent
|
|
const stubR = stub((e) => verdict(e.statementHash, { t: FR }));
|
|
const chitantaLa = async (dir, ceasMs, L, createdAtMs) => {
|
|
const mm = M.deschideMartor(dir, { ceas: () => ceasMs });
|
|
const r = mm.primeste(M.cerereMartor(L.export(), A1, { seq: 2, createdAt: iso(createdAtMs) }));
|
|
await tur(mm, { notarizeaza: notarMock(FR) });
|
|
const st = r.cod === 202 ? mm.stareaCapului(r.corp.id).corp : null; const w = mm.witnessId; mm.inchide();
|
|
return { cod: r.cod, w, st, dir };
|
|
};
|
|
const gazduit = await chitantaLa(path.join(T, 'r1-gazduit'), acumR * 1000, L1, acumR * 1000);
|
|
const m0 = M.deschideMartor(gazduit.dir, { ceas: () => acumR * 1000 }); const eqR = m0.primeste(M.cerereMartor(L1b.export(), A1, { seq: 2, createdAt: iso(acumR * 1000) })); m0.inchide();
|
|
const strain1 = await chitantaLa(path.join(T, 'r1-strain-1'), (FR - 2100) * 1000, L1, (FR - 2400) * 1000);
|
|
const strain2 = await chitantaLa(path.join(T, 'r1-strain-2'), (FR - 2100) * 1000, L1b, (FR - 2400) * 1000);
|
|
let tcR = FR - 2650; const ant1 = resumeLedger({ identity: A1, policy: pA1.policy, policyHash: pA1.policyHash, ledger: L1.export(), now: () => tcR }); ant1.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
await rand('9.R1 un martor COERENT rulat de agent (alta cheie, ceasul lui la F-2100, capul declarat la F-2400): cu --witness fixat pe martorul gazduit chitanta lui -> FAILED "martor"; fara fixare e primita, dar ancora spune witnessPinned=false si marginea lantului F-2400, si randul numeste martorul', async () => {
|
|
const fix = await M.ancoreDinChitante(ant1.export(), [strain1.st], { verifyProof: 'stub', rpc: 'stub', ruleaza: stubR, martori: [gazduit.w] });
|
|
const nefix = await M.ancoreDinChitante(ant1.export(), [strain1.st], { verifyProof: 'stub', rpc: 'stub', ruleaza: stubR });
|
|
const a = nefix.anchors[0] || {};
|
|
const cuGazduit = await M.ancoreDinChitante(ant1.export(), [gazduit.st], { verifyProof: 'stub', rpc: 'stub', ruleaza: stubR, martori: [gazduit.w] });
|
|
const vG = cuGazduit.stare === 'OK' ? verifyLedger(ant1.export(), { policy: pA1.policy, anchors: cuGazduit.anchors }) : { ok: true };
|
|
return [gazduit.cod === 202 && eqR.cod === 409 && strain1.cod === 202 && fix.stare === 'FAILED' && fix.randuri[0].rule === 'martor'
|
|
&& nefix.stare === 'OK' && a.witness === strain1.w && a.witnessPinned === false && a.chainLowerBound === FR - 1800 - 600 && nefix.randuri[0].witness === strain1.w && /NOT pinned/.test(nefix.randuri[0].detail)
|
|
&& cuGazduit.stare === 'OK' && cuGazduit.anchors[0].witnessPinned === true && !vG.ok && /backdated/.test(vG.error || ''), JSON.stringify([fix.randuri, nefix.randuri, a, cuGazduit.stare, vG.error]).slice(0, 400)];
|
|
});
|
|
await rand('9.R1 a doua ramura, refuzata de martorul gazduit ca echivocare (409), cu chitanta unui al doilea martor al agentului: fixat pe cel gazduit -> FAILED "martor"', async () => {
|
|
const r = await M.ancoreDinChitante(L1b.export(), [strain2.st], { verifyProof: 'stub', rpc: 'stub', ruleaza: stubR, martori: [gazduit.w] });
|
|
return [eqR.cod === 409 && strain2.cod === 202 && r.stare === 'FAILED' && r.randuri[0].rule === 'martor', JSON.stringify(r.randuri)];
|
|
});
|
|
await rand('9.R17 agent-cli verify: chitanta martorului strain cu --witness <gazduit> -> 2 NOT MEASURED "[martor]" (nu e marturia martorului fixat, nici o vina dovedita a registrului; pana la a cincea revizie: 1); fara --witness -> 0, iar rezultatul numeste martorul (witnessReceipts) si spune lowerBound "UNPINNED"; verify-receipt --witness <gazduit> -> 1', async () => {
|
|
const fp = scrie('r17-policy.json', pA1), fl = scrie('r17-antedatat.json', ant1.export()), fr2 = scrie('r17-receipt.json', strain1.st);
|
|
const a = spawnSync(process.execPath, [CLI, 'verify', '--ledger', fl, '--policy', fp, '--anchors', fr2, '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub, '--witness', gazduit.w], { encoding: 'utf8', env: { ...process.env, STUB_F: String(FR) } });
|
|
const b = spawnSync(process.execPath, [CLI, 'verify', '--ledger', fl, '--policy', fp, '--anchors', fr2, '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub], { encoding: 'utf8', env: { ...process.env, STUB_F: String(FR) } });
|
|
const c = spawnSync(process.execPath, [path.join(AICI, 'martor-agenti.mjs'), 'verify-receipt', '--receipt', fr2, '--witness', gazduit.w], { encoding: 'utf8' });
|
|
let jb = null; try { jb = JSON.parse(b.stdout); } catch { jb = null; }
|
|
return [a.status === 2 && /\[martor\]/.test(a.stderr) && /NOT MEASURED/.test(a.stderr) && b.status === 0 && jb && jb.witnessReceipts && jb.witnessReceipts[0].witness === strain1.w && jb.witnessReceipts[0].pinned === false && /UNPINNED/.test(jb.time.lowerBound) && c.status === 1,
|
|
`${a.status} ${a.stderr.slice(0, 120)} | ${b.status} ${b.stdout.slice(0, 160)} | ${c.status}`];
|
|
});
|
|
|
|
// ---- 9.R2: o ramura despartita DUPA ultimul cap pentru care agentul da o chitanta; --witness-url cere lista intreaga a capetelor martorului
|
|
const X = newAgentIdentity(); const pX = pol(X); let tx = Math.floor(Date.now() / 1000) - 100;
|
|
const LX = openLedger({ identity: X, policy: pX.policy, policyHash: pX.policyHash, now: () => tx }); LX.record({ kind: 'payment', to: '0xbbbb', amount: '900' });
|
|
const LXB = resumeLedger({ identity: X, policy: pX.policy, policyHash: pX.policyHash, ledger: LX.export(), now: () => tx }); // ramura B: aceeasi intrare 0
|
|
let wX = Date.now(); const FX = Math.floor(wX / 1000) + 60;
|
|
const mX = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-')), { ceas: () => wX }); const srvX = await porneste(mX, { limite: LIMITE }); const UX = `http://127.0.0.1:${srvX.port}`;
|
|
const rx0 = mX.primeste(M.cerereMartor(LX.export(), X, { seq: 0, createdAt: iso(wX) }));
|
|
await tur(mX, { notarizeaza: notarMock(FX) });
|
|
const chitX0 = mX.stareaCapului(rx0.corp && rx0.corp.id).corp;
|
|
tx += 120; wX += 120_000;
|
|
LX.record({ kind: 'payment', to: '0xbbbb', amount: '50' }); LX.record({ kind: 'payment', to: '0xbbbb', amount: '50' });
|
|
const rx2 = mX.primeste(M.cerereMartor(LX.export(), X, { seq: 2, previous: 0, createdAt: iso(wX) }));
|
|
// capul cel mai nou notarizat si el: de la a treia revizie (R3-04) --witness-url cere chitanta ultimului cap tinut (o aduce de la martor)
|
|
await tur(mX, { notarizeaza: notarMock(FX) });
|
|
LXB.record({ kind: 'payment', to: '0xbbbb', amount: '100' }); // ramura B: alta cheltuiala la seq 1
|
|
const fpX = scrie('r2-policy.json', pX), frX = scrie('r2-receipt.json', chitX0), flB = scrie('r2-ramura-b.json', LXB.export()), flA = scrie('r2-ramura-a.json', LX.export());
|
|
const cliX = (fl, extra) => ruleazaAsincron([CLI, 'verify', '--ledger', fl, '--policy', fpX, '--anchors', frX, '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub, ...extra], { STUB_F: String(FX), AERE_VERIFY_PROOF: '' });
|
|
await rand('9.R2 ramura B (seq 1 = alta plata) cu chitanta capului 0, pe care martorul o are cinstit: fara --witness-url trece (limita scrisa); cu --witness-url -> 1 "[registru]" (martorul tine seq 2, care nu e in B); ramura A -> 0', async () => {
|
|
const faraUrl = await cliX(flB, []); const cuUrl = await cliX(flB, ['--witness-url', UX]); const aCinstit = await cliX(flA, ['--witness-url', UX]);
|
|
return [rx0.cod === 202 && rx2.cod === 202 && faraUrl.status === 0 && cuUrl.status === 1 && /\[registru\]/.test(cuUrl.stderr) && aCinstit.status === 0 && /"lowerBound": "witnessed"/.test(aCinstit.stdout),
|
|
`${faraUrl.status} | ${cuUrl.status} ${cuUrl.stderr.slice(0, 200)} | ${aCinstit.status} ${aCinstit.stderr.slice(0, 160)}`];
|
|
});
|
|
await rand('9.R2 CONTROL: --witness-url catre un martor care nu raspunde -> 2 NOT MEASURED (nu un verde); un martor care tine o dovada de echivocare pentru sesiune -> FAILED "echivocare"', async () => {
|
|
const jos = await cliX(flA, ['--witness-url', 'http://127.0.0.1:9']);
|
|
const vc = M.verificaCapeteMartorului(LX.export(), { agentId: X.agentId, session: LX.export().session, heads: [], equivocations: [{ id: '0x' + '12'.repeat(32), seq: 1 }] });
|
|
return [jos.status === 2 && /NOT MEASURED/.test(jos.stderr) && vc.stare === 'FAILED' && vc.randuri.some((r) => r.rule === 'echivocare'), `${jos.status} ${jos.stderr.slice(0, 160)} | ${JSON.stringify(vc).slice(0, 200)}`];
|
|
});
|
|
await srvX.close(); mX.inchide();
|
|
|
|
// ---- 9.R3 / 9.R12: jurnalul in CIOBURI. Un ciob plin se ingheata si martorul trece la urmatorul (nu 503 pentru totdeauna); se tin doua cioburi.
|
|
const DR = fs.mkdtempSync(path.join(T, 'r3-')); let wR = Date.now();
|
|
const P = newAgentIdentity(), Q = newAgentIdentity(), R = newAgentIdentity(), S_ = newAgentIdentity();
|
|
const LP = registruBrut(P, 4), LQ0 = registruBrut(Q, 2), LR = registruBrut(R, 1), LS = registruBrut(S_, 1);
|
|
await rand('9.R3/R12 cioburile: plafon 2 frunze pe ciob; cinci capete -> toate 202 (niciun 503; notarizarea inainte de a doua rotire), ciobul 0 scos dupa al doilea ciob plin, capul lui -> 404, capul din ciobul 1 cu chitanta verificabila (jurnalul "/1"), continuitatea agentului peste cioburi, ciobul 2 numeste capul final al ciobului 1, discul fara ciobul 0', async () => {
|
|
let mR = M.deschideMartor(DR, { ceas: () => wR, maxFrunze: 2 });
|
|
const c = (L, id, seq, prev) => mR.primeste(M.cerereMartor(L, id, { seq, previous: prev, createdAt: iso(wR) }));
|
|
const rP0 = c(LP, P, 0, null), rQ0 = c(LQ0, Q, 0, null), rP1 = c(LP, P, 1, 0), rR0 = c(LR, R, 0, null);
|
|
// a doua rotire scoate ciobul 0: capul lui de jurnal final trebuie sa fie notarizat intai (revizia B3 a doua, R2-07)
|
|
await tur(mR, { notarizeaza: notarMock(Math.floor(wR / 1000) + 60) });
|
|
const rS0 = c(LS, S_, 0, null);
|
|
const h = mR.sanatate();
|
|
const q0 = mR.stareaCapului(rQ0.corp.id); const p1 = mR.stareaCapului(rP1.corp.id);
|
|
const ch = p1.corp && p1.corp.receipt ? M.verificaChitanta(p1.corp.receipt) : { ok: false };
|
|
const rP2 = c(LP, P, 2, 1); // P continua: ultimul lui cap tinut e in ciobul 1 (pastrat)
|
|
const rQ1 = c(LQ0, Q, 1, 0); // Q: singurul lui cap era in ciobul 0, scos: martorul nu il mai stie
|
|
const t = await tur(mR, { notarizeaza: notarMock(Math.floor(wR / 1000) + 60) });
|
|
const cj = mR.capulJurnalului().corp; const cioburi = cj.shards || [];
|
|
mR.inchide();
|
|
// stergerea ciobului scos e asincrona (zeci de mii de fisiere pe o stare reala): se asteapta, cel mult 10 s
|
|
for (let i = 0; i < 100 && fs.existsSync(path.join(DR, 'cioburi', '0')); i++) await new Promise((g) => setTimeout(g, 100));
|
|
mR = M.deschideMartor(DR, { ceas: () => wR, maxFrunze: 2 }); const h2 = mR.sanatate(); mR.inchide();
|
|
const coduri = [rP0, rQ0, rP1, rR0, rS0].map((r) => r.cod).join(',');
|
|
const s1 = cioburi.find((x) => x.shard === 1) || {}, s2 = cioburi.find((x) => x.shard === 2) || {};
|
|
return [coduri === '202,202,202,202,202' && h.shard === 2 && h.maxLeaves === 2 && JSON.stringify(h.shardsKept) === '[1,2]' && q0.cod === 404 && p1.cod === 200 && ch.ok && ch.checkpoint.log === M.LOG_IMPLICIT + '/1'
|
|
&& rP2.cod === 202 && rQ1.cod === 409 && rQ1.corp.error === 'not_successor' && rQ1.corp.latest === null && s2.previousShardFinal === s1.latest && !!s1.latest && t.error === null
|
|
&& !fs.existsSync(path.join(DR, 'cioburi', '0')) && h2.shard === 2 && JSON.stringify(h2.shardsKept) === '[1,2]', JSON.stringify([coduri, h, q0.cod, p1.cod, ch.rule, rP2.cod, rQ1.corp, cioburi, t]).slice(0, 500)];
|
|
});
|
|
await rand('9.R12 bugetul jurnalului pe ora (comun tuturor): peste el un cap NOU -> 503 log_busy cu Retry-After; un cap deja vazut -> 200 (nu consuma bugetul); health spune leaves si maxLeaves', async () => {
|
|
const mB = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r12-')), { ceas: () => Date.now() });
|
|
const srvB = await porneste(mB, { limite: { ...LIMITE, acceptatePeOra: { cereri: 3, ms: 3_600_000 } } }); const UB = `http://127.0.0.1:${srvB.port}`;
|
|
const capNou = () => { const id = newAgentIdentity(); return M.cerereMartor(registruBrut(id, 1), id, { seq: 0, createdAt: iso(Date.now()) }); };
|
|
const primul = capNou(); const coduri = [];
|
|
coduri.push((await postLa(UB, primul, 'r12-a')).cod); for (let i = 0; i < 2; i++) coduri.push((await postLa(UB, capNou(), 'r12-b' + i)).cod);
|
|
const peste = await postLa(UB, capNou(), 'r12-c'); const din = await postLa(UB, primul, 'r12-d');
|
|
const hb = await get('/v1/agents/witness/health', UB);
|
|
await srvB.close(); mB.inchide();
|
|
return [coduri.join(',') === '202,202,202' && peste.cod === 503 && peste.j.error === 'log_busy' && peste.antete.get('retry-after') === '300' && din.cod === 200 && hb.j.leaves === 3 && hb.j.maxLeaves === M.PLAFON_FRUNZE
|
|
&& hb.j.logBusy && hb.j.logBusy.refusedThisHour === 1,
|
|
JSON.stringify([coduri, peste.cod, peste.j, din.cod, hb.j && hb.j.maxLeaves])];
|
|
});
|
|
|
|
// ---- 9.R4: acelasi seq si acelasi hash, dar cu un predecesor care contrazice un cap tinut: echivocare (nu "deja vazut")
|
|
await rand('9.R4 un cap la un seq deja tinut, cu acelasi hash, dar cu previous care contrazice capul tinut la seq 0 -> 409 equivocation, dovada pastrata, verificabila la pozitia 0', async () => {
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 3); const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r4-')), { ceas: () => Date.now() });
|
|
const h0 = M.cerereMartor(L, id, { seq: 0, createdAt: iso(Date.now()) }), h2 = M.cerereMartor(L, id, { seq: 2, previous: 0, createdAt: iso(Date.now()) });
|
|
const c0 = mm.primeste(h0).cod, c2 = mm.primeste(h2).cod;
|
|
const st = { ...h2.statement, createdAt: iso(Date.now() + 1000), previous: { seq: 0, hash: 'ff'.repeat(32) } }; const text = JSON.stringify(st);
|
|
const fals = { v: 1, kind: h2.kind, statement: st, statementHash: sha(text), signature: { scheme: 'ml-dsa-65', publicKey: id.publicKey.export({ type: 'spki', format: 'pem' }), signature: crypto.sign(null, Buffer.from(text), id.privateKey).toString('base64') } };
|
|
const r = mm.primeste(fals); const s = mm.sanatate(); mm.inchide();
|
|
const v = r.corp && r.corp.proof ? M.verificaEchivocare(r.corp.proof) : { ok: false };
|
|
return [c0 === 202 && c2 === 202 && r.cod === 409 && r.corp.error === 'equivocation' && v.ok && v.seq === 0 && s.equivocations === 1, JSON.stringify([c0, c2, r.cod, r.corp.error || r.corp.status, v, s.equivocations])];
|
|
});
|
|
|
|
// ---- 9.R5: --chain si --max-merge-delay care nu sunt numere intregi pozitive: folosire gresita (2), nu o paza care cade deschisa
|
|
await rand('9.R5 agent-cli verify --chain 2800x -> 2 (folosire); --max-merge-delay 0 -> 2; ancoreDinChitante cu chain NaN -> refuz (biblioteca nu cade nici ea deschisa)', async () => {
|
|
const fl = scrie('r5-cinstit.json', registru(A1, pA1, [T0]).export());
|
|
const a = spawnSync(process.execPath, [CLI, 'verify', '--ledger', fl, '--policy', scrie('r5-policy.json', pA1), '--anchors', scrie('r5-receipt.json', gazduit.st), '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub, '--chain', '2800x'], { encoding: 'utf8', env: { ...process.env, STUB_F: String(FR) } });
|
|
const b = spawnSync(process.execPath, [CLI, 'verify', '--ledger', fl, '--policy', path.join(F5, 'r5-policy.json'), '--anchors', path.join(F5, 'r5-receipt.json'), '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub, '--max-merge-delay', '0'], { encoding: 'utf8', env: { ...process.env, STUB_F: String(FR) } });
|
|
let lib = null; try { await M.ancoreDinChitante(L1.export(), [gazduit.st], { verifyProof: 'stub', rpc: 'stub', ruleaza: stubR, chain: Number('2800x') }); lib = 'primit'; } catch (e) { lib = e.message; }
|
|
return [a.status === 2 && /--chain must be a positive integer/.test(a.stderr) && b.status === 2 && /--max-merge-delay/.test(b.stderr) && /chain asked must be a positive integer/.test(String(lib)), `${a.status} ${a.stderr.slice(0, 120)} | ${b.status} | ${lib}`];
|
|
});
|
|
|
|
// ---- 9.R8: vocabularul felului agent-witness-checkpoint, scris o singura data (proof-kinds) si folosit si de judecatorul martorului
|
|
await rand('9.R8 felul agent-witness-checkpoint: buildProof refuza treeSize "5", chainId "28001", maxMergeDelay 0, witness "x", log gol, createdAt "ieri"; verificaPunct refuza un plic facut de mana cu treeSize "5" ("punct-forma"); vocabularul e al felului din proof-kinds', async () => {
|
|
const baza = { root: '0x' + '5a'.repeat(32), log: 'aere-agent-witness/28001', witness: 'aere-witness:' + '7c'.repeat(20), chainId: 28001, treeSize: 3, maxMergeDelay: 1800, createdAt: '2026-10-19T09:00:00Z' };
|
|
const rele = [['treeSize', '5'], ['chainId', '28001'], ['maxMergeDelay', 0], ['witness', 'x'], ['log', ''], ['createdAt', 'ieri']].filter(([c, v]) => { try { PK.buildProof('agent-witness-checkpoint', { ...baza, [c]: v }); return true; } catch { return false; } });
|
|
const k = M.cheiaMartorului(fs.mkdtempSync(path.join(T, 'r8-'))); const bun = M.capDeJurnal(k, { treeSize: 3, root: baza.root, log: baza.log, chainId: 28001, maxMergeDelay: 1800 });
|
|
const rau1 = clon(bun); rau1.statement.treeSize = '5'; rau1.statementHash = sha(JSON.stringify(rau1.statement)); rau1.signature.signature = crypto.sign(null, Buffer.from(JSON.stringify(rau1.statement)), k.privateKey).toString('base64');
|
|
const vb = M.verificaPunct(bun), vr = M.verificaPunct(rau1);
|
|
return [rele.length === 0 && vb.ok && !vr.ok && vr.rule === 'punct-forma' && !!PK.SCHEME['agent-witness-checkpoint'].valori, JSON.stringify([rele.map(([c]) => c), vb, vr])];
|
|
});
|
|
|
|
// ---- 9.R9: dovezile de echivocare pastrate sunt marginite in TOTAL (una pe agent si politica, si cel mult maxEchivocari)
|
|
await rand('9.R9 un agent cu 12 politici (sesiuni) si 3 echivocari pe fiecare, cu plafonul total 5: 36 de 409 cu dovada intoarsa, dar numai 5 pastrate (una pe sesiune), 5 fisiere pe disc, 5 si dupa redeschidere; GET heads intoarce id-uri, nu dovezi', async () => {
|
|
const DE = fs.mkdtempSync(path.join(T, 'r9-')); let mE = M.deschideMartor(DE, { ceas: () => Date.now(), maxEchivocari: 5 }); const id = newAgentIdentity(); let n409 = 0, kept = 0, sesiune0 = null;
|
|
for (let s = 0; s < 12; s++) {
|
|
const ph = '0x' + hx(); const ses = sessionId(id.agentId, ph); if (s === 0) sesiune0 = ses;
|
|
const L = (h) => ({ agentId: id.agentId, session: ses, policyHash: ph, entries: [{ seq: 0, hash: h }] });
|
|
mE.primeste(M.cerereMartor(L(hx()), id, { createdAt: iso(Date.now()) }));
|
|
for (let k = 0; k < 3; k++) { const r = mE.primeste(M.cerereMartor(L(hx()), id, { createdAt: iso(Date.now()) })); if (r.cod === 409 && r.corp.error === 'equivocation' && M.verificaEchivocare(r.corp.proof).ok) n409++; if (r.corp.kept) kept++; }
|
|
}
|
|
const fis = ['echivocari', 'echivocari-2'].filter((d) => fs.existsSync(path.join(DE, d))).reduce((a, d) => a + fs.readdirSync(path.join(DE, d)).filter((f) => f.endsWith('.json')).length, 0);
|
|
const pag = mE.capeteAgent(id.agentId, sesiune0); mE.inchide();
|
|
mE = M.deschideMartor(DE, { ceas: () => Date.now(), maxEchivocari: 5 }); const s2 = mE.sanatate(); mE.inchide();
|
|
return [n409 === 36 && fis === 5 && s2.equivocations === 5 && pag.equivocations.length === 1 && pag.equivocations[0].a === undefined && !!pag.equivocations[0].id, JSON.stringify([n409, kept, fis, s2.equivocations, pag.equivocations])];
|
|
});
|
|
|
|
// ---- 9.R10: GET .../heads e PAGINAT si nu citeste nimic de pe disc
|
|
await rand('9.R10 GET heads?session: 250 de capete -> pagini de 100 cu `next` (100, 200, null); limit=101 -> 400; fara session: sesiunile cu numarul capetelor; capul unui rand sters de pe disc nu opreste lista (nimic citit de pe disc)', async () => {
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 250); const DP = fs.mkdtempSync(path.join(T, 'r10-'));
|
|
const mP = M.deschideMartor(DP, { ceas: () => Date.now() });
|
|
for (let s = 0; s < 250; s++) { const r = mP.primeste(M.cerereMartor(L, id, { seq: s, previous: s ? s - 1 : null, createdAt: iso(Date.now()) })); if (r.cod !== 202) throw new Error('cap ' + s + ': ' + r.cod); }
|
|
const srvP = await porneste(mP, { limite: LIMITE }); const UP = `http://127.0.0.1:${srvP.port}`;
|
|
fs.rmSync(path.join(DP, 'cioburi', '0', 'capete', mP.capeteAgent(id.agentId, L.session).heads[5].id.slice(2) + '.json'));
|
|
const p1 = await get(`/v1/agents/witness/${id.agentId}/heads?session=${L.session}`, UP), p2 = await get(`/v1/agents/witness/${id.agentId}/heads?session=${L.session}&from=100`, UP), p3 = await get(`/v1/agents/witness/${id.agentId}/heads?session=${L.session}&from=200`, UP);
|
|
const rLim = await get(`/v1/agents/witness/${id.agentId}/heads?session=${L.session}&limit=101`, UP), rTot = await get(`/v1/agents/witness/${id.agentId}/heads`, UP);
|
|
await srvP.close(); mP.inchide();
|
|
const n = (p) => (p.j && Array.isArray(p.j.heads) ? p.j.heads.length : -1);
|
|
return [p1.cod === 200 && n(p1) === 100 && p1.j.next === 100 && n(p2) === 100 && p2.j.next === 200 && n(p3) === 50 && p3.j.next === null && p3.j.latest.seq === 249 && rLim.cod === 400 && rTot.j.sessions[0].count === 250,
|
|
JSON.stringify([p1.cod, n(p1), p1.j && p1.j.next, n(p2), n(p3), rLim.cod, rTot.j && rTot.j.sessions && rTot.j.sessions[0]]).slice(0, 300)];
|
|
});
|
|
|
|
// ---- 9.R11: retrimiterea capului PUBLIC al unui agent nu ii consuma cota (numai schimbarile de stare o consuma)
|
|
await rand('9.R11 un strain retrimite capul public al victimei de 2 x limita pe agent (de la clienti diferiti) -> 200 fiecare; capul urmator al victimei -> 202 (nu 429)', async () => {
|
|
const mV = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r11-')), { ceas: () => Date.now() }); const srvV = await porneste(mV, { limite: LIMITE }); const UV = `http://127.0.0.1:${srvV.port}`;
|
|
const V = newAgentIdentity(); const L = registruBrut(V, 2);
|
|
const h0 = M.cerereMartor(L, V, { seq: 0, createdAt: iso(Date.now()) }); const c0 = (await postLa(UV, h0, 'r11-victima')).cod;
|
|
const coduri = []; for (let i = 0; i < 2 * LIMITE.postPeAgent.cereri; i++) coduri.push((await postLa(UV, h0, 'r11-strain-' + i)).cod);
|
|
const c1 = (await postLa(UV, M.cerereMartor(L, V, { seq: 1, previous: 0, createdAt: iso(Date.now()) }), 'r11-victima')).cod;
|
|
await srvV.close(); mV.inchide();
|
|
return [c0 === 202 && coduri.every((c) => c === 200) && c1 === 202, `${c0} ${[...new Set(coduri)].join(',')} x${coduri.length} ${c1}`];
|
|
});
|
|
|
|
await rand('9.P11 CONTROL (paznicul nou al lui R11): pastrarea unei dovezi NOI e o schimbare de stare: un agent care si-a consumat cota cu capete noi, apoi echivoca -> 429, dovada nepastrata', async () => {
|
|
const mV = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r11b-')), { ceas: () => Date.now() }); const srvV = await porneste(mV, { limite: LIMITE }); const UV = `http://127.0.0.1:${srvV.port}`;
|
|
const V = newAgentIdentity(); const L = registruBrut(V, LIMITE.postPeAgent.cereri);
|
|
const coduri = []; for (let s = 0; s < LIMITE.postPeAgent.cereri; s++) coduri.push((await postLa(UV, M.cerereMartor(L, V, { seq: s, previous: s ? s - 1 : null, createdAt: iso(Date.now()) }), 'r11b')).cod);
|
|
const alt = { ...L, entries: [{ seq: 0, hash: hx() }] }; const e = await postLa(UV, M.cerereMartor(alt, V, { seq: 0, createdAt: iso(Date.now()) }), 'r11b');
|
|
const h = mV.sanatate(); await srvV.close(); mV.inchide();
|
|
return [coduri.every((c) => c === 202) && e.cod === 429 && h.equivocations === 0, JSON.stringify([coduri.join(','), e.cod, e.j && e.j.error, h.equivocations])];
|
|
});
|
|
|
|
// ---- 9.R13: limitatoarele: IPv6 pe /64, chei curatate, plafon de chei
|
|
await rand('9.R13 cheia clientului: doua adrese din acelasi /64 -> aceeasi cheie, alt /64 -> alta, ::ffff:IPv4 -> IPv4; 41 de POST-uri din 41 de adrese ale aceluiasi /64 -> al 41-lea 429', async () => {
|
|
const k1 = S.cheieClient('2001:db8:1:2:aaaa::1'), k2 = S.cheieClient('2001:db8:1:2:bbbb:cccc:dddd:2'), k3 = S.cheieClient('2001:db8:1:3::1'), k4 = S.cheieClient('::ffff:198.51.100.7');
|
|
const mL = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r13-')), { ceas: () => Date.now() }); const srvL = await porneste(mL, { limite: LIMITE }); const UL = `http://127.0.0.1:${srvL.port}`;
|
|
const coduri = []; for (let i = 0; i <= LIMITE.postPeClient.cereri; i++) coduri.push((await postLa(UL, '{}', `2001:db8:77:1:${(i + 1).toString(16)}::1`)).cod);
|
|
await srvL.close(); mL.inchide();
|
|
return [k1 === k2 && k1 !== k3 && k4 === '198.51.100.7' && coduri.slice(0, -1).every((c) => c === 400) && coduri[coduri.length - 1] === 429, JSON.stringify([k1, k2, k3, k4, coduri.slice(-3)])];
|
|
});
|
|
await rand('9.R13 limitatorul: 1000 de chei noi cu plafonul 100 -> cel mult 100 tinute; dupa fereastra cheile expirate se curata si un client nou trece', async () => {
|
|
let t = 1_000_000; const l = S.limitator({ cereri: 5, ms: 1000 }, { maxChei: 100, ceas: () => t });
|
|
let limitate = 0; for (let i = 0; i < 1000; i++) if (l('c' + i)) limitate++;
|
|
const marimeLa = l.marime(); t += 1001; const dupa = l('nou'); const marimeDupa = l.marime();
|
|
return [marimeLa === 100 && dupa === false && marimeDupa === 1, JSON.stringify([marimeLa, limitate, dupa, marimeDupa])];
|
|
});
|
|
|
|
// ---- 9.R16: scrierile durabile: o scriere SCURTA nu taie frunze, o scriere cazuta se da inapoi, o stare stricata in mijloc e refuzata
|
|
const origWrite = fs.writeSync;
|
|
const cuScriere = async (inlocuire, f) => { fs.writeSync = inlocuire; try { return await f(); } finally { fs.writeSync = origWrite; } };
|
|
await rand('9.R16 fiecare fs.writeSync scrie numai jumatate (cum face un disc aproape plin): trei capete primite, si dupa redeschidere toate trei, cu chitante care verifica', async () => {
|
|
const DW = fs.mkdtempSync(path.join(T, 'r16a-')); const id = newAgentIdentity(); const L = registruBrut(id, 3);
|
|
const jumatate = function (fd, buf, ...r) { if (typeof buf === 'string') return origWrite.call(fs, fd, buf.slice(0, Math.max(1, Math.ceil(buf.length / 2)))); const off = r[0] ?? 0; const len = r[1] ?? (buf.length - off); return origWrite.call(fs, fd, buf, off, Math.max(1, Math.ceil(len / 2))); };
|
|
const coduri = await cuScriere(jumatate, () => { const mm = M.deschideMartor(DW, { ceas: () => Date.now() }); const c = []; for (let s = 0; s < 3; s++) c.push(mm.primeste(M.cerereMartor(L, id, { seq: s, previous: s ? s - 1 : null, createdAt: iso(Date.now()) })).cod); mm.inchide(); return c; });
|
|
const mm = M.deschideMartor(DW, { ceas: () => Date.now() }); await tur(mm, {}); const h = mm.sanatate(); const ids = mm.capeteAgent(id.agentId, L.session).heads.map((x) => x.id);
|
|
const toate = ids.every((i) => { const s = mm.stareaCapului(i).corp; return s.receipt && M.verificaChitanta(s.receipt).ok; }); mm.inchide();
|
|
return [coduri.join(',') === '202,202,202' && h.leaves === 3 && ids.length === 3 && toate, JSON.stringify([coduri, h.leaves, ids.length, toate])];
|
|
});
|
|
await rand('9.R16 o scriere a frunzei care cade la jumatate (ENOSPC): fisierul se taie inapoi, capul nu e primit; dupa, capul urmator si o redeschidere -> jurnalul intreg', async () => {
|
|
const DW = fs.mkdtempSync(path.join(T, 'r16b-')); const id = newAgentIdentity(); const L = registruBrut(id, 3); let mm = M.deschideMartor(DW, { ceas: () => Date.now() });
|
|
const c0 = mm.primeste(M.cerereMartor(L, id, { seq: 0, createdAt: iso(Date.now()) })).cod;
|
|
let pas = 0;
|
|
const cade = function (fd, buf, ...r) { const off = r[0] ?? 0; const inceput = typeof buf === 'string' ? buf.slice(0, 5) : Buffer.from(buf).subarray(off, off + 5).toString(); if (pas === 1) { pas = 2; throw Object.assign(new Error('ENOSPC: no space left on device, write'), { code: 'ENOSPC' }); } if (pas === 0 && inceput === '{"i":') { pas = 1; if (typeof buf === 'string') return origWrite.call(fs, fd, buf.slice(0, 20)); return origWrite.call(fs, fd, buf, off, 20); } return origWrite.call(fs, fd, buf, ...r); };
|
|
const h1 = M.cerereMartor(L, id, { seq: 1, previous: 0, createdAt: iso(Date.now()) });
|
|
let eroare = null; await cuScriere(cade, () => { try { mm.primeste(h1); } catch (e) { eroare = e.code || e.message; } });
|
|
const dupaEsec = mm.stareaCapului(h1.statementHash).cod;
|
|
const c1 = mm.primeste(M.cerereMartor(L, id, { seq: 1, previous: 0, createdAt: iso(Date.now()) })).cod, c2 = mm.primeste(M.cerereMartor(L, id, { seq: 2, previous: 1, createdAt: iso(Date.now()) })).cod; mm.inchide();
|
|
mm = M.deschideMartor(DW, { ceas: () => Date.now() }); const h = mm.sanatate(); mm.inchide();
|
|
return [c0 === 202 && pas === 2 && eroare === 'ENOSPC' && dupaEsec === 404 && c1 === 202 && c2 === 202 && h.leaves === 3, JSON.stringify([c0, pas, eroare, dupaEsec, c1, c2, h.leaves])];
|
|
});
|
|
await rand('9.R16 o stare cu un rand de frunza stricat IN MIJLOC -> deschiderea refuza (nu taie frunze confirmate); un cap de jurnal semnat peste mai multe frunze decat sunt pe disc -> refuza', async () => {
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 3);
|
|
const fa = (d) => { const mm = M.deschideMartor(d, { ceas: () => Date.now() }); for (let s = 0; s < 3; s++) mm.primeste(M.cerereMartor(L, id, { seq: s, previous: s ? s - 1 : null, createdAt: iso(Date.now()) })); mm.construiestePunct(); mm.inchide(); };
|
|
const D1 = fs.mkdtempSync(path.join(T, 'r16c-')); fa(D1); const f1 = frunzeCiob(D1); const r1 = fs.readFileSync(f1, 'utf8').split('\n'); r1[1] = r1[1].slice(0, 30); fs.writeFileSync(f1, r1.join('\n'));
|
|
let e1 = null; try { M.deschideMartor(D1, { ceas: () => Date.now() }).inchide(); } catch (e) { e1 = e.message; }
|
|
const D2 = fs.mkdtempSync(path.join(T, 'r16d-')); fa(D2); const f2 = frunzeCiob(D2); const r2 = fs.readFileSync(f2, 'utf8').split('\n'); fs.writeFileSync(f2, r2.slice(0, 2).join('\n') + '\n');
|
|
let e2 = null; try { M.deschideMartor(D2, { ceas: () => Date.now() }).inchide(); } catch (e) { e2 = e.message; }
|
|
return [/damaged and lines follow/.test(String(e1)) && /refusing to start/.test(String(e2)), `${e1} | ${e2}`];
|
|
});
|
|
// 9.R16 / 9.R18: un GET pe un cap al carui fisier lipseste: 500 cu un cod (fara cale), iar procesul ramane viu. Intr-un proces COPIL: pe forma
|
|
// veche exceptia nu era prinsa si oprea serviciul, deci oprea si proba
|
|
await rand('9.R16/R18 GET pe un cap al carui fisier lipseste de pe disc -> 500 {error, code: io_error}, fara nicio cale sau nume de dosar; serviciul raspunde mai departe (health 200)', async () => {
|
|
const DG = fs.mkdtempSync(path.join(T, 'r18-')); const fc = path.join(T, 'r18-copil.mjs');
|
|
fs.writeFileSync(fc, `import fs from 'node:fs'; import path from 'node:path'; import { pathToFileURL } from 'node:url';
|
|
const M = await import(pathToFileURL(${JSON.stringify(path.join(AICI, 'martor-agenti.mjs'))}).href); const S = await import(pathToFileURL(${JSON.stringify(path.join(AICI, 'martor-agenti-serviciu.mjs'))}).href);
|
|
const L = await import(pathToFileURL(${JSON.stringify(path.join(AICI, 'agent-ledger.mjs'))}).href);
|
|
const dir = process.argv[2]; const m = M.deschideMartor(dir); const id = L.newAgentIdentity(); const ph = '0x' + 'ab'.repeat(32);
|
|
const led = { agentId: id.agentId, session: L.sessionId(id.agentId, ph), policyHash: ph, entries: [{ seq: 0, hash: 'cd'.repeat(32) }] };
|
|
const r = m.primeste(M.cerereMartor(led, id, {})); const srv = await S.porneste(m, { port: 0 });
|
|
for (const f of fs.readdirSync(dir, { recursive: true })) if (String(f).includes(r.corp.id.slice(2)) && String(f).endsWith('.json')) fs.rmSync(path.join(dir, String(f)));
|
|
const g = await fetch('http://127.0.0.1:' + srv.port + '/v1/agents/witness/' + r.corp.id); const t = await g.text();
|
|
const h = await fetch('http://127.0.0.1:' + srv.port + '/v1/agents/witness/health');
|
|
console.log(JSON.stringify({ cod: g.status, corp: t, sanatate: h.status })); await srv.close(); m.inchide();\n`);
|
|
const c = spawnSync(process.execPath, [fc, DG], { encoding: 'utf8', timeout: 60000 });
|
|
let o = null; try { o = JSON.parse(c.stdout.trim().split('\n').pop()); } catch { o = null; }
|
|
const corp = o ? JSON.parse(o.corp) : null;
|
|
return [c.status === 0 && o && o.cod === 500 && corp.code === 'io_error' && !/[\\/]|capete|cioburi|ENOENT/.test(o.corp) && o.sanatate === 200, `cod proces ${c.status} | ${c.stdout.slice(0, 200)} | ${c.stderr.slice(0, 200)}`];
|
|
});
|
|
await rand('9.R18 o notarizare cazuta cu un mesaj care poarta o cale: in /health si in starea capului apare numai codul (unfunded; fara cod: notarization_failed), niciodata mesajul; codul notarizatorului real pe lantul 2800: chain_refused', async () => {
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r18b-')), { ceas: () => Date.now() }); const id = newAgentIdentity();
|
|
const r = mm.primeste(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) }));
|
|
await tur(mm, { notarizeaza: async () => ({ ok: false, cod: 'unfunded', motiv: "unfunded: Cannot find module '/opt/aere/notar/node_modules/ethers' Require stack" }) });
|
|
const h1 = mm.sanatate(); const s1 = mm.stareaCapului(r.corp.id).corp;
|
|
await tur(mm, { notarizeaza: async () => ({ ok: false, motiv: "ENOSPC: open '/opt/aere/martor-agenti/stare-28001/x.json'" }) });
|
|
const h2 = mm.sanatate(); mm.inchide();
|
|
const ethFals = { JsonRpcProvider: class { async getNetwork() { return { chainId: 2800n }; } }, parseEther: () => 0n, Wallet: class {} };
|
|
const nr = await notarizatorReal({ rpc: 'x', cheieFisier: path.join(T, 'r18-nu.key'), ethers: ethFals })(plicNenot);
|
|
const tot = JSON.stringify([h1, s1, h2]);
|
|
return [h1.lastCheckpoint.lastError === 'unfunded' && s1.notarization.lastError === 'unfunded' && h2.lastCheckpoint.lastError === 'notarization_failed' && !/\/opt|Require|ENOSPC/.test(tot) && nr.cod === 'chain_refused', JSON.stringify([h1.lastCheckpoint, s1.notarization, h2.lastCheckpoint, nr.cod])];
|
|
});
|
|
// ---- 9.R6 (controlul negativ planteaza fiecare paznic numit) se masoara in control-negativ-martor-agenti.mjs (acoperirea markerilor)
|
|
void mPrincipal; void g2c;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------- 10. a doua revizie adversariala B3 (2026-10-19)
|
|
// Cate un rand pentru fiecare din cele 15 constatari confirmate (numele incep cu "10.R2-<nn>"), scris asa incat pe forma de dinainte (cf56bcda6)
|
|
// sa iasa RAU, nu sa opreasca proba; un rand "10.P2-<nn>" e controlul unui paznic nou (forma veche putea trece de el din alt motiv).
|
|
async function revizia2() {
|
|
const acumY = Math.floor(Date.now() / 1000); const FY = acumY + 60;
|
|
// ---- martorul REAL al sectiunii (ceasul lui se poate muta: pagina veche a lui R2-01) si serviciul lui
|
|
let wW = Date.now();
|
|
const DW = fs.mkdtempSync(path.join(T, 'r2-w-')); const mW = M.deschideMartor(DW, { ceas: () => wW }); const srvW = await porneste(mW, { limite: LIMITE }); const UW = `http://127.0.0.1:${srvW.port}`;
|
|
const W = mW.witnessId;
|
|
try {
|
|
// ---- 10.R2-01: --witness <W> --witness-url <adresa>: numai lista SEMNATA de W, din ultimele 300 s, conteaza
|
|
const Y = newAgentIdentity(); const pY = pol(Y); let ty = acumY - 100;
|
|
const LY = openLedger({ identity: Y, policy: pY.policy, policyHash: pY.policyHash, now: () => ty }); LY.record({ kind: 'payment', to: '0xbbbb', amount: '10' }); ty += 1; LY.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
const bazaY = LY.export(); ty += 1;
|
|
const LYA = resumeLedger({ identity: Y, policy: pY.policy, policyHash: pY.policyHash, ledger: bazaY, now: () => ty }); LYA.record({ kind: 'payment', to: '0xbbbb', amount: '5' });
|
|
const LYB = resumeLedger({ identity: Y, policy: pY.policy, policyHash: pY.policyHash, ledger: bazaY, now: () => ty }); LYB.record({ kind: 'payment', to: '0xbbbb', amount: '50' });
|
|
const A_ = LYA.export(), B_ = LYB.export();
|
|
const ry0 = mW.primeste(M.cerereMartor(A_, Y, { seq: 0, createdAt: iso(wW) }));
|
|
await tur(mW, { notarizeaza: notarMock(FY) });
|
|
const chitY = mW.stareaCapului(ry0.corp && ry0.corp.id).corp;
|
|
const ry1 = mW.primeste(M.cerereMartor(A_, Y, { seq: 1, previous: 0, createdAt: iso(wW) }));
|
|
// pagina VECHE, semnata cinstit de W cu 600 s in urma (inainte de seq 2): ce poate relua un intermediar
|
|
wW = Date.now() - 600_000; const vechi = mW.capeteAgent(Y.agentId, A_.session); wW = Date.now();
|
|
const ry2 = mW.primeste(M.cerereMartor(A_, Y, { seq: 2, previous: 1, createdAt: iso(wW) }));
|
|
await tur(mW, { notarizeaza: notarMock(FY) }); // ultimul cap tinut notarizat: --witness-url cere chitanta lui (R3-04)
|
|
const fpY = scrie('r2-01-policy.json', pY), frY = scrie('r2-01-receipt.json', chitY), faY = scrie('r2-01-a.json', A_), fbY = scrie('r2-01-b.json', B_);
|
|
const verY = (fl, url, extra = []) => ruleazaAsincron([CLI, 'verify', '--ledger', fl, '--policy', fpY, '--anchors', frY, '--rpc', 'http://127.0.0.1:9', '--verify-proof', fStub, '--witness', W, '--witness-url', url, ...extra], { STUB_F: String(FY), AERE_VERIFY_PROOF: '' });
|
|
// proxy ostil in fata martorului real: copiaza tot si scoate seq 2 din lista (si din pagina semnata, cu hash-ul refacut: semnatura nu se poate reface)
|
|
const prox = await serverLocal(async (req, res, u) => {
|
|
const r = await fetch(UW + u.pathname + u.search, { headers: { 'x-real-ip': 'r2-01-proxy' } }); const j = await r.json();
|
|
if (/\/heads$/.test(u.pathname) && j && Array.isArray(j.heads)) {
|
|
j.heads = j.heads.filter((h) => h.seq < 2); j.count = j.heads.length; const u2 = j.heads[j.heads.length - 1]; j.latest = u2 ? { seq: u2.seq, hash: u2.hash, id: u2.id } : null;
|
|
if (j.signedPage && j.signedPage.statement) { const st = j.signedPage.statement; st.heads = st.heads.filter((h) => h.seq < 2); st.count = st.heads.length; const u3 = st.heads[st.heads.length - 1]; st.latest = u3 ? { seq: u3.seq, hash: u3.hash } : null; j.signedPage.statementHash = sha(JSON.stringify(st)); }
|
|
}
|
|
trimite(res, r.status, j);
|
|
});
|
|
// al doilea proxy: lasa pagina semnata NEATINSA si scoate seq 2 numai din campurile nesemnate de langa ea (heads, count, latest)
|
|
const prox2 = await serverLocal(async (req, res, u) => {
|
|
const r = await fetch(UW + u.pathname + u.search, { headers: { 'x-real-ip': 'r2-01-proxy2' } }); const j = await r.json();
|
|
if (/\/heads$/.test(u.pathname) && j && Array.isArray(j.heads)) { j.heads = j.heads.filter((h) => h.seq < 2); j.count = j.heads.length; const u2 = j.heads[j.heads.length - 1]; j.latest = u2 ? { seq: u2.seq, hash: u2.hash, id: u2.id } : null; }
|
|
trimite(res, r.status, j);
|
|
});
|
|
// impostor pur: spune in /health ca e W, si isi semneaza paginile cu cheia LUI (lista goala)
|
|
const kImp = M.cheiaMartorului(fs.mkdtempSync(path.join(T, 'r2-imp-')));
|
|
const imp = await serverLocal(async (req, res, u) => (u.pathname.endsWith('/health') ? trimite(res, 200, { ok: true, witness: W })
|
|
: trimite(res, 200, { agentId: B_.agentId, session: B_.session, heads: [], count: 0, latest: null, next: null, equivocations: [], signedPage: paginaSemnataDe(kImp, paginaGoala(kImp, B_.agentId, B_.session, Number(u.searchParams.get('from') || 0))) })));
|
|
// reluarea paginii VECHI (semnate de W, fara seq 2)
|
|
// (numai lista e cea veche; restul rutelor merg la martorul real, deci si chitanta ultimului cap din lista veche, ceruta de la R3-04)
|
|
const rel = await serverLocal(async (req, res, u) => {
|
|
if (u.pathname.endsWith('/health')) return trimite(res, 200, { ok: true, witness: W });
|
|
if (/\/heads$/.test(u.pathname)) return trimite(res, 200, vechi);
|
|
const r = await fetch(UW + u.pathname + u.search, { headers: { 'x-real-ip': 'r2-01-reluare' } }); return trimite(res, r.status, await r.json());
|
|
});
|
|
await rand('10.R2-01 --witness <W> --witness-url: martorul real prinde ramura B (1) si trece A (0); un proxy ostil care scoate capul seq 2 din lista -> 2 NOT MEASURED (nu 0); unul care il scoate numai din campurile nesemnate -> 1 "[registru]" (conteaza pagina semnata); un impostor care se da W in /health -> 2 NOT MEASURED "[martor]" (lista lui e semnata de alta cheie: nu e marturia lui W; pana la a cincea revizie: 1); o pagina VECHE a lui W, reluata -> 2', async () => {
|
|
const a = await verY(faY, UW), b = await verY(fbY, UW), p = await verY(fbY, prox.url), p2 = await verY(fbY, prox2.url), i = await verY(fbY, imp.url), r = await verY(fbY, rel.url);
|
|
return [ry0.cod === 202 && ry1.cod === 202 && ry2.cod === 202 && a.status === 0 && b.status === 1 && /\[registru\]/.test(b.stderr) && p.status === 2 && /NOT MEASURED/.test(p.stderr) && p2.status === 1 && /\[registru\]/.test(p2.stderr)
|
|
&& i.status === 2 && /\[martor\]/.test(i.stderr) && /NOT MEASURED/.test(i.stderr) && r.status === 2 && /NOT MEASURED/.test(r.stderr),
|
|
`A ${a.status} | B ${b.status} | proxy ${p.status} ${p.stderr.slice(0, 160)} | proxy nesemnat ${p2.status} ${p2.stderr.slice(0, 120)} | impostor ${i.status} ${i.stderr.slice(0, 160)} | reluare ${r.status} ${r.stderr.slice(0, 160)}`];
|
|
});
|
|
await rand('10.P2-01 CONTROL: paginile semnate de martor: o pagina cu un cap schimbat (hash refacut) -> "punct-semnatura"; fara hash refacut -> "pagina-integritate"; alt agent sau alt `from` decat cel cerut -> "pagina-forma"; pagina reala, acum -> ok; numarul semnat care nu se aduna -> NOT MEASURED', async () => {
|
|
const pg = mW.capeteAgent(Y.agentId, A_.session).signedPage; const o = { agentId: Y.agentId, session: A_.session, from: 0 };
|
|
const bun = M.verificaPaginaCapetelor(pg, o);
|
|
const x1 = clon(pg); x1.statement.heads[0].hash = 'ee'.repeat(32); x1.statementHash = sha(JSON.stringify(x1.statement));
|
|
const x2 = clon(pg); x2.statement.heads[0].hash = 'ee'.repeat(32);
|
|
const r1 = M.verificaPaginaCapetelor(x1, o), r2 = M.verificaPaginaCapetelor(x2, o), r3 = M.verificaPaginaCapetelor(pg, { ...o, agentId: B.agentId }), r4 = M.verificaPaginaCapetelor(pg, { ...o, from: 1 });
|
|
const nep = await serverLocal(async (req, res, u) => { const st = { ...paginaGoala(kImp, A_.agentId, A_.session, 0), count: 5 }; trimite(res, 200, { signedPage: paginaSemnataDe(kImp, st) }); });
|
|
const cn = await M.citesteCapeteMartorului(nep.url, A_.agentId, A_.session); await nep.close();
|
|
return [bun.ok && r1.rule === 'punct-semnatura' && r2.rule === 'pagina-integritate' && r3.rule === 'pagina-forma' && r4.rule === 'pagina-forma' && !cn.ok && /ask again/.test(cn.motiv), JSON.stringify([bun.ok, r1.rule, r2.rule, r3.rule, r4.rule, cn.motiv])];
|
|
});
|
|
await prox.close(); await prox2.close(); await imp.close(); await rel.close();
|
|
|
|
// ---- 10.R2-15 (2): --witness <alt martor> --witness-url <martorul real> -> NOT MEASURED [martor] (lista e semnata de W, nu de cel fixat; din a cincea revizie,
|
|
// R5-02, o lista a altui martor nu e o dovada impotriva registrului, deci nu mai da FAILED)
|
|
await rand('10.R2-15 agent-cli verify --witness <alt id> --witness-url <martorul real> -> 2 NOT MEASURED "[martor]" (lista e semnata de W, nu de martorul fixat)', async () => {
|
|
const r = await ruleazaAsincron([CLI, 'verify', '--ledger', faY, '--policy', fpY, '--witness', 'aere-witness:' + 'ee'.repeat(20), '--witness-url', UW], { AERE_VERIFY_PROOF: '' });
|
|
return [r.status === 2 && /list: \[martor\]/.test(r.stderr) && /NOT MEASURED/.test(r.stderr), `${r.status} ${r.stderr.slice(0, 200)}`];
|
|
});
|
|
|
|
// ---- 10.R2-04: o lista cu elemente care nu sunt capete: NEMASURAT (2), nu o exceptie si nu un verdict de ramura
|
|
await rand('10.R2-04 verificaCapeteMartorului pe heads [null], [5], [{}] -> NEMASURAT (nici exceptie, nici "FAIL [registru]")', async () => {
|
|
const v = [[null], [5], [{}]].map((heads) => M.verificaCapeteMartorului(A_, { agentId: A_.agentId, session: A_.session, heads, equivocations: [] }).stare);
|
|
return [v.every((s) => s === 'NEMASURAT'), JSON.stringify(v)];
|
|
});
|
|
const kRau = M.cheiaMartorului(fs.mkdtempSync(path.join(T, 'r2-04-')));
|
|
const rele = [[null], [5], [{}], [{ seq: 0, hash: 'zz', id: '0x' + '11'.repeat(32) }]];
|
|
await rand('10.R2-04 agent-cli verify --witness-url catre un martor care SEMNEAZA o lista cu elemente stricate ([null], [5], [{}], [{seq 0, hash "zz"}]) -> 2 NOT MEASURED de fiecare data', async () => {
|
|
const coduri = [];
|
|
for (const heads of rele) {
|
|
const s = await serverLocal(async (req, res, u) => (u.pathname.endsWith('/health') ? trimite(res, 200, { ok: true, witness: kRau.witnessId })
|
|
: trimite(res, 200, { agentId: A_.agentId, session: A_.session, heads, count: heads.length, next: null, equivocations: [], signedPage: paginaSemnataDe(kRau, { ...paginaGoala(kRau, A_.agentId, A_.session, 0), heads, count: heads.length }) })));
|
|
const r = await ruleazaAsincron([CLI, 'verify', '--ledger', faY, '--policy', fpY, '--witness-url', s.url], { AERE_VERIFY_PROOF: '' }); await s.close();
|
|
coduri.push(r.status + (/Cannot read/.test(r.stderr) ? ' (exceptie)' : ''));
|
|
}
|
|
return [coduri.every((c) => c === '2'), coduri.join(',')];
|
|
});
|
|
|
|
// ---- 10.R2-05: sub un martor fixat, o ancora simpla {seq, hash, at} nu e o chitanta; fara chitanta, marginea martorului nu e masurata
|
|
const P5 = newAgentIdentity(); const pP5 = pol(P5); const T5 = acumY - 3000;
|
|
const LP5 = registru(P5, pP5, [T5, T5 + 5, T5 + 10]);
|
|
const r5 = mW.primeste(M.cerereMartor(LP5.export(), P5, { seq: 2, createdAt: iso(wW) }));
|
|
await tur(mW, { notarizeaza: notarMock(FY) });
|
|
const chit5 = mW.stareaCapului(r5.corp && r5.corp.id).corp;
|
|
let ta5 = acumY - 2000; const ant5 = resumeLedger({ identity: P5, policy: pP5.policy, policyHash: pP5.policyHash, ledger: LP5.export(), now: () => ta5 }); ant5.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
const fp5 = scrie('r2-05-policy.json', pP5), fl5 = scrie('r2-05-antedatat.json', ant5.export()), fr5 = scrie('r2-05-receipt.json', chit5);
|
|
const fs5 = scrie('r2-05-simplu.json', [{ seq: 2, hash: LP5.export().entries[2].hash, at: T5 + 10 }]), fg5 = scrie('r2-05-gol.json', []);
|
|
const v5 = (anc, extra) => ruleazaAsincron([CLI, 'verify', '--ledger', fl5, '--policy', fp5, '--anchors', anc, '--rpc', 'http://127.0.0.1:9', '--chain', '28001', '--verify-proof', fStub, ...extra], { STUB_F: String(FY), AERE_VERIFY_PROOF: '' });
|
|
await rand('10.R2-05 registrul antedatat: cu chitanta martorului fixat -> 1 "backdated"; cu o ancora SIMPLA facuta de agent si acelasi --witness -> 2 NOT MEASURED "[martor]" (nu 0 "witnessed"; pana la a cincea revizie: 1); cu un fisier de ancore gol si --witness -> 2; cu ancora simpla si --witness-url (martorul de acolo e cel fixat) -> 1 "backdated" (chitanta capului tinut, adusa, dovedeste antedatarea; ancora simpla nu o mai poate ascunde); fara nicio fixare ancora simpla trece ca pana azi (0)', async () => {
|
|
const c = await v5(fr5, ['--witness', W]), s = await v5(fs5, ['--witness', W]), g = await v5(fg5, ['--witness', W]), u = await v5(fs5, ['--witness-url', UW]), n = await v5(fs5, []);
|
|
return [r5.cod === 202 && c.status === 1 && /backdated/.test(c.stdout) && s.status === 2 && /anchors\[0\]: \[martor\] a plain anchor/.test(s.stderr) && /NOT MEASURED/.test(s.stderr) && g.status === 2 && /NOT MEASURED/.test(g.stderr) && u.status === 1 && /backdated/.test(u.stdout + u.stderr) && n.status === 0,
|
|
`chitanta ${c.status} | simpla ${s.status} ${s.stderr.slice(0, 120)} | gol ${g.status} ${g.stderr.slice(0, 120)} | url ${u.status} ${u.stderr.slice(-200)} | nefixat ${n.status}`];
|
|
});
|
|
|
|
// ---- 10.R2-11 / 10.R2-12: codurile de iesire ale lui martor-agenti.mjs (receipt, verify-receipt) si valorile goale ale lui --witness
|
|
const MA_ = path.join(AICI, 'martor-agenti.mjs');
|
|
const idY = ry0.corp && ry0.corp.id;
|
|
await rand('10.R2-11 receipt cu martorul raspunzand 503 sau 429 -> 2 NOT MEASURED (nu 1); 404 -> 1; verify-receipt pe raspunsul GET al unui cap PENDING -> 2', async () => {
|
|
const coduri = {};
|
|
// (de la a sasea revizie, N2, receipt cere --ledger: raspunsul se scrie numai daca e chitanta unui cap al acelui registru)
|
|
const flR = scrie('r2-11-ledger.json', registruBrut(newAgentIdentity(), 1));
|
|
for (const cod of [503, 429, 404]) { const s = await serverLocal(async (req, res) => trimite(res, cod, { error: cod === 404 ? 'unknown_head' : cod === 429 ? 'rate_limited' : 'agents_witness_unavailable' })); coduri[cod] = (await ruleazaAsincron([MA_, 'receipt', '--url', s.url, '--id', idY, '--ledger', flR])).status; await s.close(); }
|
|
const idP = newAgentIdentity(); const rp = mW.primeste(M.cerereMartor(registruBrut(idP, 1), idP, { createdAt: iso(wW) }));
|
|
const pend = mW.stareaCapului(rp.corp && rp.corp.id).corp; // un cap primit, inca neacoperit de niciun cap de jurnal: pending
|
|
const vr = spawnSync(process.execPath, [MA_, 'verify-receipt', '--receipt', scrie('r2-11-pending.json', pend)], { encoding: 'utf8' });
|
|
return [rp.cod === 202 && pend.status === 'pending' && coduri[503] === 2 && coduri[429] === 2 && coduri[404] === 1 && vr.status === 2, JSON.stringify([coduri, pend.status, vr.status, vr.stdout.slice(0, 120)])];
|
|
});
|
|
const frChit = scrie('r2-12-receipt.json', chitY);
|
|
await rand('10.R2-12 verify-receipt --witness "" (o variabila goala), --witness "," si un --witness final fara valoare -> 2 (folosire gresita), nu 0; --witness <W> -> 0; --witness <alt> -> 1', async () => {
|
|
const vr = (extra) => spawnSync(process.execPath, [MA_, 'verify-receipt', '--receipt', frChit, ...extra], { encoding: 'utf8' }).status;
|
|
const c = [vr(['--witness', '']), vr(['--witness', ',']), vr(['--witness']), vr(['--witness', W]), vr(['--witness', 'aere-witness:' + 'ee'.repeat(20)])];
|
|
return [c.join(',') === '2,2,2,0,1', c.join(',')];
|
|
});
|
|
} finally { await srvW.close(); mW.inchide(); }
|
|
|
|
// ---- 10.R2-02: plafonul dovezilor nu se poate umple ca sa fie impinsa afara (sau refuzata) echivocarea reala a altui agent
|
|
await rand('10.R2-02 maxFrunze 4, plafonul 8: un strain umple 8 dovezi (4 pe capetele ciobului 0, facute cand ciobul curent era 1, si 4 pe ciobul 1); dupa rotire, echivocarea reala a lui C e PASTRATA (kept) si apare in lista lui, /health numara nepastrarile (0 la plafon), iar dupa redeschidere dovada lui se reface si verifica', async () => {
|
|
const DQ = fs.mkdtempSync(path.join(T, 'r2-02-')); let mQ = M.deschideMartor(DQ, { maxFrunze: 4, maxEchivocari: 8, ceas: () => Date.now() });
|
|
const J = newAgentIdentity(); const ses = Array.from({ length: 8 }, () => registruBrut(J, 1));
|
|
const cap = (L, id, h) => mQ.primeste(M.cerereMartor({ ...L, entries: [{ seq: 0, hash: h || L.entries[0].hash }] }, id, { createdAt: iso(Date.now()) }));
|
|
const coduri = [];
|
|
for (let i = 0; i < 5; i++) coduri.push(cap(ses[i], J).cod); // J1..J4 umplu ciobul 0; J5 roteste in ciobul 1
|
|
for (let i = 0; i < 4; i++) coduri.push(cap(ses[i], J, hx()).cod); // dovezile lui J1..J4 (ciobul curent: 1)
|
|
for (let i = 5; i < 8; i++) coduri.push(cap(ses[i], J).cod); // J6..J8: ciobul 1 plin
|
|
for (let i = 4; i < 8; i++) coduri.push(cap(ses[i], J, hx()).cod); // dovezile lui J5..J8
|
|
const pline = mQ.sanatate().equivocations;
|
|
await tur(mQ, { notarizeaza: notarMock(Math.floor(Date.now() / 1000) + 60) }); // capul final al ciobului 0 notarizat: rotirea il poate scoate
|
|
const C2 = newAgentIdentity(); const LC2 = registruBrut(C2, 1);
|
|
const c0 = cap(LC2, C2).cod; const ce = cap(LC2, C2, hx());
|
|
const lista = mQ.capeteAgent(C2.agentId, LC2.session); const h = mQ.sanatate();
|
|
const vc = M.verificaCapeteMartorului(LC2, lista);
|
|
mQ.inchide(); mQ = M.deschideMartor(DQ, { maxFrunze: 4, maxEchivocari: 8, ceas: () => Date.now() });
|
|
const h2 = mQ.sanatate(); const dv = ce.corp && ce.corp.proofId ? mQ.dovadaEchivocarii(ce.corp.proofId) : { cod: 0 }; mQ.inchide();
|
|
return [pline === 8 && c0 === 202 && ce.cod === 409 && ce.corp.kept === true && lista.equivocations.length === 1 && vc.stare === 'FAILED' && h.equivocationsNotKept && h.equivocationsNotKept.cap === 0
|
|
&& h2.equivocations === h.equivocations && dv.cod === 200 && M.verificaEchivocare(dv.corp.proof).ok,
|
|
JSON.stringify([coduri.join(','), pline, c0, ce.cod, ce.corp && ce.corp.kept, lista.equivocations, vc.stare, h.equivocations, h.equivocationsNotKept, h2.equivocations, dv.cod])];
|
|
});
|
|
await rand('10.P2-02 CONTROL: plafonul dovezilor e marginea lor prin constructie: cu maxFrunze 4 implicit 8 (CIOBURI_PASTRATE x maxFrunze), pe starea reala 40.000', async () => {
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-02b-')), { maxFrunze: 4 }); const h = mm.sanatate(); mm.inchide();
|
|
return [h.maxEquivocations === 8 && M.ECHIVOCARI_TOTAL === M.CIOBURI_PASTRATE * M.PLAFON_FRUNZE && M.ECHIVOCARI_TOTAL === 40000, JSON.stringify([h.maxEquivocations, M.ECHIVOCARI_TOTAL])];
|
|
});
|
|
|
|
// ---- 10.R2-03: la plafonul de chei al limitatorului, un client NOU nu e tinut afara (se scoate cheia cea mai veche)
|
|
await rand('10.R2-03 limitatorul la plafonul de chei (5): un client NOU nu e limitat, harta ramane la 5, cheia cea mai veche e scoasa (numarata); prin HTTP, dupa 5 /64-uri, un client nou primeste 200 la /health (nu 429), iar /health numara cheile scoase', async () => {
|
|
let t = 1_000_000; const l = S.limitator({ cereri: 5, ms: 600_000 }, { maxChei: 5, ceas: () => t });
|
|
for (let i = 0; i < 5; i++) l('c' + i);
|
|
const nou = l('nou'); const marime = l.marime(); const scoase = typeof l.scoase === 'function' ? l.scoase() : null; const vechiInapoi = l('c0');
|
|
const mL = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-03-')), { ceas: () => Date.now() }); const srvL = await porneste(mL, { limite: LIMITE, maxChei: 5 }); const UL = `http://127.0.0.1:${srvL.port}`;
|
|
for (let i = 0; i < 5; i++) await get('/v1/agents/witness/health', UL, `2001:db8:3:${i + 1}::1`);
|
|
const hn = await get('/v1/agents/witness/health', UL, '2001:db8:3:99::1');
|
|
await srvL.close(); mL.inchide();
|
|
const lim = hn.j && hn.j.limiters && hn.j.limiters.getPerClient;
|
|
return [nou === false && marime === 5 && scoase === 1 && vechiInapoi === false && hn.cod === 200 && !!lim && lim.keys <= 5 && lim.droppedAtCap >= 1, JSON.stringify([nou, marime, scoase, hn.cod, lim])];
|
|
});
|
|
|
|
// ---- 10.R2-06: arborele incremental (fara memorie pe interval): aceleasi radacini si dovezi ca merkleTree, si memoria unei stari cu un cap de jurnal pe frunza
|
|
await rand('10.R2-06 arborele incremental al martorului = merkleTree (RFC 9162) pe toate marimile 1..70 (radacina, includerea fiecarei frunze, verificata de verifyInclusion) si pe toate perechile de marimi 1..40 (consistenta, verificata de verifyConsistency)', async () => {
|
|
const sursa = (fs.readFileSync(path.join(AICI, 'martor-agenti.mjs'), 'utf8').match(/from '(\.\.\/[^']+\/arbore-merkle\.mjs)'/) || [])[1] || '../verify-layer/arbore-merkle.mjs';
|
|
const { merkleTree, verifyInclusion } = await import(pathToFileURL(path.resolve(AICI, sursa)).href);
|
|
const fr = Array.from({ length: 70 }, (_, i) => M.frunzaMartor(sha('f' + i), i)); const a = M.arboreIncremental(); let rele = 0, n = 0;
|
|
for (let s = 1; s <= 70; s++) {
|
|
a.adauga(fr[s - 1]); const r = merkleTree(fr.slice(0, s));
|
|
if (a.root() !== r.root()) rele++;
|
|
for (let i = 0; i < s; i++) { n++; const p = a.inclusionProof(i); if (JSON.stringify(p) !== JSON.stringify(r.inclusionProof(i)) || !verifyInclusion(p)) rele++; }
|
|
if (s <= 40) for (let m1 = 1; m1 <= s; m1++) { n++; const c = a.consistencyProof(m1, s); if (JSON.stringify(c) !== JSON.stringify(r.consistencyProof(m1, s)) || !M.verifyConsistency(c)) rele++; }
|
|
}
|
|
return [rele === 0 && n > 3000, `${rele} diferente din ${n} comparatii`];
|
|
});
|
|
await rand('10.R2-06 o stare cu un cap de jurnal PE FRUNZA (250 de frunze, 250 de capete de jurnal), redeschisa intr-un proces nou, tine sub 1.500 de octeti de heap pe frunza (inainte de reparatie: ~2.300, memoria pe interval a arborelui)', async () => {
|
|
const fc = path.join(T, 'r2-06-copil.mjs');
|
|
fs.writeFileSync(fc, `import fs from 'node:fs'; import path from 'node:path'; import { pathToFileURL } from 'node:url';
|
|
const M = await import(pathToFileURL(${JSON.stringify(path.join(AICI, 'martor-agenti.mjs'))}).href); const L = await import(pathToFileURL(${JSON.stringify(path.join(AICI, 'agent-ledger.mjs'))}).href);
|
|
const dir = process.argv[2]; const N = 250; fs.fsyncSync = () => {}; let t = Date.now(); const m = M.deschideMartor(dir, { ceas: () => t });
|
|
const ids = Array.from({ length: 10 }, () => L.newAgentIdentity()); const ph = '0x' + 'ab'.repeat(32);
|
|
const leds = ids.map((id) => ({ id, led: { agentId: id.agentId, session: L.sessionId(id.agentId, ph), policyHash: ph, entries: [] } }));
|
|
for (let s = 0; s < N; s++) { const X = leds[s % 10]; const q = X.led.entries.length; X.led.entries.push({ seq: q, hash: (s + 1).toString(16).padStart(64, '0') }); t += 10;
|
|
const r = m.primeste(M.cerereMartor(X.led, X.id, { seq: q, previous: q ? q - 1 : null, createdAt: new Date(t).toISOString() })); if (r.cod !== 202) throw new Error('cap ' + s); m.construiestePunct(); }
|
|
m.inchide(); globalThis.gc(); globalThis.gc(); const h0 = process.memoryUsage().heapUsed;
|
|
const m2 = M.deschideMartor(dir, { ceas: () => t }); globalThis.gc(); globalThis.gc(); const h1 = process.memoryUsage().heapUsed; const s = m2.sanatate(); m2.inchide();
|
|
console.log(JSON.stringify({ checkpoints: s.checkpoints, perLeaf: Math.round((h1 - h0) / N) }));\n`);
|
|
const c = spawnSync(process.execPath, ['--expose-gc', fc, fs.mkdtempSync(path.join(T, 'r2-06-'))], { encoding: 'utf8', timeout: 120000 });
|
|
let o = null; try { o = JSON.parse(c.stdout.trim().split('\n').pop()); } catch { o = null; }
|
|
return [c.status === 0 && o && o.checkpoints === 250 && o.perLeaf < 1500, `cod ${c.status} | ${c.stdout.trim().slice(0, 200)} | ${c.stderr.slice(0, 200)}`];
|
|
});
|
|
|
|
// ---- 10.R2-07: un ciob nu se scoate pana nu i s-a notarizat capul de jurnal final
|
|
await rand('10.R2-07 maxFrunze 2, notarizarea cade mereu: capul care ar scoate ciobul 0 (nenotarizat) -> 503 log_busy "notarization_behind" (nu 202), capul din ciobul 0 se serveste mai departe (200, nu 404), /health spune ca rotirea asteapta; dupa notarizare acelasi cap -> 202, iar chitanta luata dupa notarizare e "notarized"', async () => {
|
|
const mR7 = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-07-')), { maxFrunze: 2, ceas: () => Date.now() });
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 6); const c = (s) => mR7.primeste(M.cerereMartor(L, id, { seq: s, previous: s ? s - 1 : null, createdAt: iso(Date.now()) }));
|
|
const cade = async () => ({ ok: false, cod: 'unfunded', motiv: 'unfunded' });
|
|
const r0 = c(0); c(1); await tur(mR7, { notarizeaza: cade }); c(2); await tur(mR7, { notarizeaza: cade }); c(3);
|
|
const r4 = c(4); const g0 = mR7.stareaCapului(r0.corp.id); const h = mR7.sanatate();
|
|
await tur(mR7, { notarizeaza: notarMock(Math.floor(Date.now() / 1000) + 60) });
|
|
const g0b = mR7.stareaCapului(r0.corp.id); const r4b = c(4); const g0c = mR7.stareaCapului(r0.corp.id); mR7.inchide();
|
|
return [r4.cod === 503 && r4.corp.code === 'notarization_behind' && g0.cod === 200 && h.logBusy && h.logBusy.rotationWaitsForNotarization === true && g0b.corp.status === 'notarized' && r4b.cod === 202 && g0c.cod === 404,
|
|
JSON.stringify([r4.cod, r4.corp.code || r4.corp.status, g0.cod, h.logBusy, g0b.corp.status, r4b.cod, g0c.cod])];
|
|
});
|
|
await rand('10.P2-07 CONTROL: la deschidere, un ciob mai vechi decat cele pastrate al carui cap de jurnal final NU e notarizat ramane (cioburile 0, 1, 2), iar capul lui se serveste', async () => {
|
|
const DR7 = fs.mkdtempSync(path.join(T, 'r2-07b-')); let mm = M.deschideMartor(DR7, { maxFrunze: 2, faraLant: true, ceas: () => Date.now() });
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 5); const c = (s) => mm.primeste(M.cerereMartor(L, id, { seq: s, previous: s ? s - 1 : null, createdAt: iso(Date.now()) }));
|
|
const r0 = c(0); c(1); c(2); mm.construiestePunct();
|
|
const salv = path.join(T, 'r2-07b-ciob0'); fs.cpSync(path.join(DR7, 'cioburi', '0'), salv, { recursive: true });
|
|
c(3); c(4); mm.inchide();
|
|
for (let i = 0; i < 100 && fs.existsSync(path.join(DR7, 'cioburi', '0')); i++) await new Promise((g) => setTimeout(g, 100));
|
|
fs.cpSync(salv, path.join(DR7, 'cioburi', '0'), { recursive: true });
|
|
mm = M.deschideMartor(DR7, { maxFrunze: 2, ceas: () => Date.now() }); const h = mm.sanatate(); const g0 = mm.stareaCapului(r0.corp.id); mm.inchide();
|
|
return [JSON.stringify(h.shardsKept) === '[0,1,2]' && g0.cod === 200, JSON.stringify([h.shardsKept, g0.cod])];
|
|
});
|
|
|
|
// ---- 10.R2-08: un ciob nou se naste intreg; un ciob fara ciob.json isi reface legatura
|
|
await rand('10.R2-08 rotirea cade la scrierea ciob.json a ciobului 1 (ENOSPC): niciun dosar cioburi/1 ramas pe jumatate; dupa repornire, ciobul 1 numeste capul final al ciobului 0 (previousShardFinal), si primul lui cap de jurnal la fel; un dosar .nou-* ramas se sterge la deschidere', async () => {
|
|
const DD = fs.mkdtempSync(path.join(T, 'r2-08-')); let mm = M.deschideMartor(DD, { maxFrunze: 2, ceas: () => Date.now() });
|
|
const capNou = () => { const id = newAgentIdentity(); return mm.primeste(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) })); };
|
|
capNou(); capNou();
|
|
const orig = fs.writeSync; let cazut = null;
|
|
fs.writeSync = function (fd, buf, ...r) { const s = typeof buf === 'string' ? buf : Buffer.from(buf).toString('utf8', r[0] ?? 0, Math.min((r[0] ?? 0) + 80, buf.length)); if (/aere-agent-witness-shard-1/.test(s) && /"k": 1/.test(s)) throw Object.assign(new Error('ENOSPC: no space left on device, write'), { code: 'ENOSPC' }); return orig.call(fs, fd, buf, ...r); };
|
|
try { capNou(); } catch (e) { cazut = e.code; } finally { fs.writeSync = orig; }
|
|
const ramas = fs.existsSync(path.join(DD, 'cioburi', '1')); mm.inchide();
|
|
fs.mkdirSync(path.join(DD, 'cioburi', '.nou-9-1-1', 'capete'), { recursive: true });
|
|
mm = M.deschideMartor(DD, { maxFrunze: 2, ceas: () => Date.now() }); capNou(); mm.construiestePunct();
|
|
const sh = mm.capulJurnalului().corp.shards; const p1 = mm.plicPunct({ k: 1, n: 0 }); mm.inchide();
|
|
const s0 = sh.find((x) => x.shard === 0) || {}, s1 = sh.find((x) => x.shard === 1) || {};
|
|
const nou = fs.readdirSync(path.join(DD, 'cioburi')).filter((f) => f.startsWith('.nou'));
|
|
return [cazut === 'ENOSPC' && ramas === false && !!s0.latest && s1.previousShardFinal === s0.latest && p1.statement.previous === s0.latest && nou.length === 0,
|
|
JSON.stringify([cazut, ramas, s0.latest, s1.previousShardFinal, p1.statement.previous, nou])];
|
|
});
|
|
await rand('10.P2-08 CONTROL: o stare in care ciobul 1 exista FARA ciob.json (o creare intrerupta a formei de dinainte) se deschide cu legatura refacuta din capul final al ciobului 0', async () => {
|
|
const DD = fs.mkdtempSync(path.join(T, 'r2-08b-')); let mm = M.deschideMartor(DD, { maxFrunze: 2, ceas: () => Date.now() });
|
|
const capNou = () => { const id = newAgentIdentity(); return mm.primeste(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) })); };
|
|
capNou(); capNou(); capNou(); const fin0 = mm.capulJurnalului(null, 0).corp; mm.inchide();
|
|
fs.rmSync(path.join(DD, 'cioburi', '1', 'ciob.json'));
|
|
mm = M.deschideMartor(DD, { maxFrunze: 2, ceas: () => Date.now() }); const sh = mm.capulJurnalului().corp.shards; mm.inchide();
|
|
const s0 = sh.find((x) => x.shard === 0) || {}, s1 = sh.find((x) => x.shard === 1) || {};
|
|
return [!!s0.latest && s1.previousShardFinal === s0.latest && fin0 && fin0.frozen === true, JSON.stringify([s0.latest, s1.previousShardFinal, fin0 && fin0.frozen])];
|
|
});
|
|
|
|
// ---- 10.R2-09: consistenta ciobului inghetat pana la capul lui final, prin ruta
|
|
await rand('10.R2-09 un monitor care tine capul de jurnal (ciobul 0, marimea 3): dupa rotire GET /checkpoint?shard=0&from=3 da capul FINAL al ciobului 0 (marimea 4, previous = cel tinut) cu o dovada de consistenta care verifica fata de radacina tinuta; ?shard=7 -> 404, ?shard=x -> 400', async () => {
|
|
const m9 = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-09-')), { maxFrunze: 4, ceas: () => Date.now() }); const s9 = await porneste(m9, { limite: LIMITE }); const U9 = `http://127.0.0.1:${s9.port}`;
|
|
const capNou = () => { const id = newAgentIdentity(); return m9.primeste(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) })); };
|
|
for (let i = 0; i < 3; i++) capNou(); const tinut = m9.construiestePunct();
|
|
capNou(); for (let i = 0; i < 2; i++) capNou(); m9.construiestePunct();
|
|
const g = await get('/v1/agents/witness/checkpoint?shard=0&from=3', U9), g7 = await get('/v1/agents/witness/checkpoint?shard=7', U9), gx = await get('/v1/agents/witness/checkpoint?shard=x', U9);
|
|
await s9.close(); m9.inchide();
|
|
const fin = g.j && g.j.latest && g.j.latest.statement; const cs = g.j && g.j.consistency;
|
|
const ok = g.cod === 200 && g.j.shard === 0 && g.j.frozen === true && fin && fin.treeSize === 4 && fin.previous === tinut.statementHash && cs
|
|
&& M.verifyConsistency({ firstSize: 3, secondSize: 4, firstRoot: tinut.root, secondRoot: fin.root, path: cs.path });
|
|
return [!!ok && g7.cod === 404 && gx.cod === 400, JSON.stringify([g.cod, g.j && g.j.log, g.j && g.j.shard, fin && fin.treeSize, !!cs, g7.cod, gx.cod])];
|
|
});
|
|
|
|
// ---- 10.R2-10: lacatul ramas dupa o oprire necurata, cu pid-ul luat de alt proces viu
|
|
await rand('10.R2-10 lacatTinut: forma veche (numai pid, viu) -> tinut; scris inainte de ultima pornire a masinii -> netinut; acelasi boot, pid viu, alt moment de pornire al procesului -> netinut; acelasi moment -> tinut; pid mort -> netinut; "up:" la 100 s -> aceeasi pornire, la 1.000 s -> alta', async () => {
|
|
const t = (o, x = {}) => M.lacatTinut(typeof o === 'string' ? o : JSON.stringify(o), { propriu: 1, viu: () => true, pornire: () => '500', boot: 'linux:aaaa', ...x }).tinut;
|
|
const r = [t('123'), t({ pid: 123, boot: 'linux:bbbb', start: '500' }), t({ pid: 123, boot: 'linux:aaaa', start: '499' }), t({ pid: 123, boot: 'linux:aaaa', start: '500' }), t({ pid: 123, boot: 'linux:aaaa' }, { viu: () => false }),
|
|
t({ pid: 123, boot: 'up:1000', start: null }, { boot: 'up:1100' }), t({ pid: 123, boot: 'up:1000', start: null }, { boot: 'up:2000' })];
|
|
return [r.join(',') === 'true,false,false,true,false,true,false', r.join(',')];
|
|
});
|
|
await rand('10.R2-10 deschideMartor pe o stare al carei lacat (pid-ul unui proces VIU, alt program) e scris inainte de ultima pornire a masinii -> deschisa; CONTROL: acelasi pid, pornirea de acum -> refuz "held by process"', async () => {
|
|
const DL = fs.mkdtempSync(path.join(T, 'r2-10-')); M.deschideMartor(DL).inchide();
|
|
fs.writeFileSync(path.join(DL, '.lacat'), JSON.stringify({ pid: process.ppid, boot: 'up:1', start: null }));
|
|
let e1 = null; try { M.deschideMartor(DL).inchide(); } catch (e) { e1 = e.message; }
|
|
fs.writeFileSync(path.join(DL, '.lacat'), JSON.stringify({ pid: process.ppid, boot: M.idPornire(), start: M.pornireProces(process.ppid) }));
|
|
let e2 = null; try { M.deschideMartor(DL).inchide(); } catch (e) { e2 = e.message; }
|
|
return [e1 === null && /held by process/.test(String(e2)), `${e1} | ${e2}`];
|
|
});
|
|
|
|
// ---- 10.R2-13: un cap refuzat de bugetul jurnalului nu consuma cota agentului
|
|
await rand('10.R2-13 cota pe agent 3, bugetul jurnalului plin: patru capete NOI -> 503,503,503,503 (nu 503,503,503,429); bugetul liber, acelasi agent -> 202', async () => {
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-13-')), { ceas: () => Date.now() });
|
|
const peAgent = S.limitator({ cereri: 3, ms: 600_000 }); let plin = true; const id = newAgentIdentity(); const L = registruBrut(id, 1); const coduri = [];
|
|
for (let i = 0; i < 4; i++) coduri.push(mm.primeste(M.cerereMartor(L, id, { createdAt: iso(Date.now() + i) }), { limitaAgent: peAgent, limitaGlobala: () => plin }).cod);
|
|
plin = false; const dupa = mm.primeste(M.cerereMartor(L, id, { createdAt: iso(Date.now() + 9) }), { limitaAgent: peAgent, limitaGlobala: () => plin }); mm.inchide();
|
|
return [coduri.join(',') === '503,503,503,503' && dupa.cod === 202, `${coduri.join(',')} | ${dupa.cod}`];
|
|
});
|
|
await rand('10.P2-13 CONTROL prin serviciu (bugetul 2 pe ora, cota pe agent 1): X seq 0 -> 202, X seq 1 -> 429 (nu consuma bugetul), Y -> 202, Z -> 503 log_busy', async () => {
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-13b-')), { ceas: () => Date.now() });
|
|
const sv = await porneste(mm, { limite: { postPeClient: { cereri: 100, ms: 600_000 }, postPeAgent: { cereri: 1, ms: 600_000 }, getPeClient: { cereri: 100, ms: 600_000 }, acceptatePeOra: { cereri: 2, ms: 3_600_000 } } }); const UV = `http://127.0.0.1:${sv.port}`;
|
|
const X = newAgentIdentity(), Y2 = newAgentIdentity(), Z = newAgentIdentity(); const LX2 = registruBrut(X, 2);
|
|
const c = [];
|
|
c.push((await postLa(UV, M.cerereMartor(LX2, X, { seq: 0, createdAt: iso(Date.now()) }), 'r2-13')).cod);
|
|
c.push((await postLa(UV, M.cerereMartor(LX2, X, { seq: 1, previous: 0, createdAt: iso(Date.now()) }), 'r2-13')).cod);
|
|
c.push((await postLa(UV, M.cerereMartor(registruBrut(Y2, 1), Y2, { createdAt: iso(Date.now()) }), 'r2-13')).cod);
|
|
c.push((await postLa(UV, M.cerereMartor(registruBrut(Z, 1), Z, { createdAt: iso(Date.now()) }), 'r2-13')).cod);
|
|
await sv.close(); mm.inchide();
|
|
return [c.join(',') === '202,429,202,503', c.join(',')];
|
|
});
|
|
|
|
// ---- 10.R2-14: usa gateway-ului lasa sa treaca Retry-After al lui 503 log_busy
|
|
const GW2 = process.env.AERE_GATEWAY || path.resolve(AICI, '..', '..', 'cloud-gateway', 'gateway.mjs');
|
|
if (!fs.existsSync(GW2)) console.log(` (10.R2-14 usa gateway-ului Aere Cloud: gateway.mjs nu face parte din aceasta copie; se masoara in depozitul de dezvoltare)`);
|
|
else {
|
|
await rand('10.R2-14 prin usa gateway-ului: 503 log_busy (bugetul jurnalului plin) poarta Retry-After: 300, ca direct la martor', async () => {
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r2-14-')), { ceas: () => Date.now() });
|
|
const sv = await porneste(mm, { limite: { ...LIMITE, acceptatePeOra: { cereri: 1, ms: 3_600_000 } } });
|
|
const port = await new Promise((g) => { const s = net.createServer(); s.listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => g(p)); }); });
|
|
const c = spawn(process.execPath, [GW2], { env: { ...process.env, PORT: String(port), DIR_METRICA: fs.mkdtempSync(path.join(T, 'r2-14gw-')), RPC_URL: 'http://127.0.0.1:9', AERE_AGENTS_WITNESS_PORT: String(sv.port) }, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
let out = ''; c.stdout.on('data', (x) => { out += x; }); c.stderr.on('data', (x) => { out += x; });
|
|
try {
|
|
for (let i = 0; i < 100 && !/aere-cloud-gateway pe/.test(out); i++) await new Promise((g) => setTimeout(g, 100));
|
|
const capNou = () => { const id = newAgentIdentity(); return JSON.stringify(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) })); };
|
|
const p1 = await fetch(`http://127.0.0.1:${port}/v1/agents/witness`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: capNou() });
|
|
const p2 = await fetch(`http://127.0.0.1:${port}/v1/agents/witness`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: capNou() });
|
|
const j2 = await p2.json().catch(() => null);
|
|
return [p1.status === 202 && p2.status === 503 && j2 && j2.error === 'log_busy' && p2.headers.get('retry-after') === '300', JSON.stringify([p1.status, p2.status, j2 && j2.error, p2.headers.get('retry-after')]) + out.slice(0, 120)];
|
|
} finally { c.kill(); await sv.close(); mm.inchide(); }
|
|
});
|
|
}
|
|
|
|
// ---- 10.R2-15 (1): jumatatea "radacina" a paznicului de la deschidere, cu proba ei
|
|
await rand('10.R2-15 o stare cu o frunza din mijloc inlocuita de alt rand VALID (aceeasi pozitie, alt moment de primire, capul ei pe disc) sub un cap de jurnal semnat peste 3 frunze -> deschiderea refuza (radacina nu se reface)', async () => {
|
|
const DS = fs.mkdtempSync(path.join(T, 'r2-15-')); const mm = M.deschideMartor(DS, { ceas: () => Date.now() }); const id = newAgentIdentity(); const L = registruBrut(id, 3);
|
|
for (let s = 0; s < 3; s++) mm.primeste(M.cerereMartor(L, id, { seq: s, previous: s ? s - 1 : null, createdAt: iso(Date.now()) })); mm.construiestePunct(); mm.inchide();
|
|
const f = frunzeCiob(DS); const r = fs.readFileSync(f, 'utf8').split('\n'); const o = JSON.parse(r[1]); o.r += 1; r[1] = JSON.stringify(o); fs.writeFileSync(f, r.join('\n'));
|
|
let e = null; try { M.deschideMartor(DS, { ceas: () => Date.now() }).inchide(); } catch (x) { e = x.message; }
|
|
return [/do not give it/.test(String(e)) && /refusing to start/.test(String(e)), String(e)];
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------- 11. a treia revizie adversariala B3 (2026-10-19)
|
|
// Cate un rand pentru fiecare din cele 5 constatari confirmate ("11.R3-<nn>"), scris sa iasa RAU pe forma de dinainte (c34b34f1b); "11.P3-<nn>"
|
|
// e controlul unui paznic nou.
|
|
async function revizia3() {
|
|
const capNouIn = (mm) => { const id = newAgentIdentity(); return mm.primeste(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) })); };
|
|
const notarAcum = () => notarMock(Math.floor(Date.now() / 1000) + 60);
|
|
// o stare cu cioburile 0 si 1 pline si notarizate (maxFrunze 2), apoi capul care roteste in 2 si scoate ciobul 0, cu stergerea asincrona OPRITA
|
|
// (fs.rm nu face nimic): ce ramane pe disc cand procesul e oprit in timpul stergerii
|
|
const stareCuStergereOprita = async (DS) => {
|
|
const mm = M.deschideMartor(DS, { maxFrunze: 2, ceas: () => Date.now() });
|
|
capNouIn(mm); capNouIn(mm); await tur(mm, { notarizeaza: notarAcum() }); capNouIn(mm); capNouIn(mm); await tur(mm, { notarizeaza: notarAcum() });
|
|
const rm = fs.rm; fs.rm = (p, o, cb) => { if (typeof o === 'function') o(null); else if (cb) cb(null); };
|
|
let r; try { r = capNouIn(mm); } finally { fs.rm = rm; }
|
|
mm.inchide();
|
|
const dc = path.join(DS, 'cioburi');
|
|
const rest = ['0', ...fs.readdirSync(dc).filter((f) => f.startsWith('.sters-0-'))].map((f) => path.join(dc, f)).find((d) => fs.existsSync(d));
|
|
return { cod: r.cod, dc, rest };
|
|
};
|
|
const jumatateDinCapete = (d) => { const c = path.join(d, 'capete'); const l = fs.readdirSync(c); l.slice(0, Math.ceil(l.length / 2)).forEach((f) => fs.rmSync(path.join(c, f))); };
|
|
// dupa redeschidere: ciobul curent se umple si o rotire noua (notarul merge) primeste 202, nu 503 pentru totdeauna
|
|
const merge = async (DS) => {
|
|
let e = null, mm = null; try { mm = M.deschideMartor(DS, { maxFrunze: 2, ceas: () => Date.now() }); } catch (x) { e = x.message; }
|
|
if (!mm) return { e };
|
|
const h = mm.sanatate(); capNouIn(mm); await tur(mm, { notarizeaza: notarAcum() }); const r = capNouIn(mm); mm.inchide();
|
|
return { e, shardsKept: h.shardsKept, cod: r.cod, code: r.corp && r.corp.code, sters: fs.readdirSync(path.join(DS, 'cioburi')).filter((f) => f.startsWith('.sters-0-')).length };
|
|
};
|
|
|
|
// ---- 11.R3-01: un ciob scos de rotire nu mai poate ramane pe disc drept un ciob: se redenumeste intai, apoi se sterge
|
|
await rand('11.R3-01 o oprire in timpul stergerii asincrone a ciobului scos, cu restul in ORICE ordine (aici ciob.json si frunze.ndjson ramase, puncte/ sters, capete/ pe jumatate): la redeschidere restul nu e luat drept un ciob (cioburile 1 si 2), martorul porneste, iar rotirea urmatoare primeste 202', async () => {
|
|
const DS = fs.mkdtempSync(path.join(T, 'r3-01-')); const s = await stareCuStergereOprita(DS);
|
|
if (s.rest) { fs.rmSync(path.join(s.rest, 'puncte'), { recursive: true, force: true }); jumatateDinCapete(s.rest); }
|
|
const o = await merge(DS);
|
|
return [s.cod === 202 && !!s.rest && !o.e && JSON.stringify(o.shardsKept) === '[1,2]' && o.cod === 202 && o.sters === 0, JSON.stringify([s.cod, s.rest && path.basename(s.rest), o])];
|
|
});
|
|
await rand('11.P3-01 CONTROL: un REST al formei de dinainte, ramas pe loc in cioburi/0 fara marcaj (ciob.json si frunze.ndjson sterse primele, puncte/ gol, capete/ pe jumatate) -> sters la deschidere (cioburile 1 si 2), iar rotirea urmatoare primeste 202, nu 503 notarization_behind', async () => {
|
|
const DS = fs.mkdtempSync(path.join(T, 'r3-01b-')); const s = await stareCuStergereOprita(DS);
|
|
const d0 = path.join(s.dc, '0'); if (s.rest && s.rest !== d0) fs.renameSync(s.rest, d0);
|
|
for (const f of ['ciob.json', 'frunze.ndjson', '.de-sters']) fs.rmSync(path.join(d0, f), { force: true });
|
|
fs.rmSync(path.join(d0, 'puncte'), { recursive: true, force: true }); fs.mkdirSync(path.join(d0, 'puncte')); jumatateDinCapete(d0);
|
|
const o = await merge(DS);
|
|
return [s.cod === 202 && !o.e && JSON.stringify(o.shardsKept) === '[1,2]' && o.cod === 202, JSON.stringify([s.cod, o])];
|
|
});
|
|
|
|
// ---- 11.R3-02: o dovada contra unui cap din ciobul VECHI nu tine locul dovezii care conteaza
|
|
await rand('11.R3-02 agentul X echivoca intai contra capului lui din ciobul 0 (pastrata), apoi contra capului din ciobul 1 -> a doua ia locul primei (kept, replaced); dupa rotirea care scoate ciobul 0 sesiunea are tot o dovada, iar verificarea listei iese FAILED "echivocare"', async () => {
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r3-02-')), { maxFrunze: 4, faraLant: true, ceas: () => Date.now() });
|
|
const X = newAgentIdentity(); const L = registruBrut(X, 2);
|
|
const c = (led, seq, prev) => mm.primeste(M.cerereMartor(led, X, { seq, previous: prev, createdAt: iso(Date.now()) }));
|
|
const a0 = c(L, 0, null); for (let i = 0; i < 3; i++) capNouIn(mm); const a1 = c(L, 1, 0);
|
|
const b0 = c({ ...L, entries: [{ seq: 0, hash: hx() }] }, 0, null);
|
|
const b1 = c({ ...L, entries: [L.entries[0], { seq: 1, hash: hx() }] }, 1, 0);
|
|
for (let i = 0; i < 4; i++) capNouIn(mm); // ciobul 1 plin, apoi rotirea in 2 scoate ciobul 0
|
|
const pag = mm.capeteAgent(X.agentId, L.session); const h = mm.sanatate(); mm.inchide();
|
|
const vc = M.verificaCapeteMartorului(L, pag);
|
|
return [a0.cod === 202 && a1.cod === 202 && b0.cod === 409 && b0.corp.kept === true && b1.cod === 409 && b1.corp.kept === true && b1.corp.replaced === b0.corp.proofId
|
|
&& JSON.stringify(h.shardsKept) === '[1,2]' && pag.equivocations.length === 1 && vc.stare === 'FAILED',
|
|
JSON.stringify([a0.cod, a1.cod, b0.cod, b0.corp.kept, b1.cod, b1.corp.kept, b1.corp.replaced === b0.corp.proofId, h.shardsKept, pag.equivocations.length, vc.stare])];
|
|
});
|
|
|
|
// ---- 11.R3-03: un steag dat fara valoare, sau cu valoarea goala, e folosire gresita
|
|
const Q3 = newAgentIdentity(); const pQ3 = pol(Q3); const LQ3 = registru(Q3, pQ3, [Math.floor(Date.now() / 1000) - 100]);
|
|
const fp3 = scrie('r3-03-policy.json', pQ3), fl3 = scrie('r3-03-ledger.json', LQ3.export());
|
|
const m3 = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r3-03-')), { ceas: () => Date.now() });
|
|
const r3 = m3.primeste(M.cerereMartor(LQ3.export(), Q3, { createdAt: iso(Date.now()) })); await tur(m3, { notarizeaza: notarAcum() });
|
|
const fr3 = scrie('r3-03-receipt.json', m3.stareaCapului(r3.corp.id).corp); m3.inchide();
|
|
await rand('11.R3-03 agent-cli verify cu --witness-url sau --policy-hash puse ULTIMELE (fara valoare) si cu --revocations "" -> 2 (nu 0, cu paza scoasa); martor-agenti verify-receipt --ledger "" si --ledger pus ultimul -> 2; CONTROL: aceleasi comenzi cu valori bune -> 0', async () => {
|
|
const v = (...extra) => spawnSync(process.execPath, [CLI, 'verify', '--ledger', fl3, '--policy', fp3, ...extra], { encoding: 'utf8', env: { ...process.env, AERE_VERIFY_PROOF: '' } }).status;
|
|
const vr = (...extra) => spawnSync(process.execPath, [path.join(AICI, 'martor-agenti.mjs'), 'verify-receipt', '--receipt', fr3, ...extra], { encoding: 'utf8' }).status;
|
|
const c = [v('--witness-url'), v('--policy-hash'), v('--revocations', ''), vr('--ledger', ''), vr('--ledger'), v(), v('--policy-hash', pQ3.policyHash), vr('--ledger', fl3)];
|
|
return [c.join(',') === '2,2,2,2,2,0,0,0', c.join(',')];
|
|
});
|
|
|
|
// ---- 11.R3-04: sub --witness-url, marginea intrarilor de dupa ultimul cap tinut o da chitanta LUI, nu cea aleasa de agent
|
|
// un verificator AIP-23 inlocuit care ia momentul din notarizarea plicului (notarul-mock): fiecare chitanta cu momentul ei
|
|
const fStub2 = path.join(F5, 'verify-stub-notar.mjs');
|
|
fs.writeFileSync(fStub2, `import fs from 'node:fs'; const e = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); const t = Number(e.notarization && e.notarization.firstSeen);
|
|
console.log(JSON.stringify({ verdict: 'VALID', statementHash: e.statementHash, levels: [{ level: 'form', state: 'PASSED', detail: 'x' }, { level: 'integrity', state: 'PASSED', detail: 'x' }, { level: 'signature', state: 'PASSED', detail: 'x' },
|
|
{ level: 'finality', state: 'PASSED', detail: 'post-quantum: records this statementHash as first seen at ' + new Date(t * 1000).toISOString() + ' (' + t + ')' }] }));\n`);
|
|
const acum4 = Math.floor(Date.now() / 1000); const T4 = acum4 - 4000, C2 = acum4 - 10;
|
|
let w4 = T4 * 1000; const m4 = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r3-04-')), { ceas: () => w4 }); const s4 = await porneste(m4, { limite: LIMITE }); const U4 = `http://127.0.0.1:${s4.port}`;
|
|
try {
|
|
const Z = newAgentIdentity(); const pZ = pol(Z); const LZ = registru(Z, pZ, [T4, T4 + 5, T4 + 10]); // capul seq 2 se semneaza abia la C2
|
|
const z0 = m4.primeste(M.cerereMartor(LZ.export(), Z, { seq: 0, createdAt: iso(w4) })); await tur(m4, { notarizeaza: notarMock(T4 + 60) });
|
|
w4 = C2 * 1000; const z2 = m4.primeste(M.cerereMartor(LZ.export(), Z, { seq: 2, previous: 0, createdAt: iso(w4) })); await tur(m4, { notarizeaza: notarMock(C2 + 60) });
|
|
let ta = T4 + 30; const antZ = resumeLedger({ identity: Z, policy: pZ.policy, policyHash: pZ.policyHash, ledger: LZ.export(), now: () => ta }); antZ.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
const fpZ = scrie('r3-04-policy.json', pZ), flZ = scrie('r3-04-antedatat.json', antZ.export());
|
|
const fr0 = scrie('r3-04-r0.json', m4.stareaCapului(z0.corp.id).corp), fr02 = scrie('r3-04-r02.json', [m4.stareaCapului(z0.corp.id).corp, m4.stareaCapului(z2.corp.id).corp]);
|
|
const vZ = (anc) => ruleazaAsincron([CLI, 'verify', '--ledger', flZ, '--policy', fpZ, '--anchors', anc, '--rpc', 'http://127.0.0.1:9', '--chain', '28001', '--verify-proof', fStub2, '--witness', m4.witnessId, '--witness-url', U4], { AERE_VERIFY_PROOF: '' });
|
|
await rand('11.R3-04 martorul fixat tine capetele seq 0 si seq 2 (ultimul); intrarea 3, scrisa dupa seq 2, se declara la T0+30; agentul preda NUMAI chitanta lui seq 0 -> 1 "backdated" (chitanta ultimului cap e adusa de la --witness-url), ca si cu ambele chitante; pe forma veche: 0', async () => {
|
|
const numai0 = await vZ(fr0), ambele = await vZ(fr02);
|
|
return [z0.cod === 202 && z2.cod === 202 && numai0.status === 1 && /backdated/.test(numai0.stdout) && /taken from --witness-url/.test(numai0.stderr) && ambele.status === 1 && /backdated/.test(ambele.stdout),
|
|
`numai seq 0: ${numai0.status} ${numai0.stdout.slice(0, 160)} ${numai0.stderr.slice(-200)} | ambele: ${ambele.status}`];
|
|
});
|
|
await rand('11.P3-04 CONTROL: ultimul cap tinut de martor inca nenotarizat (pending) si fara chitanta in --anchors -> 2 NOT MEASURED (intrarile de dupa el nu sunt marginite de martor), nu 0', async () => {
|
|
const V = newAgentIdentity(); const pV = pol(V); const LV = registru(V, pV, [C2 - 20, C2 - 10]);
|
|
const v0 = m4.primeste(M.cerereMartor(LV.export(), V, { seq: 0, createdAt: iso(w4) })); await tur(m4, { notarizeaza: notarMock(C2 + 60) });
|
|
const v1 = m4.primeste(M.cerereMartor(LV.export(), V, { seq: 1, previous: 0, createdAt: iso(w4) }));
|
|
const r = await ruleazaAsincron([CLI, 'verify', '--ledger', scrie('r3-04b-ledger.json', LV.export()), '--policy', scrie('r3-04b-policy.json', pV), '--anchors', scrie('r3-04b-r0.json', m4.stareaCapului(v0.corp.id).corp), '--rpc', 'http://127.0.0.1:9', '--chain', '28001', '--verify-proof', fStub2, '--witness', m4.witnessId, '--witness-url', U4], { AERE_VERIFY_PROOF: '' });
|
|
return [v0.cod === 202 && v1.cod === 202 && r.status === 2 && /not bounded by the witness/.test(r.stderr), `${v0.cod} ${v1.cod} | ${r.status} ${r.stderr.slice(-240)}`];
|
|
});
|
|
// un cap tinut care nu e in registrul aratat, desi ULTIMUL cap tinut (si chitanta lui, adusa de la R3-04) este: numai lista il prinde
|
|
await rand('11.P3-04b CONTROL: agentul semneaza la martor seq 0 cu un hash strain si seq 1, seq 2 din registrul B (predecesorii semnati se potrivesc, chitanta ultimului cap e buna pentru B) -> verify B --witness-url iese 1 "[registru]" pe seq 0 (lista o prinde, nu chitanta)', async () => {
|
|
const Yb = newAgentIdentity(); const pYb = pol(Yb); const LB3 = registru(Yb, pYb, [C2 - 30, C2 - 20, C2 - 15]); const E = LB3.export(); const h0 = hx();
|
|
const amestec = (n) => ({ ...E, entries: [{ seq: 0, hash: h0 }, ...E.entries.slice(1, n)] });
|
|
const c0 = m4.primeste(M.cerereMartor(amestec(1), Yb, { seq: 0, createdAt: iso(w4) })).cod;
|
|
const c1 = m4.primeste(M.cerereMartor(amestec(2), Yb, { seq: 1, previous: 0, createdAt: iso(w4) })).cod;
|
|
const c2 = m4.primeste(M.cerereMartor(amestec(3), Yb, { seq: 2, previous: 1, createdAt: iso(w4) })).cod;
|
|
await tur(m4, { notarizeaza: notarMock(C2 + 60) });
|
|
const r = await ruleazaAsincron([CLI, 'verify', '--ledger', scrie('r3-04c-b.json', E), '--policy', scrie('r3-04c-policy.json', pYb), '--rpc', 'http://127.0.0.1:9', '--chain', '28001', '--verify-proof', fStub2, '--witness-url', U4], { AERE_VERIFY_PROOF: '' });
|
|
return [c0 === 202 && c1 === 202 && c2 === 202 && r.status === 1 && /\[registru\] the witness holds head seq 0/.test(r.stderr), `${c0} ${c1} ${c2} | ${r.status} ${r.stderr.slice(0, 240)}`];
|
|
});
|
|
} finally { await s4.close(); m4.inchide(); }
|
|
|
|
// ---- 11.R3-05: codurile lui receipt si submit pe raspunsuri care nu sunt ale martorului
|
|
const MA_ = path.join(AICI, 'martor-agenti.mjs');
|
|
await rand('11.R3-05 receipt la o usa care raspunde 200 cu o pagina HTML -> 2 (nu 1 cu o exceptie); submit pe drumul "nimic nou" cu starea capului 503 -> 2, iar --out nu e scris cu eroarea; CONTROL: aceeasi stare 200 -> 0, --out scris', async () => {
|
|
const html = await serverLocal(async (req, res) => { const b = Buffer.from('<html><body>edge</body></html>'); res.writeHead(200, { 'content-type': 'text/html', 'content-length': b.length }); res.end(b); });
|
|
// (de la a sasea revizie, N2, receipt cere --ledger)
|
|
const rh = await ruleazaAsincron([MA_, 'receipt', '--url', html.url, '--id', '0x' + '33'.repeat(32), '--ledger', scrie('r3-05-ledger-receipt.json', registruBrut(newAgentIdentity(), 1))]); await html.close();
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 1); const fk = path.join(F5, 'r3-05-agent.key.pem'); fs.writeFileSync(fk, id.privateKey.export({ type: 'pkcs8', format: 'pem' }));
|
|
const fl = scrie('r3-05-ledger.json', L); const idCap = '0x' + '22'.repeat(32);
|
|
const fals = (codStare) => serverLocal(async (req, res, u) => (/\/heads$/.test(u.pathname) ? trimite(res, 200, { latest: { seq: 0, hash: L.entries[0].hash, id: idCap }, heads: [], next: null })
|
|
: codStare === 200 ? trimite(res, 200, { status: 'notarized', id: idCap }) : trimite(res, codStare, { error: 'agents_witness_unavailable', hint: 'temporary' })));
|
|
const s503 = await fals(503); const fo1 = path.join(F5, 'r3-05-out-503.json'); const r1 = await ruleazaAsincron([MA_, 'submit', '--key', fk, '--ledger', fl, '--url', s503.url, '--out', fo1]); await s503.close();
|
|
const s200 = await fals(200); const fo2 = path.join(F5, 'r3-05-out-200.json'); const r2 = await ruleazaAsincron([MA_, 'submit', '--key', fk, '--ledger', fl, '--url', s200.url, '--out', fo2]); await s200.close();
|
|
return [rh.status === 2 && !/Cannot read/.test(rh.stderr) && r1.status === 2 && !fs.existsSync(fo1) && r2.status === 0 && fs.existsSync(fo2),
|
|
`receipt html ${rh.status} ${rh.stderr.slice(0, 120)} | submit 503 ${r1.status} out=${fs.existsSync(fo1)} | submit 200 ${r2.status} out=${fs.existsSync(fo2)}`];
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------- 12. a patra revizie adversariala B3 (2026-10-19)
|
|
// Cate un rand pentru fiecare din cele 6 constatari confirmate ("12.R4-<nn>"), RAU pe forma de dinainte (c5183bcb8); "12.P4-<nn>" e controlul unui
|
|
// paznic nou. Regula de proiectare (agent-cli.mjs, README): sub --witness-url ce aduce verificatorul de la adresa e MARTURIA martorului.
|
|
async function revizia4() {
|
|
const fStub4 = path.join(F5, 'verify-stub-notar-4.mjs'); // verificatorul AIP-23 inlocuit: momentul din notarizarea plicului (notarul-mock)
|
|
fs.writeFileSync(fStub4, `import fs from 'node:fs'; const e = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); const t = Number(e.notarization && e.notarization.firstSeen);
|
|
console.log(JSON.stringify({ verdict: 'VALID', statementHash: e.statementHash, levels: [{ level: 'form', state: 'PASSED', detail: 'x' }, { level: 'integrity', state: 'PASSED', detail: 'x' }, { level: 'signature', state: 'PASSED', detail: 'x' },
|
|
{ level: 'finality', state: 'PASSED', detail: 'post-quantum: records this statementHash as first seen at ' + new Date(t * 1000).toISOString() + ' (' + t + ')' }] }));\n`);
|
|
const capNouIn = (mm) => { const id = newAgentIdentity(); return mm.primeste(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) })); };
|
|
const cade = async () => ({ ok: false, cod: 'unfunded', motiv: 'unfunded' });
|
|
const verifica = (fl, fp, anc, extra) => ruleazaAsincron([CLI, 'verify', '--ledger', fl, '--policy', fp, ...(anc ? ['--anchors', anc] : []), '--rpc', 'http://127.0.0.1:9', '--chain', '28001', '--verify-proof', fStub4, ...extra], { AERE_VERIFY_PROOF: '' });
|
|
const acum = Math.floor(Date.now() / 1000);
|
|
|
|
// ---- 12.R4-01: o notarizare INTARZIATA a martorului (pana a notarului) nu face dintr-un registru cinstit un FAILED
|
|
{
|
|
const T0 = acum - 7000, T1 = acum - 3700; let w = T0 * 1000;
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r4-01-')), { ceas: () => w }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L = registru(Y, pY, [T0, T0 + 5, T1 - 10]);
|
|
const h0 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 0, createdAt: iso(w) })); await tur(mm, { notarizeaza: notarMock(T0 + 30) });
|
|
w = T1 * 1000; const h2 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 2, previous: 0, createdAt: iso(w) }));
|
|
await tur(mm, { notarizeaza: cade }); await tur(mm, { notarizeaza: notarMock(T1 + 3600) }); // primul tur cade: notarizat la T1+3600
|
|
w = Date.now();
|
|
const fp = scrie('r4-01-policy.json', pY), fl = scrie('r4-01-ledger.json', L.export());
|
|
const f0 = scrie('r4-01-r0.json', mm.stareaCapului(h0.corp.id).corp), f02 = scrie('r4-01-r02.json', [mm.stareaCapului(h0.corp.id).corp, mm.stareaCapului(h2.corp.id).corp]);
|
|
await rand('12.R4-01 registru CINSTIT, ultimul cap tinut notarizat la 3600 s dupa primire (o pana a notarului): --witness W --witness-url cu chitanta lui seq 0 -> 2 NOT MEASURED, cu motivul [intarziere] (nu 1); aceeasi chitanta intarziata predata chiar de agent -> tot 2 [intarziere] (o intarziere a notarului nu e vina agentului; pana la a cincea revizie: 1); fara --witness-url -> 0', async () => {
|
|
const a1 = await verifica(fl, fp, f0, ['--witness', mm.witnessId, '--witness-url', UU]), a2 = await verifica(fl, fp, f02, ['--witness', mm.witnessId, '--witness-url', UU]), a0 = await verifica(fl, fp, f0, ['--witness', mm.witnessId]);
|
|
return [h0.cod === 202 && h2.cod === 202 && a1.status === 2 && /\[intarziere\]/.test(a1.stderr) && /NOT MEASURED/.test(a1.stderr) && a2.status === 2 && /\[intarziere\]/.test(a2.stderr) && a0.status === 0,
|
|
`adusa ${a1.status} ${a1.stderr.slice(-220)} | predata ${a2.status} | fara url ${a0.status}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 12.R4-02: un proxy care trece neatinse paginile semnate si strica numai GET-ul unui cap tinut: 2, nu 1, si nicio exceptie
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r4-02-')), { ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L = registru(Y, pY, [acum - 30, acum - 20, acum - 10]);
|
|
const h0 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 0, createdAt: iso(Date.now()) })); const h2 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 2, previous: 0, createdAt: iso(Date.now()) }));
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const fp = scrie('r4-02-policy.json', pY), fl = scrie('r4-02-ledger.json', L.export()), f0 = scrie('r4-02-r0.json', mm.stareaCapului(h0.corp.id).corp);
|
|
const rupt = clon(mm.stareaCapului(h0.corp.id).corp); rupt.receipt.witnessReceipt = {}; const fRupt = scrie('r4-02-rupt.json', rupt);
|
|
const proxy = (strica) => serverLocal(async (req, res, u) => {
|
|
const r = await fetch(UU + u.pathname + u.search, { headers: { 'x-real-ip': 'r4-02-proxy' } }); let j = await r.json();
|
|
if (u.pathname.endsWith(h2.corp.id)) j = strica(clon(j));
|
|
trimite(res, r.status, j);
|
|
});
|
|
const stricari = [['drum', (j) => { j.receipt.witnessReceipt.path = ['0x' + '00'.repeat(32)]; return j; }], ['fara-cap', (j) => { j.receipt.witnessReceipt = {}; return j; }], ['fara-chitanta', () => ({ status: 'notarized' })]];
|
|
await rand('12.R4-02 proxy la --witness-url care strica numai GET-ul capului seq 2 (o dovada de includere stricata, o chitanta fara cap, un {status: notarized} fara chitanta) -> 2 de fiecare data, fara exceptie si fara mesaj contradictoriu; o chitanta fara cap predata de AGENT -> 2 NOT MEASURED "[chitanta-forma]" (o forma gresita nu e o vina dovedita; pana la a cincea revizie: 1), fara exceptie', async () => {
|
|
const coduri = [];
|
|
for (const [nume, f] of stricari) { const p = await proxy(f); const r = await verifica(fl, fp, f0, ['--witness', mm.witnessId, '--witness-url', p.url]); await p.close(); coduri.push(`${nume}:${r.status}${/Cannot read/.test(r.stderr) ? '(exceptie)' : ''}${/notarized, not notarized/.test(r.stderr) ? '(contradictie)' : ''}`); }
|
|
const ag = await verifica(fl, fp, fRupt, ['--witness', mm.witnessId, '--witness-url', UU]);
|
|
return [h0.cod === 202 && h2.cod === 202 && coduri.join(',') === 'drum:2,fara-cap:2,fara-chitanta:2' && ag.status === 2 && /\[chitanta-forma\]/.test(ag.stderr) && /NOT MEASURED/.test(ag.stderr) && !/Cannot read/.test(ag.stderr),
|
|
`${coduri.join(',')} | agent ${ag.status} ${ag.stderr.slice(-160)}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 12.R4-03: FIECARE cap tinut, nu doar ultimul: un cap intermediar fara chitanta nu mai lasa intrarile de dupa el antedatate
|
|
{
|
|
const T0 = acum - 4000, C2 = acum - 400, C4 = acum - 30; let w = T0 * 1000;
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r4-03-')), { ceas: () => w }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Z = newAgentIdentity(); const pZ = pol(Z); const L = registru(Z, pZ, [T0, T0 + 5, T0 + 10]);
|
|
const h0 = mm.primeste(M.cerereMartor(L.export(), Z, { seq: 0, createdAt: iso(w) })); await tur(mm, { notarizeaza: notarMock(T0 + 60) });
|
|
w = C2 * 1000; const h2 = mm.primeste(M.cerereMartor(L.export(), Z, { seq: 2, previous: 0, createdAt: iso(w) })); await tur(mm, { notarizeaza: notarMock(C2 + 60) });
|
|
let ta = T0 + 30; const L3 = resumeLedger({ identity: Z, policy: pZ.policy, policyHash: pZ.policyHash, ledger: L.export(), now: () => ta }); L3.record({ kind: 'payment', to: '0xbbbb', amount: '10' }); // intrarea 3, antedatata
|
|
ta = acum - 50; const L4 = resumeLedger({ identity: Z, policy: pZ.policy, policyHash: pZ.policyHash, ledger: L3.export(), now: () => ta }); L4.record({ kind: 'payment', to: '0xbbbb', amount: '10' }); // intrarea 4, cinstita
|
|
w = C4 * 1000; const h4 = mm.primeste(M.cerereMartor(L4.export(), Z, { seq: 4, previous: 2, createdAt: iso(w) })); await tur(mm, { notarizeaza: notarMock(C4 + 60) });
|
|
w = Date.now();
|
|
const fp = scrie('r4-03-policy.json', pZ), fl = scrie('r4-03-ledger.json', L4.export());
|
|
const st = (h) => mm.stareaCapului(h.corp.id).corp;
|
|
const f0 = scrie('r4-03-r0.json', st(h0)), fGol = scrie('r4-03-gol.json', []), f024 = scrie('r4-03-r024.json', [st(h0), st(h2), st(h4)]);
|
|
await rand('12.R4-03 martorul tine capetele 0, 2, 4; intrarea 3, scrisa dupa capul 2, se declara la T0+30: cu NUMAI chitanta lui 0 -> 1 "backdated" (chitantele lui 2 si 4 aduse de la --witness-url); cu --anchors [] -> 1; CONTROL: cu toate trei -> 1', async () => {
|
|
const a = await verifica(fl, fp, f0, ['--witness', mm.witnessId, '--witness-url', UU]), b = await verifica(fl, fp, fGol, ['--witness-url', UU]), c = await verifica(fl, fp, f024, ['--witness', mm.witnessId, '--witness-url', UU]);
|
|
return [h0.cod === 202 && h2.cod === 202 && h4.cod === 202 && a.status === 1 && /backdated/.test(a.stdout) && b.status === 1 && /backdated/.test(b.stdout) && c.status === 1 && /backdated/.test(c.stdout),
|
|
`numai 0: ${a.status} | gol: ${b.status} ${b.stderr.slice(-160)} | toate: ${c.status}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 12.P4-03b: o chitanta ADUSA, verificata ca a martorului, al carei cap tinut numeste un predecesor care nu e intrare a registrului: FAILED
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r4-03b-')), { maxFrunze: 4, faraLant: true, ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Yb = newAgentIdentity(); const pYb = pol(Yb); const B3 = registru(Yb, pYb, [acum - 20, acum - 10]); const E = B3.export(); const h0 = hx();
|
|
const c0 = mm.primeste(M.cerereMartor({ ...E, entries: [{ seq: 0, hash: h0 }] }, Yb, { seq: 0, createdAt: iso(Date.now()) })).cod;
|
|
for (let i = 0; i < 3; i++) capNouIn(mm);
|
|
const c1 = mm.primeste(M.cerereMartor({ ...E, entries: [{ seq: 0, hash: h0 }, E.entries[1]] }, Yb, { seq: 1, previous: 0, createdAt: iso(Date.now()) })).cod;
|
|
for (let i = 0; i < 4; i++) capNouIn(mm); // ciobul 1 plin, rotirea in 2 scoate ciobul 0: martorul mai tine numai seq 1
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
await rand('12.P4-03b CONTROL: martorul mai tine numai seq 1 al registrului B, cu predecesorul semnat seq 0 = un hash strain (ciobul lui scos): lista trece, iar chitanta lui seq 1, adusa si verificata, arata ramura -> 1 FAILED (vina registrului), nu 2', async () => {
|
|
const r = await verifica(scrie('r4-03b-b.json', E), scrie('r4-03b-policy.json', pYb), null, ['--witness-url', UU]);
|
|
return [c0 === 202 && c1 === 202 && r.status === 1 && /\[registru\]/.test(r.stderr) && /another branch/.test(r.stderr), `${c0} ${c1} | ${r.status} ${r.stderr.slice(-240)}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 12.R4-04: submit pe drumul principal (POST): un 200/202 care nu e starea capului trimis
|
|
const MA_ = path.join(AICI, 'martor-agenti.mjs');
|
|
await rand('12.R4-04 submit cu POST-ul raspuns 200 sau 202 cu o pagina HTML, sau 202 JSON cu alta sesiune -> 2, iar --out nu e scris (nu 0 cu "null" in --out)', async () => {
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 1); const fk = path.join(F5, 'r4-04-agent.key.pem'); fs.writeFileSync(fk, id.privateKey.export({ type: 'pkcs8', format: 'pem' })); const fl = scrie('r4-04-ledger.json', L);
|
|
const rez = [];
|
|
for (const [nume, cod, corp] of [['200-html', 200, null], ['202-html', 202, null], ['202-alta-sesiune', 202, { status: 'pending', id: '0x' + '44'.repeat(32), session: 'ab'.repeat(16), seq: 0 }]]) {
|
|
const s = await serverLocal(async (req, res, u) => {
|
|
if (/\/heads$/.test(u.pathname)) return trimite(res, 200, { latest: null, heads: [], next: null });
|
|
if (corp) return trimite(res, cod, corp);
|
|
const b = Buffer.from('<html><body>edge</body></html>'); res.writeHead(cod, { 'content-type': 'text/html', 'content-length': b.length }); res.end(b);
|
|
});
|
|
const fo = path.join(F5, `r4-04-out-${nume}.json`); const r = await ruleazaAsincron([MA_, 'submit', '--key', fk, '--ledger', fl, '--url', s.url, '--out', fo]); await s.close();
|
|
rez.push(`${nume}:${r.status}:${fs.existsSync(fo) ? 'scris' : 'nescris'}`);
|
|
}
|
|
return [rez.join(',') === '200-html:2:nescris,202-html:2:nescris,202-alta-sesiune:2:nescris', rez.join(',')];
|
|
});
|
|
|
|
// ---- 12.R4-05: --flag=valoare, greseli de tastare si steaguri date de doua ori: folosire gresita (2), in ambele linii de comanda
|
|
{
|
|
const Q4 = newAgentIdentity(); const pQ4 = pol(Q4); const LQ4 = registru(Q4, pQ4, [acum - 100]);
|
|
const fp = scrie('r4-05-policy.json', pQ4), fl = scrie('r4-05-ledger.json', LQ4.export());
|
|
const m5 = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r4-05-')), { ceas: () => Date.now() }); const r5 = m5.primeste(M.cerereMartor(LQ4.export(), Q4, { createdAt: iso(Date.now()) })); await tur(m5, { notarizeaza: notarMock(acum + 60) });
|
|
const fr = scrie('r4-05-receipt.json', m5.stareaCapului(r5.corp.id).corp); const W5 = m5.witnessId; m5.inchide();
|
|
await rand('12.R4-05 agent-cli verify --witness-url=<URL>, --policy-hash=<h>, --revocation <f> (greseala de tastare), --ledger dat de doua ori; martor-agenti verify-receipt --witness=<id>, --ledger=<l>, --ledgr <l>, --ledger de doua ori -> 2 de fiecare data (nu 0); CONTROL: formele bune -> 0', async () => {
|
|
const v = (...a) => spawnSync(process.execPath, [CLI, 'verify', ...a], { encoding: 'utf8', env: { ...process.env, AERE_VERIFY_PROOF: '' } }).status;
|
|
const vr = (...a) => spawnSync(process.execPath, [MA_, 'verify-receipt', '--receipt', fr, ...a], { encoding: 'utf8' }).status;
|
|
const c = [v('--ledger', fl, '--policy', fp, '--witness-url=http://127.0.0.1:9'), v('--ledger', fl, '--policy', fp, '--policy-hash=' + pQ4.policyHash), v('--ledger', fl, '--policy', fp, '--revocation', fp),
|
|
v('--ledger', fl, '--policy', fp, '--ledger', fl), vr('--witness=' + W5), vr('--ledger=' + fl), vr('--ledgr', fl), vr('--ledger', fl, '--ledger', fl), v('--ledger', fl, '--policy', fp, '--policy-hash', pQ4.policyHash), vr('--witness', W5, '--ledger', fl)];
|
|
return [c.join(',') === '2,2,2,2,2,2,2,2,0,0', c.join(',')];
|
|
});
|
|
}
|
|
|
|
// ---- 12.R4-06: departajarea de la deschidere intre doua dovezi ale unei sesiuni, si marcajul .de-sters, fiecare cu randul lui
|
|
await rand('12.R4-06 o inlocuire de dovada intrerupta (dovada veche pusa la loc pe disc langa cea noua): la redeschidere ramane cea NOUA (a capului din ciobul mai nou), iar cea veche e stearsa; dupa rotirea care scoate ciobul 0 sesiunea are tot o dovada', async () => {
|
|
const D6 = fs.mkdtempSync(path.join(T, 'r4-06a-')); let mm = M.deschideMartor(D6, { maxFrunze: 4, faraLant: true, ceas: () => Date.now() });
|
|
const X = newAgentIdentity(); const L = registruBrut(X, 2); const c = (led, seq, prev) => mm.primeste(M.cerereMartor(led, X, { seq, previous: prev, createdAt: iso(Date.now()) }));
|
|
c(L, 0, null); for (let i = 0; i < 3; i++) capNouIn(mm); c(L, 1, 0);
|
|
const b0 = c({ ...L, entries: [{ seq: 0, hash: hx() }] }, 0, null);
|
|
const fis = (id) => ['echivocari-2', 'echivocari'].map((d) => path.join(D6, d, String(id).slice(2) + '.json')).find((f) => fs.existsSync(f));
|
|
const f0 = fis(b0.corp.proofId); const copie0 = f0 ? fs.readFileSync(f0) : null;
|
|
const b1 = c({ ...L, entries: [L.entries[0], { seq: 1, hash: hx() }] }, 1, 0); mm.inchide();
|
|
if (f0 && copie0) fs.writeFileSync(f0, copie0); // inlocuirea intrerupta: dovada veche ramasa pe disc
|
|
mm = M.deschideMartor(D6, { maxFrunze: 4, faraLant: true, ceas: () => Date.now() });
|
|
const eq1 = mm.capeteAgent(X.agentId, L.session).equivocations; const veche = !!fis(b0.corp.proofId);
|
|
for (let i = 0; i < 4; i++) capNouIn(mm); const eq2 = mm.capeteAgent(X.agentId, L.session).equivocations; mm.inchide();
|
|
return [b0.corp.kept === true && b1.corp.kept === true && eq1.length === 1 && eq1[0].id === b1.corp.proofId && !veche && eq2.length === 1, JSON.stringify([b1.corp.kept, eq1.map((q) => q.id === b1.corp.proofId), veche, eq2.length])];
|
|
});
|
|
await rand('12.R4-06 redenumirea ciobului scos cade (EPERM), deci dosarul e marcat .de-sters; stergerea e oprita, puncte/ si jumatate din capete/ sterse, ciob.json si frunze.ndjson ramase: la redeschidere restul e sters dupa marcaj (cioburile 1 si 2), iar rotirea urmatoare primeste 202', async () => {
|
|
const DS = fs.mkdtempSync(path.join(T, 'r4-06b-')); let mm = M.deschideMartor(DS, { maxFrunze: 2, ceas: () => Date.now() }); const notar = () => notarMock(Math.floor(Date.now() / 1000) + 60);
|
|
capNouIn(mm); capNouIn(mm); await tur(mm, { notarizeaza: notar() }); capNouIn(mm); capNouIn(mm); await tur(mm, { notarizeaza: notar() });
|
|
const rm = fs.rm, ren = fs.renameSync; const d0 = path.join(DS, 'cioburi', '0');
|
|
fs.rm = (p, o, cb) => { if (typeof o === 'function') o(null); else if (cb) cb(null); };
|
|
fs.renameSync = function (a, b) { if (path.resolve(String(a)) === path.resolve(d0)) throw Object.assign(new Error('EPERM: operation not permitted, rename'), { code: 'EPERM' }); return ren.call(fs, a, b); };
|
|
let r; try { r = capNouIn(mm); } finally { fs.rm = rm; fs.renameSync = ren; }
|
|
mm.inchide();
|
|
const marcat = fs.existsSync(path.join(d0, '.de-sters'));
|
|
fs.rmSync(path.join(d0, 'puncte'), { recursive: true, force: true }); const cp = path.join(d0, 'capete'); const l = fs.existsSync(cp) ? fs.readdirSync(cp) : []; l.slice(0, Math.ceil(l.length / 2)).forEach((f) => fs.rmSync(path.join(cp, f)));
|
|
let e = null; try { mm = M.deschideMartor(DS, { maxFrunze: 2, ceas: () => Date.now() }); } catch (x) { e = x.message; mm = null; }
|
|
let h = null, r2 = null; if (mm) { h = mm.sanatate(); capNouIn(mm); await tur(mm, { notarizeaza: notar() }); r2 = capNouIn(mm); mm.inchide(); }
|
|
return [r.cod === 202 && marcat && !e && JSON.stringify(h.shardsKept) === '[1,2]' && r2.cod === 202, JSON.stringify([r.cod, marcat, e, h && h.shardsKept, r2 && r2.cod])];
|
|
});
|
|
}
|
|
|
|
// ================================================================================================ 13. a cincea revizie adversariala B3
|
|
// (2026-10-19, 6 constatari R5-01..R5-06): randurile "13.R5-<nn>", RAU pe forma de dinainte (b55038c2a). `verify` e acum o EVALUARE COMPLETA
|
|
// urmata de UN SINGUR verdict: toate dovezile se aduna, fiecare primeste o clasa (VERIFICATA, VINA-REGISTRULUI, NEMASURATA), iar verdictul e
|
|
// FAILED daca exista o vina dovedita, altfel NOT MEASURED daca o dovada ceruta e nemasurata, altfel OK. Randurile "13.P5-<nn>" sunt controalele
|
|
// claselor (o vina a agentului ramane vina, un proxy nu poate face un FAILED); ordinea in care vin dovezile o rastoarna controlul negativ, care cere
|
|
// atunci proba verde intreaga.
|
|
async function revizia5() {
|
|
const fStub5 = path.join(F5, 'verify-stub-notar-5.mjs'); // verificatorul AIP-23 inlocuit: momentul din notarizarea plicului (notarul-mock)
|
|
fs.writeFileSync(fStub5, `import fs from 'node:fs'; const e = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); const t = Number(e.notarization && e.notarization.firstSeen);
|
|
console.log(JSON.stringify({ verdict: 'VALID', statementHash: e.statementHash, levels: [{ level: 'form', state: 'PASSED', detail: 'x' }, { level: 'integrity', state: 'PASSED', detail: 'x' }, { level: 'signature', state: 'PASSED', detail: 'x' },
|
|
{ level: 'finality', state: 'PASSED', detail: 'post-quantum: records this statementHash as first seen at ' + new Date(t * 1000).toISOString() + ' (' + t + ')' }] }));\n`);
|
|
// un verificator AIP-23 care da finalitatea FAILED din cauza RPC-ului ales de VERIFICATOR (un antet servit gresit), ca verify-proof.mjs real (R5-03)
|
|
const fStubF = path.join(F5, 'verify-stub-finalitate-picata-5.mjs');
|
|
fs.writeFileSync(fStubF, `import fs from 'node:fs'; const e = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
console.log(JSON.stringify({ verdict: 'INVALID', statementHash: e.statementHash, levels: [{ level: 'form', state: 'PASSED', detail: 'x' }, { level: 'integrity', state: 'PASSED', detail: 'x' }, { level: 'signature', state: 'PASSED', detail: 'x' },
|
|
{ level: 'finality', state: 'FAILED', detail: 'the header the endpoint served for block 9 does not hash to the hash anchor 10s certificate signed' }] }));\n`);
|
|
const verifica = (fl, fp, anc, extra, { stub = fStub5, env = {} } = {}) => ruleazaAsincron([CLI, 'verify', '--ledger', fl, '--policy', fp, ...(anc ? ['--anchors', anc] : []), '--rpc', 'http://127.0.0.1:9', '--chain', '28001', '--verify-proof', stub, ...extra], { AERE_VERIFY_PROOF: '', ...env });
|
|
const cade = async () => ({ ok: false, cod: 'unfunded', motiv: 'unfunded' });
|
|
const acum = Math.floor(Date.now() / 1000);
|
|
const st = (mm, h) => mm.stareaCapului(h.corp.id).corp;
|
|
// fara exceptie: nicio linie "agent: <mesajul unei exceptii>" (asa iese main pe o exceptie, cu 1) si niciun TypeError al unei conversii
|
|
const faraExceptie = (r) => !/^agent: /m.test(r.stderr) && !/Cannot (read|convert)/.test(r.stderr);
|
|
const ultim = (r) => `${r.status} ${r.stderr.trim().split('\n').slice(-1)[0].slice(0, 170)}`;
|
|
const adauga = (id, p, L, t) => { const R = resumeLedger({ identity: id, policy: p.policy, policyHash: p.policyHash, ledger: L.export(), now: () => t }); R.record({ kind: 'payment', to: '0xbbbb', amount: '10' }); return R; };
|
|
|
|
// ---- 13.R5-01: un NEMASURAT nu mai iese INAINTEA judecatii: o antedatare dovedita de chitantele verificate ramane FAILED oricate dovezi nemasurate
|
|
// stau langa ea (un cap proaspat in asteptare, un {"status":"pending"} in --anchors, copia veche a unei chitante); si o ramura aratata de lista, la fel
|
|
{
|
|
const T0 = acum - 4000, C2 = acum - 400, C4 = acum - 30; let w = T0 * 1000;
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-01-')), { ceas: () => w }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
// Z: intrarea 3 scrisa dupa capul 2 si declarata la T0+30 (antedatata); Q: acelasi drum, cinstit; R: ramura, aratata verificatorului altfel
|
|
const Z = newAgentIdentity(), Q = newAgentIdentity(), R = newAgentIdentity(); const pZ = pol(Z), pQ = pol(Q), pR = pol(R);
|
|
const LZ = registru(Z, pZ, [T0, T0 + 5, T0 + 10]), LQ = registru(Q, pQ, [T0, T0 + 5, T0 + 10]), LR = registru(R, pR, [T0, T0 + 5, T0 + 10]);
|
|
const h0 = [[Z, LZ], [Q, LQ], [R, LR]].map(([id, L]) => mm.primeste(M.cerereMartor(L.export(), id, { seq: 0, createdAt: iso(w) })));
|
|
await tur(mm, { notarizeaza: notarMock(T0 + 60) });
|
|
w = C2 * 1000; const h2 = [[Z, LZ], [Q, LQ], [R, LR]].map(([id, L]) => mm.primeste(M.cerereMartor(L.export(), id, { seq: 2, previous: 0, createdAt: iso(w) })));
|
|
await tur(mm, { notarizeaza: cade }); // capul de jurnal facut, notarizarea cazuta: chitantele lui seq 2 sunt "checkpointed" (copiile VECHI)
|
|
const vechi2 = h2.map((h) => clon(st(mm, h)));
|
|
await tur(mm, { notarizeaza: notarMock(C2 + 60) });
|
|
const LZ4 = adauga(Z, pZ, adauga(Z, pZ, LZ, T0 + 30), acum - 50), LQ4 = adauga(Q, pQ, adauga(Q, pQ, LQ, C2 + 10), acum - 50), LR4 = adauga(R, pR, adauga(R, pR, LR, C2 + 10), acum - 50);
|
|
w = C4 * 1000; const h4 = [[Z, LZ4], [Q, LQ4], [R, LR4]].map(([id, L]) => mm.primeste(M.cerereMartor(L.export(), id, { seq: 4, previous: 2, createdAt: iso(w) })));
|
|
w = Date.now(); // capetele seq 4: primite, inca fara cap de jurnal (pending)
|
|
const LRalt = registru(R, pR, [T0, T0 + 5, T0 + 12]); // registrul aratat verificatorului de R: alta intrare 2 decat cea pe care martorul o tine
|
|
const fpZ = scrie('r5-01-pZ.json', pZ), flZ = scrie('r5-01-lZ.json', LZ4.export()), fpQ = scrie('r5-01-pQ.json', pQ), flQ = scrie('r5-01-lQ.json', LQ4.export());
|
|
const fpR = scrie('r5-01-pR.json', pR), flR = scrie('r5-01-lR.json', LRalt.export());
|
|
const PEND = { status: 'pending' };
|
|
const fZ0 = scrie('r5-01-z0.json', [st(mm, h0[0])]), fZ02p = scrie('r5-01-z02p.json', [st(mm, h0[0]), st(mm, h2[0]), PEND]), fZ0p = scrie('r5-01-z0p.json', [st(mm, h0[0]), PEND]);
|
|
const fQ0 = scrie('r5-01-q0.json', [st(mm, h0[1])]), fGol = scrie('r5-01-gol.json', []);
|
|
const W = mm.witnessId;
|
|
await rand('13.R5-01 capul seq 4 al lui Z inca in asteptare: --witness W --witness-url cu numai chitanta lui 0 -> 1 "backdated" (chitanta lui 2 adusa dovedeste antedatarea; pana la reparatie 2); --witness W fara --witness-url, chitantele 0 si 2 plus un {"status":"pending"} -> 1 (pana la reparatie 2); --witness-url, chitanta lui 0 plus un {"status":"pending"} -> 1; CONTROL cinstit (Q, acelasi drum) -> 2 [adusa] cat capul 4 asteapta', async () => {
|
|
const a = await verifica(flZ, fpZ, fZ0, ['--witness', W, '--witness-url', UU]), b = await verifica(flZ, fpZ, fZ02p, ['--witness', W]), c = await verifica(flZ, fpZ, fZ0p, ['--witness', W, '--witness-url', UU]);
|
|
const q = await verifica(flQ, fpQ, fQ0, ['--witness', W, '--witness-url', UU]);
|
|
return [h0.every((h) => h.cod === 202) && h2.every((h) => h.cod === 202) && h4.every((h) => h.cod === 202) && st(mm, h4[0]).status === 'pending'
|
|
&& a.status === 1 && /backdated/.test(a.stdout) && b.status === 1 && /backdated/.test(b.stdout) && c.status === 1 && /backdated/.test(c.stdout) && q.status === 2 && /\[adusa\]/.test(q.stderr),
|
|
`pending ${a.status} | fara url + pending ${b.status} | url + pending ${c.status} | cinstit ${ultim(q)}`];
|
|
});
|
|
await rand('13.P5-01 CONTROL ramura aratata de lista semnata (martorul tine seq 2 si 4 ale lui R, verificatorului i se arata alta intrare 2), cu capul 4 inca in asteptare -> 1 [registru]', async () => {
|
|
const r = await verifica(flR, fpR, fGol, ['--witness', W, '--witness-url', UU]);
|
|
return [r.status === 1 && /\[registru\]/.test(r.stderr) && /another branch/.test(r.stderr), ultim(r)];
|
|
});
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) }); // capetele seq 4 notarizate
|
|
const fZ0v = scrie('r5-01-z0v.json', [st(mm, h0[0]), vechi2[0]]), fQ0v = scrie('r5-01-q0v.json', [st(mm, h0[1]), vechi2[1]]);
|
|
await rand('13.R5-01 copia VECHE (checkpointed) a chitantei capului 2, pe care martorul il are acum notarizat: Z cu chitanta lui 0 si copia veche -> 1 "backdated" (chitanta notarizata a lui 2 e adusa: numai o chitanta VERIFICATA scuteste aducerea; pana la reparatie 2); Q (cinstit) cu aceeasi forma -> 0 (copia veche nu mai e ceruta); CONTROL Q fara copie -> 0', async () => {
|
|
const z = await verifica(flZ, fpZ, fZ0v, ['--witness', W, '--witness-url', UU]), q = await verifica(flQ, fpQ, fQ0v, ['--witness', W, '--witness-url', UU]), q0 = await verifica(flQ, fpQ, fQ0, ['--witness', W, '--witness-url', UU]);
|
|
return [vechi2.every((x) => x.status === 'checkpointed') && z.status === 1 && /backdated/.test(z.stdout) && q.status === 0 && /superseded/.test(q.stderr) && q0.status === 0,
|
|
`z ${ultim(z)} | q ${ultim(q)} | q fara copie ${q0.status}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 13.R5-01 (ramura prin predecesor): martorul mai tine numai seq 1 si 2 ale lui B; capul 1 numeste ca predecesor semnat un seq 0 strain (ciobul
|
|
// lui scos), deci chitanta lui 1, adusa si verificata, arata ramura; capul 2 e inca in asteptare. Pe forma veche capul in asteptare iesea cu 2 inaintea
|
|
// judecatii chitantelor aduse
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-01b-')), { maxFrunze: 4, faraLant: true, ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
const capNouIn = (m2) => { const id = newAgentIdentity(); return m2.primeste(M.cerereMartor(registruBrut(id, 1), id, { createdAt: iso(Date.now()) })); };
|
|
try {
|
|
const Yb = newAgentIdentity(); const pYb = pol(Yb); const E = registru(Yb, pYb, [acum - 20, acum - 10, acum - 5]).export(); const h0 = hx();
|
|
const cu = (n) => ({ ...E, entries: [{ seq: 0, hash: h0 }, ...E.entries.slice(1, n)] });
|
|
const c0 = mm.primeste(M.cerereMartor(cu(1), Yb, { seq: 0, createdAt: iso(Date.now()) })).cod;
|
|
for (let i = 0; i < 3; i++) capNouIn(mm);
|
|
const c1 = mm.primeste(M.cerereMartor(cu(2), Yb, { seq: 1, previous: 0, createdAt: iso(Date.now()) })).cod;
|
|
for (let i = 0; i < 4; i++) capNouIn(mm); // ciobul 1 plin, rotirea in 2 scoate ciobul 0: martorul mai tine numai seq 1
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const c2 = mm.primeste(M.cerereMartor(cu(3), Yb, { seq: 2, previous: 1, createdAt: iso(Date.now()) })); // capul 2: pending
|
|
await rand('13.R5-01 ramura aratata de PREDECESORUL semnat al capului tinut seq 1 (seq 0 strain, ciobul lui scos), cu capul 2 inca in asteptare: --witness-url -> 1 [registru] (chitanta lui 1, adusa si verificata, arata ramura; pana la reparatie 2: capul in asteptare iesea inaintea ei)', async () => {
|
|
const r = await verifica(scrie('r5-01b-b.json', E), scrie('r5-01b-policy.json', pYb), null, ['--witness-url', UU]);
|
|
return [c0 === 202 && c1 === 202 && c2.cod === 202 && c2.corp.status === 'pending' && r.status === 1 && /\[registru\]/.test(r.stderr) && /another branch/.test(r.stderr), `${c0} ${c1} ${c2.cod} ${c2.corp && c2.corp.status} | ${ultim(r)}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 13.R5-02: o lista semnata de ALT martor decat cel fixat (alta adresa, un proxy fara cheie care re-semneaza pagina reala) si o ancora simpla
|
|
// langa chitante, sub un martor fixat, nu sunt dovezi impotriva registrului: NOT MEASURED (2), nu FAILED
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-02-')), { ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
const mX = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-02x-')), { ceas: () => Date.now() }); const svX = await porneste(mX, { limite: LIMITE }); const UX = `http://127.0.0.1:${svX.port}`;
|
|
const kImp = M.cheiaMartorului(fs.mkdtempSync(path.join(T, 'r5-02k-')));
|
|
const resemn = await serverLocal(async (req, res, u) => {
|
|
const r = await fetch(UU + u.pathname + u.search, { headers: { 'x-real-ip': 'r5-02-proxy' } }); const j = await r.json();
|
|
if (/\/heads$/.test(u.pathname) && j && j.signedPage) j.signedPage = paginaSemnataDe(kImp, { ...j.signedPage.statement, witness: kImp.witnessId });
|
|
trimite(res, r.status, j);
|
|
});
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L = registru(Y, pY, [acum - 30]);
|
|
const h0 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 0, createdAt: iso(Date.now()) })); await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const fp = scrie('r5-02-p.json', pY), fl = scrie('r5-02-l.json', L.export()), f0 = scrie('r5-02-r0.json', [st(mm, h0)]);
|
|
const fS = scrie('r5-02-rs.json', [st(mm, h0), { seq: 0, hash: L.export().entries[0].hash, at: acum - 30 }]), fS1 = scrie('r5-02-s.json', [{ seq: 0, hash: L.export().entries[0].hash, at: acum - 30 }]);
|
|
const W = mm.witnessId;
|
|
await rand('13.R5-02 registru CINSTIT, --witness W: --witness-url catre ALT martor (care nu tine nimic) -> 2 [martor] pe lista; un proxy fara cheia lui W care re-semneaza pagina reala cu cheia lui -> 2 [martor] (pana la reparatie 1 amandoua); CONTROL --witness-url W -> 0', async () => {
|
|
const x = await verifica(fl, fp, f0, ['--witness', W, '--witness-url', UX]), p = await verifica(fl, fp, f0, ['--witness', W, '--witness-url', resemn.url]), c = await verifica(fl, fp, f0, ['--witness', W, '--witness-url', UU]);
|
|
return [h0.cod === 202 && x.status === 2 && /list: \[martor\]/.test(x.stderr) && p.status === 2 && /list: \[martor\]/.test(p.stderr) && c.status === 0, `alt martor ${ultim(x)} | proxy ${ultim(p)} | W ${c.status}`];
|
|
});
|
|
await rand('13.R5-02 --witness W cu chitanta lui 0 si o ancora simpla ADEVARATA langa ea -> 2 [martor] pe ancora (pana la reparatie 1); numai ancora simpla -> 2; CONTROL fara nicio fixare, aceleasi doua -> 0', async () => {
|
|
const a = await verifica(fl, fp, fS, ['--witness', W]), b = await verifica(fl, fp, fS1, ['--witness', W]), c = await verifica(fl, fp, fS, []);
|
|
return [a.status === 2 && /anchors\[1\]: \[martor\] a plain anchor/.test(a.stderr) && b.status === 2 && /anchors\[0\]: \[martor\] a plain anchor/.test(b.stderr) && c.status === 0, `ancora + chitanta ${ultim(a)} | ancora ${ultim(b)} | nefixat ${c.status}`];
|
|
});
|
|
} finally { await resemn.close(); await sv.close(); await svX.close(); mm.inchide(); mX.inchide(); }
|
|
}
|
|
|
|
// ---- 13.R5-03: o chitanta a martorului fixat, legata de intrarile registrului, care nu tine din vina notarului sau a RPC-ului ales de verificator
|
|
// (intarziere, finalitate FAILED) e NEMASURATA pe ORICE drum: predata de agent la fel ca adusa de la --witness-url
|
|
{
|
|
const T0 = acum - 7000, T1 = acum - 3700; let w = T0 * 1000;
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-03-')), { ceas: () => w }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L = registru(Y, pY, [T0, T0 + 5, T1 - 10]);
|
|
const h0 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 0, createdAt: iso(w) })); await tur(mm, { notarizeaza: notarMock(T0 + 30) });
|
|
w = T1 * 1000; const h2 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 2, previous: 0, createdAt: iso(w) }));
|
|
await tur(mm, { notarizeaza: cade }); await tur(mm, { notarizeaza: notarMock(T1 + 3600) }); // o pana a notarului: capul 2 notarizat la 3600 s
|
|
w = Date.now();
|
|
const fp = scrie('r5-03-p.json', pY), fl = scrie('r5-03-l.json', L.export()), f02 = scrie('r5-03-r02.json', [st(mm, h0), st(mm, h2)]), f0 = scrie('r5-03-r0.json', [st(mm, h0)]);
|
|
const W = mm.witnessId;
|
|
await rand('13.R5-03 registru CINSTIT, capul 2 notarizat la 3600 s dupa primire: agentul preda chitantele 0 si 2 -> 2 [intarziere] cu --witness W si fara --witness-url, si tot 2 cu --witness-url (pana la reparatie 1 amandoua); chitanta lui 2 adusa -> 2', async () => {
|
|
const a = await verifica(fl, fp, f02, ['--witness', W]), b = await verifica(fl, fp, f02, ['--witness', W, '--witness-url', UU]), c = await verifica(fl, fp, f0, ['--witness', W, '--witness-url', UU]);
|
|
return [h0.cod === 202 && h2.cod === 202 && [a, b, c].every((r) => r.status === 2 && /\[intarziere\]/.test(r.stderr) && /NOT MEASURED/.test(r.stderr)), `predata ${ultim(a)} | predata, url ${b.status} | adusa ${c.status}`];
|
|
});
|
|
await rand('13.R5-03 finalitatea FAILED data de verificatorul AIP-23 din cauza RPC-ului ales de verificator: chitanta lui 0 predata -> 2 [lant] (pana la reparatie 1); adusa de la --witness-url -> 2', async () => {
|
|
const a = await verifica(fl, fp, f0, ['--witness', W], { stub: fStubF }), b = await verifica(fl, fp, scrie('r5-03-gol.json', []), ['--witness', W, '--witness-url', UU], { stub: fStubF });
|
|
return [a.status === 2 && /\[lant\] finality FAILED/.test(a.stderr) && b.status === 2 && /\[lant\]/.test(b.stderr), `predata ${ultim(a)} | adusa ${ultim(b)}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 13.R5-04: regula (c): tipurile se judeca INAINTE de orice folosire; nicio forma de raspuns (un numar negativ, un obiect {"toString":0} in
|
|
// locul unui sir, in chitanta, in pagina listei, in `status`, in raspunsul lui submit) nu mai arunca: NOT MEASURED (2), cu motivul numit
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-04-')), { ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L = registru(Y, pY, [acum - 30, acum - 20, acum - 10]);
|
|
const h0 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 0, createdAt: iso(Date.now()) })), h2 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 2, previous: 0, createdAt: iso(Date.now()) }));
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const fp = scrie('r5-04-p.json', pY), fl = scrie('r5-04-l.json', L.export()), f0 = scrie('r5-04-r0.json', [st(mm, h0)]); const W = mm.witnessId;
|
|
const OBJ = { toString: 0 }; // in JSON {"toString":0}: String() si un sablon de text pe el arunca TypeError
|
|
const proxy = (strica, stricaPagina = null) => serverLocal(async (req, res, u) => {
|
|
const r = await fetch(UU + u.pathname + u.search, { headers: { 'x-real-ip': 'r5-04-proxy' } }); let j = await r.json();
|
|
if (strica && u.pathname.endsWith(h2.corp.id)) j = strica(clon(j));
|
|
if (stricaPagina && /\/heads$/.test(u.pathname)) j = stricaPagina(clon(j));
|
|
trimite(res, r.status, j);
|
|
});
|
|
const cazuri = [['primire-negativa', (j) => { j.receipt.witnessReceipt.receivedAtMs = -1; return j; }, null, 'chitanta-forma'], ['leafHash-obiect', (j) => { j.receipt.witnessReceipt.leafHash = OBJ; return j; }, null, 'chitanta-forma'],
|
|
['drum-obiect', (j) => { j.receipt.witnessReceipt.path = [OBJ]; return j; }, null, 'chitanta-forma'], ['statementHash-obiect', (j) => { j.receipt.statementHash = OBJ; return j; }, null, 'punct-integritate'],
|
|
['lant-obiect', (j) => { j.receipt.notarization.chainId = OBJ; return j; }, null, 'lant'], ['status-obiect-fara-chitanta', () => ({ status: OBJ }), null, 'adusa'],
|
|
['pagina-hash-obiect', null, (j) => { j.signedPage.statement.heads[0].hash = OBJ; return j; }, null]];
|
|
await rand('13.R5-04 un proxy la --witness-url (W fixat, registru CINSTIT) pune receivedAtMs -1, un {"toString":0} in leafHash, in drum, in statementHash, in chainId-ul notarizarii, in status (fara chitanta) sau in hash-ul unui cap din pagina listei -> 2 de fiecare data, cu motivul numit si FARA exceptie (pana la reparatie 1, printr-o exceptie)', async () => {
|
|
const rez = [];
|
|
for (const [nume, f, fpag, regula] of cazuri) {
|
|
const p = await proxy(f, fpag); const r = await verifica(fl, fp, f0, ['--witness', W, '--witness-url', p.url]); await p.close();
|
|
rez.push(`${nume}:${r.status}${faraExceptie(r) ? '' : '(exceptie)'}${regula && !r.stderr.includes('[' + regula + ']') ? '(fara [' + regula + '])' : ''}`);
|
|
}
|
|
return [h0.cod === 202 && h2.cod === 202 && rez.every((x) => /^[A-Za-z-]+:2$/.test(x)), rez.join(',')];
|
|
});
|
|
const neg = clon(st(mm, h0)); neg.receipt.witnessReceipt.receivedAtMs = -1; const fNeg = scrie('r5-04-neg.json', [neg]);
|
|
const fForme = scrie('r5-04-forme.json', [OBJ, { seq: OBJ, hash: L.export().entries[0].hash, at: acum - 30 }, 'text', 7, null]);
|
|
await rand('13.R5-04 agentul preda chitanta lui 0 cu receivedAtMs -1: --witness W -> 2 [chitanta-forma] fara exceptie (pana la reparatie 1, exceptie); cu --witness-url -> 0 (chitanta lui 0, adusa si verificata, tine locul copiei stricate); --anchors cu {"toString":0}, o ancora cu seq {"toString":0}, un sir, un numar, null -> 2 [forma] fara exceptie', async () => {
|
|
const a = await verifica(fl, fp, fNeg, ['--witness', W]), b = await verifica(fl, fp, fNeg, ['--witness', W, '--witness-url', UU]), c = await verifica(fl, fp, fForme, []);
|
|
return [a.status === 2 && /\[chitanta-forma\]/.test(a.stderr) && faraExceptie(a) && b.status === 0 && c.status === 2 && (c.stderr.match(/\[forma\]/g) || []).length === 5 && faraExceptie(c),
|
|
`predata ${ultim(a)} | predata, url ${ultim(b)} | forme ${ultim(c)}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
const MA_ = path.join(AICI, 'martor-agenti.mjs');
|
|
await rand('13.R5-04 submit: un 202 JSON cu id-ul, sesiunea si pozitia capului trimis, dar status {"toString":0}, sau cu id {"toString":0} -> 2, --out nescris, fara exceptie (pana la reparatie 1: --out scris, apoi exceptie)', async () => {
|
|
const id = newAgentIdentity(); const L = registruBrut(id, 1); const fk = path.join(F5, 'r5-04-agent.key.pem'); fs.writeFileSync(fk, id.privateKey.export({ type: 'pkcs8', format: 'pem' })); const fl = scrie('r5-04-sub.json', L);
|
|
const rez = [];
|
|
for (const [nume, f] of [['status', (env) => ({ status: { toString: 0 }, id: env.statementHash, session: env.statement.session, seq: env.statement.seq })], ['id', (env) => ({ status: 'pending', id: { toString: 0 }, session: env.statement.session, seq: env.statement.seq })]]) {
|
|
const s = await serverLocal(async (req, res, u) => {
|
|
if (/\/heads$/.test(u.pathname)) return trimite(res, 200, { latest: null, heads: [], next: null });
|
|
let b = ''; for await (const c of req) b += c; return trimite(res, 202, f(JSON.parse(b)));
|
|
});
|
|
const fo = path.join(F5, `r5-04-out-${nume}.json`); const r = await ruleazaAsincron([MA_, 'submit', '--key', fk, '--ledger', fl, '--url', s.url, '--out', fo]); await s.close();
|
|
rez.push(`${nume}:${r.status}:${fs.existsSync(fo) ? 'scris' : 'nescris'}${/Cannot (read|convert)/.test(r.stderr) ? '(exceptie)' : ''}`);
|
|
}
|
|
return [rez.join(',') === 'status:2:nescris,id:2:nescris', rez.join(',')];
|
|
});
|
|
}
|
|
|
|
// ---- 13.R5-05: ordinea in care vin dovezile nu schimba verdictul. O chitanta a AGENTULUI cu semnatura martorului fixat INVALIDA, sau a martorului
|
|
// fixat pentru alt registru, e vina agentului (1) oricat de devreme vine un cap in asteptare; una cu includerea stricata (semnatura martorului
|
|
// valida) e NEMASURATA (2) singura sau langa un cap in asteptare, in ambele ordini
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-05-')), { ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L = registru(Y, pY, [acum - 30, acum - 20]);
|
|
const Y2 = newAgentIdentity(); const pY2 = pol(Y2); const L2 = registru(Y2, pY2, [acum - 30]);
|
|
const h0 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 0, createdAt: iso(Date.now()) })), g0 = mm.primeste(M.cerereMartor(L2.export(), Y2, { seq: 0, createdAt: iso(Date.now()) }));
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const h1 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 1, previous: 0, createdAt: iso(Date.now()) })); // capul 1: pending
|
|
const fp = scrie('r5-05-p.json', pY), fl = scrie('r5-05-l.json', L.export()); const W = mm.witnessId; const PEND = { status: 'pending' };
|
|
const inc = clon(st(mm, h0)); inc.receipt.witnessReceipt.path = ['0x' + '00'.repeat(32)]; // includerea stricata, semnatura martorului neatinsa
|
|
const fals = clon(st(mm, h0)); fals.receipt.statement.root = '0x' + '77'.repeat(32); fals.receipt.statementHash = sha(JSON.stringify(fals.receipt.statement)); // semnatura lui W nu mai verifica
|
|
const alt = st(mm, g0); // chitanta martorului fixat, a altui registru
|
|
const v = (anc, extra) => verifica(fl, fp, scrie(`r5-05-${anc.length}-${crypto.randomBytes(3).toString('hex')}.json`, anc), ['--witness', W, ...extra]);
|
|
await rand('13.R5-05 chitanta agentului cu includerea stricata (semnatura lui W valida): singura -> 2 [includere] (pana la reparatie 1); langa un {"status":"pending"}, in ambele ordini -> 2; cu --witness-url si capul 1 in asteptare -> 2', async () => {
|
|
const a = await v([inc], []), b = await v([inc, PEND], []), c = await v([PEND, inc], []), d = await v([inc], ['--witness-url', UU]);
|
|
return [h1.cod === 202 && [a, b, c, d].every((r) => r.status === 2 && /\[includere\]/.test(r.stderr)), `${ultim(a)} | ${b.status} | ${c.status} | ${ultim(d)}`];
|
|
});
|
|
await rand('13.P5-05 CONTROL vina agentului: chitanta lui 0 cu semnatura lui W INVALIDA (capul de jurnal schimbat, hash refacut) -> 1 [punct-semnatura] singura, langa un {"status":"pending"} in ambele ordini, si cu --witness-url (capul 1 in asteptare); chitanta lui W pentru ALT registru -> 1 [registru] la fel', async () => {
|
|
const r = [await v([fals], []), await v([fals, PEND], []), await v([PEND, fals], []), await v([fals], ['--witness-url', UU]), await v([alt], []), await v([PEND, alt], []), await v([alt], ['--witness-url', UU])];
|
|
return [r.slice(0, 4).every((x) => x.status === 1 && /\[punct-semnatura\]/.test(x.stderr)) && r.slice(4).every((x) => x.status === 1 && /\[registru\]/.test(x.stderr)), r.map((x) => x.status).join(',') + ' | ' + ultim(r[3])];
|
|
});
|
|
// un proxy la --witness-url care serveste aceeasi chitanta falsificata (semnatura lui W invalida) pentru capul 0: nu e vina registrului
|
|
const pf = await serverLocal(async (req, res, u) => { const r = await fetch(UU + u.pathname + u.search, { headers: { 'x-real-ip': 'r5-05-proxy' } }); let j = await r.json(); if (u.pathname.endsWith(h0.corp.id)) j = { ...j, receipt: fals.receipt }; trimite(res, r.status, j); });
|
|
await rand('13.P5-05 CONTROL un proxy fara cheia lui W serveste, la --witness-url, chitanta capului 0 cu semnatura lui W invalida -> 2 [punct-semnatura] (o adresa nu poate face un FAILED), nu 1', async () => {
|
|
const r = await verifica(fl, fp, scrie('r5-05-gol.json', []), ['--witness', W, '--witness-url', pf.url]); await pf.close();
|
|
return [r.status === 2 && /\[punct-semnatura\]/.test(r.stderr), ultim(r)];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 13.R5-06: cate o chitanta adusa pe cap tinut, sub limita de citiri a serviciului: pe un 429 verificatorul asteapta cat spune Retry-After,
|
|
// intr-un buget comun (AERE_WITNESS_WAIT_MAX_S, implicit 900 s); peste buget, NOT MEASURED numit (HTTP 429 si cat s-a asteptat)
|
|
{
|
|
const LIM = { postPeClient: { cereri: 4000, ms: 600_000 }, postPeAgent: { cereri: 4000, ms: 600_000 }, getPeClient: { cereri: 4, ms: 1000 } };
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r5-06-')), { ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIM }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const N = 12; const Y = newAgentIdentity(); const pY = definePolicy({ agentId: Y.agentId, spend: { amount: '100000000', windowSeconds: 3600 }, recipients: ['0xbbbb'] });
|
|
let t = acum - 200; const L = openLedger({ identity: Y, policy: pY.policy, policyHash: pY.policyHash, now: () => t });
|
|
for (let i = 0; i < N; i++) { t = acum - 200 + i * 5; L.record({ kind: 'payment', to: '0xbbbb', amount: '1' }); }
|
|
const E = L.export(); let prev = null, primite = 0;
|
|
for (let i = 0; i < N; i++) { if (mm.primeste(M.cerereMartor(E, Y, { seq: i, previous: prev, createdAt: iso(Date.now()) })).cod === 202) primite++; prev = i; }
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const fp = scrie('r5-06-p.json', pY), fl = scrie('r5-06-l.json', E), fGol = scrie('r5-06-gol.json', []);
|
|
// un client care citeste de 12 ori: al cincilea raspuns e 429, cu Retry-After
|
|
let ra = null; for (let i = 0; i < 12; i++) { const r = await fetch(`${UU}/v1/agents/witness/health`, { headers: { 'x-real-ip': 'r5-06-numarator' } }); if (r.status === 429 && ra === null) ra = r.headers.get('retry-after'); await r.arrayBuffer(); }
|
|
await rand(`13.R5-06 serviciul cu 4 citiri pe client in 1 s, un registru CINSTIT cu ${N} capete tinute, --witness-url fara nicio chitanta a agentului -> 0, dupa ce verificatorul a asteptat cat a cerut Retry-After (pana la reparatie 2, "HTTP 429"); 429-ul unei citiri poarta Retry-After`, async () => {
|
|
const r = await verifica(fl, fp, fGol, ['--witness', mm.witnessId, '--witness-url', UU]);
|
|
return [primite === N && /^\d+$/.test(String(ra)) && Number(ra) === 1 && r.status === 0 && /rate-limited this verification \(HTTP 429\): \d+ s waited/.test(r.stderr), `primite ${primite}, Retry-After ${ra} | ${ultim(r)}`];
|
|
});
|
|
await rand('13.P5-06 CONTROL bugetul: acelasi registru cu AERE_WITNESS_WAIT_MAX_S=0 -> 2 NOT MEASURED numit (HTTP 429, Retry-After, bugetul); AERE_WITNESS_WAIT_MAX_S=abc -> 2 folosire gresita', async () => {
|
|
await new Promise((g) => setTimeout(g, 1100)); // o fereastra noua: lista intra, iar al patrulea cap adus primeste 429
|
|
const r = await verifica(fl, fp, fGol, ['--witness', mm.witnessId, '--witness-url', UU], { env: { AERE_WITNESS_WAIT_MAX_S: '0' } });
|
|
const u = await verifica(fl, fp, fGol, ['--witness', mm.witnessId, '--witness-url', UU], { env: { AERE_WITNESS_WAIT_MAX_S: 'abc' } });
|
|
return [r.status === 2 && /HTTP 429 \(Retry-After \d+ s; 0 s already waited of a budget of 0 s/.test(r.stderr) && /NOT MEASURED/.test(r.stderr) && u.status === 2 && /AERE_WITNESS_WAIT_MAX_S must be/.test(u.stderr), `${ultim(r)} | ${ultim(u)}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
}
|
|
|
|
// ================================================================================================ 14. a sasea revizie B3
|
|
// (2026-10-19, verificarea tintita a reparatiilor R5: N1..N4): randurile "14.N<n>", RAU pe forma de dinainte (786aa0038). N4 (cifrele din README)
|
|
// nu are rand: se masoara rulind proba si controlul.
|
|
async function revizia6() {
|
|
const fStub6 = path.join(F5, 'verify-stub-notar-6.mjs'); // verificatorul AIP-23 inlocuit: momentul din notarizarea plicului (notarul-mock)
|
|
fs.writeFileSync(fStub6, `import fs from 'node:fs'; const e = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); const t = Number(e.notarization && e.notarization.firstSeen);
|
|
console.log(JSON.stringify({ verdict: 'VALID', statementHash: e.statementHash, levels: [{ level: 'form', state: 'PASSED', detail: 'x' }, { level: 'integrity', state: 'PASSED', detail: 'x' }, { level: 'signature', state: 'PASSED', detail: 'x' },
|
|
{ level: 'finality', state: 'PASSED', detail: 'post-quantum: records this statementHash as first seen at ' + new Date(t * 1000).toISOString() + ' (' + t + ')' }] }));\n`);
|
|
const MA_ = path.join(AICI, 'martor-agenti.mjs');
|
|
const acum = Math.floor(Date.now() / 1000);
|
|
const ultim = (r) => `${r.status} ${r.stderr.trim().split('\n').slice(-1)[0].slice(0, 170)}`;
|
|
const adauga = (id, p, L, t) => { const R = resumeLedger({ identity: id, policy: p.policy, policyHash: p.policyHash, ledger: L.export(), now: () => t }); R.record({ kind: 'payment', to: '0xbbbb', amount: '10' }); return R; };
|
|
|
|
// ---- 14.N1: o chitanta din --anchors fara --rpc nu mai iese cu 2 (folosire gresita) INAINTEA listei semnate: e o dovada NEMASURATA [rpc], iar o ramura
|
|
// aratata de lista martorului fixat ramane FAILED (pana la reparatie, orice obiect cu forma de chitanta pus de agent in --anchors o ascundea)
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r6-n1-')), { ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L0 = registru(Y, pY, [acum - 30]);
|
|
const A = adauga(Y, pY, L0, acum - 20), B = adauga(Y, pY, L0, acum - 15); // A: registrul tinut de martor; B: alta intrare 1, aratata verificatorului
|
|
const h0 = mm.primeste(M.cerereMartor(A.export(), Y, { seq: 0, createdAt: iso(Date.now()) }));
|
|
const h1 = mm.primeste(M.cerereMartor(A.export(), Y, { seq: 1, previous: 0, createdAt: iso(Date.now()) }));
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const W = mm.witnessId; const fp = scrie('r6-n1-p.json', pY), fA = scrie('r6-n1-a.json', A.export()), fB = scrie('r6-n1-b.json', B.export());
|
|
const f0 = scrie('r6-n1-r0.json', [mm.stareaCapului(h0.corp.id).corp]), fForma = scrie('r6-n1-forma.json', [{ witnessReceipt: {} }]);
|
|
const v = (fl, anc, extra) => ruleazaAsincron([CLI, 'verify', '--ledger', fl, '--policy', fp, '--anchors', anc, '--chain', '28001', '--verify-proof', fStub6, ...extra], { AERE_VERIFY_PROOF: '' });
|
|
const folosire = (r) => /^agent: .*needs --rpc/m.test(r.stderr);
|
|
await rand('14.N1 fara --rpc, ramura aratata de lista semnata (martorul tine seq 0 si 1 ale lui A, verificatorului i se arata B): --anchors [chitanta adevarata a lui 0] -> 1 [registru], chitanta NEMASURATA [rpc] (pana la reparatie 2, folosire gresita, inaintea listei); --anchors [{"witnessReceipt":{}}] -> 1; CONTROL registrul CINSTIT A cu chitanta lui 0, --witness W, fara --rpc -> 2 NOT MEASURED [rpc], nu folosire gresita; cu --rpc, ramura -> 1', async () => {
|
|
const a = await v(fB, f0, ['--witness', W, '--witness-url', UU]), b = await v(fB, fForma, ['--witness', W, '--witness-url', UU]), c = await v(fA, f0, ['--witness', W]);
|
|
const d = await v(fB, f0, ['--witness', W, '--witness-url', UU, '--rpc', 'http://127.0.0.1:9']);
|
|
return [h0.cod === 202 && h1.cod === 202 && a.status === 1 && /\[registru\]/.test(a.stderr) && /anchors\[0\]: \[rpc\]/.test(a.stderr) && !folosire(a) && b.status === 1 && /\[registru\]/.test(b.stderr)
|
|
&& c.status === 2 && /anchors\[0\]: \[rpc\]/.test(c.stderr) && /^NOT MEASURED/m.test(c.stderr) && !folosire(c) && d.status === 1 && /\[registru\]/.test(d.stderr),
|
|
`ramura+chitanta ${ultim(a)} | ramura+forma ${b.status} | cinstit ${ultim(c)} | cu --rpc ${d.status}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 14.N2: `receipt` scrie --out NUMAI daca raspunsul e chitanta capului cerut (--id), a registrului dat (--ledger), cu semnatura martorului verificata
|
|
// (cu --witness, daca e dat); altfel 2 si nimic scris. Pana la reparatie un proxy fara cheie la --url dadea agentului cinstit chitanta adevarata a capului
|
|
// ALTUI agent sau una cu semnatura rupta, receipt iesea 0 si scria, iar fisierul predat iesea 1 la verify (din vina adresei)
|
|
{
|
|
const mm = M.deschideMartor(fs.mkdtempSync(path.join(T, 'r6-n2-')), { ceas: () => Date.now() }); const sv = await porneste(mm, { limite: LIMITE }); const UU = `http://127.0.0.1:${sv.port}`;
|
|
try {
|
|
const Y = newAgentIdentity(); const pY = pol(Y); const L = registru(Y, pY, [acum - 30, acum - 20]);
|
|
const Z = newAgentIdentity(); const pZ = pol(Z); const LZ = registru(Z, pZ, [acum - 30]);
|
|
const y0 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 0, createdAt: iso(Date.now()) }));
|
|
const y1 = mm.primeste(M.cerereMartor(L.export(), Y, { seq: 1, previous: 0, createdAt: iso(Date.now()) }));
|
|
const z0 = mm.primeste(M.cerereMartor(LZ.export(), Z, { seq: 0, createdAt: iso(Date.now()) }));
|
|
await tur(mm, { notarizeaza: notarMock(acum + 60) });
|
|
const W = mm.witnessId; const id0 = y0.corp.id, id1 = y1.corp.id, idZ = z0.corp.id;
|
|
const fl = scrie('r6-n2-l.json', L.export()), fp = scrie('r6-n2-p.json', pY);
|
|
// proxy-ul de pe drumul agentului: trece totul, afara de GET-ul capului 1 al lui Y, pe care il inlocuieste
|
|
const proxy = (f) => serverLocal(async (req, res, u) => { const r = await fetch(UU + u.pathname + u.search, { headers: { 'x-real-ip': 'r6-n2-proxy' } }); let j = await r.json(); if (u.pathname.endsWith(id1)) j = await f(clon(j)); trimite(res, r.status, j); });
|
|
const stare = async (id) => (await fetch(`${UU}/v1/agents/witness/${id}`, { headers: { 'x-real-ip': 'r6-n2-citire' } })).json();
|
|
const rupta = async (j) => { const b = Buffer.from(j.receipt.signature.signature, 'base64'); b[10] ^= 0xff; j.receipt.signature.signature = b.toString('base64'); return j; };
|
|
let n = 0; const iesire = () => path.join(F5, `r6-n2-out-${n++}.json`);
|
|
const receipt = (url, id, extra, out) => ruleazaAsincron([MA_, 'receipt', '--url', url, '--id', id, ...extra, '--out', out]);
|
|
await rand('14.N2 receipt printr-un proxy fara cheie la --url (fluxul din README, fara --witness): chitanta ADEVARATA a lui W pentru capul lui Z, semnatura lui W rupta intr-un octet, chitanta capului 0 al lui Y in locul capului 1 cerut -> 2 de fiecare data, cu motivul numit, nimic scris (pana la reparatie 0, scris, si predat iesea 1); CONTROL fara proxy -> 0, scris, "NOT pinned", iar verify --anchors <fisierul> --witness W --witness-url W -> 0', async () => {
|
|
const fc = iesire(); const c = await receipt(UU, id1, ['--ledger', fl], fc);
|
|
const vc = fs.existsSync(fc) ? await ruleazaAsincron([CLI, 'verify', '--ledger', fl, '--policy', fp, '--anchors', fc, '--rpc', 'http://127.0.0.1:9', '--chain', '28001', '--verify-proof', fStub6, '--witness', W, '--witness-url', UU], { AERE_VERIFY_PROOF: '' }) : { status: -1 };
|
|
const rez = [];
|
|
for (const [nume, f, motiv] of [['strain', () => stare(idZ), /receipt of another head/], ['rupta', rupta, /\[punct-semnatura\]/], ['alt-cap', () => stare(id0), /receipt of another head/]]) {
|
|
const p = await proxy(f); const fo = iesire(); const r = await receipt(p.url, id1, ['--ledger', fl], fo); await p.close();
|
|
rez.push(`${nume}:${r.status}:${fs.existsSync(fo) ? 'scris' : 'nescris'}${motiv.test(r.stderr) ? '' : '(fara motiv)'}${/Cannot (read|convert)/.test(r.stderr) ? '(exceptie)' : ''}`);
|
|
}
|
|
return [y0.cod === 202 && y1.cod === 202 && z0.cod === 202 && c.status === 0 && fs.existsSync(fc) && /NOT pinned/.test(c.stderr) && vc.status === 0 && rez.join(',') === 'strain:2:nescris,rupta:2:nescris,alt-cap:2:nescris',
|
|
`control ${ultim(c)} | verify ${vc.status} | ${rez.join(',')}`];
|
|
});
|
|
await rand('14.N2 receipt fata de --ledger si --witness: --id al capului lui Z cu registrul lui Y -> 2 [registru], nimic scris; --witness <alt martor> pe chitanta lui W -> 2 [martor], nimic scris; --witness W -> 0 "(pinned)", scris; fara --ledger -> 2 folosire gresita, nimic scris (pana la reparatie --ledger si --witness erau steaguri necunoscute)', async () => {
|
|
const fd = iesire(), d = await receipt(UU, idZ, ['--ledger', fl], fd);
|
|
const fe = iesire(), e = await receipt(UU, id1, ['--ledger', fl, '--witness', 'aere-witness:' + 'ee'.repeat(20)], fe);
|
|
const fw = iesire(), w = await receipt(UU, id1, ['--ledger', fl, '--witness', W], fw);
|
|
const ff = iesire(), f = await receipt(UU, id1, [], ff);
|
|
return [d.status === 2 && !fs.existsSync(fd) && /\[registru\]/.test(d.stderr) && e.status === 2 && !fs.existsSync(fe) && /\[martor\]/.test(e.stderr) && w.status === 0 && fs.existsSync(fw) && /\(pinned\)/.test(w.stderr)
|
|
&& f.status === 2 && !fs.existsSync(ff) && /--ledger is required/.test(f.stderr), `alt agent ${ultim(d)} | alt martor ${ultim(e)} | fixat ${ultim(w)} | fara --ledger ${ultim(f)}`];
|
|
});
|
|
} finally { await sv.close(); mm.inchide(); }
|
|
}
|
|
|
|
// ---- 14.N3: regula (c) pe drumul "nimic nou" al lui submit: `latest` (campul nesemnat al paginii) si starea capului tinut, cu valori de tip gresit, dau 2
|
|
// cu --out nescris, niciodata o exceptie (pana la reparatie: un {"toString":0} in seq sau id arunca, iar un status {"toString":0} scria --out si arunca)
|
|
{
|
|
const id = newAgentIdentity(); const p = pol(id); const E = registru(id, p, [acum - 10]).export();
|
|
const fk = path.join(F5, 'r6-n3-agent.key.pem'); fs.writeFileSync(fk, id.privateKey.export({ type: 'pkcs8', format: 'pem' })); const fl = scrie('r6-n3-l.json', E);
|
|
const X = '0x' + '11'.repeat(32), OBJ = { toString: 0 }, h0 = E.entries[0].hash;
|
|
const cazuri = [['control', { seq: 0, hash: h0, id: X }, { id: X, status: 'notarized' }], ['status-obiect', { seq: 0, hash: h0, id: X }, { id: X, status: OBJ }],
|
|
['seq-obiect', { seq: OBJ, hash: h0, id: X }, null], ['id-obiect', { seq: 0, hash: h0, id: OBJ }, null], ['hash-obiect', { seq: 0, hash: OBJ, id: X }, null], ['latest-numar', 5, null]];
|
|
await rand('14.N3 submit pe drumul "nimic nou": latest cu seq, id sau hash {"toString":0}, latest un numar, starea capului cu status {"toString":0} -> 2, --out nescris, fara exceptie (pana la reparatie 1, prin exceptie, iar pe status --out scris); CONTROL latest = intrarea 0 si starea "notarized" -> 0, --out scris', async () => {
|
|
const rez = [];
|
|
for (const [nume, latest, stareCap] of cazuri) {
|
|
const s = await serverLocal(async (req, res, u) => {
|
|
if (/\/heads$/.test(u.pathname)) return trimite(res, 200, { latest, heads: [], next: null });
|
|
if (req.method === 'GET' && stareCap) return trimite(res, 200, stareCap);
|
|
return trimite(res, 500, { error: 'not in this scenario' });
|
|
});
|
|
const fo = path.join(F5, `r6-n3-out-${nume}.json`); const r = await ruleazaAsincron([MA_, 'submit', '--key', fk, '--ledger', fl, '--url', s.url, '--out', fo]); await s.close();
|
|
rez.push(`${nume}:${r.status}:${fs.existsSync(fo) ? 'scris' : 'nescris'}${/Cannot (read|convert)/.test(r.stderr) ? '(exceptie)' : ''}`);
|
|
}
|
|
return [rez.join(',') === 'control:0:scris,status-obiect:2:nescris,seq-obiect:2:nescris,id-obiect:2:nescris,hash-obiect:2:nescris,latest-numar:2:nescris', rez.join(',')];
|
|
});
|
|
}
|
|
}
|
|
|
|
async function testnet() {
|
|
const RPC = process.env.AERE_TESTNET_RPC || 'https://testnet-rpc.aere.network';
|
|
if (!VERIFY || !fs.existsSync(VERIFY)) return sari('8. testnetul 28001', 'lipseste verificatorul AIP-23 (AERE_VERIFY_PROOF)');
|
|
let kf = process.env.AERE_TESTNET_KEY_FILE, robinet = null;
|
|
if (!kf) { kf = path.join(T, 'cheie-proba-28001.key'); const r = path.resolve(AICI, '..', 'aere-quantum-shield', 'robinet.mjs'); robinet = fs.existsSync(r) ? r : null; if (!robinet) return sari('8. testnetul 28001', 'nici AERE_TESTNET_KEY_FILE, nici clientul robinetului (tools/aere-quantum-shield/robinet.mjs)'); }
|
|
const D8 = path.join(T, 'stare-28001'); wMs = Date.now();
|
|
const mt = M.deschideMartor(D8, { ceas: () => Date.now() });
|
|
try {
|
|
const id = newAgentIdentity(); const p = pol(id); let t = Math.floor(Date.now() / 1000) - 900; const L = openLedger({ identity: id, policy: p.policy, policyHash: p.policyHash, now: () => t });
|
|
L.record({ kind: 'payment', to: '0xbbbb', amount: '10' }); t += 5; L.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
const inainte = L.export(); const cap = M.cerereMartor(inainte, id, { seq: 1 });
|
|
const r = mt.primeste(cap);
|
|
cer(r.cod === 202, '8. testnet: capul unui registru real primit de martor (202)', JSON.stringify(r.corp).slice(0, 200));
|
|
const tr = await tur(mt, { notarizeaza: notarizatorReal({ rpc: RPC, cheieFisier: kf, robinet }) });
|
|
if (tr.notarized !== 0) { if (/unfunded|chain|RPC|fetch|ECONN|timeout|failed/i.test(String(tr.error))) return sari('8. testnetul 28001', 'notarizarea n-a reusit: ' + tr.error); return cer(false, '8. testnet: capul de jurnal notarizat pe AereNotary 28001', tr.error); }
|
|
const st = mt.stareaCapului(cap.statementHash).corp;
|
|
cer(st.status === 'notarized' && st.receipt.notarization.chainId === 28001 && st.receipt.notarization.notary === NOTARI[28001] && st.receipt.notarization.txHash, `8. testnet: capul de jurnal notarizat pe AereNotary 28001 (bloc ${st.receipt.notarization.block}, firstSeen ${st.receipt.notarization.firstSeen})`, JSON.stringify(st).slice(0, 300));
|
|
let v = null; const start = Date.now();
|
|
while (Date.now() - start < 420_000) {
|
|
const f = path.join(T, 'plic-28001.json'); fs.writeFileSync(f, JSON.stringify(st.receipt));
|
|
const o = spawnSync(process.execPath, [VERIFY, f, '--json', '--rpc', RPC, '--chain', '28001'], { encoding: 'utf8', timeout: 300_000 });
|
|
try { v = JSON.parse(o.stdout); } catch { v = null; }
|
|
if (v && v.verdict !== 'PARTIAL') break;
|
|
await new Promise((g) => setTimeout(g, 20_000));
|
|
}
|
|
if (!v || v.verdict === 'PARTIAL') return sari('8. testnet: plicul martorului VALID cu finalitate post-cuantica', 'finalitatea nu a venit in 7 minute: ' + JSON.stringify(v && v.levels).slice(0, 200));
|
|
const fin = v.levels.find((x) => x.level === 'finality');
|
|
cer(v.verdict === 'VALID' && fin.state === 'PASSED' && v.levels.find((x) => x.level === 'signature').state === 'PASSED', `8. testnet: plicul martorului VALID la verificatorul AIP-23 de referinta: semnatura martorului si finalitatea post-cuantica PASSED (dupa ${Math.round((Date.now() - start) / 1000)} s)`, JSON.stringify(v).slice(0, 300));
|
|
t = Math.floor(Date.now() / 1000); const cinstit = resumeLedger({ identity: id, policy: p.policy, policyHash: p.policyHash, ledger: inainte, now: () => t }); cinstit.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
t = Math.floor(Date.parse(cap.statement.createdAt) / 1000) - 600; const antedatat = resumeLedger({ identity: id, policy: p.policy, policyHash: p.policyHash, ledger: inainte, now: () => t }); antedatat.record({ kind: 'payment', to: '0xbbbb', amount: '10' });
|
|
const F8 = path.join(T, 'cli-28001'); fs.mkdirSync(F8);
|
|
const sc = (n, o) => { const f = path.join(F8, n); fs.writeFileSync(f, JSON.stringify(o)); return f; };
|
|
const fP = sc('policy.json', p), fR = sc('receipt.json', st);
|
|
const a1 = spawnSync(process.execPath, [CLI, 'verify', '--ledger', sc('cinstit.json', cinstit.export()), '--policy', fP, '--anchors', fR, '--rpc', RPC, '--chain', '28001', '--verify-proof', VERIFY, '--witness', mt.witnessId], { encoding: 'utf8', timeout: 300_000 });
|
|
const a2 = spawnSync(process.execPath, [CLI, 'verify', '--ledger', sc('antedatat.json', antedatat.export()), '--policy', fP, '--anchors', fR, '--rpc', RPC, '--chain', '28001', '--verify-proof', VERIFY, '--witness', mt.witnessId], { encoding: 'utf8', timeout: 300_000 });
|
|
cer(a1.status === 0 && /"lowerBound": "witnessed"/.test(a1.stdout), '8. testnet: agent-cli verify --anchors <chitanta> --witness <martorul> pe lantul viu: registrul cinstit -> 0', a1.stdout.slice(0, 200) + a1.stderr);
|
|
cer(a2.status === 1 && /backdated/.test(a2.stdout), '8. testnet: registrul antedatat cu aceeasi chitanta -> 1 "backdated"', a2.stdout.slice(0, 200) + a2.stderr);
|
|
const DV = path.join(AICI, 'dovezi-martor-agenti-28001'); fs.mkdirSync(DV, { recursive: true });
|
|
const zi = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
fs.writeFileSync(path.join(DV, `martor-agenti-${zi}.json`), JSON.stringify({ v: 1, kind: 'aere-agent-witness-evidence', network: 'eip155:28001', rpc: RPC, witness: mt.witnessId, policy: p,
|
|
ledgerBefore: inainte, head: cap, receipt: st.receipt, aip23: { verdict: v.verdict, levels: v.levels }, honest: cinstit.export(), backdated: antedatat.export(),
|
|
verdicts: { honestWithReceipt: a1.status, backdatedWithReceipt: a2.status } }, null, 1) + '\n');
|
|
console.log(` (dovada in dovezi-martor-agenti-28001/martor-agenti-${zi}.json; nicio cheie privata)`);
|
|
} finally { mt.inchide(); }
|
|
}
|
|
|
|
console.log(`\nmartor-agenti: ${ok}/${ok + rau} cum trebuia${sarit ? `, ${sarit} sarite (NEMASURAT)` : ''}`);
|
|
process.exitCode = rau ? 1 : sarit ? 2 : 0;
|
|
// un rand cazut intr-o exceptie poate lasa un server de proba deschis (pe forma veche a componentei, de pilda): procesul iese oricum, cu codul lui
|
|
setTimeout(() => process.exit(process.exitCode), 300);
|