@nori-zk/proof-conversion
Version:
Verifying zkVM proofs inside o1js circuits, to generate Mina compatible proof
146 lines • 6.62 kB
JavaScript
import rootDir from '../../../utils/root_dir.js';
import { range } from '../../../utils/range.js';
import { getMlo } from '../../../plonk/get_mlo.js';
import { resolve } from 'path';
import { getRandomString } from '../../../utils/random.js';
import { computeAuxWitness } from '../../../pairing-utils/index.js';
import { readFileSync, rmSync, writeFileSync } from 'fs';
import { createDirectories, createDirectory, } from '../../../utils/cache.js';
const proofVkCacheStructure = {
proofs: range(6).map((i) => `layer${i}`),
vks: range(6).map((i) => `layer${i}`),
};
const nodeCacheStructure = range(4).map((i) => `node${i}`);
// SP1ProofWithPublicValues Sp1PlonkInputTransformed
export class Sp1PlonkComputationalPlan {
constructor() {
this.name = 'Sp1PlonkConverter';
this.stages = [
{
name: 'CreateFileSystemCache',
type: 'main-thread',
execute: (state) => {
createDirectory(state.cacheDir);
createDirectory(state.workingDir);
createDirectories(state.workingDir, proofVkCacheStructure);
createDirectories(state.workingDir, nodeCacheStructure);
},
},
{
name: 'GenerateWitness',
type: 'main-thread',
execute: (state) => {
const mlo = getMlo(state.input.encodedProof, state.input.programVK, state.input.hexPi, state.input.pi2, state.input.pi3, state.input.pi4).toJSON();
const witness = computeAuxWitness(JSON.parse(mlo));
state.witnessPath = resolve(state.workingDir, 'aux_wtns.json');
// Write the mlo and witness to the cache dir
writeFileSync(resolve(state.workingDir, 'mlo.json'), mlo);
writeFileSync(state.witnessPath, JSON.stringify(witness));
return;
},
},
// If u wanna see stdout then you can change the capture boolean key in the plonk plan to emit
{
name: 'CompileRecursion',
type: 'serial-cmd',
processCmd: (state) => {
return {
cmd: 'node',
args: [
'--max-old-space-size=6000',
resolve(rootDir, 'build', 'src', 'compile_recursion_vks.js'),
state.workingDir,
state.cacheDir,
],
capture: true,
printableArgs: [0, 1, 2],
};
},
},
{
name: 'ComputeZPK',
type: 'parallel-cmd',
processCmds: (state) => {
return range(24).map((i) => {
return {
cmd: 'node',
args: [
'--max-old-space-size=6000',
resolve(rootDir, 'build', 'src', 'plonk', 'recursion', 'prove_zkps.js'),
`zkp${i}`,
state.input.encodedProof,
state.input.programVK,
state.input.hexPi,
state.input.pi2,
state.input.pi3,
state.input.pi4,
state.witnessPath,
state.workingDir,
state.cacheDir,
],
capture: true,
printableArgs: [0, 1, 2],
};
});
},
numaOptimized: true,
},
...range(1, 6).map((i) => {
const stage = {
name: `CompressLayer${i}`,
type: 'parallel-cmd',
processCmds: (state) => {
const upperLimit = Math.pow(2, 5 - i) - 1;
return range(upperLimit + 1).map((ZKP_J) => {
return {
cmd: 'node',
args: [
'--max-old-space-size=6000',
resolve(rootDir, 'build', 'src', 'node_resolver.js'),
'24',
`${i}`,
`${ZKP_J}`,
state.workingDir,
state.cacheDir,
],
capture: true,
printableArgs: [0, 1, 2, 3, 4],
};
});
},
numaOptimized: true,
};
return stage;
}),
];
}
async init(state, input) {
// SP1 v6: encoded_proof = [exit_code(32B)][vk_root(32B)][proof_nonce(32B)][gnark_proof(864B)]
// Skip the 96-byte SP1 prefix (192 hex chars) to get the raw gnark PLONK proof.
// public_inputs[2..4] carry those same values as Fr field elements.
const inputTransformed = {
hexPi: `0x${Buffer.from(input.public_values.buffer.data).toString('hex')}`,
programVK: input.proof.Plonk.public_inputs[0],
encodedProof: `0x${input.proof.Plonk.encoded_proof.slice(192)}`,
pi2: input.proof.Plonk.public_inputs[2],
pi3: input.proof.Plonk.public_inputs[3],
pi4: input.proof.Plonk.public_inputs[4],
};
state.input = inputTransformed;
state.workingDirName = getRandomString(20);
const pwd = process.cwd();
state.workingDir = resolve(pwd, '.conversion-cache', state.workingDirName);
state.cacheDir = resolve(pwd, '.conversion-cache', 'sp1_plonk_cache');
}
async then(state) {
const output = {
vkData: JSON.parse(readFileSync(resolve(state.workingDir, 'vks', 'nodeVk.json'), 'utf8')),
proofData: JSON.parse(readFileSync(resolve(state.workingDir, 'proofs', 'layer5', 'p0.json'), 'utf8')),
};
return output;
}
async finally(state) {
rmSync(state.workingDir, { recursive: true, force: true });
}
}
//# sourceMappingURL=plonk.js.map