@nori-zk/proof-conversion
Version:
Verifying zkVM proofs inside o1js circuits, to generate Mina compatible proof
151 lines • 7.07 kB
JavaScript
import rootDir from '../../../utils/root_dir.js';
import { range } from '../../../utils/range.js';
import { Proof } from '../../../groth/proof.js';
import { resolve } from 'path';
import { Groth16Verifier } from '../../../groth/verifier.js';
import { getRandomString } from '../../../utils/random.js';
import { readFileSync, rmSync, writeFileSync } from 'fs';
import { createDirectories, createDirectory, } from '../../../utils/cache.js';
import { computeAuxWitness, computeRisc0Groth16Pairing, } from '../../../pairing-utils/index.js';
const proofVkCacheStructure = {
proofs: range(5).map((i) => `layer${i}`),
vks: range(5).map((i) => `layer${i}`),
};
const nodeCacheStructure = range(4).map((i) => `node${i}`);
export class Risc0Groth16ComputationalPlan {
constructor() {
this.name = 'Risc0Groth16Converter';
this.stages = [
{
// Create the cache and working directories
// Create the proofs and vks directories
// Create the node directories
name: 'CreateFileSystemCache',
type: 'main-thread',
execute: (state) => {
createDirectory(state.cacheDir);
createDirectory(state.workingDir);
createDirectories(state.workingDir, proofVkCacheStructure);
createDirectories(state.workingDir, nodeCacheStructure);
},
},
{
name: 'ComputePairing', // change from makeAlphaBeta
type: 'main-thread',
execute: (state) => {
const { raw_vk: vk } = state.input;
const risc0Groth16PairedVk = computeRisc0Groth16Pairing(vk);
writeFileSync(resolve(state.workingDir, 'risc_zero_vk.json'), JSON.stringify(risc0Groth16PairedVk));
writeFileSync(resolve(state.workingDir, 'risc_zero_proof.json'), JSON.stringify(state.input.risc0_proof));
state.vkPath = resolve(state.workingDir, 'risc_zero_vk.json');
state.proofPath = resolve(state.workingDir, 'risc_zero_proof.json');
},
},
{
name: 'GenerateWitness',
type: 'main-thread',
execute: (state) => {
const { vkPath, proofPath } = state;
const groth16 = new Groth16Verifier(vkPath);
// this extract the proof nega C B and public inputs but operates on the public inputs with the vk
const proof = Proof.parse(groth16.vk, proofPath);
// then this modified proof goes through the multimillerloop and gives us an f1p
const mlo = groth16.multiMillerLoop(proof).toJSON();
// F12 goes to wasm to compute the witness
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;
},
},
{
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: 'ComputeZKP',
type: 'parallel-cmd',
processCmds: (state) => {
process.env.GROTH16_VK_PATH = state.vkPath; // CHECKME what is this hackyness?
return range(16).map((i) => {
return {
cmd: 'node',
args: [
'--max-old-space-size=6000',
resolve(rootDir, 'build', 'src', 'groth', 'recursion', 'prove_zkps.js'),
`zkp${i}`,
state.proofPath,
state.witnessPath,
state.workingDir,
state.cacheDir,
],
capture: true,
printableArgs: [0, 1, 2],
};
});
},
numaOptimized: true,
},
...range(1, 5).map((i) => {
const stage = {
name: `CompressLayer${i}`,
type: 'parallel-cmd',
processCmds: (state) => {
const upperLimit = Math.pow(2, 4 - 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'),
'16',
`${i}`,
`${ZKP_J}`,
state.workingDir,
state.cacheDir,
],
capture: true,
printableArgs: [0, 1, 2, 3, 4],
};
});
},
numaOptimized: true,
};
return stage;
}),
];
}
async init(state, input) {
state.input = input;
state.workingDirName = getRandomString(20);
const pwd = process.cwd();
state.workingDir = resolve(pwd, '.conversion-cache', state.workingDirName);
state.cacheDir = resolve(pwd, '.conversion-cache', 'risc0_groth16_cache'); // THIS SHOULD MAYBE BE SET SUCH THAT WE MAKE A NEW DIR PER NUMBER OF PI/CI CHECKME
}
async then(state) {
const output = {
vkData: JSON.parse(readFileSync(resolve(state.workingDir, 'vks', 'nodeVk.json'), 'utf8')),
proofData: JSON.parse(readFileSync(resolve(state.workingDir, 'proofs', 'layer4', 'p0.json'), 'utf8')),
};
return output;
}
async finally(state) {
rmSync(state.workingDir, { recursive: true, force: true });
}
}
//# sourceMappingURL=groth16.js.map