84 lines
7.2 KiB
JavaScript
84 lines
7.2 KiB
JavaScript
// Proba verificatorului de plati (verifica-plati.mjs), fara retea: un RPC local care serveste raspunsurile INREGISTRATE de pe testnetul
|
|
// 28001 pentru cele doua dosare-dovada din dovezi-28001/ (portofelul EOA si portofelul-contract 2-din-2, inregistreaza-rpc.mjs), deci
|
|
// date reale de pe lant. Fiecare verificare are cazul ei
|
|
// care trebuie sa o inroseasca, construit din datele reale schimbate intr-un singur loc.
|
|
// node proba-verifica-plati.mjs iesire 0 = toate cum trebuia
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import http from 'node:http';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { verificaPlati } from './verifica-plati.mjs';
|
|
|
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
|
const D = path.join(AICI, 'dovezi-28001');
|
|
// doua rulari pe 28001: portofelul EOA (plati-agent-<data>) si portofelul-contract 2-din-2 (plati-agent-2of2-<data>), fiecare cu inregistrarea ei
|
|
const ultimul = (re) => fs.readdirSync(D).filter((n) => re.test(n)).sort().pop();
|
|
const dovadaF = ultimul(/^plati-agent-\d.*\.json$/), inregF = ultimul(/^rpc-inregistrat-\d.*\.json$/);
|
|
const dovada2F = ultimul(/^plati-agent-2of2-.*\.json$/), inreg2F = ultimul(/^rpc-inregistrat-2of2-.*\.json$/);
|
|
let ok = 0, rau = 0;
|
|
const cer = (c, ce) => { console.log((c ? ' OK ' : ' RAU ') + ce); c ? ok++ : rau++; };
|
|
if (!dovadaF || !inregF || !dovada2F || !inreg2F) { console.log(`NEMASURAT: lipseste un dosar-dovada sau o inregistrare RPC in ${D}`); process.exit(2); }
|
|
const dovada = JSON.parse(fs.readFileSync(path.join(D, dovadaF), 'utf8'));
|
|
const inreg = JSON.parse(fs.readFileSync(path.join(D, inregF), 'utf8'));
|
|
const dovada2 = JSON.parse(fs.readFileSync(path.join(D, dovada2F), 'utf8'));
|
|
const inreg2 = JSON.parse(fs.readFileSync(path.join(D, inreg2F), 'utf8'));
|
|
const copie = (o) => JSON.parse(JSON.stringify(o));
|
|
|
|
// un RPC local peste inregistrare (sau peste o varianta schimbata a ei)
|
|
async function rpcDin(rec) {
|
|
const srv = http.createServer((req, res) => { let s = ''; req.on('data', (x) => { s += x; }); req.on('end', () => {
|
|
const q = JSON.parse(s); let result = null;
|
|
if (q.method === 'eth_chainId') result = rec.chainId;
|
|
else if (q.method === 'eth_getTransactionReceipt') result = rec.receipts[String(q.params[0]).toLowerCase()] || null;
|
|
else if (q.method === 'eth_getLogs') result = rec.transfersOut;
|
|
// 2026-10-11 (D-FAZA2-04b, R2): macheta serveste numai ce e INREGISTRAT; o inregistrare fara `code` nu devine "fara cod",
|
|
// ci o eroare JSON-RPC, pe care verificatorul trebuie sa o trateze ca necunoscut (inregistrarile EOA poarta explicit "0x")
|
|
else if (q.method === 'eth_getCode') { if (rec.code === undefined) { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', id: q.id, error: { code: -32000, message: 'World state unavailable' } })); return; } result = rec.code; }
|
|
res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', id: q.id, result }));
|
|
}); });
|
|
await new Promise((r) => srv.listen(0, '127.0.0.1', r));
|
|
return { url: `http://127.0.0.1:${srv.address().port}`, close: () => srv.close() };
|
|
}
|
|
async function judeca(dov, rec, allTransfers = true) { const r = await rpcDin(rec); try { return await verificaPlati(dov, { rpc: r.url, allTransfers }); } finally { r.close(); } }
|
|
const pica = (v, re) => v.checks.some((c) => c.pass === false && re.test(c.name));
|
|
|
|
try {
|
|
const v0 = await judeca(dovada, inreg);
|
|
cer(v0.verdict === 'VALID' && v0.checks.every((c) => c.pass === true), `1. dosarul real, peste raspunsurile reale de pe 28001: VALID (${v0.checks.length} verificari)`);
|
|
const tx1 = dovada.payments[1].transaction.toLowerCase();
|
|
|
|
const d2 = copie(dovada); d2.ledger.entries[dovada.payments[1].entrySeq].body.action.amount = '1';
|
|
cer(pica(await judeca(d2, inreg), /the ledger verifies/), '2. CONTROL: o suma schimbata in registru -> registrul nu mai verifica');
|
|
const d3 = copie(dovada); d3.payments[1].entryHash = 'ab'.repeat(32);
|
|
cer(pica(await judeca(d3, inreg), /the entry is in the ledger/), '3. CONTROL: plata numeste o intrare care nu e in registru -> INVALID');
|
|
const d4 = copie(dovada); d4.wallet = '0x' + '11'.repeat(20);
|
|
cer(pica(await judeca(d4, inreg, false), /allowed payment from the wallet/), '4. CONTROL: alt portofel decat cel din intrari -> INVALID');
|
|
const r5 = copie(inreg); r5.receipts[tx1].status = '0x0';
|
|
cer(pica(await judeca(dovada, r5), /status 1/), '5. CONTROL: chitanta cu status 0 -> INVALID');
|
|
const r6 = copie(inreg); for (const l of r6.receipts[tx1].logs) if (l.topics.length === 3 && l.topics[2] && !/^0x0{24}/.test(l.topics[2])) l.topics[2] = '0x' + 'cd'.repeat(32);
|
|
cer(pica(await judeca(dovada, r6), /AuthorizationUsed/), '6. CONTROL: nonce-ul de pe lant nu e sha256(hash-ul intrarii) -> INVALID');
|
|
const r7 = copie(inreg); for (const l of r7.receipts[tx1].logs) if (l.data && l.data !== '0x') l.data = '0x' + (1n).toString(16).padStart(64, '0');
|
|
cer(pica(await judeca(dovada, r7), /Transfer\(wallet/), '7. CONTROL: suma din Transfer-ul de pe lant alta decat in intrare -> INVALID');
|
|
const r8 = copie(inreg); r8.transfersOut = [...r8.transfersOut, { ...r8.transfersOut[0], transactionHash: '0x' + 'ee'.repeat(32) }];
|
|
cer(pica(await judeca(dovada, r8), /every Transfer out of the wallet/), '8. CONTROL: un Transfer din portofel fara plata in registru -> INVALID');
|
|
const r9 = copie(inreg); r9.chainId = '0xaf0';
|
|
const v9 = await judeca(dovada, r9);
|
|
cer(v9.verdict === 'UNMEASURED', `9. CONTROL: un RPC al altui lant (2800) -> ${v9.verdict}, nu VALID si nu INVALID`);
|
|
const v10 = await verificaPlati(dovada, { rpc: 'http://127.0.0.1:9', allTransfers: true });
|
|
cer(v10.verdict === 'UNMEASURED', `10. CONTROL: un RPC care nu raspunde -> ${v10.verdict}`);
|
|
|
|
// portofelul-contract 2-din-2: codul de pe lant trebuie sa fie contractul compilat cu cei doi semnatari din dosar
|
|
const e2 = /is the 2-of-2 contract/;
|
|
const v11 = await judeca(dovada2, inreg2);
|
|
cer(v11.verdict === 'VALID' && v11.checks.every((c) => c.pass === true) && v11.checks.some((c) => e2.test(c.name)),
|
|
`11. dosarul portofelului 2-din-2, peste raspunsurile reale de pe 28001: VALID (${v11.checks.length} verificari, cu codul contractului)`);
|
|
const r12 = copie(inreg2); const pozitie = r12.code.length - 120; r12.code = r12.code.slice(0, pozitie) + (r12.code[pozitie] === '0' ? '1' : '0') + r12.code.slice(pozitie + 1);
|
|
cer(pica(await judeca(dovada2, r12), e2), '12. CONTROL: un singur octet schimbat in codul de pe lant -> INVALID');
|
|
const d13 = copie(dovada2); d13.walletContract.agentSigner = '0x' + '22'.repeat(20);
|
|
cer(pica(await judeca(d13, inreg2), e2), '13. CONTROL: dosarul numeste alt semnatar al agentului decat cel din codul de pe lant -> INVALID');
|
|
const r14 = copie(inreg2); r14.code = '0x'; // 2026-10-11: controlul spune EXPLICIT "cont fara cod"; o inregistrare fara camp e tacere, nu absenta (R2)
|
|
cer(pica(await judeca(dovada2, r14), e2), '14. CONTROL: la adresa portofelului nu e niciun cod (un cont cu o cheie), dar dosarul spune 2-din-2 -> INVALID');
|
|
} catch (e) { cer(false, `proba s-a oprit: ${String(e.message || e).slice(0, 160)}`); }
|
|
console.log(`\nverifica-plati: ${ok}/${ok + rau} ${rau ? 'CU ESECURI' : 'cum trebuia'}`);
|
|
process.exitCode = rau ? 1 : 0;
|