npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nori-zk/proof-conversion

v0.8.17

Published

Verifying zkVM proofs inside o1js circuits, to generate Mina compatible proof

Readme

Proof-Conversion

Description

This repository enables verification of PLONK and Groth16 proofs generated by SP1, RISC Zero zkVMs and Snarkjs inside o1js circuits, making them compatible with zkApps built on the the Mina Protocol. The codebase employs a parallel and recursive architecture to efficiently verify non-native proofs in o1js.

The infrastructure provides format-specific conversion pipelines for each supported proof system. SP1 proofs (both PLONK and Groth16) use gnark's compressed serialization format internally, which the conversion process decompresses and transforms. All supported systems (SP1, RISC Zero, snarkjs) generate BN254 curve-based proofs, enabling a common cryptographic foundation. While each proof system requires dedicated handling due to format differences, the modular architecture separates proof decompression, verification key processing, and o1js compatible serialization into reusable components.

The repository handles Groth16 proofs from multiple sources: SP1's gnark-based implementation, RISC Zero's format, and snarkjs proofs (commonly used with circom circuits). Each format undergoes conversion to normalize curve point representations and compute the necessary pairing precomputations, producing proofs verifiable in o1js circuits on the Mina Protocol.


CLI tool

Installation

npm install -g @nori-zk/proof-conversion

Quick start

List available commands:

nori-proof-converter

Get detailed help for a specific command:

nori-proof-converter describe <command>
# OR run the command without args:
nori-proof-converter <command>

Convert a proof (object mode - single JSON file):

nori-proof-converter sp1Plonk ./example-proofs/sp1_plonk_obj_v5.json

Convert a proof (args mode - multiple JSON files):

nori-proof-converter snarkjsGroth16 ./example-proofs/snarkjs_groth16_args_proof.json ./example-proofs/snarkjs_groth16_args_vk.json ./example-proofs/snarkjs_groth16_args_public_inputs.json

Input modes

The CLI supports two input modes depending on the command:

1. Object mode (always supported)

  • Provide a single JSON file containing all required fields
  • The file structure is validated against the command's schema
nori-proof-converter sp1Plonk path/to/sp1_proof.json

2. Args mode (command-dependent)

  • Provide multiple JSON files as separate arguments in a specific order
  • Each file is validated against its corresponding schema
  • Run describe <command> to see if a command supports args mode and the required file order
nori-proof-converter snarkjsGroth16 path/to/proof.json path/to/vk.json path/to/public_inputs.json

Available conversion commands

sp1Plonk

Convert SP1 PLONK proofs into verifiable o1js proofs for the Mina Protocol.

  • Args mode: Not supported
  • Object mode: Supported

Object mode example:

nori-proof-converter sp1Plonk ./example-proofs/sp1_plonk_obj_v5.json

sp1Groth16

Convert SP1 Groth16 proofs into verifiable o1js proofs for the Mina Protocol.

  • Args mode: Not supported
  • Object mode: Supported

Object mode example:

nori-proof-converter sp1Groth16 ./example-proofs/sp1_groth16_obj_v5.json

risc0Groth16

Convert RISC Zero Groth16 proofs into verifiable o1js proofs for the Mina Protocol.

  • Args mode: Supported (files: proof.json, vk.json)
  • Object mode: Supported

Args mode example:

nori-proof-converter risc0Groth16 ./example-proofs/risc_zero_groth16_args_proof.json ./example-proofs/risc_zero_groth16_args_vk.json

Object mode example:

nori-proof-converter risc0Groth16 ./example-proofs/risc_zero_groth16_obj.json

snarkjsGroth16

Convert snarkjs/circom Groth16 proofs into verifiable o1js proofs for the Mina Protocol.

  • Args mode: Supported (files: proof.json, vk.json, public_inputs.json)
  • Object mode: Supported

Args mode example:

nori-proof-converter snarkjsGroth16 ./example-proofs/snarkjs_groth16_args_proof.json ./example-proofs/snarkjs_groth16_args_vk.json ./example-proofs/snarkjs_groth16_args_public_inputs.json

Object mode example:

nori-proof-converter snarkjsGroth16 ./example-proofs/snarkjs_groth16_obj.json

The describe command

Get detailed information about any command including schemas, supported modes, and examples. You can use the describe command or run the command without arguments:

nori-proof-converter describe snarkjsGroth16
# OR
nori-proof-converter snarkjsGroth16

Example output:

=== snarkjsGroth16 ===

Object-mode schema:

Provide one JSON file path arguments

Expected schemas for the file:
    {
      "proof": {
        "protocol": "groth16",
        "curve": "bn128",
        "pi_a": "ProjectivePoint",
        "pi_b": "ComplexProjectivePoint",
        "pi_c": "ProjectivePoint"
      },
      "vk": {
        "protocol": "groth16",
        "curve": "bn128",
        "nPublic": "BoundedNumberUnion(0..6)",
        "vk_alpha_1": "ProjectivePoint",
        "vk_beta_2": "ComplexProjectivePoint",
        "vk_gamma_2": "ComplexProjectivePoint",
        "vk_delta_2": "ComplexProjectivePoint",
        "vk_alphabeta_12": "ArrayOfLength[2]<ComplexProjectivePoint>",
        "IC": "ArrayOfBoundedLength[0..7]<ProjectivePoint>"
      },
      "publicInputs": "ArrayOfBoundedLength[0..6]<String>"
    }

Object-mode usage:
  $ nori-proof-converter snarkjsGroth16 path/to/snarkjs_groth16_input.json

Args-mode (file-per-key) schemas:

Provide '3' JSON file path arguments, in order: proof, vk, publicInputs

Expected schemas for each file:

  proof.json:
    {
      "protocol": "groth16",
      "curve": "bn128",
      "pi_a": "ProjectivePoint",
      "pi_b": "ComplexProjectivePoint",
      "pi_c": "ProjectivePoint"
    }

  vk.json:
    {
      "protocol": "groth16",
      "curve": "bn128",
      "nPublic": "BoundedNumberUnion(0..6)",
      "vk_alpha_1": "ProjectivePoint",
      "vk_beta_2": "ComplexProjectivePoint",
      "vk_gamma_2": "ComplexProjectivePoint",
      "vk_delta_2": "ComplexProjectivePoint",
      "vk_alphabeta_12": "ArrayOfLength[2]<ComplexProjectivePoint>",
      "IC": "ArrayOfBoundedLength[0..7]<ProjectivePoint>"
    }

  publicInputs.json:
    "ArrayOfBoundedLength[0..6]<String>"

Args-mode usage:
  $ nori-proof-converter snarkjsGroth16 path/to/proof.json path/to/vk.json path/to/public_inputs.json

Automatic schema validation

The CLI validates all inputs against type-safe schemas before conversion:

$ nori-proof-converter snarkjsGroth16 path/to/invalid_proof.json path/to/invalid_vk.json path/to/invalid_public_inputs.json

Args-mode validation failed for 'snarkjsGroth16'.

Provide '3' JSON file path arguments, in order: proof, vk, publicInputs

Expected schemas for each file:

  path/to/invalid_proof.json (proof.json):
    {
      "protocol": "groth16",
      "curve": "bn128",
      "pi_a": "ProjectivePoint",
      "pi_b": "ComplexProjectivePoint",
      "pi_c": "ProjectivePoint"
    }

  path/to/invalid_vk.json (vk.json):
    {
      "protocol": "groth16",
      "curve": "bn128",
      "nPublic": "BoundedNumberUnion(0..6)",
      "vk_alpha_1": "ProjectivePoint",
      "vk_beta_2": "ComplexProjectivePoint",
      "vk_gamma_2": "ComplexProjectivePoint",
      "vk_delta_2": "ComplexProjectivePoint",
      "vk_alphabeta_12": "ArrayOfLength[2]<ComplexProjectivePoint>",
      "IC": "ArrayOfBoundedLength[0..7]<ProjectivePoint>"
    }

  path/to/invalid_public_inputs.json (publicInputs.json):
    "ArrayOfBoundedLength[0..6]<String>"

Validation errors:

  path/to/invalid_proof.json (proof.json):
    proof["protocol"]: must be exactly "groth16", got "groth6"
    proof["pi_b"]: expected ComplexProjectivePoint, got [[String, String], [String, String], String, [String, String]]
    proof["extra_key"]: unexpected extra key

  path/to/invalid_vk.json (vk.json):
    vk["nPublic"]: expected BoundedNumberUnion(0..6), got 10 which exceeds maximum 6
    vk["vk_gamma_2"]: expected ComplexProjectivePoint, got [[String, String], [String, String], [String, Number]]
    vk["vk_delta_2"]: expected ComplexProjectivePoint, got [[String, String], [String, String], [String, String], [String, String]]
    vk["vk_alphabeta_12"] should have type ArrayOfLength[2]<ComplexProjectivePoint>
      vk["vk_alphabeta_12"][0]: expected ComplexProjectivePoint, got [[String, String], [String, String], [String, String], Number]
    vk["IC"]: expected ArrayOfBoundedLength[0..7]<ProjectivePoint>, got array of length 9, exceeding maximum 7

  path/to/invalid_public_inputs.json (publicInputs.json):
    publicInputs: expected ArrayOfBoundedLength[0..6]<String>, got object

Due to the validation issues running 'snarkjsGroth16' in 'args' mode cannot continue

Parallel processing

You can change the number of child processes it spawns by setting the MAX_PROCESSES environment variable:

export MAX_PROCESSES=16
nori-proof-converter sp1Plonk path/to/proof.json

Default is 1. Beyond MAX_PROCESSES=32 no performance gains can be expected.

Output file naming convention

Converted proofs are automatically saved with the command name appended before the extension:

$ nori-proof-converter sp1Plonk path/to/my_proof.json
# Creates: path/to/my_proof.sp1Plonk.json

Troubleshooting

Updating the CLI

Local reinstallation:

npm run relink

Global reinstallation:

npm uninstall -g @nori-zk/proof-conversion
npm install -g @nori-zk/proof-conversion

If getting a permission denied error, check npm's awareness of linked modules npm ls -g --depth=0 --link=true, remove symlinks manually if necessary, and run npm run relink.


Typescript API

This is a TypeScript-first API that incorporates Rust components via WebAssembly for performance-critical cryptographic operations.

Installation

npm ci @nori-zk/proof-conversion --save

Ensure you have o1js as a peer dependency. Currently supported version 2.12.0.

Available conversion "plans"

import { performSp1Plonk, performRisc0Groth16, performSnarkjsGroth16, performSp1Groth16 } from '@nori-zk/proof-conversion';

Usage example in and ESM Node.js project

import {
  ComputationalPlanExecutor,
  performSp1Plonk,
  type SP1ProofWithPublicValuesPlonkNoTee,
} from '@nori-zk/proof-conversion';
import { assertExactStructure } from '@nori-zk/proof-conversion/validation';
import { readFileSync } from 'fs';
// Optionally include this package, to see internal progress of the executor during proof conversion
import { LogPrinter } from 'esm-iso-logger';

async function main() {
  new LogPrinter('NoriProofConverter');
  const maxProcesses = 10;
  const executor = new ComputationalPlanExecutor(maxProcesses);
  const sp1ProofStr = readFileSync('./example-proofs/v5.json', 'utf8');
  const sp1Proof: unknown = JSON.parse(sp1ProofStr);
  // If sp1Proof is invalid ValidationError('SP1ProofWithPublicValuesPlonkNoTee validation failed: <specificInformation>') will be thrown!
  assertExactStructure(
    sp1Proof,
    performSp1Plonk.schema,
    'SP1ProofWithPublicValuesPlonkNoTee'
  );
  // sp1Proof is now fully typed!
  const validatedSP1Proof: SP1ProofWithPublicValuesPlonkNoTee = sp1Proof;
  const result = await performSp1Plonk(executor, validatedSP1Proof);
  console.log('Finished conversion', result);
}

main().catch(console.error);

Converted proof types

ProofDataOutput

Proof data in o1js format.

Contains a serialized o1js proof along with its public inputs and outputs, plus metadata about recursive proof composition.

  • maxProofsVerified: Number of proofs this circuit verifies recursively. This is a structural property of the proof's circuit, not a verification mode:

    • 0: Circuit does not verify any other proofs (base case/leaf proof)
    • 1: Circuit verifies exactly 1 other proof inside its execution
    • 2: Circuit verifies exactly 2 other proofs inside its execution (binary tree recursion)
  • proof: Base64-encoded o1js proof in Pickles format. This is the native proof representation used by o1js, containing all the cryptographic witness data and commitments required for verification.

  • publicInput: Array of public input field elements as decimal strings. Each string represents a field element in the Pallas base field (255-bit modulus).

  • publicOutput: Array of public output field elements as decimal strings. Each string represents a field element in the Pallas base field (255-bit modulus).

interface ProofDataOutput {
  publicInput: string[];
  publicOutput: string[];
  maxProofsVerified: 0 | 1 | 2;
  proof: string;
}

VkDataOutput

Verification key data in o1js format.

Contains a serialized o1js verification key with its cryptographic hash.

  • data: Base64-encoded o1js verification key in Pickles format. Contains all the cryptographic parameters needed to verify proofs: constraint system commitments, setup parameters, and domain configuration.

  • hash: Cryptographic hash of the verification key for integrity checking and identification.

interface VkDataOutput {
  data: string;
  hash: string;
}

ConversionOutput

Complete conversion output in o1js format.

Bundles together the verification key and proof data for o1js verification. This is the return type of all proof conversion functions (performSp1Plonk, performRisc0Groth16, performSnarkjsGroth16, performSp1Groth16).

  • vkData: Verification key in o1js format (see VkDataOutput)
  • proofData: Proof in o1js format with public I/O (see ProofDataOutput)
interface ConversionOutput {
  vkData: VkDataOutput;
  proofData: ProofDataOutput;
}

Minimal export

The package exports via the /min path, useful types and some limited utilities omitting anything which relies on a Node.js dependancy. This is largely to support frontend applications. Note none of the proof conversion "plans" e.g. performSp1Plonk, the ComputationalPlanExecutor nor the wasm based utilities are exported.

import {
  parsePlonkPublicInputsProvable,
  wordToBytes,
  NodeProofLeft,
  FrC,
  DeferredPromise
} from '@nori-zk/proof-conversion/min'; // These utilities are also available in '@nori-zk/proof-conversion'

import type {
  ConversionOutput,
  Risc0Groth16Vk,
  Risc0Groth16PairedVk,
  Risc0Groth16Proof,
  Risc0Groth16Input,
  SnarkjsGroth16Proof,
  SnarkjsGroth16VK,
  SnarkjsGroth16Input,
  SP1ProofWithPublicValues,
  SP1ProofWithPublicValuesGroth16NoTee,
  SP1ProofWithPublicValuesPlonkNoTee,
} from '@nori-zk/proof-conversion/min'; // These types are also available in '@nori-zk/proof-conversion'

Wasm utilities

The package exports via the /wasm-utils path, useful wasm based utilities are included to assist proof conversions from one format to another - omitting anything which relies on a Node.js dependancy.

import {
  computeAuxWitness,
  convertSp1Groth16ToO1js,
  convertSnarkjsGroth16ToO1js,
  computeRisc0Groth16Pairing,
} from '@nori-zk/proof-conversion/wasm-utils'; // These types are also available in '@nori-zk/proof-conversion'

Validation utilities

The package exports runtime validation utilities via the /validation path. These are platform-agnostic (work in both Node.js and browser) and provide type-safe schema validation with detailed error messages.

import {
  assertExactStructure,
  ValidationError,
  isString,
  isNumber,
  isArray,
  isStringArray,
  isAffinePoint2d,
  isComplexAffinePoint2d,
  // ... many more validators
} from '@nori-zk/proof-conversion/validation';

Key exports:

  • assertExactStructure(obj, schema, context) - Validates an object against a schema definition and throws ValidationError with detailed diagnostics on failure. Uses TypeScript assertion signatures to narrow types on success.

  • ValidationError - Error class thrown when validation fails, containing path-aware error messages.

  • Type guard validators:

    • Primitives: isString, isNumber, isBoolean, isNull, isUndefined
    • Arrays: isArray(validator), isStringArray, isNumberArray, isArrayOfLength(n), isArrayOfBoundedLength(min, max)
    • Crypto types: isAffinePoint2d, isComplexAffinePoint2d, isProjectivePoint, isComplexProjectivePoint

Example usage with real proof schemas:

import {
  assertExactStructure,
  isString,
  isProjectivePoint,
  isComplexProjectivePoint,
  isArrayOfBoundedLength,
  isBoundedNumberUnion,
} from '@nori-zk/proof-conversion/validation';

// Define schema for snarkjs Groth16 verification key
const snarkjsGroth16VKSchema = {
  protocol: 'groth16' as const,  // Literal value - must be exactly "groth16"
  curve: 'bn128' as const,       // Literal value - must be exactly "bn128"
  nPublic: isBoundedNumberUnion({ min: 0, max: 6 }),  // Number between 0-6
  vk_alpha_1: isProjectivePoint,         // [x, y, z] array of field element strings
  vk_beta_2: isComplexProjectivePoint,   // [[x0,x1], [y0,y1], [z0,z1]] nested arrays
  vk_gamma_2: isComplexProjectivePoint,
  vk_delta_2: isComplexProjectivePoint,
  IC: isArrayOfBoundedLength(isProjectivePoint, { minLength: 0, maxLength: 7 }),
};

// Validate untrusted input
const untrustedVK: unknown = JSON.parse(vkJsonString);

// This throws ValidationError with detailed path-aware messages if invalid
assertExactStructure(untrustedVK, snarkjsGroth16VKSchema, "Snarkjs Groth16 VK");

// After validation, TypeScript knows the exact type
// untrustedVK is now: { protocol: "groth16", curve: "bn128", nPublic: number, ... }

Integration with API methods:

All proof conversion functions (performSp1Plonk, performRisc0Groth16, etc.) are decorated with the @ApiMethod decorator, which:

  • Attaches the validation schema to the function (accessible via .schema property)
  • Enables runtime validation as shown in the usage example above: performSp1Plonk.schema
  • Provides CLI support by converting positional arguments to typed objects
  • Ensures type safety across the entire conversion pipeline

See src/api/*/schema.ts files for the actual schemas used by each conversion method.

Full package (Node.js only)

The main package export @nori-zk/proof-conversion includes everything from /min plus Node.js-specific functionality for running proof conversion plans. These plans spawn child processes to perform parallel proof conversion operations.

Additional Node.js-only exports:

import {
  performSp1Plonk,
  performRisc0Groth16,
  performSnarkjsGroth16,
  performSp1Groth16,
  ComputationalPlanExecutor,
} from '@nori-zk/proof-conversion';
  • performSp1Plonk - Convert SP1 PLONK proofs into verifiable o1js proofs for the Mina Protocol
  • performSp1Groth16 - Convert SP1 Groth16 proofs into verifiable o1js proofs for the Mina Protocol
  • performRisc0Groth16 - Convert RISC Zero Groth16 proofs into verifiable o1js proofs for the Mina Protocol
  • performSnarkjsGroth16 - Convert snarkjs Groth16 proofs into verifiable o1js proofs for the Mina Protocol
  • ComputationalPlanExecutor - Executor for running conversion plans with parallel processing

These require Node.js because they spawn child processes and perform filesystem operations for intermediate proof data.


Numa

sudo apt install numactl

Installing numactl if your system supports it is highly recommended. Note without it, it is expected that you will see crashes on x86/x86_64 environments. If it is installed, this library will automatically use it.


Optional Kernel Tuning

sudo sysctl -w vm.zone_reclaim_mode=0
sudo sysctl -w vm.overcommit_memory=1
sudo sysctl -w vm.swappiness=10
sudo cpupower frequency-set -g performance

Overview of o1js-blobstream by Geometry Research

Refer to the Gitbook documentation for details on o1js-blobstream.


Low level tinkering

If you wish to do additional research with this library, bash scripts exist which allow you interact with the lower level programs of the library. When doing this ensure you setup numactl and in addition you will need parallel.

Install parallel

sudo apt install parallel

Depending on the CPU model, specificaly NUMA nodes setup, you may need to adjust values in scrips/plonk_tree.sh


License

This project is licensed under either:

at your option.

The SPDX license identifier for this project is:
MIT OR Apache-2.0.