The public history carried kat/__pycache__/mlkem768_reference.cpython-314.pyc, a compiled Python artifact embedding the operator's absolute local path. Text secret scanners do not read compiled binaries, which is exactly how it slipped through, and removing it from the tip would have left it reachable through the old root commits. So this repository is republished from a single clean root. This root also carries, from the previously unpublished line of work: - corrected LICENSE year, LICENSING.md, VERIFY-POLICY.md, and CITATIONS-UNRESOLVED.md remeasured 2026-08-11 (101 paths, README aligned) - O-018: run_consensus_verification.py ran 19 of 29 models and reported PASS; it now runs all 29, and computemarket_smt.py gains resolveByTimeout / reclaimUnsettled cases plus a negative control - O-006: the word 'audited' removed from next to Bouncy Castle, twice, after a concurrent edit resurrected it - O-014: prior art named and dated - Algorand's native falcon_verify shipped about ten months before AERE's precompiles; the primacy claim is withdrawn where it was implied - bench/ scripts parametrized so they actually run for an outsider (the earlier textual sanitization left $STAGING unexpanded inside Python strings) - AIP-2/AIP-3 errata with measured figures, spec remeasurements at 2026-08-01, and the spec-zk-stack retractions (owner is an operational key, not the Foundation; 'maximally sound' withdrawn; aggregator V1 deprecated) The redacted bench-host environment files from the sanitized line are kept exactly as published; the unredacted local variants are not carried.
488 lines
23 KiB
Java
488 lines
23 KiB
Java
// Standalone (no-Besu-classpath) reference + self-test for the FRI VERIFIER, component (d) of the PQ
|
|
// STARK-verify precompile 0x0AE8 (direct SP1/Plonky3 inner FRI/STARK verify). Mirrors
|
|
// fri_verify_reference.py and fri_verify_reference.mjs. It verifies real FRI proofs emitted by the
|
|
// pinned Plonky3 p3-fri 0.4.3-succinct prover (the ground-truth extractor, pq-stark/fri-extractor).
|
|
//
|
|
// HONEST SCOPE: this implements the FOLD + OPENING relations (verify_query), taking the transcript
|
|
// derived betas / query indices / reduced openings as INPUTS. The transcript BINDING that derives them
|
|
// in-circuit is component (f) (the duplex-sponge challenger) and remains un-ported, so even with this
|
|
// passing KAT the top-level 0x0AE8 stays FAIL-CLOSED. The Poseidon2 permutation (component (b)) and the
|
|
// FieldMerkleTreeMmcs verify_batch (component (c)) below are copied verbatim from the CONFIRMED
|
|
// MmcsBabyBearSelfTest; the F_{p^4} arithmetic (component (a)) matches the precompile's BabyBearExt4.
|
|
//
|
|
// javac -d out FriVerifySelfTest.java
|
|
// java -cp out FriVerifySelfTest [ground_truth.json] # run the conformance self-test
|
|
// java -cp out FriVerifySelfTest --emit [ground_truth.json] # emit the shared vector set (JSON)
|
|
|
|
import java.math.BigInteger;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Paths;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.HashMap;
|
|
|
|
public final class FriVerifySelfTest {
|
|
|
|
static final long P = 2013265921L; // BabyBear 2^31 - 2^27 + 1
|
|
static final long R_INV = 943718400L; // (2^32)^{-1} mod p (Poseidon2 internal-layer Montgomery factor)
|
|
static final long GENERATOR = 31L; // BabyBear multiplicative generator (order p-1)
|
|
static final long EXT_W = 11L; // F_{p^4} = F_p[x]/(x^4 - 11) non-residue
|
|
static final int WIDTH = 16;
|
|
static final int RATE = 8;
|
|
static final int OUT = 8;
|
|
static final int DIGEST_ELEMS = 8;
|
|
|
|
static final long[][] M4 = {{2, 3, 1, 1}, {1, 2, 3, 1}, {1, 1, 2, 3}, {3, 1, 1, 2}};
|
|
static final long[] INTERNAL_DIAG_M1_16 = {
|
|
P - 2, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 32768
|
|
};
|
|
static final long[][] RC_EXTERNAL = {
|
|
{1321363468L, 285374923L, 858595076L, 131742120L, 550898981L, 109281027L, 1548327248L, 299186948L,
|
|
1198120888L, 1302311359L, 568137078L, 1484856917L, 1301979945L, 725688886L, 941758026L, 323341913L},
|
|
{1049323172L, 822409348L, 1406080127L, 1279024384L, 214862539L, 904628921L, 1320747287L, 11578228L,
|
|
1036373712L, 1474430466L, 1430509860L, 111174484L, 1124450171L, 85382027L, 679880882L, 243277213L},
|
|
{1338495990L, 1523013347L, 1841068573L, 578194469L, 47683837L, 1790441672L, 1628061601L, 1716216090L,
|
|
1635810049L, 1115145248L, 1117524270L, 678640014L, 1962751651L, 1367401392L, 11688709L, 1950824358L},
|
|
{528649031L, 1937116923L, 1460949223L, 1193074357L, 1221801411L, 1183923117L, 433505619L, 1928933309L,
|
|
505759755L, 285671663L, 1047265910L, 909281502L, 1258966486L, 864761693L, 307024510L, 504858517L},
|
|
{1467478033L, 1754565867L, 432187324L, 1452390672L, 881974300L, 550050336L, 1447309270L, 939419487L,
|
|
1783112406L, 1166910332L, 107514714L, 580516863L, 2003318760L, 854475946L, 934896823L, 994783668L},
|
|
{1841107561L, 438269126L, 1550523825L, 913322122L, 600932628L, 583000098L, 1262690949L, 105797869L,
|
|
277542016L, 170491952L, 365854467L, 1479645308L, 1457660602L, 1635879552L, 499155053L, 741227047L},
|
|
{651389942L, 464828001L, 89696107L, 360044673L, 230330371L, 1773129416L, 1380150763L, 745014723L,
|
|
793475694L, 1361274828L, 1443741698L, 51616650L, 731414218L, 1087554954L, 1273943885L, 311581717L},
|
|
{702702762L, 1473247301L, 132108357L, 1348260424L, 476775430L, 1438949459L, 2434448L, 1349232398L,
|
|
1954471898L, 1762138591L, 1271221795L, 1593266476L, 864488771L, 139147729L, 1053373910L, 422842363L},
|
|
};
|
|
static final long[] RC_INTERNAL = {
|
|
402771160L, 320708227L, 1122772462L, 100431997L, 202594011L, 1226485372L, 1088619034L, 64118538L,
|
|
109828860L, 724723599L, 1662837151L, 797753907L, 1075635743L,
|
|
};
|
|
static final int ROUNDS_F = 8;
|
|
static final int ROUNDS_P = 13;
|
|
|
|
// ================= base field F_p =================
|
|
static long reduce(final long a) { long m = a % P; if (m < 0) m += P; return m; }
|
|
static long add(final long a, final long b) { long s = a + b; if (s >= P) s -= P; return s; }
|
|
static long sub(final long a, final long b) { long s = a - b; if (s < 0) s += P; return s; }
|
|
static long mul(final long a, final long b) { return (reduce(a) * reduce(b)) % P; }
|
|
static long powBase(final long base, long e) {
|
|
long b = reduce(base); long acc = 1L;
|
|
while (e > 0) { if ((e & 1L) == 1L) acc = mul(acc, b); b = mul(b, b); e >>= 1; }
|
|
return acc;
|
|
}
|
|
static long twoAdicGenerator(final int bits) { return powBase(GENERATOR, (P - 1) >> bits); }
|
|
|
|
// ================= F_{p^4} = F_p[x]/(x^4 - 11), elements long[4] = [c0,c1,c2,c3] =================
|
|
static long[] extFromBase(final long a) { return new long[]{reduce(a), 0, 0, 0}; }
|
|
static long[] extAdd(final long[] a, final long[] b) {
|
|
return new long[]{add(a[0], b[0]), add(a[1], b[1]), add(a[2], b[2]), add(a[3], b[3])};
|
|
}
|
|
static long[] extSub(final long[] a, final long[] b) {
|
|
return new long[]{sub(a[0], b[0]), sub(a[1], b[1]), sub(a[2], b[2]), sub(a[3], b[3])};
|
|
}
|
|
static long[] extMul(final long[] a, final long[] b) {
|
|
final long[] t = new long[7];
|
|
for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) t[i + j] = add(t[i + j], mul(a[i], b[j]));
|
|
return new long[]{add(t[0], mul(EXT_W, t[4])), add(t[1], mul(EXT_W, t[5])), add(t[2], mul(EXT_W, t[6])), t[3]};
|
|
}
|
|
static long[] extPow(final long[] a, final BigInteger e) {
|
|
long[] base = a.clone(); long[] acc = new long[]{1, 0, 0, 0}; BigInteger ee = e;
|
|
while (ee.signum() > 0) { if (ee.testBit(0)) acc = extMul(acc, base); base = extMul(base, base); ee = ee.shiftRight(1); }
|
|
return acc;
|
|
}
|
|
static long[] extInv(final long[] a) {
|
|
if (a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0) return new long[]{0, 0, 0, 0};
|
|
final BigInteger p = BigInteger.valueOf(P);
|
|
return extPow(a, p.pow(4).subtract(BigInteger.TWO));
|
|
}
|
|
static boolean extEq(final long[] a, final long[] b) {
|
|
for (int i = 0; i < 4; i++) if (reduce(a[i]) != reduce(b[i])) return false;
|
|
return true;
|
|
}
|
|
|
|
// ================= Poseidon2 (component (b), CONFIRMED) =================
|
|
static long sbox(final long x) { final long x2 = mul(x, x); final long x4 = mul(x2, x2); return mul(x4, mul(x2, x)); }
|
|
static long[] m4Apply(final long a, final long b, final long c, final long d) {
|
|
final long[] out = new long[4];
|
|
for (int row = 0; row < 4; row++) out[row] = reduce(M4[row][0] * a + M4[row][1] * b + M4[row][2] * c + M4[row][3] * d);
|
|
return out;
|
|
}
|
|
static void externalLayer(final long[] s) {
|
|
final long[][] blk = new long[4][];
|
|
for (int b = 0; b < 4; b++) blk[b] = m4Apply(s[4 * b], s[4 * b + 1], s[4 * b + 2], s[4 * b + 3]);
|
|
final long[] colSum = new long[4];
|
|
for (int j = 0; j < 4; j++) colSum[j] = reduce(blk[0][j] + blk[1][j] + blk[2][j] + blk[3][j]);
|
|
for (int b = 0; b < 4; b++) for (int j = 0; j < 4; j++) s[4 * b + j] = add(blk[b][j], colSum[j]);
|
|
}
|
|
static void internalLayer(final long[] s) {
|
|
long sum = 0;
|
|
for (final long v : s) sum = add(sum, v);
|
|
for (int i = 0; i < WIDTH; i++) s[i] = mul(R_INV, add(sum, mul(s[i], INTERNAL_DIAG_M1_16[i])));
|
|
}
|
|
static long[] permute(final long[] state16) {
|
|
final long[] s = new long[WIDTH];
|
|
for (int i = 0; i < WIDTH; i++) s[i] = reduce(state16[i]);
|
|
externalLayer(s);
|
|
final int half = ROUNDS_F / 2;
|
|
for (int r = 0; r < half; r++) {
|
|
for (int i = 0; i < WIDTH; i++) s[i] = add(s[i], RC_EXTERNAL[r][i]);
|
|
for (int i = 0; i < WIDTH; i++) s[i] = sbox(s[i]);
|
|
externalLayer(s);
|
|
}
|
|
for (int r = 0; r < ROUNDS_P; r++) { s[0] = add(s[0], RC_INTERNAL[r]); s[0] = sbox(s[0]); internalLayer(s); }
|
|
for (int r = half; r < ROUNDS_F; r++) {
|
|
for (int i = 0; i < WIDTH; i++) s[i] = add(s[i], RC_EXTERNAL[r][i]);
|
|
for (int i = 0; i < WIDTH; i++) s[i] = sbox(s[i]);
|
|
externalLayer(s);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
// ================= MMCS verify_batch (component (c), CONFIRMED) =================
|
|
static long[] hashIter(final long[] elems) {
|
|
final long[] state = new long[WIDTH];
|
|
int i = 0;
|
|
while (i < elems.length) {
|
|
final int end = Math.min(i + RATE, elems.length);
|
|
for (int j = i; j < end; j++) state[j - i] = reduce(elems[j]);
|
|
final long[] permuted = permute(state);
|
|
System.arraycopy(permuted, 0, state, 0, WIDTH);
|
|
i += RATE;
|
|
}
|
|
final long[] out = new long[OUT];
|
|
System.arraycopy(state, 0, out, 0, OUT);
|
|
return out;
|
|
}
|
|
static long[] compress2to1(final long[] left8, final long[] right8) {
|
|
final long[] pre = new long[WIDTH];
|
|
for (int i = 0; i < DIGEST_ELEMS; i++) pre[i] = reduce(left8[i]);
|
|
for (int i = 0; i < DIGEST_ELEMS; i++) pre[DIGEST_ELEMS + i] = reduce(right8[i]);
|
|
final long[] post = permute(pre);
|
|
final long[] out = new long[DIGEST_ELEMS];
|
|
System.arraycopy(post, 0, out, 0, DIGEST_ELEMS);
|
|
return out;
|
|
}
|
|
static int nextPow2(final int n) { if (n <= 1) return 1; return Integer.highestOneBit(n - 1) << 1; }
|
|
static boolean eqDigest(final long[] a, final long[] b) {
|
|
if (a.length != b.length) return false;
|
|
for (int i = 0; i < a.length; i++) if (reduce(a[i]) != reduce(b[i])) return false;
|
|
return true;
|
|
}
|
|
static long[] concatOpened(final Integer[] order, final int[] heights, final int[] ptr, final int padded, final long[][] opened) {
|
|
final List<Long> buf = new ArrayList<>();
|
|
while (ptr[0] < heights.length && nextPow2(heights[ptr[0]]) == padded) {
|
|
for (final long v : opened[order[ptr[0]]]) buf.add(v);
|
|
ptr[0]++;
|
|
}
|
|
final long[] out = new long[buf.size()];
|
|
for (int i = 0; i < out.length; i++) out[i] = buf.get(i);
|
|
return out;
|
|
}
|
|
static boolean verifyBatch(final long[] commit, final int[][] dims, final int index, final long[][] opened, final long[][] pf) {
|
|
final int n = dims.length;
|
|
if (n == 0) return false;
|
|
final Integer[] order = new Integer[n];
|
|
for (int i = 0; i < n; i++) order[i] = i;
|
|
java.util.Arrays.sort(order, (a, b) -> dims[b][1] != dims[a][1] ? dims[b][1] - dims[a][1] : a - b);
|
|
final int[] heights = new int[n];
|
|
for (int i = 0; i < n; i++) heights[i] = dims[order[i]][1];
|
|
final int[] ptr = {0};
|
|
int currHeightPadded = nextPow2(heights[0]);
|
|
long[] root = hashIter(concatOpened(order, heights, ptr, currHeightPadded, opened));
|
|
int idx = index;
|
|
for (final long[] sibling : pf) {
|
|
final long[] left; final long[] right;
|
|
if ((idx & 1) == 0) { left = root; right = sibling; } else { left = sibling; right = root; }
|
|
root = compress2to1(left, right);
|
|
idx >>= 1;
|
|
currHeightPadded >>= 1;
|
|
if (ptr[0] < n && nextPow2(heights[ptr[0]]) == currHeightPadded) {
|
|
root = compress2to1(root, hashIter(concatOpened(order, heights, ptr, currHeightPadded, opened)));
|
|
}
|
|
}
|
|
return eqDigest(root, commit);
|
|
}
|
|
|
|
// ================= FRI verify_query (component (d)) =================
|
|
static int reverseBitsLen(final int x, final int bitLen) {
|
|
int r = 0;
|
|
for (int i = 0; i < bitLen; i++) r = (r << 1) | ((x >> i) & 1);
|
|
return r;
|
|
}
|
|
|
|
static final class Layer { long[] sibling; long[][] openingProof; }
|
|
|
|
/** Reproduce p3-fri verifier::verify_query for one query. Returns folded eval (F_{p^4}) or null if a
|
|
* commit-phase MMCS opening fails. */
|
|
static long[] verifyQuery(final int logBlowup, final int logMaxHeight, final long[][] commits,
|
|
final long[][] betas, final int index, final long[][] roFull, final Layer[] layers) {
|
|
long[] folded = new long[]{0, 0, 0, 0};
|
|
final long g = twoAdicGenerator(logMaxHeight);
|
|
long[] x = extFromBase(powBase(g, reverseBitsLen(index, logMaxHeight)));
|
|
final long[] gen1 = extFromBase(twoAdicGenerator(1)); // order-2 root = -1, embedded
|
|
int idx = index;
|
|
final int numLayers = logMaxHeight - logBlowup;
|
|
for (int layer = 0; layer < numLayers; layer++) {
|
|
final int lfh = logMaxHeight - 1 - layer;
|
|
folded = extAdd(folded, roFull[lfh + 1]);
|
|
|
|
final int isib = idx ^ 1;
|
|
final int ipair = idx >> 1;
|
|
|
|
final long[][] evals = {folded.clone(), folded.clone()};
|
|
evals[isib % 2] = layers[layer].sibling.clone();
|
|
|
|
final long[] row = new long[8];
|
|
System.arraycopy(evals[0], 0, row, 0, 4);
|
|
System.arraycopy(evals[1], 0, row, 4, 4);
|
|
final int height = 1 << lfh;
|
|
if (!verifyBatch(commits[layer], new int[][]{{8, height}}, ipair, new long[][]{row}, layers[layer].openingProof)) {
|
|
return null;
|
|
}
|
|
|
|
final long[] xSib = extMul(x, gen1);
|
|
final long[][] xs = (isib % 2 == 1) ? new long[][]{x, xSib} : new long[][]{xSib, x};
|
|
final long[] beta = betas[layer];
|
|
final long[] num = extMul(extSub(beta, xs[0]), extSub(evals[1], evals[0]));
|
|
final long[] den = extSub(xs[1], xs[0]);
|
|
folded = extAdd(evals[0], extMul(num, extInv(den)));
|
|
|
|
idx = ipair;
|
|
x = extMul(x, x);
|
|
}
|
|
return folded;
|
|
}
|
|
|
|
// ================= case model + verify (accept / reject) =================
|
|
static final class Query { int index; long[] roTop; Layer[] layers; }
|
|
static final class FriCase {
|
|
String name; int logBlowup, logMaxHeight, numQueries;
|
|
long[][] commits; long[][] betas; long[] finalPoly; Query[] queries;
|
|
}
|
|
|
|
static long[][] buildRoFull(final int logMaxHeight, final long[] roTop) {
|
|
final long[][] ro = new long[logMaxHeight + 2][4];
|
|
ro[logMaxHeight] = roTop.clone();
|
|
return ro;
|
|
}
|
|
|
|
static boolean verifyCase(final FriCase c, final String tamper) {
|
|
long[] finalPoly = c.finalPoly.clone();
|
|
long[][] betas = new long[c.betas.length][];
|
|
for (int i = 0; i < betas.length; i++) betas[i] = c.betas[i].clone();
|
|
if ("final".equals(tamper)) finalPoly[0] = (finalPoly[0] + 1) % P;
|
|
if ("beta".equals(tamper)) betas[0][0] = (betas[0][0] + 1) % P;
|
|
|
|
for (final Query q : c.queries) {
|
|
final Layer[] layers = new Layer[q.layers.length];
|
|
for (int i = 0; i < layers.length; i++) {
|
|
layers[i] = new Layer();
|
|
layers[i].sibling = q.layers[i].sibling.clone();
|
|
layers[i].openingProof = new long[q.layers[i].openingProof.length][];
|
|
for (int j = 0; j < layers[i].openingProof.length; j++) layers[i].openingProof[j] = q.layers[i].openingProof[j].clone();
|
|
}
|
|
if ("sibling".equals(tamper)) layers[0].sibling[0] = (layers[0].sibling[0] + 1) % P;
|
|
if ("proof".equals(tamper)) layers[0].openingProof[0][0] = (layers[0].openingProof[0][0] + 1) % P;
|
|
|
|
final long[][] roFull = buildRoFull(c.logMaxHeight, q.roTop);
|
|
final long[] folded = verifyQuery(c.logBlowup, c.logMaxHeight, c.commits, betas, q.index, roFull, layers);
|
|
if (folded == null) return false;
|
|
if (!extEq(folded, finalPoly)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ================= minimal JSON parser (numbers/strings/arrays/objects/bools) =================
|
|
static final class JP {
|
|
final String s; int i;
|
|
JP(final String s) { this.s = s; }
|
|
void ws() { while (i < s.length() && Character.isWhitespace(s.charAt(i))) i++; }
|
|
Object val() {
|
|
ws(); final char c = s.charAt(i);
|
|
if (c == '{') return obj();
|
|
if (c == '[') return arr();
|
|
if (c == '"') return str();
|
|
if (c == 't') { i += 4; return Boolean.TRUE; }
|
|
if (c == 'f') { i += 5; return Boolean.FALSE; }
|
|
if (c == 'n') { i += 4; return null; }
|
|
return num();
|
|
}
|
|
Map<String, Object> obj() {
|
|
final Map<String, Object> m = new HashMap<>(); i++; ws();
|
|
if (s.charAt(i) == '}') { i++; return m; }
|
|
while (true) {
|
|
ws(); final String k = str(); ws(); i++; // ':'
|
|
m.put(k, val()); ws();
|
|
if (s.charAt(i) == ',') { i++; continue; }
|
|
i++; break; // '}'
|
|
}
|
|
return m;
|
|
}
|
|
List<Object> arr() {
|
|
final List<Object> a = new ArrayList<>(); i++; ws();
|
|
if (s.charAt(i) == ']') { i++; return a; }
|
|
while (true) {
|
|
a.add(val()); ws();
|
|
if (s.charAt(i) == ',') { i++; continue; }
|
|
i++; break; // ']'
|
|
}
|
|
return a;
|
|
}
|
|
String str() {
|
|
final StringBuilder b = new StringBuilder(); i++; // opening quote
|
|
while (s.charAt(i) != '"') { if (s.charAt(i) == '\\') i++; b.append(s.charAt(i++)); }
|
|
i++; return b.toString();
|
|
}
|
|
Long num() {
|
|
final int start = i;
|
|
while (i < s.length() && "+-0123456789.eE".indexOf(s.charAt(i)) >= 0) i++;
|
|
return Long.parseLong(s.substring(start, i));
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
static long[] la(final Object o) {
|
|
final List<Object> l = (List<Object>) o;
|
|
final long[] out = new long[l.size()];
|
|
for (int i = 0; i < out.length; i++) out[i] = (Long) l.get(i);
|
|
return out;
|
|
}
|
|
@SuppressWarnings("unchecked")
|
|
static long[][] laa(final Object o) {
|
|
final List<Object> l = (List<Object>) o;
|
|
final long[][] out = new long[l.size()][];
|
|
for (int i = 0; i < out.length; i++) out[i] = la(l.get(i));
|
|
return out;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
static FriCase[] parseGroundTruth(final String json, final long[][] permOut, final long[][] gensOut) {
|
|
final JP jp = new JP(json);
|
|
final Map<String, Object> root = (Map<String, Object>) jp.val();
|
|
permOut[0] = la(root.get("perm_zeros"));
|
|
gensOut[0] = la(root.get("two_adic_generators"));
|
|
final List<Object> cases = (List<Object>) root.get("cases");
|
|
final FriCase[] out = new FriCase[cases.size()];
|
|
for (int ci = 0; ci < out.length; ci++) {
|
|
final Map<String, Object> cm = (Map<String, Object>) cases.get(ci);
|
|
final FriCase c = new FriCase();
|
|
c.name = (String) cm.get("name");
|
|
c.logBlowup = (int) (long) (Long) cm.get("log_blowup");
|
|
c.logMaxHeight = (int) (long) (Long) cm.get("log_max_height");
|
|
c.numQueries = (int) (long) (Long) cm.get("num_queries");
|
|
c.commits = laa(cm.get("commit_phase_commits"));
|
|
c.betas = laa(cm.get("betas"));
|
|
c.finalPoly = la(cm.get("final_poly"));
|
|
final List<Object> qs = (List<Object>) cm.get("queries");
|
|
c.queries = new Query[qs.size()];
|
|
for (int qi = 0; qi < c.queries.length; qi++) {
|
|
final Map<String, Object> qm = (Map<String, Object>) qs.get(qi);
|
|
final Query q = new Query();
|
|
q.index = (int) (long) (Long) qm.get("index");
|
|
q.roTop = la(qm.get("ro_top"));
|
|
final List<Object> ls = (List<Object>) qm.get("layers");
|
|
q.layers = new Layer[ls.size()];
|
|
for (int li = 0; li < q.layers.length; li++) {
|
|
final Map<String, Object> lm = (Map<String, Object>) ls.get(li);
|
|
final Layer L = new Layer();
|
|
L.sibling = la(lm.get("sibling_value"));
|
|
L.openingProof = laa(lm.get("opening_proof"));
|
|
q.layers[li] = L;
|
|
}
|
|
c.queries[qi] = q;
|
|
}
|
|
out[ci] = c;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// ================= JSON emit (shared cross-language vector set) =================
|
|
static String ja(final long[] v) {
|
|
final StringBuilder b = new StringBuilder("[");
|
|
for (int i = 0; i < v.length; i++) { if (i > 0) b.append(','); b.append(reduce(v[i])); }
|
|
return b.append(']').toString();
|
|
}
|
|
static String jaa(final long[][] v) {
|
|
final StringBuilder b = new StringBuilder("[");
|
|
for (int i = 0; i < v.length; i++) { if (i > 0) b.append(','); b.append(ja(v[i])); }
|
|
return b.append(']').toString();
|
|
}
|
|
|
|
static void emit(final String path) throws Exception {
|
|
final long[][] permOut = new long[1][];
|
|
final long[][] gensOut = new long[1][];
|
|
final FriCase[] cases = parseGroundTruth(new String(Files.readAllBytes(Paths.get(path))), permOut, gensOut);
|
|
|
|
final long[] permZeros = permute(new long[16]);
|
|
final long[] gens = new long[28];
|
|
for (int b = 0; b < 28; b++) gens[b] = twoAdicGenerator(b);
|
|
|
|
final StringBuilder sb = new StringBuilder();
|
|
sb.append("{\"permZeros\":").append(ja(permZeros));
|
|
sb.append(",\"twoAdicGenerators\":").append(ja(gens));
|
|
sb.append(",\"cases\":[");
|
|
for (int ci = 0; ci < cases.length; ci++) {
|
|
final FriCase c = cases[ci];
|
|
if (ci > 0) sb.append(',');
|
|
final long[][] folded = new long[c.queries.length][];
|
|
for (int qi = 0; qi < c.queries.length; qi++) {
|
|
final long[][] roFull = buildRoFull(c.logMaxHeight, c.queries[qi].roTop);
|
|
folded[qi] = verifyQuery(c.logBlowup, c.logMaxHeight, c.commits, c.betas, c.queries[qi].index, roFull, c.queries[qi].layers);
|
|
}
|
|
sb.append("{\"name\":\"").append(c.name).append("\"");
|
|
sb.append(",\"logBlowup\":").append(c.logBlowup);
|
|
sb.append(",\"logMaxHeight\":").append(c.logMaxHeight);
|
|
sb.append(",\"numQueries\":").append(c.numQueries);
|
|
sb.append(",\"folded\":").append(jaa(folded));
|
|
sb.append(",\"finalPoly\":").append(ja(c.finalPoly));
|
|
sb.append(",\"accept\":").append(verifyCase(c, null));
|
|
sb.append(",\"rejectSibling\":").append(!verifyCase(c, "sibling"));
|
|
sb.append(",\"rejectProof\":").append(!verifyCase(c, "proof"));
|
|
sb.append(",\"rejectBeta\":").append(!verifyCase(c, "beta"));
|
|
sb.append(",\"rejectFinal\":").append(!verifyCase(c, "final"));
|
|
sb.append("}");
|
|
}
|
|
sb.append("]}");
|
|
System.out.print(sb);
|
|
}
|
|
|
|
// ================= self-test (conformance) =================
|
|
static int pass = 0, fail = 0;
|
|
static void check(final String name, final boolean cond) { if (cond) pass++; else { fail++; System.out.println("FAIL " + name); } }
|
|
|
|
static void selfTest(final String path) throws Exception {
|
|
final long[][] permOut = new long[1][];
|
|
final long[][] gensOut = new long[1][];
|
|
final FriCase[] cases = parseGroundTruth(new String(Files.readAllBytes(Paths.get(path))), permOut, gensOut);
|
|
|
|
// sanity: perm + generators match the confirmed sub-components
|
|
check("sanity.perm_zeros", java.util.Arrays.equals(permute(new long[16]), permOut[0]));
|
|
for (int b = 0; b < 28; b++) check("sanity.gen." + b, twoAdicGenerator(b) == gensOut[0][b]);
|
|
|
|
for (final FriCase c : cases) {
|
|
check("accept." + c.name, verifyCase(c, null));
|
|
check("reject.sibling." + c.name, !verifyCase(c, "sibling"));
|
|
check("reject.proof." + c.name, !verifyCase(c, "proof"));
|
|
check("reject.beta." + c.name, !verifyCase(c, "beta"));
|
|
check("reject.final." + c.name, !verifyCase(c, "final"));
|
|
}
|
|
|
|
System.out.println("FriVerifySelfTest PASS=" + pass + " FAIL=" + fail);
|
|
if (fail != 0) { System.out.println("FRI_VERIFY_SELFTEST_FAILED"); System.exit(1); }
|
|
System.out.println("FRI_VERIFY_SELFTEST_OK");
|
|
}
|
|
|
|
public static void main(final String[] args) throws Exception {
|
|
boolean emit = false;
|
|
String path = "fri_ground_truth.json";
|
|
for (final String a : args) {
|
|
if (a.equals("--emit")) emit = true;
|
|
else if (!a.startsWith("--")) path = a;
|
|
}
|
|
if (emit) emit(path);
|
|
else selfTest(path);
|
|
}
|
|
}
|