---
authors: ["Vivian Jeng"]
title: "Mopro: Comparison of Circom Provers"
image: "/articles/mopro-comparison-of-circom-provers/mopro-comparison-of-circom-provers-cover.webp"
tldr: "This post was written by [Vivian Jeng](https://mirror.xyz/privacy-scaling-explorations.eth/GLbuCflH0hu_DncKxiC2No5w3LZJAGw4QaCB-HYD5e0), a developer on the Mopro team."
date: "2025-01-21"
canonical: "https://mirror.xyz/privacy-scaling-explorations.eth/GLbuCflH0hu_DncKxiC2No5w3LZJAGw4QaCB-HYD5e0"
tags:
  [
    "mopro",
    "circom",
    "zero-knowledge proofs",
    "witness generation",
    "proof generation",
    "snarkjs",
    "rapidsnark",
    "mobile",
    "performance",
    "toolkits",
  ]
projects: ["mopro"]
---

[Mopro](https://github.com/zkmopro/mopro) is a toolkit designed to make mobile proving simple and efficient. With the growing dominance of mobile app users over web app users, and the observation that `snarkjs` is less performant compared to native provers like `rapidsnark`, we recognized the need to optimize proving speed for mobile devices. To address this, one of Mopro's primary goals is to evaluate various provers across different platforms to identify the most effective solution, ensuring we focus on the best-performing prover for mobile environments.

## Introduction

Throughout 2024, we compared various Groth16 provers for Circom. Our goal was to demonstrate that native provers (written in C++ or Rust) outperform `snarkjs` in terms of speed. Along the way, we uncovered some fascinating insights, which we’re excited to share with you in this post.

To understand a Groth16 prover, let’s break it down into two main components: **witness generation** and **proof generation**.

**Witness Generation:** This step involves processing inputs along with witness calculation functions to produce the necessary witness values for a circuit. It's a purely computational step and does not involve any zero-knowledge properties.

**Proof Generation:** Once the witness is generated, this step takes the witness and the zkey (generated by `snarkjs`) to compute the polynomial commitments and produce a succinct zero-knowledge proof.

Ideally, developers should have the flexibility to switch between different witness generation and proof generation implementations. This would allow them to leverage the fastest options available, optimizing performance and enhancing their development experience.

However, each of these tools presents unique challenges. In the following sections, we will delve into these challenges in detail and provide a comparison table for clarity.

## Witness Generation

### `snarkjs`

- [https://github.com/iden3/snarkjs](https://github.com/iden3/snarkjs)

`snarkjs` is one of the most widely used tools for generating Groth16 proofs and witnesses. Written in JavaScript, it runs seamlessly across various environments, including browsers on both desktops and mobile devices. However, it faces performance challenges with large circuits. For instance, an RSA circuit can take around 15 seconds to process, while a more complex circuit like zk-email may require up to a minute to generate a proof. This highlights the need for optimized solutions, such as leveraging mobile-native capabilities and even mobile GPUs, to significantly enhance performance.

### `witnesscalc`

- [https://github.com/0xPolygonID/witnesscalc](https://github.com/0xPolygonID/witnesscalc)

`witnesscalc` is a lightweight, C++-based tool designed for efficient witness generation for circuits compiled with Circom. It offers a faster alternative to JavaScript-based tools like `snarkjs`. With cross-platform support and compatibility with other ZKP tools, Witnesscalc is ideal for handling performance-sensitive applications and large circuits.

While Witnesscalc performs exceptionally well with circuits such as RSA, Anon Aadhaar, Open Passport, and zkEmail, integrating it into Mopro presents challenges due to its C++ implementation, whereas Mopro is built on Rust. We are actively working to bridge this gap to leverage its performance benefits within the mobile proving ecosystem.

### `wasmer`

- [https://github.com/arkworks-rs/circom-compat](https://github.com/arkworks-rs/circom-compat)

One option available in Rust is `circom-compat`, maintained by the Arkworks team. This library uses the `.wasm` file generated by Circom and relies on the Rust crate `wasmer` to execute the witness generation. However, wasmer doesn’t run natively on devices—it creates a WebAssembly execution environment for the `.wasm` file. As a result, the performance of wasmer is comparable to the WebAssembly performance of `snarkjs` running in a browser.

Initially, we encountered memory issues with wasmer during implementation ([issue #1](https://github.com/zkmopro/mopro/issues/1)). Later, we discovered that the Apple App Store does not support any wasmer functions or frameworks, making it impossible to publish apps using this solution on the App Store or TestFlight ([issue #107](https://github.com/zkmopro/mopro/issues/107)). As a result, we decided to abandon this approach for Mopro.

### `circom-witness-rs`

- [https://github.com/philsippl/circom-witness-rs](https://github.com/philsippl/circom-witness-rs)

Another Rust-based option is `circom-witness-rs`, developed by the Worldcoin team. Unlike solutions that rely on WebAssembly (wasm) output from the Circom compiler, this tool directly utilizes `.cpp` and `.dat` files generated by Circom. It employs the `cxx` crate to execute functions within the `.cpp` files, enhanced with optimizations such as dead code elimination. This approach has demonstrated excellent performance, particularly with Semaphore circuits. However, we discovered that it encounters compatibility issues with certain circuits, such as RSA, limiting its applicability for broader use cases.

### `circom-witnesscalc`

- [https://github.com/iden3/circom-witnesscalc](https://github.com/iden3/circom-witnesscalc)

The team at iden3 took over this project and began maintaining it under the name `circom-witnesscalc`. While it heavily draws inspiration from `circom-witness-rs`, it inherits the same limitation—it does not support RSA circuits. For more details, refer to the "Unimplemented Features" section in the [README](https://github.com/iden3/circom-witnesscalc?tab=readme-ov-file#unimplemented-features).

### `rust-witness`

- [https://github.com/chancehudson/rust-witness](https://github.com/chancehudson/rust-witness)

Currently, Mopro utilizes a tool called `rust-witness`, developed by a member of the Mopro team. This tool leverages `w2c2` to translate WebAssembly (`.wasm`) files into portable C code. By transpiling `.wasm` files from Circom into C binaries, rust-witness has demonstrated compatibility across all circuits and platforms tested so far, including desktop, iOS, and Android. Additionally, its performance has shown to be slightly better than that of wasmer.

## Proof Generation

### `snarkjs`

- [https://github.com/iden3/snarkjs](https://github.com/iden3/snarkjs)

As mentioned earlier, `snarkjs` is the most commonly used tool for generating Groth16 proofs. However, its performance still has room for improvement.

### `rapidsnark`

- [https://github.com/iden3/rapidsnark](https://github.com/iden3/rapidsnark)

Rapidsnark, developed by the iden3 team, is an alternative to `snarkjs` designed to deliver faster Groth16 proof generation. Similar to `witnesscalc`, it is written in C++. While it shows promising performance, we are still working on integrating it into Mopro.

### `ark-works`

- [https://github.com/arkworks-rs/circom-compat](https://github.com/arkworks-rs/circom-compat)

The primary Rust-based option is `circom-compat`, maintained by the Arkworks team. Arkworks is a Rust ecosystem designed for programmable cryptography, deliberately avoiding dependencies on native libraries like `gmp`. In our experiments, Arkworks has proven to work seamlessly with all circuits and platforms. If you have Rust installed, you can easily execute Groth16 proving using Arkworks without any issues. As a result, Mopro has adopted this approach to generate proofs for cross-platform applications.

## Comparison Table

Here, we present a table comparing different witness generators and proof generators to provide a clearer understanding of their features and performance.

In this comparison, we use `circom-witnesscalc` as a representative for both `circom-witness-rs` and `circom-witnesscalc`, as they share fundamentally similar implementations and characteristics.

![Comparison of witness generators](/articles/mopro-comparison-of-circom-provers/QQlHHr5UHsiYu4QrJLsAZ.webp)

Comparison of witness generators

![Comparison of proof generators](/articles/mopro-comparison-of-circom-provers/UkNa-hAgUN5E7GAOjFbGE.webp)

Comparison of proof generators

## Conclusion

In conclusion, we found that the `witnesscalc` and `rapidsnark` stack offers the best performance, but integrating it into Rust presents significant challenges. These tools rely heavily on C++ and native dependencies like `gmp`, `cmake`, and `nasm`. Our goal is to integrate these tools into Rust to make them more accessible for application development. Similar to how `snarkjs` seamlessly integrates into JavaScript projects like Semaphore and ZuPass, having a Rust-compatible stack would simplify building cross-platform applications. Providing only an executable limits flexibility and usability for developers. In 2025, we are prioritizing efforts to enable seamless integration of these tools into Rust or to provide templates for customized circuits.

We recognize the difficulty in choosing the right tools and are committed to supporting developers in this journey. If you need assistance, feel free to reach out to the Mopro team on Telegram: [@zkmopro](https://t.me/zkmopro).

1.  We are actively working on integrating `witnesscalc` into Mopro. Please refer to [issue #284](https://github.com/zkmopro/mopro/issues/284) [↩](https://doc-compare-circom.mopro.pages.dev/blog/circom-comparison/#user-content-fnref-1-ca0b0e)
2.  Please refer to [PR #255](https://github.com/zkmopro/mopro/pull/255) to see how to use `circom-witnesscalc` with Mopro. [↩](https://doc-compare-circom.mopro.pages.dev/blog/circom-comparison/#user-content-fnref-2-ca0b0e)
3.  [waku-org](https://github.com/waku-org) has investigated this approach; however, it does not outperform snarkjs in terms of performance. Please refer to [this comment](https://github.com/zkmopro/mopro/issues/202#issuecomment-2236923108) for more details. [↩](https://doc-compare-circom.mopro.pages.dev/blog/circom-comparison/#user-content-fnref-3-ca0b0e)
4.  We are actively working on integrating `rapidsnark` into Mopro. Please refer to [issue #285](https://github.com/zkmopro/mopro/issues/285) [↩](https://doc-compare-circom.mopro.pages.dev/blog/circom-comparison/#user-content-fnref-4-ca0b0e)
