// 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-) si portofelul-contract 2-din-2 (plati-agent-2of2-), 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; else if (q.method === 'eth_getCode') result = rec.code || '0x'; 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); delete r14.code; 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;