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.
207 lines
8.5 KiB
Java
207 lines
8.5 KiB
Java
/*
|
|
* Copyright contributors to the AERE Network.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Standalone self-test for the FriQueryIndex helper that is embedded in
|
|
* pqc-fork/precompiles/Sp1StarkVerifierPrecompiledContract.java (the PQ STARK-verify precompile
|
|
* 0x0AE8). The logic below is a VERBATIM copy of that helper's arithmetic, extracted so it can be
|
|
* compiled and run WITHOUT the Besu classpath (plain `javac` + `java`), giving a genuinely-running
|
|
* Java validation of the exact code that ships in the precompile.
|
|
*
|
|
* HONEST SCOPE. This tests ONE constant-free slice of FRI component (d): the query-index derivation
|
|
* (sample_bits) and the per-layer folding-index walk. It verifies NOTHING about a real STARK proof,
|
|
* and the top-level 0x0AE8 verifier stays fail-closed. It asserts hand-computed golden vectors and
|
|
* structural invariants; cross-language agreement with the Python and Node references is checked by
|
|
* test_fri_query_index.py. Conformance to a real SP1 v6.1.0 trace is [MEASURE] (port spec 8).
|
|
*
|
|
* Run: javac FriQueryIndexSelfTest.java && java FriQueryIndexSelfTest
|
|
* Emits JSON on stdout (the shared vector set) when invoked as: java FriQueryIndexSelfTest --emit
|
|
*/
|
|
public final class FriQueryIndexSelfTest {
|
|
|
|
static final long BABYBEAR_P = 2013265921L; // 2^31 - 2^27 + 1
|
|
|
|
// ==== helper logic (identical to Sp1StarkVerifierPrecompiledContract.FriQueryIndex) ============
|
|
|
|
/** Low `bits` bits of a canonical BabyBear representative. [VERIFY] LSB vs MSB vs p3-challenger. */
|
|
static int sampleBits(final long canonicalU32, final int bits) {
|
|
if (bits < 0 || bits >= 31) {
|
|
throw new IllegalArgumentException("bits must be in [0,31): one BabyBear sample carries ~31 bits");
|
|
}
|
|
if (canonicalU32 < 0 || canonicalU32 >= BABYBEAR_P) {
|
|
throw new IllegalArgumentException("canonicalU32 must be canonical in [0, p)");
|
|
}
|
|
return (int) (canonicalU32 & ((1L << bits) - 1L));
|
|
}
|
|
|
|
/** One FRI folding-index step: [index, sibling, index_pair, parity]. */
|
|
static int[] foldStep(final int index) {
|
|
return new int[] {index, index ^ 1, index >> 1, index & 1};
|
|
}
|
|
|
|
/** Full per-query folding-index walk; row r = {layer, index, sibling, index_pair, parity}. */
|
|
static int[][] walk(final int index, final int logMaxHeight, final int logFinalPolyLen) {
|
|
if (logMaxHeight < 0 || logFinalPolyLen < 0 || logFinalPolyLen > logMaxHeight) {
|
|
throw new IllegalArgumentException("bad log heights");
|
|
}
|
|
if (index < 0 || index >= (1 << logMaxHeight)) {
|
|
throw new IllegalArgumentException("index out of range for log_max_height");
|
|
}
|
|
final int rounds = logMaxHeight - logFinalPolyLen;
|
|
final int[][] out = new int[rounds][5];
|
|
int cur = index;
|
|
for (int layer = 0; layer < rounds; layer++) {
|
|
final int[] s = foldStep(cur);
|
|
out[layer][0] = layer;
|
|
out[layer][1] = s[0];
|
|
out[layer][2] = s[1];
|
|
out[layer][3] = s[2];
|
|
out[layer][4] = s[3];
|
|
cur = cur >> 1;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static int finalIndex(final int index, final int logMaxHeight, final int logFinalPolyLen) {
|
|
return index >> (logMaxHeight - logFinalPolyLen);
|
|
}
|
|
|
|
// ==== tests ====================================================================================
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
public static void main(final String[] args) {
|
|
if (args.length > 0 && args[0].equals("--emit")) {
|
|
System.out.print(emitSharedVectorsJson());
|
|
return;
|
|
}
|
|
|
|
// 1. Hand-computed golden walk: index=11 (0b1011), log_max_height=4, final poly len 0.
|
|
// round0: 11 -> sib 10, pair 5, par 1
|
|
// round1: 5 -> sib 4, pair 2, par 1
|
|
// round2: 2 -> sib 3, pair 1, par 0
|
|
// round3: 1 -> sib 0, pair 0, par 1 ; final index 11>>4 = 0.
|
|
int[][] g = walk(11, 4, 0);
|
|
int[][] expect = {
|
|
{0, 11, 10, 5, 1},
|
|
{1, 5, 4, 2, 1},
|
|
{2, 2, 3, 1, 0},
|
|
{3, 1, 0, 0, 1},
|
|
};
|
|
check("golden.walk.len", g.length == 4);
|
|
for (int i = 0; i < expect.length; i++) {
|
|
check("golden.walk.row" + i, java.util.Arrays.equals(g[i], expect[i]));
|
|
}
|
|
check("golden.walk.final", finalIndex(11, 4, 0) == 0);
|
|
|
|
// 2. Hand-computed golden with a non-trivial final poly: index=22, lmh=5, lfp=1 -> 4 rounds,
|
|
// final index 22>>4 = 1 (in [0,2)).
|
|
int[][] g2 = walk(22, 5, 1);
|
|
int[][] expect2 = {
|
|
{0, 22, 23, 11, 0},
|
|
{1, 11, 10, 5, 1},
|
|
{2, 5, 4, 2, 1},
|
|
{3, 2, 3, 1, 0},
|
|
};
|
|
check("golden.walk2.len", g2.length == 4);
|
|
for (int i = 0; i < expect2.length; i++) {
|
|
check("golden.walk2.row" + i, java.util.Arrays.equals(g2[i], expect2[i]));
|
|
}
|
|
check("golden.walk2.final", finalIndex(22, 5, 1) == 1);
|
|
|
|
// 3. sample_bits hand goldens: 20 = 0b10100.
|
|
check("golden.samplebits.20_2", sampleBits(20, 2) == 0); // low 2 bits: 00
|
|
check("golden.samplebits.20_3", sampleBits(20, 3) == 4); // low 3 bits: 100
|
|
check("golden.samplebits.20_4", sampleBits(20, 4) == 4); // low 4 bits: 0100
|
|
check("golden.samplebits.20_5", sampleBits(20, 5) == 20); // low 5 bits: 10100
|
|
check("golden.samplebits.mixed", sampleBits(0x6ae81234L, 5) == 0x14);
|
|
|
|
// 4. Invariants over many random cases.
|
|
java.util.Random rnd = new java.util.Random(0xAE8);
|
|
for (int t = 0; t < 20000; t++) {
|
|
int lmh = 1 + rnd.nextInt(27); // 1..27, single-sample safe
|
|
int lfp = rnd.nextInt(lmh + 1); // 0..lmh
|
|
int idx = rnd.nextInt(1 << lmh);
|
|
int[][] w = walk(idx, lmh, lfp);
|
|
check("inv.rounds", w.length == lmh - lfp);
|
|
int cur = idx;
|
|
boolean ok = true;
|
|
for (int[] row : w) {
|
|
ok &= (row[1] == cur); // index tracks the running fold
|
|
ok &= (row[2] == (cur ^ 1)); // sibling differs in exactly bit 0
|
|
ok &= (Integer.bitCount(row[1] ^ row[2]) == 1);
|
|
ok &= (row[3] == (cur >> 1)); // index_pair halves
|
|
ok &= (row[4] == (cur & 1)); // parity
|
|
cur >>= 1;
|
|
}
|
|
ok &= (finalIndex(idx, lmh, lfp) == cur);
|
|
ok &= (cur < (1 << lfp)); // collapses into final-poly domain
|
|
check("inv.walk", ok);
|
|
|
|
int bits = rnd.nextInt(30);
|
|
long v = (long) (rnd.nextDouble() * BABYBEAR_P) % BABYBEAR_P;
|
|
int sb = sampleBits(v, bits);
|
|
check("inv.samplebits.range", sb >= 0 && sb < (1 << bits));
|
|
check("inv.samplebits.lowbits", sb == (int) (v & ((1L << bits) - 1L)));
|
|
}
|
|
// full-width sample_bits is identity for canonical reps that fit
|
|
check("inv.samplebits.identity", sampleBits(0x0abcdefL, 28) == 0x0abcdef);
|
|
|
|
System.out.printf("Java FriQueryIndexSelfTest: PASS=%d FAIL=%d%n", pass, fail);
|
|
if (fail != 0) {
|
|
System.exit(1);
|
|
}
|
|
}
|
|
|
|
// Emit the exact shared vector set (same cases as the Node/Python references) as JSON, so the
|
|
// harness can diff all three languages byte-for-byte.
|
|
static String emitSharedVectorsJson() {
|
|
int[][] walkCases = {
|
|
{11, 4, 0}, {22, 5, 1}, {0, 6, 0}, {63, 6, 0}, {12345, 20, 3}, {1, 1, 0},
|
|
};
|
|
long[][] sampleCases = {
|
|
{20, 2}, {20, 3}, {20, 5}, {0x6ae81234L, 5}, {0x6ae81234L, 20}, {BABYBEAR_P - 1, 10},
|
|
};
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("{\"walks\":[");
|
|
for (int c = 0; c < walkCases.length; c++) {
|
|
int idx = walkCases[c][0], lmh = walkCases[c][1], lfp = walkCases[c][2];
|
|
int[][] w = walk(idx, lmh, lfp);
|
|
if (c > 0) sb.append(",");
|
|
sb.append("{\"index\":").append(idx)
|
|
.append(",\"logMaxHeight\":").append(lmh)
|
|
.append(",\"logFinalPolyLen\":").append(lfp)
|
|
.append(",\"final_index\":").append(finalIndex(idx, lmh, lfp))
|
|
.append(",\"steps\":[");
|
|
for (int i = 0; i < w.length; i++) {
|
|
if (i > 0) sb.append(",");
|
|
sb.append("{\"layer\":").append(w[i][0])
|
|
.append(",\"index\":").append(w[i][1])
|
|
.append(",\"sibling\":").append(w[i][2])
|
|
.append(",\"index_pair\":").append(w[i][3])
|
|
.append(",\"parity\":").append(w[i][4]).append("}");
|
|
}
|
|
sb.append("]}");
|
|
}
|
|
sb.append("],\"samples\":[");
|
|
for (int c = 0; c < sampleCases.length; c++) {
|
|
long v = sampleCases[c][0];
|
|
int bits = (int) sampleCases[c][1];
|
|
if (c > 0) sb.append(",");
|
|
sb.append("{\"v\":").append(v).append(",\"bits\":").append(bits)
|
|
.append(",\"out\":").append(sampleBits(v, bits)).append("}");
|
|
}
|
|
sb.append("]}");
|
|
return sb.toString();
|
|
}
|
|
}
|