326 lines
46 KiB
JavaScript
326 lines
46 KiB
JavaScript
// Controlul negativ al martorului gazduit al agentilor (martor-agenti.mjs, martor-agenti-serviciu.mjs, proba-martor-agenti.mjs; B3, 2026-10-19):
|
|
// fiecare paznic scos intr-o COPIE a componentei trebuie sa inroseasca proba NUMITA, cu proba chiar rulata (rezumatul ei exista); pe copia
|
|
// neatinsa, proba e verde. Trei stari, ca la celelalte controale ale agentilor: ROSU cum trebuia (randul numit pica), CONTROL CAZUT (randul a
|
|
// ramas verde) si STRICAT (tiparul nu apare exact o data, sau proba nu ajunge la rezumat), iar ultimele doua se numara esecuri.
|
|
// Plantarile se ancoreaza pe partea STABILA a randului (conditia cu markerul ei PAZA-MARTOR), nu pe tot randul, si toate ruleaza (JavaScript:
|
|
// o conditie inlocuita cu `false`), deci un rosu e al probei, nu al unui fisier care nu se mai incarca.
|
|
// ACOPERIREA (2026-10-19, revizia adversariala B3, constatarea 6): pana la reparatie controlul planta 24 de paznici si README-ul spunea "each
|
|
// guard", desi primire (de doua ori), martor, lant (pe lant si pe nivelul semnaturii), promisiune, sesiune, integritate si punct-integritate nu
|
|
// erau plantati. Acum FIECARE marker PAZA-MARTOR din martor-agenti.mjs si martor-agenti-serviciu.mjs trebuie sa fie in tiparul unei plantari, sau
|
|
// pe lista EXCEPTII cu motivul scris; un marker neacoperit e un ESEC al controlului. Garda isi are si ea martorul: cu o plantare scoasa, trebuie
|
|
// sa numeasca exact markerul ei. Pe langa markeri, plantarile pazesc si ordinea si forma reparatiilor din revizie (cota inainte de "deja vazut",
|
|
// scrierea pana la ultimul octet, taierea inapoi, paginarea, cheia /64, linia de comanda, vocabularul felului in proof-kinds, usa gateway-ului).
|
|
// A DOUA REVIZIE (2026-10-19, 15 constatari R2-01..R2-15): fiecare paznic nou are plantarea lui, tinta randul lui 10.R2-<nn> / 10.P2-<nn>; garda de
|
|
// acoperire priveste acum si agent-cli.mjs (R2-15: potrivirea --witness / --witness-url si ancorele simple sub un martor fixat nu aveau nici
|
|
// plantare, nici rand), iar paznicul radacinii de la deschidere e impartit in doua (marimea, radacina), fiecare cu randul lui.
|
|
// A TREIA REVIZIE (2026-10-19, 5 constatari R3-01..R3-05): fiecare paznic nou are plantarea lui, tinta randul lui 11.R3-<nn> / 11.P3-<nn>.
|
|
// A PATRA REVIZIE (2026-10-19, 6 constatari R4-01..R4-06): regula de proiectare a lui --witness-url (agent-cli.mjs, README) si paznicii ei, fiecare
|
|
// cu plantarea lui si tinta 12.R4-<nn> / 12.P4-<nn>; paznicul `martor-adusa-vina` are doua plantari, una pe fiecare sens (aduse FAILED / ramura 2).
|
|
// A CINCEA REVIZIE (2026-10-19, 6 constatari R5-01..R5-06): verify e o EVALUARE COMPLETA cu un singur verdict (agent-cli.mjs, README); paznicii ei
|
|
// (ordinea claselor in verdict, clasa fiecarei chitante, copia veche, tipurile, asteptarea pe 429) au plantarile lor, tinta 13.R5-<nn> / 13.P5-<nn>, iar
|
|
// plantarile NEUTRE rastoarna ordinea in care vin dovezile si cer proba verde intreaga. Plantarea scurtaturii `--witness ocolit` a disparut cu scurtatura.
|
|
// A SASEA REVIZIE (2026-10-19, verificarea tintita a reparatiilor R5, N1..N3): o chitanta fara --rpc ca dovada NEMASURATA [rpc] (doua plantari, una pe
|
|
// fiecare sens), `receipt` care scrie numai chitanta verificata a capului cerut (id, registru, martorul fixat, fiecare cu plantarea lui) si tipurile pe
|
|
// drumul "nimic nou" al lui submit; tinta 14.N<n>.
|
|
|
|
|
|
// Copia pastreaza asezarea relativa (dosarul componentei si surorile ei, `../aere-verify-layer` sau `../verify-layer`, `../proof-kinds`), derivata
|
|
// din importurile modulului, in arborele de dezvoltare si in copia publicata. Verificatorul AIP-23 si gateway-ul (daca sunt) se dau probei
|
|
// din locul lor, neatinse: AERE_VERIFY_PROOF, AERE_GATEWAY; plantarea usii gateway-ului lucreaza pe o copie a dosarului cloud-gateway.
|
|
// node control-negativ-martor-agenti.mjs iesire 0 = martorul verde, acoperirea intreaga si toate plantarile rosii pe randul lor
|
|
import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path';
|
|
import { spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url';
|
|
|
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
|
const MA = 'martor-agenti.mjs', MS = 'martor-agenti-serviciu.mjs', CLI = 'agent-cli.mjs', PKF = '../proof-kinds/proof-kinds.mjs', GWF = 'gateway';
|
|
const PLANTARI = [
|
|
// [nume, fisier, tipar, inlocuire, randul probei (inceputul numelui lui)]
|
|
// ---- cererea (verificaCapSemnat)
|
|
['plicul neobiect nejudecat (forma)', MA, 'if (!(obiect(env) && obiect(env.statement) /*PAZA-MARTOR:forma*/)) return', 'if (false) return', '2. CONTROL ritm pe client'],
|
|
['forma capului nejudecata', MA, 'if (!(formaBuna /*PAZA-MARTOR:forma*/)) return', 'if (false) return', '1. CONTROL: forma capului'],
|
|
['continutul registrului primit in cerere', MA, 'if (!(inPlus.length === 0 /*PAZA-MARTOR:continut*/)) return', 'if (false) return', '1. CONTROL: intrarile registrului langa cap'],
|
|
['sesiunea nederivata din agent si politica', MA, 'if (!(st.session === sessionId(st.agentId, st.policyHash) /*PAZA-MARTOR:sesiune*/)) return', 'if (false) return', '1. CONTROL: o sesiune care nu e cea derivata'],
|
|
['integritatea capului nejudecata', MA, 'if (!(sha(text) === env.statementHash /*PAZA-MARTOR:integritate*/)) return', 'if (false) return', '1. CONTROL: declaratia schimbata fara hash refacut'],
|
|
['cheia agentului poate fi oricare', MA, 'if (!(!!pub && pub.asymmetricKeyType === ALG /*PAZA-MARTOR:cheie*/)) return', 'if (!(!!pub /*PAZA-MARTOR:cheie*/)) return', '1. CONTROL: un cap semnat cu o cheie Ed25519'],
|
|
['agentId nederivat din cheie', MA, 'if (!(agentIdFromKey(pub) === st.agentId /*PAZA-MARTOR:identitate*/)) return', 'if (false) return', '1. CONTROL: cheia lui A semneaza un cap cu id-ul lui B'],
|
|
['semnatura capului nejudecata', MA, 'if (!(verificaSemnaturaText(text, pub, env.signature.signature) /*PAZA-MARTOR:semnatura*/)) return', 'if (false) return', '1. CONTROL: declaratia schimbata si hash-ul refacut'],
|
|
['momentul cererii nejudecat fata de ceasul martorului', MA, 'if (!(Math.abs(t - Number(acumS)) <= toleranta /*PAZA-MARTOR:timp*/)) return', 'if (false) return', '1. CONTROL: un cap declarat cu 400 s'],
|
|
// ---- capul de jurnal si chitanta (verificaPunct, verificaChitanta)
|
|
['cheia care semneaza capul de jurnal nu trebuie sa fie a martorului numit', MA, 'if (!(!!pub && pub.asymmetricKeyType === ALG && witnessIdFromKey(pub) === witnessId /*PAZA-MARTOR:martor*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal semnat de alta cheie'],
|
|
['semnatura martorului pe capul de jurnal nejudecata', MA, 'if (!(verificaSemnaturaText(JSON.stringify(env.statement), pub, s.signature) /*PAZA-MARTOR:punct-semnatura*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal schimbat (hash refacut'],
|
|
['forma capului de jurnal nejudecata', MA, 'if (!(obiect(env) && env.kind === CHECKPOINT_KIND + \'-attestation\' && formaPunctului(st) /*PAZA-MARTOR:punct-forma*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal cu marimea 0'],
|
|
['integritatea capului de jurnal nejudecata', MA, 'if (!(H32.test(sir(env.statementHash)) && sha(JSON.stringify(st)) === env.statementHash /*PAZA-MARTOR:punct-integritate*/)) return', 'if (false) return', '3. CONTROL chitanta: capul de jurnal schimbat fara hash refacut'],
|
|
['forma chitantei nejudecata', MA, '&& Array.isArray(w.path) /*PAZA-MARTOR:chitanta-forma*/)) return', '&& Array.isArray(w.path) || true)) return', '3. CONTROL chitanta: chitanta fara leafHash'],
|
|
['martorul FIXAT de verificator nejudecat', MA, 'if (!(!fixati || fixati.includes(st.witness) /*PAZA-MARTOR:martor*/)) return', 'if (false) return', '9.R1 un martor COERENT'],
|
|
['frunza nelegata de momentul primirii', MA, 'if (!(frunzaMartor(w.head.statementHash, w.receivedAtMs) === w.leafHash /*PAZA-MARTOR:frunza*/)) return', 'if (false) return', '3. CONTROL chitanta: alt moment de primire'],
|
|
['dovada de includere falsa primita', MA, 'if (!(w.treeSize === st.treeSize && verifyInclusion({ leafHash: w.leafHash, index: w.leafIndex, treeSize: w.treeSize, path: w.path, rootHash: st.root }) /*PAZA-MARTOR:includere*/)) return', 'if (false) return', '3. CONTROL chitanta: un element al drumului schimbat'],
|
|
['regula de primire a martorului nejudecata in chitanta', MA, 'if (!(Math.abs(C - R) <= toleranta /*PAZA-MARTOR:primire*/)) return', 'if (false) return', '3. CONTROL: o chitanta a unui martor care a scris in frunza'],
|
|
['promisiunea nejudecata', MA, 'verificaSemnaturaMartor(pr, st.witness).ok /*PAZA-MARTOR:promisiune*/)) return', 'verificaSemnaturaMartor(pr, st.witness).ok || true)) return', '3. CONTROL chitanta: promisiunea cu alt moment'],
|
|
['capul martorizat nu mai trebuie sa fie al registrului verificat', MA, '&& (!h.previous || (!!pe && pe.hash === h.previous.hash)) /*PAZA-MARTOR:registru*/)) return', '&& true) && false) return', '3. CONTROL chitanta: judecata pe alta ramura'],
|
|
// ---- ancorele (ancoreDinChitante)
|
|
['lantul notarizarii nu mai trebuie sa fie al capului de jurnal si cel cerut', MA, 'if (!(lantNotar === plic.statement.chainId && (!chain || chain === plic.statement.chainId) /*PAZA-MARTOR:lant*/)) {', 'if (false) {', '4. CONTROL: chitanta pe alta ramura'],
|
|
['plicul judecat de verificatorul AIP-23 nu mai trebuie sa fie acesta', MA, "if (!(String(v.statementHash || '').toLowerCase() === plic.statementHash /*PAZA-MARTOR:lant*/)) {", 'if (false) {', '4. CONTROL: verificatorul a judecat alt statementHash'],
|
|
['semnatura martorului la verificatorul AIP-23 nu mai trebuie sa fie PASSED', MA, "if (!(!!sg && sg.state === 'PASSED' /*PAZA-MARTOR:lant*/)) {", 'if (false) {', '4. CONTROL: semnatura martorului FAILED'],
|
|
['finalitatea nu mai trebuie sa fie PASSED', MA, "if (!(fs2.stare === 'PASSED' /*PAZA-MARTOR:lant*/)) {", 'if (false) {', '4. CONTROL: finalitate in asteptare'],
|
|
['primirea dupa momentul lantului primita', MA, 'if (!(l.receivedAt <= F + toleranta /*PAZA-MARTOR:primire*/)) {', 'if (false) {', '4. CONTROL: lantul a vazut capul de jurnal INAINTE'],
|
|
['intarzierea maxima nejudecata', MA, 'if (!(F - l.receivedAt <= mmd + toleranta /*PAZA-MARTOR:intarziere*/)) {', 'if (false) {', '4. CONTROL: capul de jurnal vazut de lant la 4000 s'],
|
|
['radacina neverificata pe lant (momentul luat din notarizarea scrisa in chitanta)', MA, 'const fs2 = firstSeenFromVerdict(v);', "const fs2 = { stare: 'PASSED', at: Number(n.firstSeen) };", '4. CONTROL: finalitate FAILED'],
|
|
['lantul cerut nevalid primit de biblioteca', MA, 'if (!(chain == null || (Number.isSafeInteger(chain) && chain > 0))) throw', 'if (false) throw', '9.R5 agent-cli verify --chain 2800x'],
|
|
// ---- lista intreaga a capetelor martorului (verificaCapeteMartorului)
|
|
// (tinta mutata la a treia revizie: de la R3-04 chitanta ultimului cap prinde si ea ramura din 9.R2, deci randul acela nu mai deosebea paznicul)
|
|
['un cap martorizat care nu e in registrul aratat primit', MA, 'if (!(!!e && e.hash === h.hash /*PAZA-MARTOR:registru*/)) {', 'if (false) {', '11.P3-04b CONTROL'],
|
|
['o dovada de echivocare tinuta de martor ignorata', MA, 'if (!(eq.length === 0 /*PAZA-MARTOR:echivocare*/)) {', 'if (false) {', '9.R2 CONTROL: --witness-url'],
|
|
// ---- deschiderea starii
|
|
['un rand de frunza stricat in mijloc taiat in tacere', MA, 'if (!(bun /*PAZA-MARTOR:frunze-mijloc*/)) throw', 'if (false) throw', '9.R16 o stare cu un rand de frunza stricat'],
|
|
['randul taiat la coada ramas in jurnal', MA, "if (coada !== '' /*PAZA-MARTOR:coada*/) {", 'if (false) {', '3. CONTROL: un rand de frunza taiat la coada'],
|
|
['un cap de jurnal peste frunze pierdute primit la deschidere (marimea)', MA, 'if (!(p.treeSize <= ciob.frunze.length /*PAZA-MARTOR:radacina-marime*/)) throw', 'if (false) throw', '9.R16 o stare cu un rand de frunza stricat'],
|
|
// revizia B3 a doua, R2-15: jumatatea "radacina" are acum plantarea ei si randul ei (o frunza din mijloc inlocuita de alt rand valid)
|
|
['radacina capului de jurnal nejudecata la deschidere', MA, 'if (!(ciob.arbore.root(p.treeSize) === p.root /*PAZA-MARTOR:radacina*/)) throw', 'if (false) throw', '10.R2-15 o stare cu o frunza din mijloc'],
|
|
['cioburile vechi pastrate (memoria si discul cresc)', MA, 'for (const j of [...cioburi.keys()]) if (j <= k - CIOBURI_PASTRATE) scoateCiob(j);', '', '9.R3/R12 cioburile'],
|
|
// ---- primirea (primeste, echivoca)
|
|
['mai multe dovezi pastrate pe agent si politica', MA, '!z.eq /*PAZA-MARTOR:echivocare-una*/', 'true', '2. CONTROL: zece echivocari'],
|
|
['dovezile pastrate fara plafon total', MA, 'echivocari.size < maxEchivocari /*PAZA-MARTOR:echivocare-plafon*/', 'true', '9.R9 un agent cu 12 politici'],
|
|
['pastrarea unei dovezi noi nu consuma cota agentului', MA, 'if (limitaAgent && limitaAgent(proof.agentId) /*PAZA-MARTOR:ritm*/) return', 'if (false) return', '9.P11 CONTROL (paznicul nou al lui R11)'],
|
|
['echivocarea nepastrata (fara index)', MA, 'echivocari.set(pid, q) /*PAZA-MARTOR:echivocare*/;', '', '2. GET heads?session'],
|
|
['echivocarea la aceeasi pozitie nedetectata', MA, 'if (laSeq && !(laSeq.e === st.hash) /*PAZA-MARTOR:echivocare*/) return', 'if (false) return', '2. ECHIVOCARE: alta ramura a lui A'],
|
|
['echivocarea prin predecesor nedetectata', MA, 'if (laPrev && !(laPrev.e === st.previous.hash) /*PAZA-MARTOR:echivocare*/) return', 'if (false) return', '2. ECHIVOCARE prin predecesor'],
|
|
['"deja vazut" judecat inaintea predecesorului contrazis', MA, 'const laPrev = st.previous ? gaseste(lista, st.previous.seq) : null;', 'const laPrev = st.previous ? gaseste(lista, st.previous.seq) : null; if (laSeq && laSeq.e === st.hash) return stareaCapului(laSeq.h);', '9.R4 un cap la un seq deja tinut'],
|
|
['un cap mai VECHI primit', MA, 'if (!(!ultim || st.seq > ultim.q) /*PAZA-MARTOR:vechi*/) return', 'if (false) return', '2. CONTROL: un cap mai VECHI'],
|
|
['consistenta capetelor nejudecata (succesorul)', MA, 'if (!(prevBun /*PAZA-MARTOR:succesor*/)) return', 'if (false) return', '2. CONTROL: un cap care nu numeste ca predecesor'],
|
|
['limita de ritm pe agent scoasa', MA, 'if (limitaAgent && limitaAgent(st.agentId) /*PAZA-MARTOR:ritm*/) return', 'if (false) return', '2. CONTROL ritm pe agent'],
|
|
['limita pe agent judecata pe corpul neautentificat', MA, 'const v = verificaCapSemnat(env, { acumS: acumMs / 1000, toleranta });', "if (limitaAgent && env && env.statement && limitaAgent(String(env.statement.agentId))) return { cod: 429, corp: { error: 'rate_limited' } }; const v = verificaCapSemnat(env, { acumS: acumMs / 1000, toleranta });", '2. CONTROL: 14 cereri cu semnatura rea'],
|
|
['cota pe agent consumata de un cap deja vazut (retrimiterea capului public)', MA, 'if (dupaCap.has(id)) return stareaCapului(id);', "if (limitaAgent && limitaAgent(st.agentId)) return { cod: 429, corp: { error: 'rate_limited' } }; if (dupaCap.has(id)) return stareaCapului(id);", '9.R11 un strain retrimite'],
|
|
['bugetul jurnalului pe ora scos', MA, 'if (bugetPlin /*PAZA-MARTOR:buget*/) return', 'if (false) return', '9.R12 bugetul jurnalului'],
|
|
['ciobul plin nerotit', MA, 'if (!(cur.frunze.length < maxFrunze /*PAZA-MARTOR:plin*/)) roteste();', 'if (false) roteste();', '9.R3/R12 cioburile'],
|
|
// ---- scrierile durabile, paginarea, erorile publice
|
|
['o scriere scurta neterminata (un singur writeSync)', MA, 'off += n;', 'off = buf.length;', '9.R16 fiecare fs.writeSync'],
|
|
['o scriere cazuta lasata pe disc (fara taiere inapoi)', MA, 'try { fs.ftruncateSync(fd, inainte); fs.fsyncSync(fd); } catch { try { fs.truncateSync(f, inainte); } catch { e.taiereEsuata = true; } }', '', '9.R16 o scriere a frunzei care cade'],
|
|
['mesajul notarizarii cazute public in lastError', MA, "else p.lastError = rez && typeof rez.cod === 'string' && /^[a-z_]{1,40}$/.test(rez.cod) ? rez.cod : 'notarization_failed';", 'else p.lastError = String(rez && rez.motiv);', '9.R18 o notarizare cazuta'],
|
|
['GET heads nepaginat', MA, 'const a = primulDeLa(l, from); const pagina = l.slice(a, a + limit);', 'const a = 0; const pagina = l;', '9.R10 GET heads?session'],
|
|
['submit iese 1 cand martorul tine deja intrarea ceruta', MA, 'if (ultim.seq === s && e && e.hash === ultim.hash) {', 'if (false) {', '9.R7 submit fara nimic nou'],
|
|
// ---- serviciul
|
|
['limita de ritm pe client scoasa', MS, 'if (peClient(client) /*PAZA-MARTOR:ritm*/) return', 'if (false) return', '2. CONTROL ritm pe client'],
|
|
['plafonul de chei al limitatorului scos', MS, 'if (!(m.size < maxChei /*PAZA-MARTOR:ritm-plafon*/)) {', 'if (false) {', '9.R13 limitatorul'],
|
|
['cheile expirate ale limitatorului necuratate', MS, 'if (m.size >= maxChei && acum - ultimaCuratare >= 1000) curata(acum);', '', '9.R13 limitatorul'],
|
|
['cheia unui client IPv6 pe adresa intreaga (nu pe /64)', MS, 'return tot.slice(0, 4).map(', 'return tot.slice(0, 8).map(', '9.R13 cheia clientului'],
|
|
['mesajul unei erori interne in raspunsul public', MS, "code: e && /^E[A-Z]+$/.test(String(e.code)) ? 'io_error' : 'internal'", "code: 'io_error', detail: String(e && e.message)", '9.R16/R18 GET pe un cap'],
|
|
['notarizarea pe alt lant decat 28001', MS, 'if (!(chainId === LANT /*PAZA-MARTOR:sandbox*/)) return', 'if (false) return', '3. CONTROL: notarizatorul pe un RPC care serveste lantul 2800'],
|
|
// ---- linia de comanda a verificatorului
|
|
['martorii fixati nu mai ajung la verificare', CLI, 'let fixati = martori.length ? martori : null;', 'let fixati = null;', '9.R17 agent-cli verify'],
|
|
['--chain nevalid primit (paza care cade deschisa)', CLI, 'if (!/^[1-9]\\d{0,14}$/.test(String(v))) throw new Folosire(', 'if (false) throw new Folosire(', '9.R5 agent-cli verify --chain 2800x'],
|
|
['un martor care nu raspunde la --witness-url luat drept verde', CLI, "if (!c.ok) adauga({ sursa: 'list', clasa: 'NEMASURATA',", "if (!c.ok) adauga({ sursa: 'list', clasa: 'VERIFICATA',", '9.R2 CONTROL: --witness-url'],
|
|
// ---- a doua revizie adversariala B3 (2026-10-19): fiecare paznic nou, cu randul lui 10.R2-<nn> / 10.P2-<nn>
|
|
// R2-01: lista capetelor, semnata de martor pagina cu pagina, din ultimele 300 s
|
|
['forma paginii de capete nejudecata', MA, 'if (!(formaBuna /*PAZA-MARTOR:pagina-forma*/)) return', 'if (false) return', '10.P2-01 CONTROL'],
|
|
['integritatea paginii de capete nejudecata', MA, 'if (!(H32.test(sir(env.statementHash)) && sha(JSON.stringify(st)) === env.statementHash /*PAZA-MARTOR:pagina-integritate*/)) return', 'if (false) return', '10.P2-01 CONTROL'],
|
|
['semnatura martorului pe pagina de capete nejudecata', MA, 'if (!(s.ok /*PAZA-MARTOR:pagina-semnatura*/)) return', 'if (false) return', '10.R2-01 --witness <W>'],
|
|
['o pagina veche (reluata) primita drept lista de acum', MA, 'if (!(Math.abs(t - Number(acumS)) <= toleranta /*PAZA-MARTOR:pagina-vechi*/)) return', 'if (false) return', '10.R2-01 --witness <W>'],
|
|
['paginile care nu dau numarul si ultimul cap semnate primite', MA, 'if (!(aduna /*PAZA-MARTOR:pagina-numar*/)) return', 'if (false) return', '10.P2-01 CONTROL'],
|
|
['lista citita din campurile NESEMNATE de langa pagina', MA, 'witness = st.witness; heads.push(...st.heads);', 'witness = st.witness; heads.push(...(Array.isArray(r.j.heads) ? r.j.heads : []).map((h) => ({ seq: h.seq, hash: h.hash, id: h.id })));', '10.R2-01 --witness <W>'],
|
|
['lista semnata de alt martor decat cel fixat primita', CLI, 'else if (!(!fixati || fixati.includes(c.witness)) /*PAZA-MARTOR:martor-url*/)', 'else if (false)', '10.R2-15 agent-cli verify --witness <alt id>'],
|
|
// R2-02: dovezile de echivocare legate de ciobul capului contrazis; plafonul = marginea prin constructie
|
|
['dovada legata de ciobul curent, nu de al capului contrazis', MA, 'k: tinut.k /*PAZA-MARTOR:echivocare-ciob*/', 'k: cur.k', '10.R2-02 maxFrunze 4'],
|
|
['plafonul dovezilor fix (2.000), nu marginea lor prin constructie', MA, 'if (maxEchivocari == null) maxEchivocari = CIOBURI_PASTRATE * maxFrunze;', 'if (maxEchivocari == null) maxEchivocari = 2000;', '10.P2-02 CONTROL'],
|
|
// R2-03: la plafonul de chei se scoate cheia cea mai veche
|
|
['la plafonul de chei clientul NOU e refuzat (nu e scoasa cheia cea mai veche)', MS, '{ m.delete(m.keys().next().value); scoase++; }', 'return true;', '10.R2-03 limitatorul'],
|
|
// R2-04: o lista cu elemente care nu sunt capete
|
|
['un element care nu e cap primit de verificaCapeteMartorului', MA, 'if (!(rau < 0 /*PAZA-MARTOR:capete-forma*/)) return', 'if (false) return', '10.R2-04 verificaCapeteMartorului'],
|
|
// R2-05: sub un martor fixat, numai chitante ale lui
|
|
['ancora simpla primita sub un martor fixat', CLI, 'if (fixare /*PAZA-MARTOR:martor-simple*/) adauga(', 'if (false) adauga(', '10.R2-05 registrul antedatat'],
|
|
['--witness fara nicio chitanta a lui primit', CLI, 'if (!(!martori.length || dovezi.some((d) => d.chitanta) || (tinute && tinute.length)) /*PAZA-MARTOR:martor-fara-chitanta*/)', 'if (false)', '10.R2-05 registrul antedatat'],
|
|
// R2-07: un ciob nu se scoate pana nu i s-a notarizat capul de jurnal final
|
|
['rotirea scoate un ciob nenotarizat', MA, 'if (!(!plin || !rotireaPierdeNenotarizat() /*PAZA-MARTOR:rotire-notarizata*/)) {', 'if (false) {', '10.R2-07 maxFrunze 2'],
|
|
['la deschidere un ciob vechi nenotarizat sters', MA, 'if (!(faraLant || finalNotarizatPeDisc(k) /*PAZA-MARTOR:ciob-vechi-notarizat*/)) {', 'if (false) {', '10.P2-07 CONTROL'],
|
|
// R2-08: ciobul nou se naste intreg; un ciob fara ciob.json isi reface legatura
|
|
['ciobul nou nascut pe jumatate (dosarul inaintea lui ciob.json)', MA, "scrieAtomic(path.join(t, 'ciob.json'), meta); fs.renameSync(t, d) /*PAZA-MARTOR:ciob-intreg*/; fsyncDosar(dc);", 'fs.renameSync(t, d); scrieAtomic(fcb, meta);', '10.R2-08 rotirea cade'],
|
|
['legatura ciobului fara ciob.json nerefacuta', MA, 'previousShardFinal: legat /*PAZA-MARTOR:ciob-legatura*/', 'previousShardFinal: null', '10.P2-08 CONTROL'],
|
|
// R2-09: ruta capului de jurnal pentru un ciob pastrat (si inghetat)
|
|
['ruta capului de jurnal numai pe ciobul curent', MA, 'const c = shard == null ? cur : cioburi.get(Number(shard)) /*PAZA-MARTOR:ciob-cerut*/;', 'const c = cur;', '10.R2-09 un monitor'],
|
|
// R2-10: lacatul isi poarta pornirea masinii si a procesului
|
|
['lacatul de dinainte de ultima pornire a masinii tinut', MA, 'if (!(o.boot == null || aceeasiPornire(o.boot, boot) /*PAZA-MARTOR:lacat-pornire*/)) return', 'if (false) return', '10.R2-10 lacatTinut'],
|
|
['lacatul cu pid-ul luat de alt proces tinut', MA, 'if (!(acum == null || acum === String(o.start) /*PAZA-MARTOR:lacat-proces*/)) return', 'if (false) return', '10.R2-10 lacatTinut'],
|
|
// R2-11 / R2-12: codurile de iesire ale lui martor-agenti.mjs si valorile goale ale lui --witness
|
|
['receipt iese 1 la un martor care raspunde 503 sau 429', MA, 'if (r.status >= 500 || r.status === 429 /*PAZA-MARTOR:cod-indisponibil*/) {', 'if (false) {', '10.R2-11 receipt'],
|
|
['verify-receipt iese 1 pe raspunsul unui cap pending', MA, "if (obiect(x) && !esteChitanta(x) && x.status === 'pending' /*PAZA-MARTOR:cod-pending*/) {", 'if (false) {', '10.R2-11 receipt'],
|
|
['valorile goale ale lui --witness aruncate (verify-receipt nefixat)', MA, "(rest[i + 1] === undefined ? [''] : String(rest[i + 1]).split(',')) /*PAZA-MARTOR:steag-gol*/", "(rest[i + 1] === undefined ? [] : String(rest[i + 1]).split(',').filter(Boolean))", '10.R2-12 verify-receipt'],
|
|
// R2-13: bugetul jurnalului citit inaintea cotei agentului, consumat dupa ea
|
|
['cota agentului consumata inaintea bugetului jurnalului', MA, 'const bugetPlin = ', "if (limitaAgent && limitaAgent(st.agentId)) return { cod: 429, corp: { error: 'rate_limited' } }; const bugetPlin = ", '10.R2-13 cota pe agent 3'],
|
|
['bugetul jurnalului consumat la citire (serviciul fara `ar`)', MS, "{ ar: () => acceptate.ar('jurnal') /*PAZA-MARTOR:buget-citit*/ }", '{}', '10.P2-13 CONTROL prin serviciu'],
|
|
// R2-14: Retry-After prin usa gateway-ului
|
|
['usa gateway-ului arunca Retry-After', GWF, "? { 'retry-after': ra } : {})", '? {} : {})', '10.R2-14 prin usa gateway-ului'],
|
|
// ---- a treia revizie adversariala B3 (2026-10-19): fiecare paznic nou, cu randul lui 11.R3-<nn> / 11.P3-<nn>
|
|
// R3-01: un ciob de scos se redenumeste intai; un rest al formei de dinainte se sterge la deschidere
|
|
['ciobul de scos sters pe loc, fara sa fie redenumit intai', MA, 'fs.renameSync(d, t) /*PAZA-MARTOR:ciob-sters-intai*/; fsyncDosar(dc); tinta = t;', 'void t;', '11.R3-01 o oprire'],
|
|
['restul unei stergeri intrerupte tinut drept un ciob la deschidere', MA, 'if (restDeStergere(k) /*PAZA-MARTOR:ciob-rest*/) {', 'if (false) {', '11.P3-01 CONTROL'],
|
|
// revizia B3 a patra, R4-06: fiecare jumatate a restului cu plantarea si randul ei
|
|
['un rest fara ciob.json sau frunze.ndjson tinut drept un ciob', MA, "(!fs.existsSync(path.join(d, 'ciob.json')) || !fs.existsSync(path.join(d, 'frunze.ndjson'))) /*PAZA-MARTOR:ciob-fara-fisiere*/", 'false', '11.P3-01 CONTROL'],
|
|
['un ciob marcat .de-sters tinut drept un ciob', MA, "fs.existsSync(path.join(dc, String(k), '.de-sters')) /*PAZA-MARTOR:ciob-marcat*/", 'false', '12.R4-06 redenumirea'],
|
|
// R3-02: dovada care traieste cel mai mult pe sesiune
|
|
['o dovada contra unui cap din ciobul vechi tine locul celei care conteaza', MA, 'const inlocuieste = !!q0 && q0.k < tinut.k /*PAZA-MARTOR:echivocare-mai-noua*/;', 'const inlocuieste = false;', '11.R3-02 agentul X'],
|
|
// R3-03: un steag fara valoare, sau cu valoarea goala, e folosire gresita
|
|
['un steag fara valoare primit in tacere (martor-agenti.mjs)', MA, "if (!(v !== undefined && v !== '' && !/^--[a-z]/.test(v)) /*PAZA-MARTOR:steag-fara-valoare*/) throw", 'if (false) throw', '11.R3-03 agent-cli verify'],
|
|
['un steag fara valoare primit in tacere (agent-cli.mjs)', CLI, "if (!(v !== undefined && !/^--[a-z]/.test(v) && (v !== '' || GOALE_PERMISE.includes(f))) /*PAZA-MARTOR:steag-fara-valoare*/) throw", 'if (false) throw', '11.R3-03 agent-cli verify'],
|
|
// R3-04: chitanta ultimului cap tinut de martor, sub --witness-url
|
|
['chitanta unui cap tinut nu mai e ceruta (marginea o dau chitantele alese de agent)', CLI, "if (dovezi.some((d) => d.clasa === 'VERIFICATA' && d.id === h.id) /*PAZA-MARTOR:martor-chitanta-ceruta*/) continue;", 'if (true) continue;', '11.R3-04 martorul fixat'],
|
|
// revizia B3 a cincea, R5-01: numai o chitanta VERIFICATA a capului scuteste aducerea (forma veche: orice chitanta cu acelasi id, verificata sau nu)
|
|
['o chitanta NEVERIFICATA cu acelasi id (o copie veche) scuteste aducerea chitantei capului tinut', CLI, "if (dovezi.some((d) => d.clasa === 'VERIFICATA' && d.id === h.id) /*PAZA-MARTOR:martor-chitanta-ceruta*/) continue;", 'if (dovezi.some((d) => d.id === h.id)) continue;', '13.R5-01 copia VECHE'],
|
|
// R3-05: codurile lui receipt si submit
|
|
['submit "nimic nou" iese 0 pe o stare a capului care nu e a lui', MA, "if (!(r.status === 200 && obiect(r.j) && r.j.id === ultim.id && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-nimic-nou*/) {", 'if (false) {', '11.R3-05 receipt'],
|
|
['receipt pe un 200 care nu e JSON (exceptie, 1)', MA, "if (!(obiect(r.j) && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-ne-json*/) {", 'if (false) {', '11.R3-05 receipt'],
|
|
// ---- a patra revizie adversariala B3 (2026-10-19): regula de proiectare a lui --witness-url si paznicii ei, cu randurile 12.R4-<nn> / 12.P4-<nn>
|
|
['numai ULTIMUL cap tinut cu chitanta (capetele intermediare fara)', CLI, 'tinute || [] /*PAZA-MARTOR:martor-fiecare-cap*/', '(tinute || []).slice(-1)', '12.R4-03 martorul tine'],
|
|
['id-ul capului unei chitante citit fara garda (exceptie pe o forma gresita)', CLI, "const idCap = (a) => { const p = M.chitantaDin(a); const w = p && typeof p === 'object' ? p.witnessReceipt : null; const h = w && typeof w === 'object' ? w.head : null; return h && typeof h === 'object' && typeof h.statementHash === 'string' ? h.statementHash : null; } /*PAZA-MARTOR:chitanta-id-sigur*/;", 'const idCap = (a) => M.chitantaDin(a).witnessReceipt.head.statementHash;', '12.R4-02 proxy'],
|
|
// (revizia a cincea: paznicul `martor-adusa-vina` al formei de dinainte e acum clasa dovezii, pe ORICE drum: `chitanta-alt-registru`, cu cele doua sensuri)
|
|
['o chitanta care nu tine LOCAL judecata vina registrului, oricare ar fi regula (o includere stricata face un FAILED)', CLI, "const vina = l.rule === 'registru' /*PAZA-MARTOR:chitanta-alt-registru*/ || falsificata;", 'const vina = true;', '13.R5-05 chitanta agentului cu includerea stricata'],
|
|
['o chitanta a martorului fixat pentru alt registru (o ramura) luata drept NEMASURAT', CLI, "l.rule === 'registru' /*PAZA-MARTOR:chitanta-alt-registru*/", 'false', '12.P4-03b CONTROL'],
|
|
['submit pe un 200/202 care nu e starea capului trimis iese 0', MA, "(r.status === 200 || r.status === 202) && !(obiect(r.j) && H32.test(sir(r.j.id)) && r.j.session === env.statement.session && r.j.seq === env.statement.seq && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-trimis*/", 'false', '12.R4-04 submit'],
|
|
['un steag necunoscut primit in tacere (agent-cli.mjs)', CLI, 'if (!(STEAGURI[cmd].includes(s) /*PAZA-MARTOR:steag-necunoscut*/)) throw', 'if (false) throw', '12.R4-05 agent-cli'],
|
|
['un steag necunoscut primit in tacere (martor-agenti.mjs)', MA, 'if (!(STEAGURI[cmd].includes(s) /*PAZA-MARTOR:steag-necunoscut*/)) throw', 'if (false) throw', '12.R4-05 agent-cli'],
|
|
['un steag dat de doua ori primit (agent-cli.mjs)', CLI, "if (!(!vazute.has(s) || s === '--witness') /*PAZA-MARTOR:steag-dublu*/) throw", 'if (false) throw', '12.R4-05 agent-cli'],
|
|
['un steag dat de doua ori primit (martor-agenti.mjs)', MA, "if (!(!vazute.has(s) || s === '--witness') /*PAZA-MARTOR:steag-dublu*/) throw", 'if (false) throw', '12.R4-05 agent-cli'],
|
|
['departajarea a doua dovezi la deschidere rasturnata (ramane cea veche)', MA, 'vechi && vechi.k >= x.k /*PAZA-MARTOR:echivocare-departajare*/', 'vechi && vechi.k <= x.k', '12.R4-06 o inlocuire'],
|
|
// ---- a cincea revizie adversariala B3 (2026-10-19): verify ca EVALUARE COMPLETA cu un singur verdict, tipurile, asteptarea pe 429; randurile 13.R5-<nn> / 13.P5-<nn>
|
|
// R5-01: verdictul se da DUPA ce s-a adunat tot, iar o vina dovedita trece inaintea oricarui NEMASURAT
|
|
['un NEMASURAT judecat inaintea unei vine dovedite (verdictul dupa ordinea claselor)', CLI, "const verdict = vina.length ? 'FAILED' : nemasurate.length ? 'NOT MEASURED' : 'OK' /*PAZA-MARTOR:verdict-vina-intai*/;", "const verdict = nemasurate.length ? 'NOT MEASURED' : vina.length ? 'FAILED' : 'OK';", '13.R5-01 capul seq 4'],
|
|
['un NEMASURAT iesit pe drum, inaintea judecatii (forma de dinainte de a cincea revizie)', CLI, 'const adauga = (d) => { dovezi.push({ ceruta: true, ...d }); };', "const adauga = (d) => { dovezi.push({ ceruta: true, ...d }); if (d.clasa === 'NEMASURATA') { console.error('NOT MEASURED: ' + d.detail + ' (planted: an early exit)'); process.exit(2); } };", '13.R5-01 capul seq 4'],
|
|
['o copie veche a unei chitante ramane ceruta langa chitanta verificata a aceluiasi cap', CLI, "dovezi.some((x) => x !== d && x.clasa === 'VERIFICATA' && x.id === d.id) /*PAZA-MARTOR:copie-veche*/", 'false', '13.R5-01 copia VECHE'],
|
|
// R5-03 / R5-05: clasa unei chitante care nu tine: vina agentului numai pe o semnatura INVALIDA a martorului fixat (predata) sau pe alt registru
|
|
['o pana a notarului sau a RPC-ului ales de verificator judecata vina registrului', CLI, "clasa: 'NEMASURATA' /*PAZA-MARTOR:lant-nemasurat*/", "clasa: 'VINA'", '13.R5-03 registru CINSTIT'],
|
|
['o chitanta a agentului cu semnatura martorului fixat INVALIDA luata drept NEMASURATA', CLI, "const falsificata = sursa === 'agent' && numit !== null && (!fixati || fixati.includes(numit)) /*PAZA-MARTOR:chitanta-falsificata*/;", 'const falsificata = false;', '13.P5-05 CONTROL vina agentului'],
|
|
['o chitanta ADUSA cu semnatura martorului invalida judecata vina registrului (o adresa face un FAILED)', CLI, "sursa === 'agent' && numit !== null", 'numit !== null', '13.P5-05 CONTROL un proxy'],
|
|
// R5-04: regula (c), tipurile judecate inainte de folosire
|
|
['submit primeste un 200/202 al carui `status` nu e un sir ({"toString":0}): --out scris si iesire 0', MA, "r.j.seq === env.statement.seq && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-trimis*/", 'r.j.seq === env.statement.seq) /*PAZA-MARTOR:cod-trimis*/', '13.R5-04 submit'],
|
|
['un receivedAtMs negativ primit de forma chitantei (exceptia vine mai jos, in frunza)', MA, 'w.receivedAtMs >= 0 /*PAZA-MARTOR:chitanta-tipuri*/', 'true', '13.R5-04 un proxy'],
|
|
['un camp care trebuie sa fie sir trecut prin String() (exceptie pe {"toString":0})', MA, "const sir = (x) => (typeof x === 'string' ? x : '');", 'const sir = (x) => String(x);', '13.R5-04 un proxy'],
|
|
// R5-06: o citire peste limita spune cand se poate reincerca, iar verificatorul asteapta
|
|
['pe un 429 cu Retry-After verificatorul nu mai asteapta', MA, 'if (!(r.status === 429 && Number.isSafeInteger(r.retryAfter) && r.retryAfter <= buget.ramasS && i < 50) /*PAZA-MARTOR:asteapta-429*/) return r;', 'if (true) return r;', '13.R5-06 serviciul'],
|
|
['429-ul unei citiri fara Retry-After', MS, "{ 'retry-after': String(ra) } /*PAZA-MARTOR:get-retry-after*/", '{}', '13.R5-06 serviciul'],
|
|
// ---- a sasea revizie B3 (2026-10-19, verificarea tintita a reparatiilor R5): N1..N3, randurile 14.N<n>
|
|
// N1: o chitanta fara --rpc e o dovada NEMASURATA [rpc], judecata la locul ei; doua plantari pe acelasi paznic, una pe fiecare sens (iesirea timpurie
|
|
// a formei de dinainte, care ascunde ramura aratata de lista; si paznicul scos, care ia chitanta drept VERIFICATA fara lant)
|
|
['o chitanta fara --rpc iese pe drum cu 2, inaintea judecatii (forma de dinainte de a sasea revizie: folosire gresita)', CLI, 'if (!rpc /*PAZA-MARTOR:fara-rpc*/) return {', "if (!rpc) throw new Folosire('a witness receipt needs --rpc <url> (planted: the early exit of before the sixth review)'); if (false) return {", '14.N1 fara --rpc'],
|
|
['o chitanta fara --rpc judecata VERIFICATA (marginea luata fara lant)', CLI, 'if (!rpc /*PAZA-MARTOR:fara-rpc*/) return {', 'if (false) return {', '14.N1 fara --rpc'],
|
|
// N2: receipt scrie numai chitanta capului cerut, a registrului dat, cu semnatura martorului verificata (cu cel fixat, daca e dat)
|
|
['receipt scrie raspunsul adresei fara sa il verifice (forma de dinainte de a sasea revizie)', MA, 'if (!(!!l && l.ok) /*PAZA-MARTOR:receipt-verificata*/) {', 'if (false) {', '14.N2 receipt printr-un proxy'],
|
|
['receipt nu cere ca chitanta sa fie a capului cerut (--id)', MA, 'capPrimit === idc /*PAZA-MARTOR:receipt-id*/', 'true', '14.N2 receipt printr-un proxy'],
|
|
['receipt nu leaga chitanta de registrul dat (--ledger)', MA, 'ledger /*PAZA-MARTOR:receipt-registru*/,', 'ledger: null,', '14.N2 receipt fata de --ledger'],
|
|
['receipt nu cere martorul fixat prin --witness', MA, 'martori: martori.length ? martori : null /*PAZA-MARTOR:receipt-fixat*/', 'martori: null', '14.N2 receipt fata de --ledger'],
|
|
// N3: regula (c) pe drumul "nimic nou" al lui submit
|
|
['submit "nimic nou" foloseste `latest` (campul nesemnat) fara sa ii judece tipurile', MA, 'if (!(ultim === null || (obiect(ultim) && Number.isSafeInteger(ultim.seq) && ultim.seq >= 0 && HASH_INTRARE.test(sir(ultim.hash)) && H32.test(sir(ultim.id)))) /*PAZA-MARTOR:nimic-nou-latest*/) {', 'if (false) {', '14.N3 submit'],
|
|
['submit "nimic nou" primeste o stare a capului cu status care nu e sir ({"toString":0}): --out scris, apoi exceptie', MA, "r.j.id === ultim.id && typeof r.j.status === 'string') /*PAZA-MARTOR:cod-nimic-nou*/", 'r.j.id === ultim.id) /*PAZA-MARTOR:cod-nimic-nou*/', '14.N3 submit'],
|
|
// ---- vocabularul felului (proof-kinds, citit si de judecatorul martorului) si usa gateway-ului
|
|
['vocabularul felului agent-witness-checkpoint slabit in proof-kinds', PKF, 'treeSize: [(v) => Number.isSafeInteger(v) && v >= 1,', 'treeSize: [(v) => true,', '9.R8 felul agent-witness-checkpoint'],
|
|
['usa gateway-ului primeste un POST din browser (Origin)', GWF, "if (req.method === 'POST' && req.headers.origin !== undefined) return json(res, 403,", 'if (false) return json(res, 403,', '9.R13 gateway: un POST'],
|
|
];
|
|
// PLANTARILE NEUTRE (revizia B3 a cincea, R5-05): schimba numai ORDINEA in care verify aduna si judeca dovezile. Verdictul e o functie de multimea
|
|
// dovezilor, nu de ordinea lor, deci cu oricare din ele proba trebuie sa ramana VERDE intreaga; un rand rosu aici e un ESEC al componentei (ordinea
|
|
// a ajuns sa conteze), numarat ca orice control cazut. [nume, fisier, tipar, inlocuire]
|
|
const NEUTRE = [
|
|
['ordinea adunarii rasturnata: capetele tinute (si chitantele lor aduse) inaintea lui --anchors', CLI, 'await fazaAnchors(); await fazaCapete();', 'await fazaCapete(); await fazaAnchors();'],
|
|
['intrarile din --anchors judecate de la ultima la prima', CLI, 'for (const [i, a] of toate2.entries()) {', 'for (const [i, a] of [...toate2.entries()].reverse()) {'],
|
|
['capetele tinute parcurse de la ultimul la primul', CLI, 'for (const h of tinute || [] /*PAZA-MARTOR:martor-fiecare-cap*/) {', 'for (const h of [...(tinute || [])].reverse()) {'],
|
|
];
|
|
// markerii care NU au plantare, fiecare cu motivul (azi niciunul: o exceptie noua se scrie aici, cu motivul, nu se lasa neacoperita). De la a doua
|
|
// revizie (R2-15) garda priveste si agent-cli.mjs: paznicii martorului din linia de comanda poarta markeri, deci si plantari.
|
|
const EXCEPTII = [];
|
|
const PROPRII = ['agent-policy.mjs', 'agent-ledger.mjs', 'agent-aprobare.mjs', 'agent-ancora.mjs', CLI, MA, MS, 'proba-martor-agenti.mjs'];
|
|
// surorile: din importurile relative '../<dosar>/<fisier>' ale modulului (aceeasi asezare in arborele de dezvoltare si in copia publicata)
|
|
const SURORI = [...fs.readFileSync(path.join(AICI, MA), 'utf8').matchAll(/from '\.\.\/([^'/]+)\/([^'/]+)'/g)].map((x) => [x[1], x[2]]);
|
|
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 GW_DIR = path.resolve(AICI, '..', '..', 'cloud-gateway'); const GW = path.join(GW_DIR, 'gateway.mjs');
|
|
const fisierPlantat = (t, d, fisier) => (fisier === GWF ? path.join(t, 'cloud-gateway', 'gateway.mjs') : path.join(d, fisier));
|
|
function copie(cuGateway = false) {
|
|
const t = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-martor-ctl-')); const d = path.join(t, path.basename(AICI)); fs.mkdirSync(d);
|
|
for (const f of PROPRII) fs.copyFileSync(path.join(AICI, f), path.join(d, f));
|
|
for (const [dir, f] of SURORI) { fs.mkdirSync(path.join(t, dir), { recursive: true }); fs.copyFileSync(path.join(AICI, '..', dir, f), path.join(t, dir, f)); }
|
|
if (cuGateway) fs.cpSync(GW_DIR, path.join(t, 'cloud-gateway'), { recursive: true, filter: (s) => !/node_modules/.test(s) });
|
|
return { t, d };
|
|
}
|
|
function ruleaza(d, gateway = fs.existsSync(GW) ? GW : null) {
|
|
return new Promise((resolve) => {
|
|
const c = spawn(process.execPath, [path.join(d, 'proba-martor-agenti.mjs')], { env: { ...process.env, AERE_VERIFY_PROOF: VERIFY ? path.resolve(VERIFY) : '', ...(gateway ? { AERE_GATEWAY: gateway } : {}) } });
|
|
let out = ''; const ceas = setTimeout(() => c.kill(), 300000);
|
|
c.stdout.on('data', (x) => { out += x; }); c.stderr.on('data', (x) => { out += x; });
|
|
c.on('close', (cod) => { clearTimeout(ceas); const linii = out.split('\n'); resolve({ cod, rulat: /martor-agenti: \d+\/\d+ cum trebuia/.test(out), rosii: linii.filter((l) => l.startsWith(' RAU ')), sarite: linii.filter((l) => l.startsWith(' -- ')) }); });
|
|
});
|
|
}
|
|
// o plantare NEUTRA: proba rulata si verde intreaga (zero rosii, zero sarite in afara usii gateway-ului)
|
|
async function neutru([nume, fisier, din, inl]) {
|
|
const { t, d } = copie();
|
|
try {
|
|
const f = fisierPlantat(t, d, fisier); const src = fs.readFileSync(f, 'utf8');
|
|
if (src.split(din).length !== 2) return [false, ` STRICAT (neutra) ${nume}: tiparul apare de ${src.split(din).length - 1} ori in ${fisier}`];
|
|
fs.writeFileSync(f, src.replace(din, () => inl));
|
|
const r = await ruleaza(d);
|
|
if (!r.rulat) return [false, ` STRICAT (neutra) ${nume}: proba nu a ajuns la rezumat (cod ${r.cod})`];
|
|
const sarite = r.sarite.filter((l) => !/usa gateway-ului/.test(l));
|
|
if (!r.rosii.length && !sarite.length) return [true, ` VERDE cum trebuia (neutra) ${nume} (proba intreaga verde: ordinea nu schimba nimic)`];
|
|
return [false, ` CONTROL CAZUT (neutra) ${nume}: ${r.rosii.length} rosii, ${sarite.length} sarite: ${[...r.rosii, ...sarite].join(' | ').slice(0, 300)}`];
|
|
} finally { fs.rmSync(t, { recursive: true, force: true }); }
|
|
}
|
|
async function planteaza([nume, fisier, din, inl, tinta]) {
|
|
const cuGw = fisier === GWF; if (cuGw && !fs.existsSync(GW)) return [true, ` (sarit pe nume: ${nume}: gateway.mjs nu e in aceasta copie)`];
|
|
const { t, d } = copie(cuGw);
|
|
try {
|
|
const f = fisierPlantat(t, d, fisier); const src = fs.readFileSync(f, 'utf8');
|
|
if (src.split(din).length !== 2) return [false, ` STRICAT ${nume}: tiparul apare de ${src.split(din).length - 1} ori in ${fisier}`];
|
|
fs.writeFileSync(f, src.replace(din, () => inl));
|
|
const r = await ruleaza(d, cuGw ? f : undefined);
|
|
if (!r.rulat) return [false, ` STRICAT ${nume}: proba nu a ajuns la rezumat (cod ${r.cod})`];
|
|
if (r.rosii.some((l) => l.startsWith(' RAU ' + tinta))) return [true, ` ROSU cum trebuia ${nume} (randul '${tinta}' pica)`];
|
|
return [false, ` CONTROL CAZUT ${nume}: randul '${tinta}' a ramas verde (${r.rosii.length} rosii altundeva)`];
|
|
} finally { fs.rmSync(t, { recursive: true, force: true }); }
|
|
}
|
|
// ACOPERIREA: fiecare aparitie a unui marker PAZA-MARTOR din MA si MS sta in tiparul unei plantari (sau pe EXCEPTII); intoarce markerii neacoperiti
|
|
function acoperire(plantari) {
|
|
const lipsa = []; let total = 0;
|
|
for (const fisier of [MA, MS, CLI]) {
|
|
const src = fs.readFileSync(path.join(AICI, fisier), 'utf8');
|
|
const spatii = plantari.filter((p) => p[1] === fisier).map((p) => { const i = src.indexOf(p[2]); return i < 0 ? null : [i, i + p[2].length]; }).filter(Boolean);
|
|
for (const mk of src.matchAll(/PAZA-MARTOR:([a-z-]+)/g)) {
|
|
total++;
|
|
const rand = src.slice(0, mk.index).split('\n').length;
|
|
if (spatii.some(([a, b]) => mk.index >= a && mk.index < b)) continue;
|
|
if (EXCEPTII.some(([f, nume, ancora]) => f === fisier && nume === mk[1] && src.slice(Math.max(0, mk.index - 200), mk.index + 50).includes(ancora))) continue;
|
|
lipsa.push(`${fisier}:${rand} ${mk[1]}`);
|
|
}
|
|
}
|
|
return { total, lipsa };
|
|
}
|
|
let rele = 0;
|
|
for (const [dir, f] of SURORI) if (!fs.existsSync(path.join(AICI, '..', dir, f))) { console.log(` STRICAT sora ${dir}/${f} nu e langa componenta: copia nu se poate face`); process.exitCode = 1; process.exit(); }
|
|
const ac = acoperire(PLANTARI);
|
|
if (!ac.lipsa.length) console.log(` OK acoperirea: toti cei ${ac.total} markeri PAZA-MARTOR din ${MA}, ${MS} si ${CLI} au plantarea lor (exceptii: ${EXCEPTII.length})`);
|
|
|
|
else { rele++; console.log(` ESEC acoperirea: ${ac.lipsa.length} din ${ac.total} markeri fara plantare si fara exceptie scrisa: ${ac.lipsa.join(', ')}`); }
|
|
// martorul acoperirii: cu o plantare scoasa, garda trebuie sa numeasca exact markerul ei (altfel o garda care nu vede nimic ar iesi verde)
|
|
const scoasa = PLANTARI.findIndex((p) => p[0] === 'promisiunea nejudecata');
|
|
const ac2 = acoperire(PLANTARI.filter((_, i) => i !== scoasa));
|
|
if (scoasa >= 0 && ac2.lipsa.length === ac.lipsa.length + 1 && ac2.lipsa.some((x) => / promisiune$/.test(x))) console.log(` OK martorul acoperirii: fara plantarea '${PLANTARI[scoasa][0]}' garda numeste markerul ei (${ac2.lipsa.find((x) => / promisiune$/.test(x))})`);
|
|
else { rele++; console.log(` STRICAT martorul acoperirii: fara o plantare, garda nu a numit markerul ei (${JSON.stringify(ac2.lipsa).slice(0, 200)})`); }
|
|
// --numai-acoperirea: acoperirea si unicitatea fiecarui tipar in fisierul lui, fara rularile probei (o verificare rapida in timpul lucrului)
|
|
if (process.argv.includes('--numai-acoperirea')) {
|
|
const { t, d } = copie(fs.existsSync(GW));
|
|
for (const [nume, fisier, din] of [...PLANTARI, ...NEUTRE]) { if (fisier === GWF && !fs.existsSync(GW)) continue; const n = fs.readFileSync(fisierPlantat(t, d, fisier), 'utf8').split(din).length - 1; if (n !== 1) { rele++; console.log(` STRICAT ${nume}: tiparul apare de ${n} ori in ${fisier}`); } }
|
|
fs.rmSync(t, { recursive: true, force: true });
|
|
console.log(rele ? `ACOPERIREA: ${rele} probleme` : `ACOPERIREA: ${ac.total} markeri acoperiti, ${PLANTARI.length} tipare unice, ${NEUTRE.length} neutre (probele NU au rulat)`); process.exitCode = rele ? 1 : 0; process.exit();
|
|
}
|
|
const c0 = copie(); const m = await ruleaza(c0.d); fs.rmSync(c0.t, { recursive: true, force: true });
|
|
// martorul: proba ajunge la rezumat, zero rosii; un rand sarit e primit numai pe nume (usa gateway-ului cand gateway-ul nu e in aceasta copie)
|
|
const saritePermise = m.sarite.filter((l) => !/usa gateway-ului/.test(l));
|
|
if (m.rulat && !m.rosii.length && !saritePermise.length) console.log(` OK martorul: copia neatinsa verde (cod ${m.cod}${m.sarite.length ? ', ' + m.sarite.length + ' rand sarit pe nume: usa gateway-ului' : ''})`);
|
|
else { rele++; console.log(` STRICAT martorul nu e verde (cod ${m.cod}, ${m.rosii.length} rosii, ${saritePermise.length} sarite: ${[...m.rosii, ...saritePermise].join(' | ').slice(0, 400)})`); }
|
|
const LUCRARI = [...PLANTARI.map((p) => () => planteaza(p)), ...NEUTRE.map((p) => () => neutru(p))];
|
|
const rez = new Array(LUCRARI.length); let i = 0;
|
|
// cate plantari ruleaza deodata: AERE_CONTROL_PARALEL (implicit 12). Masurat 2026-10-19 pe laptop (16 fire): dupa prima revizie 4 deodata 529 s,
|
|
// 8 deodata 311 s, 12 deodata 270 s; dupa a patra (112 plantari) ~475 s cu 12 si ~747 s cu 4; dupa a sasea (134 de lucrari:
|
|
// 131 de plantari si 3 neutre, proba de 158 de randuri) masurarea e in README, randul acestui control (pana la a sasea revizie comentariul trimitea la
|
|
// masurarile de dupa a cincea, 126 de lucrari, care nu ajunsesera in README). Rulatorul B2 (scripts/deschise/ruleaza-probe.sh) da acestui control
|
|
// 1650 s din a sasea revizie (1200 s pana atunci; aritmetica in probele-b2.sh); fiecare rulare a probei din el are 300 s (ceasul din ruleaza()).
|
|
const PARALEL = /^[1-9]\d?$/.test(String(process.env.AERE_CONTROL_PARALEL || '')) ? Number(process.env.AERE_CONTROL_PARALEL) : 12;
|
|
await Promise.all(Array.from({ length: PARALEL }, async () => { while (i < LUCRARI.length) { const k = i++; rez[k] = await LUCRARI[k](); } }));
|
|
for (const [bun, linie] of rez) { console.log(linie); if (!bun) rele++; }
|
|
console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `DOVEDIT: martorul verde, acoperirea intreaga (${ac.total} markeri), ${PLANTARI.length} din ${PLANTARI.length} paznici scosi -> randul lor rosu, ${NEUTRE.length} din ${NEUTRE.length} plantari neutre (ordinea) -> proba verde intreaga`);
|
|
process.exitCode = rele ? 1 : 0;
|