// Standalone (no-Besu-classpath) reference for the Mixed Matrix Commitment Scheme (MMCS) that // Plonky3/SP1 use over BabyBear, component (c) of the PQ STARK-verify precompile 0x0AE8, plus a // self-test. It is a verbatim mirror of the Sp1StarkVerifierPrecompiledContract.Mmcs logic (same // PaddingFreeSponge hasher, TruncatedPermutation compressor, FieldMerkleTree build/open/verify over // the CONFIRMED Poseidon2-BabyBear permutation), compilable and runnable without the Besu EVM jars so // the MMCS can be exercised offline and cross-checked against the Python and Node references. // // HONEST SCOPE (CONFIRMED 2026-07-19). This is component (c) of the six-component STARK port // (docs/AERE-STARK-VERIFIER-PORT-SPEC.md section 4). The construction is the exact SP1 inner config // (PaddingFreeSponge hasher, TruncatedPermutation compressor, // FieldMerkleTreeMmcs<...,8> so DIGEST_ELEMS=8), verbatim from p3-merkle-tree's own mmcs.rs tests, and // the root/opening/verify outputs reproduce real known-answer vectors emitted by executing the pinned // p3-merkle-tree / p3-symmetric / p3-commit 0.4.3-succinct crates (checksums matched). It verifies // NOTHING about a real proof and the top-level 0x0AE8 precompile stays fail-closed. See // mmcs_babybear_reference.py / spec-mmcs-babybear.md for the full source citation. // // Usage: // javac -d out MmcsBabyBearSelfTest.java // java -cp out MmcsBabyBearSelfTest # run the self-test (prints PASS/FAIL counts) // java -cp out MmcsBabyBearSelfTest --emit # emit the shared cross-language vector set (JSON) import java.util.ArrayList; import java.util.List; public final class MmcsBabyBearSelfTest { 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 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 ---- 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 mul(final long a, final long b) { return (reduce(a) * reduce(b)) % P; } 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]))); } // The CONFIRMED Poseidon2-BabyBear width-16 permutation (component (b)). static long[] permute(final long[] state16) { if (state16.length != WIDTH) throw new IllegalArgumentException("state must have 16 elements"); 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 primitives ---- // PaddingFreeSponge: overwrite-mode, padding-free sponge. static long[] hashIter(final long[] elems) { final long[] state = new long[WIDTH]; // all-zero 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; } // TruncatedPermutation: permute(left||right) truncated to 8 lanes. static long[] compress2to1(final long[] left8, final long[] right8) { if (left8.length != DIGEST_ELEMS || right8.length != DIGEST_ELEMS) throw new IllegalArgumentException("digest length 8"); 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; } // ---- helpers ---- static int log2Ceil(final int n) { if (n <= 1) return 0; return 32 - Integer.numberOfLeadingZeros(n - 1); } static int nextPow2(final int n) { if (n <= 1) return 1; return Integer.highestOneBit(n - 1) << 1; } static final long[] DEFAULT_DIGEST = new long[DIGEST_ELEMS]; // A row-major matrix. static final class Matrix { final long[][] rows; final int height; final int width; Matrix(final long[][] r) { rows = r; height = r.length; width = r.length > 0 ? r[0].length : 0; } long[] row(final int i) { return rows[i].clone(); } } static Matrix makeMatrix(final int height, final int width, final int base) { final long[][] rows = new long[height][width]; for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) rows[i][j] = base + (long) i * width + j; return new Matrix(rows); } // ---- tree build (FieldMerkleTree::new) ---- static final class Tree { final long[][][] digestLayers; // [layer][node][8] final int logMaxHeight; final Matrix[] matrices; Tree(final long[][][] d, final int l, final Matrix[] m) { digestLayers = d; logMaxHeight = l; matrices = m; } } static Tree buildTree(final Matrix[] matrices) { if (matrices.length == 0) throw new IllegalArgumentException("no matrices"); // height property check final int[] hs = new int[matrices.length]; for (int i = 0; i < matrices.length; i++) hs[i] = matrices[i].height; final int[] sortedH = hs.clone(); java.util.Arrays.sort(sortedH); for (int k = 1; k < sortedH.length; k++) { if (sortedH[k - 1] != sortedH[k] && nextPow2(sortedH[k - 1]) == nextPow2(sortedH[k])) { throw new IllegalStateException("heights rounding to same power of two must be equal"); } } // stable sort tallest-first final Integer[] order = new Integer[matrices.length]; for (int i = 0; i < order.length; i++) order[i] = i; java.util.Arrays.sort(order, (a, b) -> { if (matrices[b].height != matrices[a].height) return matrices[b].height - matrices[a].height; return a - b; }); final Matrix[] ordered = new Matrix[matrices.length]; for (int i = 0; i < order.length; i++) ordered[i] = matrices[order[i]]; final int maxHeight = ordered[0].height; final int logMaxHeight = log2Ceil(maxHeight); final int maxHeightPadded = nextPow2(maxHeight); int idx = 0; final List tallest = new ArrayList<>(); while (idx < ordered.length && ordered[idx].height == maxHeight) tallest.add(ordered[idx++]); final List layers = new ArrayList<>(); final long[][] layer0 = new long[maxHeightPadded][]; for (int i = 0; i < maxHeightPadded; i++) layer0[i] = DEFAULT_DIGEST.clone(); for (int i = 0; i < maxHeight; i++) { layer0[i] = hashIter(concatRows(tallest, i)); } layers.add(layer0); while (layers.get(layers.size() - 1).length > 1) { final long[][] prev = layers.get(layers.size() - 1); final int nextLenPadded = prev.length / 2; final List inject = new ArrayList<>(); while (idx < ordered.length && nextPow2(ordered[idx].height) == nextLenPadded) inject.add(ordered[idx++]); layers.add(compressAndInject(prev, inject, nextLenPadded)); } final long[][][] dl = layers.toArray(new long[0][][]); return new Tree(dl, logMaxHeight, matrices); } static long[] concatRows(final List mats, final int rowIdx) { int total = 0; for (final Matrix m : mats) total += m.width; final long[] out = new long[total]; int p = 0; for (final Matrix m : mats) { final long[] row = m.row(rowIdx); System.arraycopy(row, 0, out, p, row.length); p += row.length; } return out; } static long[][] compressAndInject(final long[][] prev, final List inject, final int nextLenPadded) { final long[][] out = new long[nextLenPadded][]; for (int i = 0; i < nextLenPadded; i++) out[i] = DEFAULT_DIGEST.clone(); if (inject.isEmpty()) { for (int i = 0; i < nextLenPadded; i++) out[i] = compress2to1(prev[2 * i], prev[2 * i + 1]); return out; } final int injectHeight = inject.get(0).height; for (int i = 0; i < injectHeight; i++) { final long[] digest = compress2to1(prev[2 * i], prev[2 * i + 1]); final long[] rowsDigest = hashIter(concatRows(inject, i)); out[i] = compress2to1(digest, rowsDigest); } for (int i = injectHeight; i < nextLenPadded; i++) { final long[] digest = compress2to1(prev[2 * i], prev[2 * i + 1]); out[i] = compress2to1(digest, DEFAULT_DIGEST.clone()); } return out; } static long[] commitRoot(final Tree t) { return t.digestLayers[t.digestLayers.length - 1][0]; } static long[][] openings(final int index, final Tree t) { final long[][] op = new long[t.matrices.length][]; for (int m = 0; m < t.matrices.length; m++) { final int bitsReduced = t.logMaxHeight - log2Ceil(t.matrices[m].height); op[m] = t.matrices[m].row(index >> bitsReduced); } return op; } static long[][] proof(final int index, final Tree t) { final long[][] pf = new long[t.logMaxHeight][]; for (int i = 0; i < t.logMaxHeight; i++) pf[i] = t.digestLayers[i][(index >> i) ^ 1]; return pf; } // ---- verify_batch ---- 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) -> { if (dims[b][1] != dims[a][1]) return dims[b][1] - dims[a][1]; return 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) { final long[] nxt = hashIter(concatOpened(order, heights, ptr, currHeightPadded, opened)); root = compress2to1(root, nxt); } } return eqDigest(root, commit); } static long[] concatOpened(final Integer[] order, final int[] heights, final int[] ptr, final int padded, final long[][] opened) { final List buf = new ArrayList<>(); while (ptr[0] < heights.length && nextPow2(heights[ptr[0]]) == padded) { final long[] row = opened[order[ptr[0]]]; for (final long v : row) 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 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; } // ---- CONFIRMED conformance vectors (from the pinned library) ---- static final long[] PERM_ZEROS = {1787823396L, 953829438L, 89382455L, 347481625L, 1754527224L, 916217775L, 1056029082L, 410644796L, 1169123478L, 1854704276L, 1195829987L, 1485264906L, 1824644035L, 1948268315L, 847945433L, 190591038L}; // Each case: name; matspec (list of [height,width,base]); index; root; openings; proof. static final class Case { final String name; final int[][] matspec; final int index; final long[] root; final long[][] openings; final long[][] proof; Case(String n, int[][] ms, int idx, long[] r, long[][] o, long[][] p) { name = n; matspec = ms; index = idx; root = r; openings = o; proof = p; } Matrix[] matrices() { final Matrix[] out = new Matrix[matspec.length]; for (int i = 0; i < matspec.length; i++) out[i] = makeMatrix(matspec[i][0], matspec[i][1], matspec[i][2]); return out; } int[][] dims() { final int[][] d = new int[matspec.length][2]; for (int i = 0; i < matspec.length; i++) { d[i][0] = matspec[i][1]; d[i][1] = matspec[i][0]; } return d; } } static final Case[] CASES = { new Case("single_8x2", new int[][]{{8, 2, 10}}, 3, new long[]{35761595L, 1133593632L, 733114748L, 517674920L, 1449914397L, 74512820L, 381712048L, 469819303L}, new long[][]{{16, 17}}, new long[][]{{1513856679L, 1890540197L, 1949407553L, 586338782L, 197781917L, 573541314L, 1955108120L, 661229895L}, {57846071L, 657849236L, 262378030L, 1091294242L, 669362455L, 676261940L, 190131986L, 664894526L}, {1143223726L, 1793027126L, 1353127284L, 491971160L, 1959272008L, 1195367436L, 417822678L, 1372117039L}}), new Case("single_6x2", new int[][]{{6, 2, 100}}, 5, new long[]{1043564500L, 1812685593L, 1597353357L, 1392680266L, 1355213241L, 1159759876L, 1586103175L, 799081422L}, new long[][]{{110, 111}}, new long[][]{{1089158652L, 427625262L, 1157475631L, 1692145862L, 1671348918L, 1986544368L, 497315490L, 1960470114L}, {1787823396L, 953829438L, 89382455L, 347481625L, 1754527224L, 916217775L, 1056029082L, 410644796L}, {747170349L, 1608481817L, 1569266992L, 1154307224L, 561605764L, 1907985030L, 1454474361L, 357054770L}}), new Case("single_8x1", new int[][]{{8, 1, 200}}, 6, new long[]{447902873L, 1264273264L, 1781377203L, 392525377L, 451230220L, 285479002L, 124104889L, 737840045L}, new long[][]{{206}}, new long[][]{{854476755L, 1853946983L, 1409001429L, 1897109439L, 1880752521L, 1036730533L, 1817549359L, 907866104L}, {757904997L, 571589503L, 673816220L, 1066413580L, 473597103L, 1271204909L, 363424036L, 1450652756L}, {402779725L, 764084359L, 1406275333L, 1158469515L, 32391761L, 236523006L, 1039588073L, 264072612L}}), new Case("mixed_8x2_4x3", new int[][]{{8, 2, 10}, {4, 3, 300}}, 5, new long[]{902263792L, 353615665L, 93922356L, 1251424848L, 837594048L, 2005066510L, 431363100L, 287722769L}, new long[][]{{20, 21}, {306, 307, 308}}, new long[][]{{1732750591L, 1876733121L, 1364724824L, 1847015379L, 1792368333L, 483325461L, 1174939365L, 1939380322L}, {932176417L, 320077372L, 1748871293L, 625438914L, 179521004L, 655947905L, 828588297L, 31952751L}, {930401557L, 471785873L, 904189917L, 1626475238L, 550043796L, 1590748862L, 306781755L, 496390863L}}), new Case("mixed_8x1_4x2_2x2", new int[][]{{8, 1, 1}, {4, 2, 50}, {2, 2, 400}}, 6, new long[]{439675894L, 37405611L, 255560432L, 531897590L, 349476430L, 1463998586L, 540993751L, 1307259928L}, new long[][]{{7}, {56, 57}, {402, 403}}, new long[][]{{723290459L, 412642417L, 484634914L, 785252854L, 182396116L, 1183763863L, 630430401L, 175545021L}, {1257356383L, 1204259532L, 1915921675L, 53621155L, 1015367194L, 742423503L, 1998129904L, 1554700909L}, {13474675L, 1966185163L, 1255599565L, 471961853L, 1823737756L, 1607265064L, 1964440250L, 1219296485L}}), new Case("mixed_5x2_3x1", new int[][]{{5, 2, 20}, {3, 1, 70}}, 4, new long[]{700351145L, 394944774L, 166238365L, 1683786139L, 1659356855L, 1460803726L, 573770743L, 1196572690L}, new long[][]{{28, 29}, {72}}, new long[][]{{0, 0, 0, 0, 0, 0, 0, 0}, {1714214715L, 1781831455L, 644202942L, 667527548L, 1395787195L, 671219385L, 1433934871L, 1883643874L}, {1521468259L, 1627990232L, 1145489794L, 839124709L, 507242897L, 777414579L, 429233007L, 1147797058L}}), }; // ================================= self-test ================================= static int pass = 0; static int fail = 0; static void check(final String name, final boolean cond) { if (cond) pass++; else { fail++; System.out.println("FAIL " + name); } } static boolean eq2d(final long[][] a, final long[][] b) { if (a.length != b.length) return false; for (int i = 0; i < a.length; i++) if (!eqDigest(a[i], b[i])) return false; return true; } static void selfTest() { // perm sanity check("perm.zeros", eqDigest(permute(new long[WIDTH]), PERM_ZEROS)); // primitive KATs check("hash_item_5", eqDigest(hashIter(new long[]{5}), new long[]{881553380L, 703286570L, 452412164L, 1683859154L, 394218790L, 501691982L, 498441819L, 937656841L})); check("hash_slice_2", eqDigest(hashIter(new long[]{1, 2}), new long[]{1843359319L, 912981492L, 1448073574L, 280410802L, 1934669154L, 1885461061L, 224340142L, 529679527L})); check("hash_slice_3", eqDigest(hashIter(new long[]{1, 2, 3}), new long[]{1831345102L, 1426305082L, 956789587L, 1706209078L, 311264389L, 552910277L, 9044084L, 1828165221L})); check("hash_slice_8", eqDigest(hashIter(new long[]{1, 2, 3, 4, 5, 6, 7, 8}), new long[]{8999572L, 1033765830L, 347083905L, 1304769627L, 1321299677L, 1106238442L, 1523849989L, 954594225L})); check("hash_slice_9", eqDigest(hashIter(new long[]{1, 2, 3, 4, 5, 6, 7, 8, 9}), new long[]{1134257664L, 40304233L, 1823880005L, 1134792540L, 180685702L, 1633847360L, 1460964786L, 872499523L})); check("compress_h2_h3", eqDigest(compress2to1(hashIter(new long[]{1, 2}), hashIter(new long[]{1, 2, 3})), new long[]{1968576159L, 1450511489L, 1750728079L, 899535959L, 1052761632L, 829425096L, 282044891L, 1592617075L})); for (final Case c : CASES) { final Matrix[] mats = c.matrices(); final Tree t = buildTree(mats); final long[] root = commitRoot(t); final long[][] op = openings(c.index, t); final long[][] pf = proof(c.index, t); check("case." + c.name + ".root", eqDigest(root, c.root)); check("case." + c.name + ".openings", eq2d(op, c.openings)); check("case." + c.name + ".proof", eq2d(pf, c.proof)); // verify accepts the emitted opening check("case." + c.name + ".verify", verifyBatch(c.root, c.dims(), c.index, c.openings, c.proof)); // verify rejects a tampered proof final long[][] tampered = new long[c.proof.length][]; for (int i = 0; i < c.proof.length; i++) tampered[i] = c.proof[i].clone(); tampered[0][0] = add(tampered[0][0], 1); check("case." + c.name + ".tamper", !verifyBatch(c.root, c.dims(), c.index, c.openings, tampered)); // self-consistency: open EVERY valid leaf index and confirm the recomputed root matches. Valid // indices are [0, tallest actual height); padded rows above it are never opened (open_batch // would index past the tallest matrix, exactly as in Plonky3 where LDE domains are powers of two). int maxHeightActual = 0; for (final Matrix m : mats) maxHeightActual = Math.max(maxHeightActual, m.height); for (int q = 0; q < maxHeightActual; q++) { final long[][] qop = openings(q, t); final long[][] qpf = proof(q, t); check("case." + c.name + ".self." + q, verifyBatch(root, c.dims(), q, qop, qpf)); } } System.out.println("MmcsBabyBearSelfTest PASS=" + pass + " FAIL=" + fail); if (fail != 0) { System.out.println("MMCS_BABYBEAR_SELFTEST_FAILED"); System.exit(1); } System.out.println("MMCS_BABYBEAR_SELFTEST_OK"); } // ================================= shared-vector emit (JSON) ================================= static String jArr(final long[] a) { final StringBuilder sb = new StringBuilder("["); for (int i = 0; i < a.length; i++) { if (i > 0) sb.append(','); sb.append(a[i]); } return sb.append(']').toString(); } static String jArr2(final long[][] a) { final StringBuilder sb = new StringBuilder("["); for (int i = 0; i < a.length; i++) { if (i > 0) sb.append(','); sb.append(jArr(a[i])); } return sb.append(']').toString(); } static void emit() { final StringBuilder sb = new StringBuilder(); sb.append('{'); sb.append("\"constants\":{\"P\":").append(P).append(",\"width\":").append(WIDTH) .append(",\"rate\":").append(RATE).append(",\"out\":").append(OUT) .append(",\"digestElems\":").append(DIGEST_ELEMS).append('}'); sb.append(",\"permZeros\":").append(jArr(permute(new long[WIDTH]))); sb.append(",\"prim\":{") .append("\"hash_item_5\":").append(jArr(hashIter(new long[]{5}))) .append(",\"hash_slice_2\":").append(jArr(hashIter(new long[]{1, 2}))) .append(",\"hash_slice_3\":").append(jArr(hashIter(new long[]{1, 2, 3}))) .append(",\"hash_slice_8\":").append(jArr(hashIter(new long[]{1, 2, 3, 4, 5, 6, 7, 8}))) .append(",\"hash_slice_9\":").append(jArr(hashIter(new long[]{1, 2, 3, 4, 5, 6, 7, 8, 9}))) .append(",\"compress_h2_h3\":").append(jArr(compress2to1(hashIter(new long[]{1, 2}), hashIter(new long[]{1, 2, 3})))) .append('}'); sb.append(",\"cases\":["); for (int k = 0; k < CASES.length; k++) { final Case c = CASES[k]; if (k > 0) sb.append(','); final Matrix[] mats = c.matrices(); final Tree t = buildTree(mats); final long[] root = commitRoot(t); final long[][] op = openings(c.index, t); final long[][] pf = proof(c.index, t); sb.append("{\"name\":\"").append(c.name).append('"') .append(",\"index\":").append(c.index) .append(",\"root\":").append(jArr(root)) .append(",\"openings\":").append(jArr2(op)) .append(",\"proof\":").append(jArr2(pf)) .append(",\"rootMatch\":").append(eqDigest(root, c.root)) .append(",\"openingsMatch\":").append(eq2d(op, c.openings)) .append(",\"proofMatch\":").append(eq2d(pf, c.proof)) .append(",\"verifyOk\":").append(verifyBatch(c.root, c.dims(), c.index, c.openings, c.proof)) .append('}'); } sb.append("]}"); System.out.print(sb); } public static void main(final String[] args) { if (args.length > 0 && args[0].equals("--emit")) emit(); else selfTest(); } }