pqc-migration-toolkit/proba-vocabular.mjs
Aere Network de9a79298c Aere PQC migration toolkit: measure an address quantum exposure from its bytecode
Zero dependencies, no account, no API. Every command takes --rpc, so it runs
against a node you operate and never needs ours. That is the point: an exposure
measurement you cannot re-run is an opinion with a logo on it.

Ships with two self-checks meant to be run by you, not just by us. The second,
proba-vocabular.mjs, exists because of a real defect found on 2026-08-16: the
scanner emits RED / YELLOW / GREEN, two downstream files each wrote their own
copy of that list, and both wrote AMBER, so a hybrid contract, exactly a client
who has already started migrating, was reported as unmeasurable. Both sides were
self-consistent, so nothing we had could see it. The vocabulary now has one
exported source, and the test walks the whole path with a negative control that
makes it able to fail.

The same scanner returns RED about our own contracts. See VERIFY-US.md.
2026-08-17 10:31:31 +03:00

96 lines
5.5 KiB
JavaScript

#!/usr/bin/env node
'use strict';
/*
* proba-vocabular.mjs - proves that every verdict the scanner can emit survives the whole way
* to the client, and that a genuinely unmeasurable input still comes out as NOT MEASURED.
*
* WHY IT EXISTS. The scanner emitted RED / YELLOW / GREEN. Two downstream files each wrote their
* own list, and both wrote AMBER. So a hybrid contract, which is exactly the client who has
* already started migrating and is the most likely to pay, was reported as "we could not measure
* this address" in the free scan and as NOT MEASURED in the paid report. Measured 2026-08-16.
*
* The defect was invisible to every test we had, because each side was self-consistent. Only a
* test that walks the WHOLE path, scanner output to client wording, can see it. That is this file.
*
* CONTROL POZITIV: each of the three verdicts must survive end to end.
* CONTROL NEGATIV: an input that carries no verdict at all must still degrade to NOT MEASURED,
* otherwise this test would pass even if the reader returned a constant, and would prove nothing.
*
* node proba-vocabular.mjs exit 0 all good, 1 something drifted
*/
import { execFileSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { VERDICTE, citesteVerdict } from './lib/bytecode.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const linii = [];
const spune = (ok, ce, obs) => { linii.push({ ok, ce, obs }); console.log(' ' + (ok ? 'PASS' : 'FAIL') + ' ' + ce + (obs ? ' ' + obs : '')); };
// Bytecode fixtures chosen so the scanner reaches each of its three branches offline, with no RPC.
// hibrid : uses a PQ precompile AND a classical one -> YELLOW
// doar PQ : uses only a PQ precompile -> GREEN
// clasic : uses only ecrecover -> RED
// Nota platita la prima rulare a acestei probe: fixtura pentru GREEN continea PUSH1 0x01 ca
// argument de CALL, iar scanerul l-a citit, corect, ca adresa lui ecrecover, deci fixtura iesea
// YELLOW si verdictul GREEN nu era atins de nimic. Proba a semnalat-o singura, si de aia are linia
// care cere ca FIECARE verdict din vocabular sa fie atins de cel putin o fixtura.
const FIXTURI = {
YELLOW: '0x602060006080600060006001610ae1f150600160006080600060006001610001f15000',
GREEN: '0x60206000602060006000610ae1611388f15000',
RED: '0x602060006080600060006001610001f15000',
};
function scan(bytecode) {
try {
return execFileSync(process.execPath,
['scan.js', '0x1111111111111111111111111111111111111111', '--bytecode', bytecode],
{ cwd: __dirname, encoding: 'utf8', timeout: 60000 }).trim();
} catch (e) { return String((e.stdout || '') + (e.stderr || e.message)).trim(); }
}
console.log('proba de vocabular: fiecare verdict al scanerului ajunge intreg la client\n');
console.log('vocabularul, din singura lui sursa: ' + VERDICTE.join(', ') + '\n');
// ── control pozitiv: fiecare verdict produs de scaner e citit ca atare ───────────────────────
console.log('CONTROL POZITIV, fiecare verdict pe tot drumul:');
const vazute = new Set();
for (const [asteptat, bytecode] of Object.entries(FIXTURI)) {
const text = scan(bytecode);
const emis = (text.match(/^\s*([A-Z]{3,14})\b/m) || [, '(niciunul)'])[1];
const citit = citesteVerdict(text) || 'NOT MEASURED';
vazute.add(emis);
spune(citit === emis && citit !== 'NOT MEASURED',
'scanerul emite ' + emis + ', consumatorul citeste ' + citit,
emis === asteptat ? '' : '(fixtura viza ' + asteptat + ', dar ce conteaza e ca ce s-a emis a fost citit)');
}
// Every verdict the vocabulary declares must actually be reachable by some fixture, otherwise a
// branch could rot unnoticed exactly the way YELLOW did.
for (const v of VERDICTE) {
spune(vazute.has(v), 'verdictul ' + v + ' e atins de cel putin o fixtura', vazute.has(v) ? '' : 'nicio fixtura nu il produce, deci nimeni nu l-ar fi prins daca se strica');
}
// ── control negativ: fara el, proba de mai sus ar trece si daca cititorul minte ──────────────
console.log('\nCONTROL NEGATIV, ca proba de mai sus sa insemne ceva:');
const fara = citesteVerdict('acest text nu poarta niciun verdict\nnicio linie nu incepe cu unul\n');
spune(fara === null, 'un text fara verdict da null, deci NOT MEASURED la consumator', 'a dat ' + (fara === null ? 'null' : fara));
const gol = citesteVerdict('');
spune(gol === null, 'textul gol da null', 'a dat ' + (gol === null ? 'null' : gol));
// A word that merely CONTAINS a verdict must not be mistaken for one.
const aproape = citesteVerdict(' GREENISH pasture\n REDACTED line\n');
spune(aproape === null, 'un cuvant care doar contine un verdict nu e luat drept verdict', 'a dat ' + (aproape === null ? 'null' : aproape));
// ── verdict ─────────────────────────────────────────────────────────────────────────────────
const rele = linii.filter((l) => !l.ok).length;
console.log('\n' + (linii.length - rele) + ' din ' + linii.length + ' verificari trec');
if (rele) {
console.log('VERDICT: vocabularul a divergat undeva pe drum. Verdictele nu ajung intregi la client.');
process.exitCode = 1;
} else {
console.log('VERDICT: fiecare verdict al scanerului ajunge intreg la client, si un text fara verdict');
console.log('ramane NOT MEASURED. Proba poate deveni rosie, deci verdele ei inseamna ceva.');
}