<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://zkmopro.org/blog</id>
    <title>Mopro Blog</title>
    <updated>2026-01-26T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://zkmopro.org/blog"/>
    <subtitle>Mopro Blog</subtitle>
    <icon>https://zkmopro.org/img/logo_sm.svg</icon>
    <entry>
        <title type="html"><![CDATA[Client-Side GPU Acceleration for ZK: A Path to Everyday Ethereum Privacy]]></title>
        <id>https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy</id>
        <link href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy"/>
        <updated>2026-01-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Thanks to Alex Kuzmin, Andy Guzman, Miha Stopar, Sinu, and Zoey for their generous feedback and review.]]></summary>
        <content type="html"><![CDATA[<p><em>Thanks to Alex Kuzmin, Andy Guzman, Miha Stopar, Sinu, and Zoey for their generous feedback and review.</em></p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="tldr">TL;DR<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#tldr" class="hash-link" aria-label="Direct link to TL;DR" title="Direct link to TL;DR">​</a></h2>
<ul>
<li><strong>The Problem</strong>: Delegating ZK proof generation to servers often fails to preserve privacy, as the server sees your private inputs, though there are recent researches on private proof delegation <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-2-1c095a" id="user-content-fnref-2-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-37-1c095a" id="user-content-fnref-37-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">2</a></sup> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-38-1c095a" id="user-content-fnref-38-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">3</a></sup> to mitigate this issue. Ultimately, true privacy requires client-side proving, but current performance is too slow for mainstream adoption.</li>
<li><strong>The Opportunity</strong>: Modern mobile phones and laptops contain GPUs well-suited to accelerating parallelizable ZK primitives (NTT, MSM, hashing). Benchmarks show field operations on smaller fields like M31 achieve more than 100x throughput compared to BN254 on an Apple M3 chip <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-19-1c095a" id="user-content-fnref-19-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">4</a></sup>.</li>
<li><strong>The Gap</strong>: No standard cryptographic library exists for client-side GPU implementations. Projects reinvent primitives from scratch, and best practices for mobile-specific constraints (hybrid CPU-GPU coordination, thermal management) remain unexplored.</li>
<li><strong>Post-Quantum Alignment</strong>: Smaller-field operations in post-quantum (PQ) schemes (hash-based, lattice-based) map naturally to GPU 32-bit ALUs, making this exploration more valuable as the ecosystem prepares for quantum threats.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="1-privacy-is-hygiene">1. Privacy is Hygiene<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#1-privacy-is-hygiene" class="hash-link" aria-label="Direct link to 1. Privacy is Hygiene" title="Direct link to 1. Privacy is Hygiene">​</a></h2>
<p>Ethereum's public ledger ensures transparency, but it comes at a steep cost: every transaction is permanently visible, exposing users' financial histories, preferences, and behaviors. Chain analysis tools can easily link pseudonymous addresses to real-world identities, potentially turning the blockchain into a surveillance machine.</p>
<p>As Vitalik put it, <a href="https://x.com/VitalikButerin/status/1992732552814305728" target="_blank" rel="noopener noreferrer">"Privacy is not a feature, but hygiene."</a> It is a fundamental requirement for a safe decentralized system that people are willing to adopt for everyday use.</p>
<p><img decoding="async" loading="lazy" alt="privacy-is-hygiene" src="https://zkmopro.org/assets/images/privacy-is-hygiene-01921a859ae3282e945468998d0363ba.png" class="img_KKSl"></p>
<p>Delegating ZK proof generation to servers undermines this hygiene. While server-side proving is fast and convenient, it requires sharing raw inputs with third parties. Imagine handing your bank statement to a stranger to "anonymize" it for you. If a server sees your IP or, even worse, your transaction details, privacy is compromised regardless of the proof's validity. Though recent studies explore private delegation of provers <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-2-1c095a" id="user-content-fnref-2-1c095a-2" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup>, true sovereignty requires client-side proving: users generate proofs on their own devices, keeping data private from the start.</p>
<p>This is especially critical for everyday Ethereum privacy. Think of sending payments, voting in DAOs, or managing identity-related credentials (e.g. health records) without fear of exposure. Without client-side capabilities, privacy-preserving tech remains niche, adopted only by privacy-maximalist geeks. Hence, accelerating proofs on consumer hardware could be the bedrock for creating mass adoption of privacy, making it as seamless as signing a transaction today.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="2-gpu-acceleration-a-multi-phase-necessity-for-client-proving">2. GPU Acceleration: A Multi-Phase Necessity for Client Proving<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#2-gpu-acceleration-a-multi-phase-necessity-for-client-proving" class="hash-link" aria-label="Direct link to 2. GPU Acceleration: A Multi-Phase Necessity for Client Proving" title="Direct link to 2. GPU Acceleration: A Multi-Phase Necessity for Client Proving">​</a></h2>
<p>Committing resources to client-side GPU acceleration for ZKP is both a short-term and long-term opportunity for Ethereum's privacy landscape. GPUs excel at parallel computations, aligning perfectly with core primitives that could be used in privacy-preserving techs like ZKP and FHE, and client devices (phones, laptops) increasingly feature growing GPU capabilities. This could reduce proving times to interactive levels, fostering mass adoption.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="current-status-and-progress">Current Status and Progress<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#current-status-and-progress" class="hash-link" aria-label="Direct link to Current Status and Progress" title="Direct link to Current Status and Progress">​</a></h3>
<p>Although client-side proving is a reality, the user experience is still catching up. <a href="https://ethproofs.org/csp-benchmarks" target="_blank" rel="noopener noreferrer">CSP benchmarks from ethproofs.org</a> show that while consumer devices can now generate proofs in seconds, the process often takes longer than 100ms. For scenarios requiring real-time experience, like payments or some DeFi applications, this delay creates noticeable friction that hinders adoption.</p>
<table><thead><tr><th style="text-align:center"><img decoding="async" loading="lazy" alt="csp-benchmark" src="https://zkmopro.org/assets/images/csp-benchmark-d8ee3f59345276d608c9eeab2d3ae059.png" class="img_KKSl"></th></tr></thead><tbody><tr><td style="text-align:center">CSP benchmarks from ethproofs.org <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-21-1c095a" id="user-content-fnref-21-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">5</a></sup>. Snapshot taken on January 19, 2026</td></tr></tbody></table>
<p>Several zkVM projects working on real-time proving have leveraged server-side GPUs (e.g. CUDA on RTX-5090) for ~79x improvement on average proving times, according to site metrics from <a href="https://web.archive.org/web/20250128162122/https://ethproofs.org/" target="_blank" rel="noopener noreferrer">Jan 28, 2025</a> to <a href="https://web.archive.org/web/20260123033924/https://ethproofs.org/" target="_blank" rel="noopener noreferrer">Jan 23, 2026</a>, validating the potential of GPU acceleration for proving.</p>
<p>On the client side, though still in the early stages of PoC and exploration, several projects highlight potential:</p>
<ul>
<li><a href="https://dev.ingonyama.com/start/architecture/install_gpu_backend#overview" target="_blank" rel="noopener noreferrer">Ingonyama ICICLE Metal</a>: Optimizes ZK primitives such as MSM and NTT for Apple's Metal API, achieving up to 5x acceleration on iOS/macOS GPUs (v3.6 release detailed in their <a href="https://medium.com/@ingonyama/icicle-goes-metal-v3-6-163fa7bbfa44" target="_blank" rel="noopener noreferrer">blog post</a>).</li>
<li><a href="https://github.com/zkmopro/gpu-acceleration/releases/tag/v0.2.0" target="_blank" rel="noopener noreferrer">zkMopro Metal MSM v2</a>: Explores MSM acceleration on Apple GPUs, improved 40-100x over v1. For details, check this <a href="https://pse.dev/blog/mopro-metal-msm-v2" target="_blank" rel="noopener noreferrer">write-up</a>. Building on ZPrize 2023 winners Tal and Koh's WebGPU innovations (inspired by their implementation) <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-5-1c095a" id="user-content-fnref-5-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">6</a></sup>.</li>
<li><a href="https://github.com/ligeroinc/ligero-prover" target="_blank" rel="noopener noreferrer">Ligetron</a>: Leverages WebGPU for faster SHA-256 and NTT operations, enabling cross-platform ZK both natively and in browsers.</li>
</ul>
<p>Broader efforts from ZPrize and Geometry.xyz underscore growing momentum, though protocol integration remains limited <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-8-1c095a" id="user-content-fnref-8-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">7</a></sup><sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-7-1c095a" id="user-content-fnref-7-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">8</a></sup>.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="opportunities-for-acceleration">Opportunities for Acceleration<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#opportunities-for-acceleration" class="hash-link" aria-label="Direct link to Opportunities for Acceleration" title="Direct link to Opportunities for Acceleration">​</a></h3>
<p>Several ZK primitives are inherently parallelizable, making GPUs a natural fit:</p>
<ul>
<li><strong>MSM (Multi-Scalar Multiplication)</strong>: Sums scalar multiplications on elliptic curves; parallel via independent additions, bottleneck in elliptic curve-related schemes.</li>
<li><strong>NTT (Number Theoretic Transform)</strong>: A fast algorithm for polynomial multiplication similar to FFT but over finite fields; parallel via butterfly ops, core to polynomial operations in many schemes.</li>
<li><strong>Polynomial Evaluation and Matrix-Vector Multiplication</strong>: Computes polynomial values at points or matrix-vector products; high-throughput on GPU shaders via SIMD parallelism.</li>
<li><strong>Merkle Tree Commitments</strong>: Builds hashed tree structures for efficient verification; parallel-friendly for batch processing and hashing.</li>
</ul>
<p>Additionally, building blocks in interactive oracle proofs (IOPs), such as the sumcheck protocol <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-35-1c095a" id="user-content-fnref-35-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">9</a></sup>, which reduces multivariate polynomial sums to univariate checks, benefit from massive parallelism in tasks such as summing large datasets or verifying constraints. While promising for GPU acceleration in schemes like WHIR <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-17-1c095a" id="user-content-fnref-17-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">10</a></sup> or GKR-based systems <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-36-1c095a" id="user-content-fnref-36-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">11</a></sup>, sumcheck is not currently the primary bottleneck in most polynomial commitment schemes (PCS).</p>
<p>These primitives map directly to bottlenecks in popular PCS, as shown in the table below.</p>
<table><thead><tr><th>PCS Type</th><th>Typical Schemes</th><th>Family (Hardness Assumption)</th><th>Primary Prover Bottleneck</th><th>Acceleration Potential</th><th>PQ Secure</th></tr></thead><tbody><tr><td><strong>KZG</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-9-1c095a" id="user-content-fnref-9-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">12</a></sup></td><td>Groth16, PlonK, Libra</td><td>Pairing-based (-SDH)</td><td>MSM</td><td><strong>Moderate</strong>: Memory-bandwidth limited</td><td>No</td></tr><tr><td><strong>IPA</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-10-1c095a" id="user-content-fnref-10-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">13</a></sup></td><td>Halo2, Bulletproofs, Hyrax</td><td>Discrete Log (DL)</td><td>MSM</td><td><strong>Moderate</strong>: Parallel EC additions</td><td>No</td></tr><tr><td><strong>Hyrax</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-11-1c095a" id="user-content-fnref-11-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">14</a></sup></td><td>GKR-based SNARKs</td><td>Discrete Log (DL)</td><td>MSM</td><td><strong>Moderate</strong>: Similar to IPA but multilinear</td><td>No</td></tr><tr><td><strong>LaBRADOR</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-12-1c095a" id="user-content-fnref-12-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">15</a></sup></td><td>LaBRADOR-based SNARKs</td><td>Lattice-based (SIS / LWE)</td><td>NTT, Matrix-Vector Ops</td><td><strong>High</strong>: Fast small-integer arithmetic</td><td>Yes</td></tr><tr><td><strong>FRI</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-13-1c095a" id="user-content-fnref-13-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">16</a></sup> / <strong>STIR</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-14-1c095a" id="user-content-fnref-14-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">17</a></sup></td><td>STARKs, Plonky2</td><td>Hash-based (CRHF)</td><td>Hashing, NTT</td><td><strong>High</strong>: Extremely parallel throughput</td><td>Yes</td></tr><tr><td><strong>WHIR</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-17-1c095a" id="user-content-fnref-17-1c095a-2" data-footnote-ref="true" aria-describedby="footnote-label">10</a></sup></td><td>Multivariate SNARKs</td><td>Hash-based (CRHF)</td><td>Hashing</td><td><strong>High</strong>: Extremely parallel throughput</td><td>Yes</td></tr><tr><td><strong>Basefold</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-15-1c095a" id="user-content-fnref-15-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">18</a></sup></td><td>Basefold-SNARKs</td><td>Hash-based (CRHF)</td><td>Linear Encoding &amp; Hashing</td><td><strong>High</strong>: Field-agnostic parallel encoding</td><td>Yes</td></tr><tr><td><strong>Brakedown</strong> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-16-1c095a" id="user-content-fnref-16-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">19</a></sup></td><td>Binius, Orion</td><td>Hash-based (CRHF)</td><td>Linear Encoding</td><td><strong>High</strong>: Bit-slicing (SIMD)</td><td>Yes</td></tr></tbody></table>
<p><em>Table 1: ZK primitives mapped to PCS bottlenecks and GPU potential</em></p>
<p>Moreover, VOLE-based ZK proving systems, such as QuickSilver <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-39-1c095a" id="user-content-fnref-39-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">20</a></sup>, Wolverine <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-40-1c095a" id="user-content-fnref-40-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">21</a></sup>, and Mac'n'Cheese <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-41-1c095a" id="user-content-fnref-41-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">22</a></sup>, utilize vector oblivious linear evaluation (VOLE) to construct efficient homomorphic commitments. These act as PCS based on symmetric cryptography or learning parity with noise (LPN) assumptions. The systems are post-quantum secure. Their main prover bottlenecks are VOLE extensions, hashing, and linear operations, which make them well-suited for GPU acceleration through parallel correlation generation and batch processing.</p>
<p>In the short term, primitives related to ECC (e.g. MSM in pairing-based schemes) are suitable for accelerating existing, well-adopted proving systems like Groth16 or those using KZG commitments, providing immediate performance gains for current Ethereum applications.</p>
<p>Quantum preparedness adds urgency for the long term: schemes on smaller fields (e.g. 31-bit fields like Mersenne 31, Baby Bear, or Koala Bear <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-18-1c095a" id="user-content-fnref-18-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">23</a></sup>) align better with GPU native words (32-bit), yielding higher throughput. Field-Ops Benchmarks <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-19-1c095a" id="user-content-fnref-19-1c095a-2" data-footnote-ref="true" aria-describedby="footnote-label">4</a></sup> confirm this. Under the same settings (not optimal for max performance but for fairness of experiment), smaller fields like M31 achieve over 100 Gops/s on client GPUs, compared to &lt;1 Gops/s for larger ones like BN254.</p>
<table><thead><tr><th>Operation</th><th>Metal GOP/s</th><th>WebGPU GOP/s</th><th>Ratio</th></tr></thead><tbody><tr><td>u32_add</td><td>264.2</td><td>250.1</td><td>1.06x</td></tr><tr><td>u64_add</td><td>177.5</td><td>141.1</td><td>1.26x</td></tr><tr><td>m31_field_add</td><td>146.0</td><td>121.7</td><td>1.20x</td></tr><tr><td>m31_field_mul</td><td>112.0</td><td>57.9</td><td>1.93x</td></tr><tr><td>bn254_field_add</td><td>7.9</td><td>1.0</td><td>7.64x</td></tr><tr><td>bn254_field_mul</td><td>0.63</td><td>0.08</td><td>7.59x</td></tr></tbody></table>
<p>As quantum tech advances, exploration related to PQ primitives (e.g. lattice-based or hash-based operations) will drive mid-to-long-term client-side proving advancements, ensuring resilience against quantum threats like <a href="https://www.federalreserve.gov/econres/feds/files/2025093pap.pdf" target="_blank" rel="noopener noreferrer">"Harvest Now, Decrypt Later" (HNDL) attacks, as mentioned in the Federal Reserve HNDL Paper</a> and underscored by the <a href="https://x.com/drakefjustin/status/2014791629408784816" target="_blank" rel="noopener noreferrer">Ethereum Foundation's long-term quantum strategy</a>, which is a critical factor if we are going to defend people's privacy on a daily-life scale.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="3-challenges-bridging-the-gap-to-everyday-privacy">3. Challenges: Bridging the Gap to Everyday Privacy<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#3-challenges-bridging-the-gap-to-everyday-privacy" class="hash-link" aria-label="Direct link to 3. Challenges: Bridging the Gap to Everyday Privacy" title="Direct link to 3. Challenges: Bridging the Gap to Everyday Privacy">​</a></h2>
<p>Despite progress, some hurdles remain between the current status and everyday usability.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="31-fragmented-implementation">3.1 Fragmented Implementation<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#31-fragmented-implementation" class="hash-link" aria-label="Direct link to 3.1 Fragmented Implementation" title="Direct link to 3.1 Fragmented Implementation">​</a></h3>
<ul>
<li>No standard crypto library exists for client-side GPUs, forcing developers to reinvent wheels from scratch, which is often not fully optimized.</li>
<li>Lack of State-of-the-Art (SoTA) references for new explorations, leading to duplicated efforts.</li>
<li>Inconsistent benchmarking: metrics like performance, I/O overhead, peak memory, and thermal/power traces are rarely standardized, complicating comparisons. Luckily, PSE's client-side proving team is improving this <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-21-1c095a" id="user-content-fnref-21-1c095a-2" data-footnote-ref="true" aria-describedby="footnote-label">5</a></sup>.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="32-limited-quantum-resistant-support">3.2 Limited Quantum-Resistant Support<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#32-limited-quantum-resistant-support" class="hash-link" aria-label="Direct link to 3.2 Limited Quantum-Resistant Support" title="Direct link to 3.2 Limited Quantum-Resistant Support">​</a></h3>
<p>Most GPU accelerations have focused on PCS related to elliptic-curve cryptography (ECC) like IPA, Hyrax, and KZG (pairing-based). All are vulnerable to quantum attacks via Shor's algorithm. These operate on larger fields (e.g. 254-bit), inefficient for GPUs' 32-bit native operations, and schemes like Groth16 face theoretical acceleration upper bounds, as analyzed in Ingonyama's blog post on hardware acceleration for ZKP <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-22-1c095a" id="user-content-fnref-22-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">24</a></sup>. On the other hand, PQ schemes such as those based on lattices or hashes (e.g. FRI variants) are underexplored on client GPUs but offer greater parallelism.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="33-device-specific-constraints">3.3 Device-Specific Constraints<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#33-device-specific-constraints" class="hash-link" aria-label="Direct link to 3.3 Device-Specific Constraints" title="Direct link to 3.3 Device-Specific Constraints">​</a></h3>
<p>Client devices differ vastly from servers: limited resources, shared GPU duties (e.g. graphics rendering), and thermal risks. Blindly offloading to GPUs can cause crashes or battery drain <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-23-1c095a" id="user-content-fnref-23-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">25</a></sup> <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-34-1c095a" id="user-content-fnref-34-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">26</a></sup>. Hybrid CPU-GPU approaches, inspired by <a href="https://vitalik.eth.limo/general/2024/09/02/gluecp.html" target="_blank" rel="noopener noreferrer">Vitalik's Glue and Coprocessor architecture</a>, need further exploration. Key unknowns include:</p>
<ul>
<li>Task dispatching hyperparameters.</li>
<li>Caching in unified memory. Check <a href="https://github.com/ingonyama-zk/metal-poc" target="_blank" rel="noopener noreferrer">Ingonyama's Metal PoC</a> for zero-cost CPU-GPU transfers.</li>
<li>Balancing operations: GPUs for parallelism, CPUs for sequential tasks.</li>
</ul>
<table><thead><tr><th style="text-align:center"><img decoding="async" loading="lazy" alt="Apple&amp;#39;s UMA" src="https://zkmopro.org/assets/images/apple-uma-20bda10a3837a7d1744a4da175b46a76.png" class="img_KKSl"></th></tr></thead><tbody><tr><td style="text-align:center">Apple GPUs have the unified memory model in which the CPU and the GPU share system memory</td></tr></tbody></table>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="4-pses-roadmap-for-gpu-acceleration">4. PSE's Roadmap for GPU Acceleration<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#4-pses-roadmap-for-gpu-acceleration" class="hash-link" aria-label="Direct link to 4. PSE's Roadmap for GPU Acceleration" title="Direct link to 4. PSE's Roadmap for GPU Acceleration">​</a></h2>
<p>To achieve client-side GPU acceleration's full potential, we propose a structured roadmap focused on collaboration and sustainability:</p>
<ol>
<li>
<p><strong>Build Standard Cryptography GPU Libraries for ZK</strong></p>
<ul>
<li>Mirror Arkworks's success in Rust zkSNARKs: Create modular, reusable libs optimized for client GPUs (e.g. low RAM, sustained performance) <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-25-1c095a" id="user-content-fnref-25-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">27</a></sup>.</li>
<li>Prioritize PQ foundations to enable HNDL-resistant solutions. For example, lattice-based primitives like NTT that scale with GPU parallelism <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-20-1c095a" id="user-content-fnref-20-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">28</a></sup>.</li>
<li>Make it community-owned, involving Ethereum, ZK, and privacy ecosystems for joint development, accelerating iteration and adoption in projects like mobile digital wallets.</li>
</ul>
</li>
<li>
<p><strong>Develop Best-Practice References</strong></p>
<ul>
<li>Integrate GPU acceleration into client-side suitable protocols, including at least one PQ scheme and mainstream ones like Plonky3 <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-26-1c095a" id="user-content-fnref-26-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">29</a></sup> or Jolt <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-27-1c095a" id="user-content-fnref-27-1c095a" data-footnote-ref="true" aria-describedby="footnote-label">30</a></sup>.</li>
<li>Optimize for mobile, tuning for sustained performance over peaks and avoiding thermal throttling via hybrid CPU-GPU coordination.</li>
<li>Create demo protocols as end-to-end references running on real devices with GPU boosts, serving as "how-to" guides for adopting standard libs.</li>
</ul>
</li>
</ol>
<p>This path transitions from siloed projects to ecosystem-wide tools, paving the way for explorations in sections like our starting points below.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="5-starting-points">5. Starting Points<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#5-starting-points" class="hash-link" aria-label="Direct link to 5. Starting Points" title="Direct link to 5. Starting Points">​</a></h2>
<p><a href="https://blog.zksecurity.xyz/posts/webgpu/" target="_blank" rel="noopener noreferrer">zkSecurity's WebGPU overview</a> highlights its constraints versus native APIs like Metal or Vulkan. For example, limited integer support and abstraction overheads.</p>
<p>To validate, a PoC was conducted to compare field operations (M31 vs. BN254) across APIs <sup><a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fn-19-1c095a" id="user-content-fnref-19-1c095a-3" data-footnote-ref="true" aria-describedby="footnote-label">4</a></sup>. Key findings:</p>
<ul>
<li><strong>u64 Emulation Overhead</strong>: Metal's native 64-bit support edges WebGPU (1.26x slower due to u32 emulation), compounding on complex ops.</li>
<li><strong>Field Size vs. Throughput</strong>: Smaller fields shine. M31 hits 100+ Gops/s; BN254 &lt;1 Gops/s (favoring PQ schemes).</li>
<li><strong>Complexity Amplifies Gaps</strong>: Simple u32 ops show parity (1.06x); advanced arithmetic like Montgomery multiplication widens to 7x for BN254.</li>
</ul>
<p>Start with native APIs first, such as Metal for iOS (sustainable boosts), then WebGPU for cross-platform reach. This prioritizes real-world viability over premature optimization.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="6-conclusion">6. Conclusion<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#6-conclusion" class="hash-link" aria-label="Direct link to 6. Conclusion" title="Direct link to 6. Conclusion">​</a></h2>
<p>Client-side GPU acceleration for ZKPs represents both immediate and long-term opportunities for Ethereum privacy, potentially improving UX for adopting privacy-preserving tech and enabling quantum-secure daily-life use cases like private payments. By addressing fragmentation, embracing PQ schemes, and optimizing for mobile devices, we can make privacy "hygiene" for everyone (from casual users to privacy-maximalists). We believe that with collaborative efforts from existing players and the broader community, everyday Ethereum privacy is within reach.</p>
<!-- -->
<section data-footnotes="true" class="footnotes"><h2 class="anchor anchorWithStickyNavbar_wa2S sr-only" id="footnote-label">Footnotes<a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#footnote-label" class="hash-link" aria-label="Direct link to Footnotes" title="Direct link to Footnotes">​</a></h2>
<ol>
<li id="user-content-fn-2-1c095a">
<p>Kasra Abbaszadeh, Hossein Hafezi, Jonathan Katz, &amp; Sarah Meiklejohn. (2025). Single-Server Private Outsourcing of zk-SNARKs. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2025/2113" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2025/2113</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-2-1c095a" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-2-1c095a-2" data-footnote-backref="" aria-label="Back to reference 1-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-37-1c095a">
<p>Takamichi Tsutsumi. (2025). TEE based private proof delegation. <a href="https://pse.dev/blog/tee-based-ppd" target="_blank" rel="noopener noreferrer">https://pse.dev/blog/tee-based-ppd</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-37-1c095a" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-38-1c095a">
<p>Takamichi Tsutsumi. (2025). Constant-Depth NTT for FHE-Based Private Proof Delegation. <a href="https://pse.dev/blog/const-depth-ntt-for-fhe-based-ppd" target="_blank" rel="noopener noreferrer">https://pse.dev/blog/const-depth-ntt-for-fhe-based-ppd</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-38-1c095a" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-19-1c095a">
<p>Field-Ops Benchmarks. GitHub. <a href="https://github.com/moven0831/field-ops-benchmarks" target="_blank" rel="noopener noreferrer">https://github.com/moven0831/field-ops-benchmarks</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-19-1c095a" data-footnote-backref="" aria-label="Back to reference 4" class="data-footnote-backref">↩</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-19-1c095a-2" data-footnote-backref="" aria-label="Back to reference 4-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-19-1c095a-3" data-footnote-backref="" aria-label="Back to reference 4-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-21-1c095a">
<p>Proving Backends Benchmarks by Client-side Proving Team from PSE. <a href="https://ethproofs.org/csp-benchmarks" target="_blank" rel="noopener noreferrer">https://ethproofs.org/csp-benchmarks</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-21-1c095a" data-footnote-backref="" aria-label="Back to reference 5" class="data-footnote-backref">↩</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-21-1c095a-2" data-footnote-backref="" aria-label="Back to reference 5-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-5-1c095a">
<p>ZPrize 2023 Entry: Tal and Koh's WebGPU MSM Implementation. GitHub. <a href="https://github.com/z-prize/2023-entries/tree/main/prize-2-msm-wasm/webgpu-only/tal-derei-koh-wei-jie" target="_blank" rel="noopener noreferrer">https://github.com/z-prize/2023-entries/tree/main/prize-2-msm-wasm/webgpu-only/tal-derei-koh-wei-jie</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-5-1c095a" data-footnote-backref="" aria-label="Back to reference 6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8-1c095a">
<p>ZPrize. (2023). Announcing the 2023 ZPrize Winners. <a href="https://www.zprize.io/blog/announcing-the-2023-zprize-winners" target="_blank" rel="noopener noreferrer">https://www.zprize.io/blog/announcing-the-2023-zprize-winners</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-8-1c095a" data-footnote-backref="" aria-label="Back to reference 7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7-1c095a">
<p>Geometry: Curve Arithmetic Implementation in Metal. GitHub. <a href="https://github.com/geometryxyz/msl-secp256k1" target="_blank" rel="noopener noreferrer">https://github.com/geometryxyz/msl-secp256k1</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-7-1c095a" data-footnote-backref="" aria-label="Back to reference 8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-35-1c095a">
<p>Carsten Lund, Lance Fortnow, Howard Karloff, &amp; Noam Nisan. (1992). Algebraic methods for interactive proof systems. Journal of the ACM (JACM). <a href="https://dl.acm.org/doi/10.1145/146585.146605" target="_blank" rel="noopener noreferrer">https://dl.acm.org/doi/10.1145/146585.146605</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-35-1c095a" data-footnote-backref="" aria-label="Back to reference 9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-17-1c095a">
<p>Gal Arnon, Alessandro Chiesa, Giacomo Fenzi, &amp; Eylon Yogev. (2024). WHIR: Reed–Solomon Proximity Testing with Super-Fast Verification. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2024/1586" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2024/1586</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-17-1c095a" data-footnote-backref="" aria-label="Back to reference 10" class="data-footnote-backref">↩</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-17-1c095a-2" data-footnote-backref="" aria-label="Back to reference 10-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-36-1c095a">
<p>Shafi Goldwasser, Yael Tauman Kalai, &amp; Guy N. Rothblum. (2008). Delegating computation: interactive proofs for muggles. In Proceedings of the fortieth annual ACM symposium on Theory of computing. <a href="https://dl.acm.org/doi/10.1145/1374376.1374396" target="_blank" rel="noopener noreferrer">https://dl.acm.org/doi/10.1145/1374376.1374396</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-36-1c095a" data-footnote-backref="" aria-label="Back to reference 11" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9-1c095a">
<p>Aniket Kate, Gregory M. Zaverucha, &amp; Ian Goldberg. (2010). Constant-Size Commitments to Polynomials and Their Applications. In Advances in Cryptology - ASIACRYPT 2010. Springer. <a href="https://link.springer.com/chapter/10.1007/978-3-642-17373-8_11" target="_blank" rel="noopener noreferrer">https://link.springer.com/chapter/10.1007/978-3-642-17373-8_11</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-9-1c095a" data-footnote-backref="" aria-label="Back to reference 12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-10-1c095a">
<p>Jonathan Bootle, Andrea Cerulli, Pyrros Chaidos, Jens Groth, &amp; Christophe Petit. (2016). Efficient Zero-Knowledge Arguments for Arithmetic Circuits in the Discrete Log Setting. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2016/263" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2016/263</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-10-1c095a" data-footnote-backref="" aria-label="Back to reference 13" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-11-1c095a">
<p>Riad S. Wahby, Ioanna Tzialla, abhi shelat, Justin Thaler, &amp; Michael Walfish. (2017). Doubly-efficient zkSNARKs without trusted setup. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2017/1132" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2017/1132</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-11-1c095a" data-footnote-backref="" aria-label="Back to reference 14" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-12-1c095a">
<p>Ward Beullens &amp; Gregor Seiler. (2022). LaBRADOR: Compact Proofs for R1CS from Module-SIS. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2022/1341" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2022/1341</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-12-1c095a" data-footnote-backref="" aria-label="Back to reference 15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13-1c095a">
<p>Eli Ben-Sasson, Iddo Bentov, Yinon Horesh, &amp; Michael Riabzev. (2018). Scalable, transparent, and post-quantum secure computational integrity. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2018/046" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2018/046</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-13-1c095a" data-footnote-backref="" aria-label="Back to reference 16" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-14-1c095a">
<p>Gal Arnon, Alessandro Chiesa, Giacomo Fenzi, &amp; Eylon Yogev. (2024). STIR: Reed–Solomon Proximity Testing with Fewer Queries. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2024/390" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2024/390</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-14-1c095a" data-footnote-backref="" aria-label="Back to reference 17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-15-1c095a">
<p>Hadas Zeilberger, Binyi Chen, &amp; Ben Fisch. (2023). BaseFold: Efficient Field-Agnostic Polynomial Commitment Schemes from Foldable Codes. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2023/1705" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2023/1705</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-15-1c095a" data-footnote-backref="" aria-label="Back to reference 18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-16-1c095a">
<p>Alexander Golovnev, Jonathan Lee, Srinath Setty, Justin Thaler, &amp; Riad S. Wahby. (2021). Brakedown: Linear-time and field-agnostic SNARKs for R1CS. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2021/1043" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2021/1043</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-16-1c095a" data-footnote-backref="" aria-label="Back to reference 19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-39-1c095a">
<p>Kang Yang, Pratik Sarkar, Chenkai Weng, &amp; Xiao Wang. (2021). QuickSilver: Efficient and Affordable Zero-Knowledge Proofs for Circuits and Polynomials over Any Field. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2021/076" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2021/076</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-39-1c095a" data-footnote-backref="" aria-label="Back to reference 20" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-40-1c095a">
<p>Chenkai Weng, Kang Yang, Jonathan Katz, &amp; Xiao Wang. (2020). Wolverine: Fast, Scalable, and Communication-Efficient Zero-Knowledge Proofs for Boolean and Arithmetic Circuits. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2020/925" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2020/925</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-40-1c095a" data-footnote-backref="" aria-label="Back to reference 21" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-41-1c095a">
<p>Carsten Baum, Alex J. Malozemoff, Marc B. Rosen, &amp; Peter Scholl. (2020). Mac'n'Cheese: Zero-Knowledge Proofs for Boolean and Arithmetic Circuits with Nested Disjunctions. Cryptology ePrint Archive. <a href="https://eprint.iacr.org/2020/1410" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2020/1410</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-41-1c095a" data-footnote-backref="" aria-label="Back to reference 22" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-18-1c095a">
<p>Voidkai. Efficient Prime Fields for Zero-knowledge proof. HackMD. <a href="https://hackmd.io/@Voidkai/BkNX3xUZA" target="_blank" rel="noopener noreferrer">https://hackmd.io/@Voidkai/BkNX3xUZA</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-18-1c095a" data-footnote-backref="" aria-label="Back to reference 23" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-22-1c095a">
<p>Ingonyama. (2023, September 27). Revisiting Paradigm’s “Hardware Acceleration for Zero Knowledge Proofs”. Medium. <a href="https://medium.com/@ingonyama/revisiting-paradigms-hardware-acceleration-for-zero-knowledge-proofs-5dffacdc24b4" target="_blank" rel="noopener noreferrer">https://medium.com/@ingonyama/revisiting-paradigms-hardware-acceleration-for-zero-knowledge-proofs-5dffacdc24b4</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-22-1c095a" data-footnote-backref="" aria-label="Back to reference 24" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-23-1c095a">
<p>"GPU in Your Pockets", zkID Days Talk at Devconnect 2025. <a href="https://streameth.org/6918e24caaaed07b949bbdd1/playlists/69446a85e505fdb0fa31a760" target="_blank" rel="noopener noreferrer">https://streameth.org/6918e24caaaed07b949bbdd1/playlists/69446a85e505fdb0fa31a760</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-23-1c095a" data-footnote-backref="" aria-label="Back to reference 25" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-34-1c095a">
<p>Benoit-Cattin, T., Velasco-Montero, D., &amp; Fernández-Berni, J. (2020). Impact of Thermal Throttling on Long-Term Visual Inference in a CPU-Based Edge Device. Electronics, 9(12), 2106. <a href="https://doi.org/10.3390/electronics9122106" target="_blank" rel="noopener noreferrer">https://doi.org/10.3390/electronics9122106</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-34-1c095a" data-footnote-backref="" aria-label="Back to reference 26" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-25-1c095a">
<p>Arkworks. <a href="https://arkworks.rs/" target="_blank" rel="noopener noreferrer">https://arkworks.rs/</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-25-1c095a" data-footnote-backref="" aria-label="Back to reference 27" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-20-1c095a">
<p>Jillian Mascelli &amp; Megan Rodden. (2025, September). “Harvest Now Decrypt Later”: Examining Post-Quantum Cryptography and the Data Privacy Risks for Distributed Ledger Networks. Federal Reserve Board. <a href="https://www.federalreserve.gov/econres/feds/files/2025093pap.pdf" target="_blank" rel="noopener noreferrer">https://www.federalreserve.gov/econres/feds/files/2025093pap.pdf</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-20-1c095a" data-footnote-backref="" aria-label="Back to reference 28" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-26-1c095a">
<p>Plonky3. GitHub. <a href="https://github.com/Plonky3/Plonky3" target="_blank" rel="noopener noreferrer">https://github.com/Plonky3/Plonky3</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-26-1c095a" data-footnote-backref="" aria-label="Back to reference 29" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-27-1c095a">
<p>Jolt. GitHub. <a href="https://github.com/a16z/jolt" target="_blank" rel="noopener noreferrer">https://github.com/a16z/jolt</a> <a href="https://zkmopro.org/blog/client-side-gpu-everyday-ef-privacy#user-content-fnref-27-1c095a" data-footnote-backref="" aria-label="Back to reference 30" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content>
        <author>
            <name>Moven Tsai</name>
            <uri>https://github.com/moven0831</uri>
        </author>
        <category label="client-side proving" term="client-side proving"/>
        <category label="gpu" term="gpu"/>
        <category label="post-quantum" term="post-quantum"/>
        <category label="privacy" term="privacy"/>
        <category label="zkp" term="zkp"/>
        <category label="metal" term="metal"/>
        <category label="webgpu" term="webgpu"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Reflections on 2025: The Mopro Story]]></title>
        <id>https://zkmopro.org/blog/2025-mopro-retrospective</id>
        <link href="https://zkmopro.org/blog/2025-mopro-retrospective"/>
        <updated>2026-01-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[2025 has been a strong year for Mopro and the evolution of mobile proving. Over the past year, we showed that Mopro is not just a tool for advanced developers, but one that hackers and beginners can adopt with ease.]]></summary>
        <content type="html"><![CDATA[<p>2025 has been a strong year for Mopro and the evolution of mobile proving. Over the past year, we showed that Mopro is not just a tool for advanced developers, but one that hackers and beginners can adopt with ease.</p>
<p>This progress was also showcased at DevConnect, where we presented Mopro’s approach to mobile proving and shared real-world learnings from building and deploying zk applications on mobile. You can watch the <a href="https://streameth.org/6918e24caaaed07b949bbdd1/playlists/69446a85e505fdb0fa31a760?video=69455130e505fdb0fa4e102f" target="_blank" rel="noopener noreferrer">full talk</a>.</p>
<p>The following milestones highlight what we accomplished.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="mobile-sdk-management-solution">Mobile SDK Management Solution<a href="https://zkmopro.org/blog/2025-mopro-retrospective#mobile-sdk-management-solution" class="hash-link" aria-label="Direct link to Mobile SDK Management Solution" title="Direct link to Mobile SDK Management Solution">​</a></h2>
<p>We realized that for most zk protocol developers, building a full frontend is not essential—especially when the goal is to demonstrate a proof of concept or integrate the protocol into an existing product. What is essential, however, is <strong>a reliable and easy-to-use SDK</strong>.</p>
<p>To address this, we built reusable templates with Mopro bindings that can be integrated with minimal mobile development experience. These templates are designed to be easily extended into SDKs for multiple platforms, enabling zk protocol teams—such as <a href="https://zk.email/" target="_blank" rel="noopener noreferrer">zkEmail</a>, <a href="https://semaphore.pse.dev/" target="_blank" rel="noopener noreferrer">Semaphore</a>, and others—to focus on protocol design rather than frontend or mobile engineering.</p>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>For more details, please refer to our <a href="https://zkmopro.org/blog/mopro-native-packages">blog</a> and <a href="https://zkmopro.org/docs/sdk/overview">documentation</a>.</p></div></div>
<p>Our approach is inspired by the <a href="https://docs.reclaimprotocol.org/" target="_blank" rel="noopener noreferrer"><strong>Reclaim Protocol</strong></a> philosophy: a protocol should support as many platform SDKs as possible to unlock a broader range of applications and products.</p>
<p align="center"><img src="https://zkmopro.org/img/reclaim-protocol-sdk.jpg" alt="Reclaim Protocol SDK" style="width:300px"></p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="expanded-ffi-support">Expanded FFI Support<a href="https://zkmopro.org/blog/2025-mopro-retrospective#expanded-ffi-support" class="hash-link" aria-label="Direct link to Expanded FFI Support" title="Direct link to Expanded FFI Support">​</a></h2>
<p>Although <a href="https://github.com/mozilla/uniffi-rs" target="_blank" rel="noopener noreferrer">UniFFI</a> provides a robust foundation for Rust FFI, it still requires manual effort to integrate with modern cross-platform frameworks such as Flutter and React Native. This added complexity can be a barrier for mobile developers.</p>
<p>In 2025, we streamlined this process by integrating multi-platform FFI generation into <a href="https://crates.io/crates/mopro-ffi" target="_blank" rel="noopener noreferrer"><strong>mopro-ffi</strong></a>. Today, Mopro can automatically generate bindings for <strong>Swift</strong>, <strong>Kotlin</strong>, <strong>Flutter</strong> (with <a href="https://github.com/fzyzcjy/flutter_rust_bridge" target="_blank" rel="noopener noreferrer"><code>flutter_rust_bridge</code></a>), <strong>React Native</strong> (with <a href="https://github.com/jhugman/uniffi-bindgen-react-native" target="_blank" rel="noopener noreferrer"><code>uniffi-bindgen-react-native</code></a>), and <strong>WebAssembly</strong> (with <a href="https://github.com/wasm-bindgen/wasm-bindgen" target="_blank" rel="noopener noreferrer"><code>wasm-bindgen</code></a>), eliminating the need to maintain platform-specific glue code. Developers define their interfaces once in Rust, and Mopro handles the rest.</p>
<div class="theme-admonition theme-admonition-note admonition_iIk3 alert alert--secondary"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_S87P"><p>For example, in a React Native app, developers need to manually write glue code in <strong>Swift, Kotlin, and TypeScript</strong>, as described in the <a href="https://github.com/zkmopro/react-native-app/tree/99c691062cda694de0676615c23b870a84edf5d9?tab=readme-ov-file#4-define-react-native-module" target="_blank" rel="noopener noreferrer">README</a>.</p><p>Similarly, in a Flutter app, glue code must be written in <strong>Swift, Kotlin, and Dart</strong> (See the <a href="https://github.com/zkmopro/flutter-app/tree/5ba8236f02da98f509d8bef07e273bd140c8bf93?tab=readme-ov-file#integrating-your-zkp" target="_blank" rel="noopener noreferrer">README</a>).</p><p>This results in <em>hundreds of lines of manually maintained code</em>, which does not scale well as the number of FFI-exported functions grows.</p><p>With <code>flutter_rust_bridge</code> and <code>uniffi-bindgen-react-native</code>, these glue layers are generated <em>automatically</em>, allowing mobile developers to interact with the Rust APIs directly from React Native and Flutter without writing or maintaining platform-specific binding code.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="noir-support">Noir Support<a href="https://zkmopro.org/blog/2025-mopro-retrospective#noir-support" class="hash-link" aria-label="Direct link to Noir Support" title="Direct link to Noir Support">​</a></h2>
<p>Alongside our existing support for Circom and Halo2, we integrated <strong>Noir</strong> into Mopro in 2025, recognizing its growing adoption as a frontend language for zero-knowledge circuits. Inspired by the <a href="https://zkpassport.id/" target="_blank" rel="noopener noreferrer">zkPassport</a> team, we enabled mobile proving and verification for Noir-based circuits.</p>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>For more details, please refer to our <a href="https://zkmopro.org/blog/noir-integraion">blog</a> and <a href="https://zkmopro.org/docs/adapters/noir">documentation</a>.</p></div></div>
<p>Throughout the year, we participated in multiple NoirHack events and NoirCon, where we built <a href="https://github.com/vivianjeng/stealthnote-mobile" target="_blank" rel="noopener noreferrer">Stealthnote Mobile</a>, achieving up to <strong>10×</strong> faster performance than the <a href="https://stealthnote.xyz/" target="_blank" rel="noopener noreferrer">browser version</a> and a <strong>superior user experience</strong>. We also released an example demonstrating <a href="https://github.com/moven0831/mopro-wallet-connect-noir" target="_blank" rel="noopener noreferrer">how Noir proofs can be verified on-chain</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="hackathon-projects">Hackathon Projects<a href="https://zkmopro.org/blog/2025-mopro-retrospective#hackathon-projects" class="hash-link" aria-label="Direct link to Hackathon Projects" title="Direct link to Hackathon Projects">​</a></h2>
<p>Throughout 2025, we participated in several hackathons and received <strong>over 50 project submissions</strong> built with Mopro. Below are a selection of standout projects developed this year.</p>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>Read our reflections from <a href="https://zkmopro.org/blog/ethglobal-cannes-2025">ETHGlobal Cannes</a> and <a href="https://zkmopro.org/blog/ethglobal-new-delhi">ETHGlobal New Delhi</a>.</p></div></div>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="zuma">Zuma<a href="https://zkmopro.org/blog/2025-mopro-retrospective#zuma" class="hash-link" aria-label="Direct link to Zuma" title="Direct link to Zuma">​</a></h3>
<p><strong>Anonymous Event Attendance</strong></p>
<p align="center"><img src="https://zkmopro.org/img/blog-zuma.jpg" alt="Hackathon Zuma" style="width:500px"></p>
<p><em>ETHGlobal Trifecta — Zero Knowledge</em></p>
<ul>
<li>GitHub: <a href="https://github.com/chengggkk/zuma" target="_blank" rel="noopener noreferrer">https://github.com/chengggkk/zuma</a></li>
<li>Submission: <a href="https://ethglobal.com/showcase/zuma-6vnoj" target="_blank" rel="noopener noreferrer">https://ethglobal.com/showcase/zuma-6vnoj</a></li>
</ul>
<p>Zuma is a <strong>ZK-powered Luma-style application</strong>, inspired by <a href="https://luma.com/" target="_blank" rel="noopener noreferrer">Luma</a> and <a href="https://zupass.org/#/login" target="_blank" rel="noopener noreferrer">Zupass</a>. It combines the seamless user experience of native event apps with the strong privacy guarantees of zero-knowledge proofs.</p>
<p>When a user attends an event, the app generates a <strong>Semaphore proof</strong> to attest to attendance. Verifiers only need to validate the proof to confirm that the user is an attendee—without learning any personal information such as the user’s email address or name.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="stealthnote-mobile">Stealthnote Mobile<a href="https://zkmopro.org/blog/2025-mopro-retrospective#stealthnote-mobile" class="hash-link" aria-label="Direct link to Stealthnote Mobile" title="Direct link to Stealthnote Mobile">​</a></h3>
<p><strong>Glassdoor-Style Anonymous Forum</strong></p>
<p align="center"><img src="https://zkmopro.org/img/blog-stealthnote-mobile.jpg" alt="Hackathon Stealthnote Mobile" style="width:500px"></p>
<p><em>NoirHack 2025</em></p>
<ul>
<li>GitHub: <a href="https://github.com/vivianjeng/stealthnote-mobile" target="_blank" rel="noopener noreferrer">https://github.com/vivianjeng/stealthnote-mobile</a></li>
</ul>
<p>Stealthnote Mobile is inspired by stealthnote.xyz and demonstrates that <strong>Noir proofs can be generated and verified directly on mobile devices</strong>. Compared to the browser-based implementation, the mobile app achieves <strong>up to 10× faster performance</strong>.</p>
<p>While the browser version takes approximately <strong>20 seconds</strong> to generate a Google OAuth proof, the mobile app completes the same operation in about <strong>2 seconds</strong>, resulting in a significantly improved user experience. In the future, the app could integrate <strong>push notifications</strong> to notify users when new posts are available.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="mobiscale">MobiScale<a href="https://zkmopro.org/blog/2025-mopro-retrospective#mobiscale" class="hash-link" aria-label="Direct link to MobiScale" title="Direct link to MobiScale">​</a></h3>
<p><strong>Privacy-Preserving AI Biometric Verification with zkVM on Mobile</strong></p>
<p align="center"><img src="https://zkmopro.org/img/blog-mobiscale.jpg" alt="Hackathon MobiScale" style="width:500px"></p>
<p><em>ETHGlobal Cannes 2025</em></p>
<ul>
<li>GitHub: <a href="https://github.com/ElusAegis/MobiScale" target="_blank" rel="noopener noreferrer">https://github.com/ElusAegis/MobiScale</a></li>
<li>Submission: <a href="https://ethglobal.com/showcase/mobiscale-n9vj6" target="_blank" rel="noopener noreferrer">https://ethglobal.com/showcase/mobiscale-n9vj6</a></li>
</ul>
<p>Mobiscale represents a major technical milestone for Mopro, showcasing how mobile hardware can enable <strong>zkVMs on smartphones</strong>. The project combines <strong>AI-powered facial recognition</strong> with Apple’s Secure Enclave to verify identity in a privacy-preserving way.</p>
<p>Users upload a passport photo and take a live selfie, which are compared locally by an AI model to establish identity with high confidence. The system then generates zero-knowledge proofs using <strong>RISC Zero</strong> and <strong>Noir</strong> circuits, allowing third parties to verify identity claims without accessing biometric data, confidence scores, or image hashes.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="zerosurf">ZeroSurf<a href="https://zkmopro.org/blog/2025-mopro-retrospective#zerosurf" class="hash-link" aria-label="Direct link to ZeroSurf" title="Direct link to ZeroSurf">​</a></h3>
<p><strong>Seamless Browser-to-Mobile ZK Age Verification</strong></p>
<p align="center"><img src="https://zkmopro.org/img/blog-zerosurf.jpg" alt="Hackathon ZeroSurf" style="width:700px"></p>
<p><em>ETHGlobal New Delhi 2025</em></p>
<ul>
<li>GitHub: <a href="https://github.com/Krane-Apps/zerosurf-anon-aadhaar" target="_blank" rel="noopener noreferrer">https://github.com/Krane-Apps/zerosurf-anon-aadhaar</a></li>
<li>Submission: <a href="https://ethglobal.com/showcase/zerosurf-9988k" target="_blank" rel="noopener noreferrer">https://ethglobal.com/showcase/zerosurf-9988k</a></li>
</ul>
<p>ZeroSurf demonstrates a highly smooth user experience between browser applications and mobile native apps through <strong>deep linking</strong>. When accessing a website with age restrictions, users are redirected via a URL to a mobile app, where a zero-knowledge proof (in this case, an <strong>Anon Aadhaar proof</strong>) is generated.</p>
<p>The proof is then returned to the website, enabling verification without exposing sensitive information. This project expands the use cases for mobile-native provers and shows how browser-based applications can directly benefit from mobile zero-knowledge proving.</p>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>Explore more projects at <a href="https://zkmopro.org/docs/projects/" target="_blank" rel="noopener noreferrer">https://zkmopro.org/docs/projects/</a></p></div></div>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="future-direction">Future Direction<a href="https://zkmopro.org/blog/2025-mopro-retrospective#future-direction" class="hash-link" aria-label="Direct link to Future Direction" title="Direct link to Future Direction">​</a></h2>
<p align="center"><img src="https://zkmopro.org/img/blog-roadmap.jpg" alt="2026 Roadmap" style="width:700px"></p>
<p>In 2026, we propose the following direction for the community. While Mopro handles the FFI layer and ensures that proving systems work reliably across platforms, we also want to raise awareness that reaching users on different platforms requires more than just cross-platform compatibility.</p>
<p>To truly enable adoption, <strong>zk protocol developers should provide platform-specific SDKs</strong> that can be easily consumed by application developers on iOS, Android, web, and beyond. By offering well-designed SDKs tailored to each platform’s ecosystem, zk applications can achieve broader reach, better user experience, and faster integration across diverse platforms.</p>
<p>As for the Mopro team, we plan to collaborate with more projects to help them build and ship their <strong>protocol SDKs</strong>. We will continue exploring <strong>zkVMs on mobile</strong> and advancing <strong>client-side GPU research</strong> across different proving systems.</p>
<p>In addition, we aim to engage more deeply with <strong>mobile development communities and events</strong>, promoting the adoption of zero-knowledge proofs in real-world mobile applications.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="conclusion">Conclusion<a href="https://zkmopro.org/blog/2025-mopro-retrospective#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion">​</a></h2>
<p>2025 was a milestone year for Mopro, proving that mobile zk is not only possible, but practical and accessible. As we move into 2026, we’re excited to keep pushing the boundaries of mobile proving and work closely with the community to bring zero-knowledge to more platforms and applications.</p>]]></content>
        <author>
            <name>Vivian Jeng</name>
            <uri>https://github.com/vivianjeng</uri>
        </author>
        <category label="retrospective" term="retrospective"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ETHGlobal New Delhi: Advancing Client-Side Privacy]]></title>
        <id>https://zkmopro.org/blog/ethglobal-new-delhi</id>
        <link href="https://zkmopro.org/blog/ethglobal-new-delhi"/>
        <updated>2025-10-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[At ETHGlobal New Delhi this September, the Mopro and zkPDF teams sponsored two tracks focused on bringing general privacy to Ethereum. The hackathon delivered impressive projects that pushed boundaries in both infrastructure and application development.]]></summary>
        <content type="html"><![CDATA[<p>At <a href="https://ethglobal.com/events/newdelhi/prizes#ethereum-foundation" target="_blank" rel="noopener noreferrer">ETHGlobal New Delhi</a> this September, the Mopro and zkPDF teams sponsored two tracks focused on bringing general privacy to Ethereum. The hackathon delivered impressive projects that pushed boundaries in both infrastructure and application development.</p>
<p>Several submissions exceeded expectations with standout UX features. <strong>Deeplink integration</strong> enables seamless transitions between mobile apps and browsers, allowing native mobile proving across existing browser applications that require ZK (like age verification for websites). <strong>NFC integration</strong> demonstrated tap-to-prove and tap-to-verify capabilities, creating an experience as intuitive as Apple Pay. These implementations show the maturity of client-side ZK proving and its readiness for real-world adoption.</p>
<div style="display:flex;justify-content:center;margin:2rem 0"><table style="border:2px solid #444;border-radius:8px;padding:1rem;max-width:500px"><tbody><tr><td style="padding:1rem;text-align:center"><p style="font-style:italic;margin-bottom:1rem;color:#888;font-size:0.95rem"></p><p><a href="https://zkmopro.org/blog/ethglobal-new-delhi#-third-prize-zerosurf">ZeroSurf</a> demo: Privacy-preserving age verification with deeplink integration</p><p></p><div style="display:flex;justify-content:center;margin:2rem 0"><blockquote class="twitter-tweet" data-width="400px"><a href="https://twitter.com/x/status/1979055018683556051">Loading tweet...</a></blockquote></div></td></tr></tbody></table></div>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="infrastructure-track-client-side-privacy">Infrastructure Track: Client-Side Privacy<a href="https://zkmopro.org/blog/ethglobal-new-delhi#infrastructure-track-client-side-privacy" class="hash-link" aria-label="Direct link to Infrastructure Track: Client-Side Privacy" title="Direct link to Infrastructure Track: Client-Side Privacy">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="-grand-prize-accessfi">🏆 Grand Prize: AccessFI<a href="https://zkmopro.org/blog/ethglobal-new-delhi#-grand-prize-accessfi" class="hash-link" aria-label="Direct link to 🏆 Grand Prize: AccessFI" title="Direct link to 🏆 Grand Prize: AccessFI">​</a></h3>
<blockquote>
<p><a href="https://ethglobal.com/showcase/accessfi-8v4ns" target="_blank" rel="noopener noreferrer">View Project</a> | <a href="https://github.com/coderwithsense/EthGlobalDelhi" target="_blank" rel="noopener noreferrer">GitHub</a></p>
</blockquote>
<p>AccessFI reimagines event payments with NFC-powered privacy. Users receive P-256 compatible SECP256k1 NFC cards linked to their wallets, enabling instant tap-to-pay for tickets, registration, food, and merchandise while preserving privacy through deterministic encryption.</p>
<p>The system eliminates payment friction with 5-second NFC transactions that work on any EVM chain. Privacy is maintained through ZK proofs that verify user eligibility without exposing personal data. A single card handles all event interactions: tap-to-buy tickets, tap-to-register, tap-to-pay for concessions.</p>
<p><img decoding="async" loading="lazy" alt="AccessFI Flow" src="https://zkmopro.org/assets/images/ethglobal-new-delhi-accessfi-c5d89ed03b221978e1cb8df7a5a0e9cb.jpg" class="img_KKSl"></p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="application-track-general-privacy">Application Track: General Privacy<a href="https://zkmopro.org/blog/ethglobal-new-delhi#application-track-general-privacy" class="hash-link" aria-label="Direct link to Application Track: General Privacy" title="Direct link to Application Track: General Privacy">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="-first-prize-zkether">🥇 First Prize: zkETHer<a href="https://zkmopro.org/blog/ethglobal-new-delhi#-first-prize-zkether" class="hash-link" aria-label="Direct link to 🥇 First Prize: zkETHer" title="Direct link to 🥇 First Prize: zkETHer">​</a></h3>
<blockquote>
<p><a href="https://ethglobal.com/showcase/zkether-geppk" target="_blank" rel="noopener noreferrer">View Project</a> | <a href="https://github.com/yashsharma22003/zkETHer-app" target="_blank" rel="noopener noreferrer">GitHub</a></p>
</blockquote>
<p>zkETHer implements a privacy-preserving protocol for ERC20 tokens, functioning as a non-custodial mixer. Users deposit fixed amounts by submitting cryptographic commitments to an on-chain Merkle tree, then withdraw to new addresses using ZK proofs generated on their mobile devices.</p>
<p>The protocol uses X25519 (ECDH) for key exchange, HKDF-SHA256 for deriving secrets, and Poseidon2 hash for commitments. Mopro enables computationally intensive ZK proofs to be generated directly on phones, making privacy accessible without specialized hardware.</p>
<p>The circuit implementation is robust, though real-world feasibility needs improvement for production deployment. The architecture demonstrates how mobile-first proving can bring mixer-style privacy to standard ERC20 tokens.</p>
<div style="display:flex;justify-content:center;margin:1.5rem 0"><table style="border:2px solid #444;border-radius:8px;padding:1rem;max-width:500px"><tbody><tr><td style="padding:1rem;text-align:center"><p style="font-style:italic;margin-bottom:1rem;color:#888;font-size:0.95rem"></p><p>zkETHer demo: Privacy-preserving ERC20 mixer with mobile ZK proving</p><p></p><video width="400" controls="" style="border-radius:4px;max-width:100%"><source src="https://ethglobal.storage/projects/geppk/video/high.mp4?t=1760977955579" type="video/mp4"><p>Your browser does not support the video tag.</p></video></td></tr></tbody></table></div>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="-second-prize-wisk">🥈 Second Prize: Wisk<a href="https://zkmopro.org/blog/ethglobal-new-delhi#-second-prize-wisk" class="hash-link" aria-label="Direct link to 🥈 Second Prize: Wisk" title="Direct link to 🥈 Second Prize: Wisk">​</a></h3>
<blockquote>
<p><a href="https://ethglobal.com/showcase/wisk-gdvfw" target="_blank" rel="noopener noreferrer">View Project</a> | <a href="https://github.com/YadlaMani/wisk" target="_blank" rel="noopener noreferrer">GitHub</a></p>
</blockquote>
<p>Wisk rethinks background verification for the digital age with zkPDF. Instead of sharing documents with third parties, users prove specific claims about government-issued certificates without revealing the full content.</p>
<p>The system integrates with <a href="https://www.digilocker.gov.in/" target="_blank" rel="noopener noreferrer">India's DigiLocker</a> to verify official PDFs. Using zkPDF, Wisk validates the government's digital signature embedded in PDFs and generates ZKPs for requested fields (name, PAN number, credentials). The entire process happens in the browser—raw documents never leave the user's device.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="-third-prize-zerosurf">🥉 Third Prize: ZeroSurf<a href="https://zkmopro.org/blog/ethglobal-new-delhi#-third-prize-zerosurf" class="hash-link" aria-label="Direct link to 🥉 Third Prize: ZeroSurf" title="Direct link to 🥉 Third Prize: ZeroSurf">​</a></h3>
<blockquote>
<p><a href="https://ethglobal.com/showcase/zerosurf-9988k" target="_blank" rel="noopener noreferrer">View Project</a> | <a href="https://github.com/Krane-Apps/zerosurf-anon-aadhaar" target="_blank" rel="noopener noreferrer">GitHub</a></p>
</blockquote>
<p>ZeroSurf is a mobile browser with built-in ZK age verification using <a href="https://github.com/anon-aadhaar" target="_blank" rel="noopener noreferrer">Anon Aadhaar</a>. The smooth deeplink integration allows users to prove age requirements without revealing birth dates, enabling privacy-preserving access to age-restricted content.</p>
<p>The implementation showcases how deeplinks can bridge mobile browsers and ZK proving apps, creating frictionless user experiences for privacy-preserving authentication.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="key-takeaways--future-explorations">Key Takeaways &amp; Future Explorations<a href="https://zkmopro.org/blog/ethglobal-new-delhi#key-takeaways--future-explorations" class="hash-link" aria-label="Direct link to Key Takeaways &amp; Future Explorations" title="Direct link to Key Takeaways &amp; Future Explorations">​</a></h2>
<p>The hackathon revealed several promising directions for client-side privacy:</p>
<p><strong>UX innovations like NFC and deeplinks</strong> proved that privacy-preserving technology can match the convenience of traditional systems. These features should be modularized within Mopro to improve developer experience. We'll invite teams that built these integrations to contribute reusable components.</p>
<p><strong>Photo-identity integrity</strong> emerged as a recurring theme across multiple projects, adding security layers to identity verification. Integrating solutions like <a href="https://docs.rarimo.com/zkml-bionetta/" target="_blank" rel="noopener noreferrer">Rarimo's Bionetta</a> and zkCamera with mobile-native proving through Mopro could strengthen this approach.</p>
<p>We're excited to see more UX innovations emerge in future hackathons. Whether it's tap-to-prove bringing native mobile experiences, smooth deeplink transitions between apps and browsers, or entirely new interaction patterns—the goal is providing developers with easy-to-use building blocks. By modularizing these patterns in Mopro, we can transform what took teams days to build during the hackathon into features that take minutes to integrate.</p>
<p>Beyond these UX enhancements, there are also two fundamental challenges worth exploring:</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="zktls-and-mobile-proving">zkTLS and Mobile Proving<a href="https://zkmopro.org/blog/ethglobal-new-delhi#zktls-and-mobile-proving" class="hash-link" aria-label="Direct link to zkTLS and Mobile Proving" title="Direct link to zkTLS and Mobile Proving">​</a></h3>
<p>zkTLS enables portable, verifiable data from any HTTPS connection without server cooperation. Using multi-party computation (MPC), zkTLS allows users to prove statements about web data—like account balances, transaction histories, or credentials—without revealing the underlying information or requiring platform APIs.</p>
<p><a href="https://github.com/tlsnotary" target="_blank" rel="noopener noreferrer"><strong>TLSNotary</strong></a> leads the MPC-based approach, using garbled circuits to split TLS session keys between users and notaries, ensuring neither party can forge proofs alone. This creates portable proofs of web data while preserving privacy.</p>
<p>Mobile integration remains an open challenge. While TLSNotary works well on desktop, coordinating MPC between mobile apps and browsers presents unique technical hurdles. Solving this would unlock powerful use cases: proving income from banking apps, verifying social media reputation, or demonstrating transaction history—all without sharing credentials or raw data.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="unified-zk-registry-system">Unified ZK Registry System<a href="https://zkmopro.org/blog/ethglobal-new-delhi#unified-zk-registry-system" class="hash-link" aria-label="Direct link to Unified ZK Registry System" title="Direct link to Unified ZK Registry System">​</a></h3>
<p>The ZK identity landscape is fragmented. Projects like <a href="https://github.com/anon-aadhaar" target="_blank" rel="noopener noreferrer">Anon Aadhaar</a>, passport-based zkID solutions, and zkPDF each maintain separate on-chain registries. Users face redundant verifications, and developers must integrate with each system independently.</p>
<p><a href="https://eips.ethereum.org/EIPS/eip-7812" target="_blank" rel="noopener noreferrer">ERC-7812</a> proposes a solution: a singleton on-chain registry using Sparse Merkle Trees to store commitments to private data. Statements can be verified via ZK proofs without revealing underlying information.</p>
<p>With unified client libraries built around ERC-7812 and integrated with Mopro, developers would call one API after generating proofs on-device, regardless of proof type. The real power emerges in cross-application identity: a user proves their age once with Anon Aadhaar in one Mopro app, committing to ERC-7812. Later, a Mopro app using different schemes verifies that commitment without re-proving. The unified registry enables seamless credential reuse across the mobile applications while preserving privacy.</p>]]></content>
        <author>
            <name>Moven Tsai</name>
            <uri>https://github.com/moven0831</uri>
        </author>
        <category label="ethglobal" term="ethglobal"/>
        <category label="hackathon" term="hackathon"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Announcing the ETHGlobal Cannes 2025 Mopro Track Winners: Advancing Mobile Proving]]></title>
        <id>https://zkmopro.org/blog/ethglobal-cannes-2025</id>
        <link href="https://zkmopro.org/blog/ethglobal-cannes-2025"/>
        <updated>2025-07-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[ETHGlobal Cannes brought together some of the brightest minds in blockchain development, and we're thrilled to announce the winners of the Mopro track. Over 36 intense hours, developers pushed the boundaries of client-side proving with real-world applications on mobile device.]]></summary>
        <content type="html"><![CDATA[<p><a href="https://ethglobal.com/events/cannes" target="_blank" rel="noopener noreferrer">ETHGlobal Cannes</a> brought together some of the brightest minds in blockchain development, and we're thrilled to announce the winners of the Mopro track. Over 36 intense hours, developers pushed the boundaries of client-side proving with real-world applications on mobile device.</p>
<p>With innovative submissions ranging from protocol-level integrations to consumer-facing applications, Mopro track showed how mobile ZK proving is becoming increasingly practical. Teams leveraged Mopro's mobile proving capabilities to build everything from tamper-proof media verification to post-quantum secure smart accounts with <a href="https://eip7702.io/" target="_blank" rel="noopener noreferrer">EIP-7702</a>, proving that privacy-preserving technology can be both powerful and user-friendly.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="the-power-of-client-side-proving">The Power of Client-Side Proving<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#the-power-of-client-side-proving" class="hash-link" aria-label="Direct link to The Power of Client-Side Proving" title="Direct link to The Power of Client-Side Proving">​</a></h2>
<p>Before diving into the winners, it's worth highlighting what made this hackathon special. While many ZK applications rely on server-side proving or trusted execution environments (TEEs), Mopro enables developers to generate zero-knowledge proofs directly on mobile devices. This approach puts privacy control back in users' hands and opens up new possibilities for decentralized applications.</p>
<p>The submissions demonstrated two key trends: protocol developers integrating zkVM such as <a href="https://github.com/risc0/risc0" target="_blank" rel="noopener noreferrer">RISC-0</a> and libraries like <a href="https://github.com/Consensys/gnark" target="_blank" rel="noopener noreferrer">Gnark</a> with Mopro, and application developers building user-facing products that leverage existing ZK circuits. Both approaches are critical for the ecosystem's growth.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="the-winners">The Winners<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#the-winners" class="hash-link" aria-label="Direct link to The Winners" title="Direct link to The Winners">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="-first-place-mobiscale---photo-id-verification-with-apples-secure-enclave">🥇 First Place: Mobiscale - Photo-ID Verification with Apple's Secure Enclave<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#-first-place-mobiscale---photo-id-verification-with-apples-secure-enclave" class="hash-link" aria-label="Direct link to 🥇 First Place: Mobiscale - Photo-ID Verification with Apple's Secure Enclave" title="Direct link to 🥇 First Place: Mobiscale - Photo-ID Verification with Apple's Secure Enclave">​</a></h3>
<p><strong>Project:</strong> <a href="https://ethglobal.com/showcase/mobiscale-n9vj6" target="_blank" rel="noopener noreferrer">Mobiscale</a><br>
<strong>GitHub:</strong> <a href="https://github.com/ElusAegis/MobiScale" target="_blank" rel="noopener noreferrer">ElusAegis/MobiScale</a></p>
<p>Mobiscale achieved what many thought impossible in a hackathon setting: a complete end-to-end proof of photo-ID verification running entirely on a mobile device in under 90 seconds. The team cleverly combined Apple's Secure Enclave for facial recognition, RISC-0 for TEE attestation verification, and <a href="https://noir-lang.org/" target="_blank" rel="noopener noreferrer">Noir</a> with the <a href="https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg" target="_blank" rel="noopener noreferrer">Barretenberg proving backend</a> for ECDSA signature validation.</p>
<p>What makes this project remarkable is its practical approach to identity verification. By computing cosine similarity between a passport photo and a live selfie within the TEE, then proving this computation happened correctly using ZK proofs, Mobiscale demonstrates a privacy-preserving liveness check that could be deployed in the near future. The integration with Mopro shows how mobile proving can complement hardware security features to create robust identity solutions.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="-second-place-zkipper---pq-secure-eip-7702-smart-accounts">🥈 Second Place: Zkipper - PQ-Secure EIP-7702 Smart Accounts<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#-second-place-zkipper---pq-secure-eip-7702-smart-accounts" class="hash-link" aria-label="Direct link to ��🥈 Second Place: Zkipper - PQ-Secure EIP-7702 Smart Accounts" title="Direct link to 🥈 Second Place: Zkipper - PQ-Secure EIP-7702 Smart Accounts">​</a></h3>
<p><strong>Project:</strong> <a href="https://ethglobal.com/showcase/zkipper-czc3z" target="_blank" rel="noopener noreferrer">Zkipper</a><br>
<strong>GitHub:</strong> <a href="https://github.com/ZKNoxHQ/ZKipper" target="_blank" rel="noopener noreferrer">ZKNoxHQ/ZKipper</a></p>
<p>The ZKNox team showcased their cryptographic expertise at the hackathon with "Zkipper," a project that turns the ARX chips in ETHGlobal wristbands into transaction signers, enabling post‑quantum–secure smart accounts via EIP‑7702.</p>
<p>The technical achievement here is twofold: First, they successfully integrated Gnark with Mopro within the 36-hour timeframe, which is a significant contribution to the ecosystem. Second, they implemented <a href="https://falcon-sign.info/" target="_blank" rel="noopener noreferrer">Falcon512</a> post-quantum signatures to secure smart accounts, preventing "Bybit-style" attacks by separating admin commands onto distinct devices. This approach shows how Mopro can enable hardware-based security solutions that are both quantum-resistant and user-friendly.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="-third-place-proov---tamper-proof-media-with-zk">🥉 Third Place: 👀Proov! - Tamper-Proof Media with ZK<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#-third-place-proov---tamper-proof-media-with-zk" class="hash-link" aria-label="Direct link to 🥉 Third Place: 👀Proov! - Tamper-Proof Media with ZK" title="Direct link to 🥉 Third Place: 👀Proov! - Tamper-Proof Media with ZK">​</a></h3>
<p><strong>Project:</strong> <a href="https://ethglobal.com/showcase/eyes-proov-at10u" target="_blank" rel="noopener noreferrer">👀Proov!</a><br>
<strong>GitHub:</strong> <a href="https://github.com/undefinedlab/PROOV_ZK" target="_blank" rel="noopener noreferrer">undefinedlab/PROOV_ZK</a></p>
<p>👀Proov! stood out not just for its technical implementation but for its exceptional UI/UX design. The team created a complete solution for tamper-proof media capsules, combining Mopro-generated proofs with AI-powered image summaries, decentralized storage on Walrus, and verification on Flow blockchain.</p>
<p>The project offers one-tap proof generation with instant cryptographic capsule creation, selective privacy controls, and future-disclosure capabilities. By embedding tamper-proof QR codes in image capsules, they made every photo machine-verifiable and portable across platforms like Instagram, X, and Telegram. This shows how ZKP can be packaged into consumer-friendly applications without sacrificing security or privacy.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="other-notable-submissions">Other Notable Submissions<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#other-notable-submissions" class="hash-link" aria-label="Direct link to Other Notable Submissions" title="Direct link to Other Notable Submissions">​</a></h2>
<p>Beyond the top three winners, the Mopro track at ETHGlobal Cannes attracted submissions that collectively show the diversity of ZK use cases on mobile. Teams explored verification and attestation use cases through projects like <a href="https://ethglobal.com/showcase/proofofparticipation-xca0a" target="_blank" rel="noopener noreferrer">ProofOfParticipation</a> (GPS-based event attendance), <a href="https://ethglobal.com/showcase/zkage-proof-mobile-2yroo" target="_blank" rel="noopener noreferrer">ZKAge Proof Mobile</a> (age verification for restricted services). <a href="https://ethglobal.com/showcase/bidet-gdvtq" target="_blank" rel="noopener noreferrer">Bidet</a>'s privacy-preserving NFC tag game showcased gaming and social use cases, and <a href="https://ethglobal.com/showcase/proofoffunds-zczod" target="_blank" rel="noopener noreferrer">ProofOfFunds</a> showed financial privacy by letting users prove they meet a cryptocurrency balance threshold without disclosing exact amounts or wallet addresses.</p>
<p>These submissions integrated various technical stacks across different proving frameworks—Circom and Noir—and mobile platforms—Swift (iOS), Kotlin (Android), React Native, and Flutter—alongside Mopro’s proving capabilities. This integration validates the platform’s flexibility across use cases and tech stacks. The diversity of both applications and technical aspect highlights the ecosystem’s readiness for real-world deployment and shows how mobile proving can address privacy challenges across multiple industries.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="key-insights-and-developer-feedback">Key Insights and Developer Feedback<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#key-insights-and-developer-feedback" class="hash-link" aria-label="Direct link to Key Insights and Developer Feedback" title="Direct link to Key Insights and Developer Feedback">​</a></h2>
<p>The hackathon provided valuable insights into the state of mobile ZK proving:</p>
<p><strong>What's Working Well:</strong></p>
<ul>
<li>Mopro's developer experience received consistently positive feedback</li>
<li>Teams successfully integrated various proving systems (RISC-0, Noir, Gnark) with Mopro</li>
<li>The ecosystem is mature enough for developers to build meaningful applications in 36 hours</li>
</ul>
<p><strong>Challenges Identified:</strong></p>
<ul>
<li>Writing circuits remains the biggest pain point for developers</li>
<li>On-chain verification varies in stability (Circom is more mature, Noir is catching up)</li>
<li>Developers need single-architecture templates for iOS-only or Android-only builds</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="looking-forward-the-future-of-client-side-proving">Looking Forward: The Future of Client-Side Proving<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#looking-forward-the-future-of-client-side-proving" class="hash-link" aria-label="Direct link to Looking Forward: The Future of Client-Side Proving" title="Direct link to Looking Forward: The Future of Client-Side Proving">​</a></h2>
<p>The diversity of submissions—from geo-proof games adapted from <a href="https://github.com/zkVerify/explorations/tree/main/mopro-proof-of-geolocation/proof-of-geolocation" target="_blank" rel="noopener noreferrer">zkVerify's proof-of-geolocation circuits</a> to age verification systems—shows that mobile ZK proving is ready for mainstream adoption. While Mopro may not pursue the same level of direct adoption as protocols aimed at end users, it serves a critical role as an incubation platform for client-side ZK applications especially on mobile phone.</p>
<p>Based on developer feedback, we're prioritizing several improvements:</p>
<ul>
<li><strong>Enhanced Templates</strong> - Expanding variety for different use cases (<a href="https://github.com/zkmopro/mopro/issues/503" target="_blank" rel="noopener noreferrer">Issue #503</a>, <a href="https://github.com/zkmopro/mopro/issues/438" target="_blank" rel="noopener noreferrer">Issue #438</a>)</li>
<li><strong>Single Architecture Support</strong> - iOS-only and Android-only bindings for cross-platform frameworks like Flutter (<a href="https://github.com/zkmopro/mopro/issues/502" target="_blank" rel="noopener noreferrer">Issue #502</a>) and React Native (<a href="https://github.com/zkmopro/mopro/issues/501" target="_blank" rel="noopener noreferrer">Issue #501</a>).</li>
<li><strong>Improved DevEx</strong> - Better naming for custom bindings (<a href="https://github.com/zkmopro/mopro/issues/500" target="_blank" rel="noopener noreferrer">Issue #500</a>)</li>
<li><strong>Documentation</strong> - Simplified architecture overview (<a href="https://github.com/zkmopro/mopro/issues/498" target="_blank" rel="noopener noreferrer">Issue #498</a>)</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="acknowledgments">Acknowledgments<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#acknowledgments" class="hash-link" aria-label="Direct link to Acknowledgments" title="Direct link to Acknowledgments">​</a></h2>
<p>We'd like to thank all teams who participated in the Mopro track at ETHGlobal Cannes. Your innovation, dedication, and feedback are invaluable in advancing the state of mobile proving.</p>
<p>Special recognition goes to the ETHGlobal team for organizing an exceptional event and providing the infrastructure that makes these innovations possible.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="get-involved">Get Involved<a href="https://zkmopro.org/blog/ethglobal-cannes-2025#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved">​</a></h2>
<p>The work showcased at ETHGlobal Cannes is just the beginning. If you're interested in building with Mopro or contributing to the ecosystem:</p>
<ul>
<li>Explore the <a href="https://zkmopro.org/docs/intro" target="_blank" rel="noopener noreferrer">Mopro documentation</a></li>
<li>Check out the <a href="https://zkmopro.org/docs/projects" target="_blank" rel="noopener noreferrer">project gallery</a> for inspiration</li>
<li>Join the <a href="https://t.me/zkmopro" target="_blank" rel="noopener noreferrer">community discussions on Telegram</a> and contribute to the <a href="https://github.com/zkmopro/mopro/issues" target="_blank" rel="noopener noreferrer">open issues</a></li>
</ul>]]></content>
        <author>
            <name>Moven Tsai</name>
            <uri>https://github.com/moven0831</uri>
        </author>
        <category label="ethglobal" term="ethglobal"/>
        <category label="hackathon" term="hackathon"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Metal MSM v2: Exploring MSM Acceleration on Apple GPUs]]></title>
        <id>https://zkmopro.org/blog/metal-msm-v2</id>
        <link href="https://zkmopro.org/blog/metal-msm-v2"/>
        <updated>2025-07-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Key Takeaways]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithStickyNavbar_wa2S" id="key-takeaways">Key Takeaways<a href="https://zkmopro.org/blog/metal-msm-v2#key-takeaways" class="hash-link" aria-label="Direct link to Key Takeaways" title="Direct link to Key Takeaways">​</a></h2>
<ul>
<li>Hybrid CPU-GPU approaches are essential for fully exploiting limited hardware such as mobile devices, improving MSM computation and accelerating proof generation.</li>
<li>To unify GPU acceleration efforts, <a href="https://www.w3.org/TR/webgpu/" target="_blank" rel="noopener noreferrer">WebGPU's</a> vendor-agnostic API and <a href="https://www.w3.org/TR/WGSL/" target="_blank" rel="noopener noreferrer">WGSL</a> offer promising solutions that compile to native formats like <a href="https://registry.khronos.org/SPIR-V/" target="_blank" rel="noopener noreferrer">SPIR-V</a> (for Vulkan on Android) and <a href="https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf" target="_blank" rel="noopener noreferrer">MSL</a> (for Metal on Apple devices).</li>
<li>GPU acceleration for post-quantum proving systems could enable their widespread adoption.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="introduction">Introduction<a href="https://zkmopro.org/blog/metal-msm-v2#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p>GPU acceleration harnesses the massive parallelism of Graphics Processing Units (GPUs) to dramatically speed up tasks that would otherwise overwhelm traditional CPUs. Because GPUs can execute thousands of threads simultaneously, they have become indispensable for compute-intensive workloads such as machine-learning model training and modern cryptographic algorithms.</p>
<p>This technology plays a crucial role in advancing privacy-preserving applications, as zero-knowledge proofs (ZKPs) currently face a significant bottleneck due to the high computational cost of their core operations. By accelerating these operations, we can generate proofs more quickly and cost-effectively, which is essential for the broader adoption of privacy-focused solutions across Ethereum and other blockchain platforms.</p>
<p>Currently, research on GPU acceleration for cryptography remains fragmented, with each platform relying on its own framework: <a href="https://developer.apple.com/metal/" target="_blank" rel="noopener noreferrer">Metal</a> on Apple devices, <a href="https://developer.nvidia.com/vulkan" target="_blank" rel="noopener noreferrer">Vulkan</a> on Android, and <a href="https://en.wikipedia.org/wiki/CUDA" target="_blank" rel="noopener noreferrer">CUDA</a> on NVIDIA hardware. Aside from CUDA, most GPU frameworks lack mature ecosystems of cryptographic libraries (e.g., NVIDIA's <a href="https://github.com/NVIDIA/CUDALibrarySamples/tree/master/cuPQC" target="_blank" rel="noopener noreferrer">cuPQC</a> and <a href="https://github.com/NVIDIA/CUDALibrarySamples/tree/master/cuFFT" target="_blank" rel="noopener noreferrer">cuFFT</a>).</p>
<p>Therefore, <a href="https://github.com/zkmopro/mopro" target="_blank" rel="noopener noreferrer">Mopro</a> is investing in GPU acceleration through related grants (<a href="https://github.com/zkmopro/mopro/issues/21" target="_blank" rel="noopener noreferrer">Issue #21</a>, <a href="https://github.com/zkmopro/mopro/issues/22" target="_blank" rel="noopener noreferrer">Issue #22</a>, and <a href="https://github.com/zkmopro/mopro/issues/153" target="_blank" rel="noopener noreferrer">Issue #153</a>), as it advances our mission to make mobile proving both accessible and practical.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="a-primer-on-multi-scalar-multiplication">A Primer on Multi-Scalar Multiplication<a href="https://zkmopro.org/blog/metal-msm-v2#a-primer-on-multi-scalar-multiplication" class="hash-link" aria-label="Direct link to A Primer on Multi-Scalar Multiplication" title="Direct link to A Primer on Multi-Scalar Multiplication">​</a></h2>
<p><a href="https://hackmd.io/@drouyang/SyYwhWIso" target="_blank" rel="noopener noreferrer">Multi-Scalar Multiplication (MSM)</a> is an essential primitive in elliptic curve cryptography, particularly in pairing-based proving systems widely used for privacy-preserving applications. MSM involves computing a sum of the form <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi><mo>=</mo><msubsup><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></msubsup><mo stretchy="false">(</mo><msub><mi>k</mi><mi>i</mi></msub><mo>⋅</mo><msub><mi>P</mi><mi>i</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">Q = \sum_{i=1}^{n}(k_i \cdot P_i)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8778em;vertical-align:-0.1944em"></span><span class="mord mathnormal">Q</span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:1.104em;vertical-align:-0.2997em"></span><span class="mop"><span class="mop op-symbol small-op" style="position:relative;top:0em">∑</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8043em"><span style="top:-2.4003em;margin-left:0em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mrel mtight">=</span><span class="mord mtight">1</span></span></span></span><span style="top:-3.2029em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">n</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2997em"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0315em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span>, where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">k_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03148em">k</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.0315em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> are scalars and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>P</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">P_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.15em"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.13889em">P</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em"><span style="top:-2.55em;margin-left:-0.1389em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em"><span></span></span></span></span></span></span></span></span></span> are points on an elliptic curve, such as <a href="https://hackmd.io/@jpw/bn254" target="_blank" rel="noopener noreferrer">BN254</a>. This computationally intensive operation is ideal for GPU acceleration.</p>
<p><a href="https://github.com/zkmopro/gpu-acceleration/tree/v0.2.0" target="_blank" rel="noopener noreferrer">Metal MSM v2</a> is an open-source implementation, licensed under MIT and Apache 2.0, that optimizes MSM on Apple GPUs using the Metal Shading Language (MSL). Building on its predecessor, Metal MSM v2 offers significant performance improvements through algorithmic and GPU-specific optimizations, laying the foundation for further research into mobile proving acceleration with GPUs.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="recap-on-metal-msm-v1">Recap on Metal MSM v1<a href="https://zkmopro.org/blog/metal-msm-v2#recap-on-metal-msm-v1" class="hash-link" aria-label="Direct link to Recap on Metal MSM v1" title="Direct link to Recap on Metal MSM v1">​</a></h2>
<p><a href="https://github.com/zkmopro/gpu-acceleration/releases/tag/v0.1.0" target="_blank" rel="noopener noreferrer">The first version of Metal MSM</a> (v1) was an initial attempt to bring MSM computations on the BN254 curve to Apple GPUs, leveraging parallelism and optimizations like precomputation from the EdMSM paper by Bootle et al.<sup><a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fn-1-6ee944" id="user-content-fnref-1-6ee944" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup>. While it showed the potential for GPU acceleration, profiling result revealed key limitations:</p>
<ul>
<li><strong>Low GPU Occupancy:</strong> At only 32%, the GPU was underutilized, leading to inefficient computation.</li>
<li><strong>High Memory Footprint:</strong> Peak VRAM usage was excessive, causing GPU hang errors on real mobile devices for instance sizes ≥ 2^14.</li>
<li><strong>Performance Bottlenecks:</strong> For an input size of 2^20 points, v1 took 41 seconds on an M1 Pro MacBook, indicating substantial room for improvement.</li>
</ul>
<p>These challenges drove the development of a newer version, which introduces targeted optimizations to address these issues. For full context, refer to the detailed <a href="https://hackmd.io/Q6c2LUxzTzGlEcZF1NBRpA" target="_blank" rel="noopener noreferrer">Metal MSM v1 Summary Report</a> and <a href="https://hackmd.io/@moven0831/profiling-metal-msm" target="_blank" rel="noopener noreferrer">Metal MSM v1 Profiling Report</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="metal-msm-v2-whats-new">Metal MSM v2: What's New<a href="https://zkmopro.org/blog/metal-msm-v2#metal-msm-v2-whats-new" class="hash-link" aria-label="Direct link to Metal MSM v2: What's New" title="Direct link to Metal MSM v2: What's New">​</a></h2>
<p>Metal MSM v2 introduces key enhancements over v1, significantly improving performance and resource efficiency. It adopts the sparse matrix approach from the cuZK paper by Lu et al.<sup><a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fn-2-6ee944" id="user-content-fnref-2-6ee944" data-footnote-ref="true" aria-describedby="footnote-label">2</a></sup>, treating MSM elements as sparse matrices to reduce memory usage and convert operations described in <a href="https://dl.acm.org/doi/10.1109/SFCS.1976.21" target="_blank" rel="noopener noreferrer">Pippenger's algorithm</a> into efficient sparse matrix algorithms.</p>
<p>The implementation draws inspiration from <a href="https://github.com/z-prize/2023-entries/tree/main/prize-2-msm-wasm/webgpu-only/tal-derei-koh-wei-jie" target="_blank" rel="noopener noreferrer">Derei and Koh's WebGPU MSM implementation</a> for <a href="https://www.zprize.io/#2023" target="_blank" rel="noopener noreferrer">ZPrize 2023</a>. However, targeting the BN254 curve (unlike the BLS12-377 curve used by Zprize 2023) required different optimization strategies, particularly for Montgomery multiplications and for using Jacobian coordinates instead of projective or extended twisted Edwards coordinates.</p>
<p>Due to differences between shading languages (CUDA for cuZK, WGSL for WebGPU, and MSL for Metal), additional GPU programming efforts were necessary. For instance, dynamic kernel dispatching, which is straightforward in CUDA, required workarounds in Metal through host-side dispatching at runtime.</p>
<p>Key improvements include:</p>
<ul>
<li><strong>Dynamic Workgroup Sizing</strong>: Workgroup sizes are adjusted based on input size and GPU architecture using a <code>scale_factor</code> and <code>thread_execution_width</code>. These parameters were optimized through experimentation to maximize GPU utilization as mentioned in <a href="https://github.com/zkmopro/gpu-acceleration/pull/86" target="_blank" rel="noopener noreferrer">PR #86</a>.</li>
<li><strong>Dynamic Window Sizes</strong>: A <a href="https://github.com/zkmopro/gpu-acceleration/commit/f5c3fcd2dd1e32766e5713a5d8e6e19ebe00f6f6" target="_blank" rel="noopener noreferrer">window_size_optimizer</a> calculates optimal window sizes using a cost function from the cuZK paper, with empirical adjustments for real devices, as detailed in <a href="https://github.com/zkmopro/gpu-acceleration/pull/87" target="_blank" rel="noopener noreferrer">PR #87</a>.</li>
<li><strong>MSL-Level Optimizations</strong>: Loop unrolling and explicit access qualifiers, implemented in <a href="https://github.com/zkmopro/gpu-acceleration/pull/88" target="_blank" rel="noopener noreferrer">PR #88</a>, enhance kernel efficiency, with potential for further gains via SIMD refactoring.</li>
</ul>
<p>Benchmarks on an M3 MacBook Air with 24GB memory show 40x–100x improvements over v1 and ~10x improvement over <a href="https://github.com/ICME-Lab/msm-webgpu" target="_blank" rel="noopener noreferrer">ICME Labs' WebGPU MSM</a> on BN254, adapted from Derei and Koh's BLS12-377 work. While still slower than CPU-only <a href="https://github.com/arkworks-rs" target="_blank" rel="noopener noreferrer">Arkworks MSM</a> on small &amp; medium input sizes, v2 lays the groundwork for a future CPU+GPU hybrid approach.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="how-metal-msm-v2-works">How Metal MSM v2 Works<a href="https://zkmopro.org/blog/metal-msm-v2#how-metal-msm-v2-works" class="hash-link" aria-label="Direct link to How Metal MSM v2 Works" title="Direct link to How Metal MSM v2 Works">​</a></h2>
<p>The general flow follows <a href="https://hackmd.io/HNH0DcSqSka4hAaIfJNHEA" target="_blank" rel="noopener noreferrer">Koh's technical writeup</a>. We pack affine points and scalars on the CPU into a locality-optimized byte format, upload them to the GPU, and encode points into Montgomery form for faster modular multiplications. Scalars are split into <a href="https://hackmd.io/@drouyang/signed-bucket-index" target="_blank" rel="noopener noreferrer">signed chunks</a> to enable the <a href="https://hackmd.io/HkVWGwsRTM2HeBL1VN0lAw" target="_blank" rel="noopener noreferrer">Non-Adjacent Form (NAF)</a> method, halving both bucket count and memory during accumulation.</p>
<p>Next, we apply a parallel sparse-matrix transposition (adapted from Wang et al.'s work<sup><a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fn-3-6ee944" id="user-content-fnref-3-6ee944" data-footnote-ref="true" aria-describedby="footnote-label">3</a></sup>) to identify matching scalar chunks and group points into buckets. Then, using a sparse-matrix–vector product (SMVP) and the <code>pBucketPointsReduction</code> algorithm (Algorithm 4 in the cuZK paper<sup><a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fn-2-6ee944" id="user-content-fnref-2-6ee944-2" data-footnote-ref="true" aria-describedby="footnote-label">2</a></sup>), we split buckets among GPU threads, compute each thread's running sum, and scale it by the required factor.</p>
<p>After GPU processing, we transfer each thread's partial sums back to the CPU for final aggregation. Since the remaining point count is small and <a href="https://en.wikipedia.org/wiki/Horner%27s_method" target="_blank" rel="noopener noreferrer">Horner's Method</a> is sequential and efficient on the CPU, we perform the final sum there.</p>
<p>The use of sparse matrices is a key innovation in Metal MSM v2, reducing memory requirements and boosting parallelism compared to previous approaches.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="understanding-the-theoretical-acceleration-upper-bound">Understanding the Theoretical Acceleration Upper Bound<a href="https://zkmopro.org/blog/metal-msm-v2#understanding-the-theoretical-acceleration-upper-bound" class="hash-link" aria-label="Direct link to Understanding the Theoretical Acceleration Upper Bound" title="Direct link to Understanding the Theoretical Acceleration Upper Bound">​</a></h2>
<p>In the Groth16 proving system, Number Theoretic Transform (NTT) and MSM account for 70–80% of the proving time. According to <a href="https://en.wikipedia.org/wiki/Amdahl%27s_law" target="_blank" rel="noopener noreferrer">Amdahl's Law</a>, the maximum speedup is limited by unoptimized components:</p>
<p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mtext>Speedup</mtext><mtext>overall</mtext></msub><mo>=</mo><mstyle displaystyle="true" scriptlevel="0"><mfrac><mn>1</mn><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><msub><mtext>time</mtext><mtext>optimized</mtext></msub><mo stretchy="false">)</mo><mo>+</mo><mstyle displaystyle="true" scriptlevel="0"><mfrac><msub><mtext>time</mtext><mtext>optimized</mtext></msub><msub><mtext>speedup</mtext><mtext>optimized</mtext></msub></mfrac></mstyle></mrow></mfrac></mstyle></mrow><annotation encoding="application/x-tex">\text{Speedup}_{\text{overall}} = \dfrac{1}{(1 - \text{time}_{\text{optimized}}) + \dfrac{\text{time}_{\text{optimized}}}{\text{speedup}_{\text{optimized}}}}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9386em;vertical-align:-0.2441em"></span><span class="mord"><span class="mord text"><span class="mord">Speedup</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.242em"><span style="top:-2.4559em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">overall</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em"></span></span><span class="base"><span class="strut" style="height:3.6225em;vertical-align:-2.3011em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3214em"><span style="top:-2.11em"><span class="pstrut" style="height:3.3449em"></span><span class="mord"><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mord text"><span class="mord">time</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">optimized</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3449em"><span style="top:-2.314em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord text"><span class="mord">speedup</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.242em"><span style="top:-2.4559em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">optimized</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3802em"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-3.677em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord"><span class="mord text"><span class="mord">time</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em"><span style="top:-2.55em;margin-right:0.05em"><span class="pstrut" style="height:2.7em"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord text mtight"><span class="mord mtight">optimized</span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.0662em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-3.5749em"><span class="pstrut" style="height:3.3449em"></span><span class="frac-line" style="border-bottom-width:0.04em"></span></span><span style="top:-4.0219em"><span class="pstrut" style="height:3.3449em"></span><span class="mord"><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.3011em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></p>
<p>If 80% of the prover time is optimized with infinite speedup, the theoretical maximum is 5x. However, data I/O overhead reduces practical gains. For more details, see <a href="https://medium.com/@ingonyama/revisiting-paradigms-hardware-acceleration-for-zero-knowledge-proofs-5dffacdc24b4" target="_blank" rel="noopener noreferrer">Ingonyama's blog post on Hardware Acceleration for ZKP</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="benchmark-results">Benchmark Results<a href="https://zkmopro.org/blog/metal-msm-v2#benchmark-results" class="hash-link" aria-label="Direct link to Benchmark Results" title="Direct link to Benchmark Results">​</a></h2>
<p>Benchmarks conducted on an M3 MacBook Air compare Metal MSM v2 with the Arkworks v0.4.x CPU implementation across various input sizes.</p>
<p><img decoding="async" loading="lazy" alt="Metal MSM v2 Benchmark Results" src="https://zkmopro.org/assets/images/metal-msm-v2-benchmark-c358622f216caca6673b74261528f4ea.png" class="img_KKSl"></p>
<table><thead><tr><th rowspan="2" style="text-align:center">Scheme</th><th colspan="7" style="text-align:center">Input Size (ms)</th></tr><tr><th style="text-align:center">2<sup>12</sup></th><th style="text-align:center">2<sup>14</sup></th><th style="text-align:center">2<sup>16</sup></th><th style="text-align:center">2<sup>18</sup></th><th style="text-align:center">2<sup>20</sup></th><th style="text-align:center">2<sup>22</sup></th><th style="text-align:center">2<sup>24</sup></th></tr></thead><tbody style="text-align:center"><tr><th style="text-align:center"><a href="https://github.com/arkworks-rs">Arkworks v0.4.x</a><br>(CPU, Baseline)</th><td>6</td><td>19</td><td>69</td><td>245</td><td>942</td><td>3,319</td><td>14,061</td></tr><tr><th style="text-align:center"><a href="https://github.com/zkmopro/gpu-acceleration/tree/v0.1.0">Metal MSM v0.1.0</a><br>(GPU)</th><td>143<br>(-23.8x)</td><td>273<br>(-14.4x)</td><td>1,730<br>(-25.1x)</td><td>10,277<br>(-41.9x)</td><td>41,019<br>(-43.5x)</td><td>555,877<br>(-167.5x)</td><td>N/A</td></tr><tr><th style="text-align:center"><a href="https://github.com/zkmopro/gpu-acceleration/tree/v0.2.0">Metal MSM v0.2.0</a><br>(GPU)</th><td>134<br>(-22.3x)</td><td>124<br>(-6.5x)</td><td>253<br>(-3.7x)</td><td>678<br>(-2.8x)</td><td>1,702<br>(-1.8x)</td><td>5,390<br>(-1.6x)</td><td>22,241<br>(-1.6x)</td></tr><tr><th style="text-align:center"><a href="https://github.com/ICME-Lab/msm-webgpu">ICME WebGPU MSM</a><br>(GPU)</th><td>N/A</td><td>N/A</td><td>2,719<br>(-39.4x)</td><td>5,418<br>(-22.1x)</td><td>17,475<br>(-18.6x)</td><td>N/A</td><td>N/A</td></tr><tr><th style="text-align:center"><a href="https://github.com/moven0831/icicle/tree/bn254-metal-benchmark">ICICLE-Metal v3.8.0</a><br>(GPU)</th><td>59<br>(-9.8x)</td><td>54<br>(-2.8x)</td><td>89<br>(-1.3x)</td><td>149<br>(+1.6x)</td><td>421<br>(+2.2x)</td><td>1,288<br>(+2.6x)</td><td>4,945<br>(+2.8x)</td></tr><tr><th style="text-align:center"><p><a href="https://github.com/ElusAegis/metal-msm-gpu-acceleration">ElusAegis' Metal MSM</a><br>(GPU)</p></th><td>58<br>(-9.7x)</td><td>69<br>(-3.6x)</td><td>100<br>(-1.4x)</td><td>207<br>(+1.2x)</td><td>646<br>(+1.5x)</td><td>2,457<br>(+1.4x)</td><td>11,353<br>(+1.2x)</td></tr><tr><th style="text-align:center"><p><a href="https://github.com/ElusAegis/metal-msm-gpu-acceleration">ElusAegis' Metal MSM</a><br>(CPU+GPU)</p></th><td>13<br>(-2.2x)</td><td>19<br>(-1.0x)</td><td>53<br>(+1.3x)</td><td>126<br>(+1.9x)</td><td>436<br>(+2.2x)</td><td>1,636<br>(+2.0x)</td><td>9,199<br>(+1.5x)</td></tr></tbody></table>
<blockquote>
<p>Negative values indicate slower performance relative to the CPU baseline. The performance gap narrows for larger inputs.</p>
</blockquote>
<p>Notes:</p>
<ul>
<li>For ICME WebGPU MSM, input size 2^12 causes M3 chip machines to crash; sizes not listed on the project's GitHub page are shown as "N/A"</li>
<li>For Metal MSM v0.1.0, the 2^24 benchmark was abandoned due to excessive runtime.</li>
</ul>
<p>While Metal MSM v2 isn't faster than CPUs across all hardware configurations, its open-source nature, competitive performance relative to other GPU implementations, and ongoing improvements position it well for continued advancement.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="profiling-insights">Profiling Insights<a href="https://zkmopro.org/blog/metal-msm-v2#profiling-insights" class="hash-link" aria-label="Direct link to Profiling Insights" title="Direct link to Profiling Insights">​</a></h2>
<p>Profiling on an M1 Pro MacBook provides detailed insights into the improvements from v1 to v2:</p>
<table><thead><tr><th>metric</th><th>v1</th><th>v2</th><th>gain</th></tr></thead><tbody><tr><td>end-to-end latency</td><td>10.3 s</td><td><strong>0.42 s</strong></td><td><strong>24x</strong></td></tr><tr><td>GPU occupancy</td><td>32 %</td><td><strong>76 %</strong></td><td>+44 pp</td></tr><tr><td>CPU share</td><td>19 %</td><td><strong>&lt;3 %</strong></td><td>–16 pp</td></tr><tr><td>peak VRAM</td><td>1.6 GB</td><td><strong>220 MB</strong></td><td>–7.3×</td></tr></tbody></table>
<p>These metrics highlight the effectiveness of v2's optimizations:</p>
<ul>
<li><strong>Latency Reduction:</strong> A 24-fold decrease in computation time for 2^20 inputs.</li>
<li><strong>Improved GPU Utilization:</strong> Occupancy increased from 32% to 76%, indicating better use of GPU resources.</li>
<li><strong>Reduced CPU Dependency:</strong> CPU share dropped below 3%, allowing the GPU to handle most of the workload.</li>
<li><strong>Lower Memory Footprint:</strong> Peak VRAM usage decreased from 1.6 GB to 220 MB, a 7.3-fold reduction.</li>
</ul>
<p>Profiling also identified buffer reading throughput as a primary bottleneck in v1, which v2 mitigates through better workload distribution and sparse matrix techniques. See detailed profiling reports: <a href="https://hackmd.io/@yaroslav-ya/rJkpqc_Nke" target="_blank" rel="noopener noreferrer">v1 Profiling Report</a> and <a href="https://hackmd.io/@yaroslav-ya/HyFA7XAQll" target="_blank" rel="noopener noreferrer">v2 Profiling Report</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="comparison-to-other-implementations">Comparison to Other Implementations<a href="https://zkmopro.org/blog/metal-msm-v2#comparison-to-other-implementations" class="hash-link" aria-label="Direct link to Comparison to Other Implementations" title="Direct link to Comparison to Other Implementations">​</a></h2>
<p>Metal MSM v2 is tailored for Apple's Metal API, setting it apart from other GPU-accelerated MSM implementations:</p>
<ul>
<li><strong>Derei and Koh's WebGPU MSM on BLS12</strong>: Designed for WebGPU, this implementation targets browser-based environments and may not fully leverage Apple-specific hardware optimizations.</li>
<li><strong>ICME labs WebGPU MSM on BN254</strong>: Adapted from Derei and Koh's WebGPU work for the BN254 curve, it is ~10x slower than Metal MSM v2 for inputs from 2^16 to 2^20 on M3 MacBook Air.</li>
<li><strong>cuZK</strong>: A CUDA-based implementation for NVIDIA GPUs, operating on a different hardware ecosystem and using different algorithmic approaches.</li>
</ul>
<p>Metal MSM v2's use of sparse matrices and dynamic workgroup sizing provides advantages on Apple hardware, particularly for large input sizes. While direct benchmark comparisons are limited, internal reports suggest that v2 achieves performance on par with or better than other WebGPU/Metal MSM implementations at medium scales.</p>
<p>It's worth noting that the state-of-the-art Metal MSM implementation is <a href="https://medium.com/@ingonyama/icicle-goes-metal-v3-6-163fa7bbfa44" target="_blank" rel="noopener noreferrer">Ingonyama's ICICLE-Metal</a> (since ICICLE v3.6). Readers can try it by following:</p>
<ul>
<li><a href="https://github.com/ingonyama-zk/icicle/tree/main/examples/rust/msm" target="_blank" rel="noopener noreferrer">ICICLE Rust MSM example</a></li>
<li><a href="https://github.com/moven0831/icicle/tree/bn254-metal-benchmark" target="_blank" rel="noopener noreferrer">Experimental BN254 Metal benchmark</a></li>
</ul>
<p>Another highlight is <a href="https://github.com/ElusAegis/metal-msm-gpu-acceleration" target="_blank" rel="noopener noreferrer">ElusAegis' Metal MSM implementation</a> for BN254, which was forked from version 1 of Metal MSM. To the best of our knowledge, his pure GPU implementation further improves the allocation and algorithmic structure to add more parallelism, resulting in <strong>2x</strong> faster performance compared to Metal MSM v2.</p>
<p>Moreover, by integrating this GPU implementation with optimized MSM on the CPU side from the <a href="https://github.com/privacy-scaling-explorations/halo2curves" target="_blank" rel="noopener noreferrer">halo2curves</a> library, he developed a hybrid approach that splits MSM tasks between CPU and GPU and then aggregates the results. This strategy achieves an additional <strong>30–40%</strong> speedup over a CPU-only implementation. This represents an encouraging result for GPU acceleration in pairing-based ZK systems and suggests a promising direction for Metal MSM v3.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="future-work">Future Work<a href="https://zkmopro.org/blog/metal-msm-v2#future-work" class="hash-link" aria-label="Direct link to Future Work" title="Direct link to Future Work">​</a></h2>
<p>The Metal MSM team has outlined several exciting directions for future development:</p>
<ul>
<li><strong>SIMD Refactoring:</strong> Enhance SIMD utilization and memory coalescing to further boost performance.</li>
<li><strong>Advanced Hybrid Approach:</strong> Integrate with Arkworks 0.5 for a more sophisticated CPU-GPU hybrid strategy.</li>
<li><strong>Android Support</strong>: Port kernels to Vulkan compute/WebGPU on Android, targeting Qualcomm Adreno (e.g., Adreno 7xx series) and ARM Mali (e.g., G77/G78/G710) GPUs.</li>
<li><strong>Cross-Platform Support:</strong> Explore WebGPU compatibility to enable broader platform support.</li>
<li><strong>Dependency Updates:</strong> Transition to newer versions of <a href="https://github.com/madsmtm/objc2" target="_blank" rel="noopener noreferrer">objc2</a> and <a href="https://crates.io/crates/objc2-metal" target="_blank" rel="noopener noreferrer">objc2-metal</a>, and Metal 4 to leverage the latest <a href="https://developer.apple.com/videos/play/wwdc2025/262/" target="_blank" rel="noopener noreferrer">MTLTensor features</a>, enabling multi-dimensional data to be passed to the GPU.</li>
</ul>
<p>Beyond these technical improvements, we are also interested in:</p>
<ul>
<li><strong>Exploration of PQ proving schemes:</strong> With the limited acceleration achievable from pairing-based proving schemes, we're motivated to explore PQ-safe proving schemes that have strong adoption potential over the next 3–5 years. These schemes, such as lattice-based proofs, involve extensive linear algebra operations that can benefit from GPUs' parallel computing capabilities.</li>
<li><strong>Crypto Math Library for GPU:</strong> Develop comprehensive libraries for cryptographic computations across multiple GPU frameworks, including Metal, Vulkan, and WebGPU, to expand the project's overall scope and impact.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="conclusion">Conclusion<a href="https://zkmopro.org/blog/metal-msm-v2#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion">​</a></h2>
<p>Metal MSM v2 represents a leap forward in accelerating Multi-Scalar Multiplication on Apple GPUs. By addressing the limitations of v1 through sparse matrix techniques, dynamic thread management, and other novel optimization techniques, it achieves substantial performance gains for Apple M-series chips and iPhones.</p>
<p>However, two challenges remain:</p>
<ul>
<li>First, GPUs excel primarily with large input sizes (typically around 2^26 or larger). Most mobile proving scenarios use smaller circuit sizes, generally ranging from 2^16 to 2^20, which limits the GPU's ability to fully leverage its parallelism. Therefore, optimizing GPU performance for these smaller workloads remains a key area for improvement.</li>
<li>Second, mobile GPUs inherently possess fewer cores and comparatively lower processing power than their desktop counterparts, constraining achievable performance. This hardware limitation necessitates further research into hybrid approaches and optimization techniques to maximize memory efficiency and power efficiency within the constraints of mobile devices.</li>
</ul>
<p>Addressing these challenges will require ongoing algorithmic breakthroughs, hardware optimizations, and seamless CPU–GPU integration. Collectively, these efforts pave a clear path for future research and practical advancements that enable the mass adoption of privacy-preserving applications.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="get-involved">Get Involved<a href="https://zkmopro.org/blog/metal-msm-v2#get-involved" class="hash-link" aria-label="Direct link to Get Involved" title="Direct link to Get Involved">​</a></h2>
<p>We welcome researchers and developers interested in GPU acceleration, cryptographic computations, or programmable cryptography to join our efforts:</p>
<ul>
<li><a href="https://github.com/zkmopro/gpu-acceleration/tree/v0.2.0" target="_blank" rel="noopener noreferrer">GPU Exploration Repository</a> (latest version includes Metal MSM v2)</li>
<li><a href="https://github.com/zkmopro/mopro" target="_blank" rel="noopener noreferrer">Mopro</a> (Mobile Proving)</li>
</ul>
<p>For further inquiries or collaborations, feel free to reach out through the project's GitHub discussions or directly via our <a href="https://t.me/zkmopro" target="_blank" rel="noopener noreferrer">Mopro community on Telegram</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="special-thanks">Special Thanks<a href="https://zkmopro.org/blog/metal-msm-v2#special-thanks" class="hash-link" aria-label="Direct link to Special Thanks" title="Direct link to Special Thanks">​</a></h2>
<p>We extend our sincere gratitude to <a href="https://x.com/yaroslav_ya" target="_blank" rel="noopener noreferrer">Yaroslav Yashin</a>, Artem Grigor, and <a href="https://x.com/weijie_eth" target="_blank" rel="noopener noreferrer">Wei Jie Koh</a> for reviewing this post and for their valuable contributions that made it all possible.</p>
<!-- -->
<section data-footnotes="true" class="footnotes"><h2 class="anchor anchorWithStickyNavbar_wa2S sr-only" id="footnote-label">Footnotes<a href="https://zkmopro.org/blog/metal-msm-v2#footnote-label" class="hash-link" aria-label="Direct link to Footnotes" title="Direct link to Footnotes">​</a></h2>
<ol>
<li id="user-content-fn-1-6ee944">
<p>Bootle, J., &amp; Chiesa, A., &amp; Hu, Y. (2022). "Gemini: elastic SNARKs for diverse environments." IACR Cryptology ePrint Archive, 2022/1400: <a href="https://eprint.iacr.org/2022/1400" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2022/1400</a> <a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fnref-1-6ee944" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2-6ee944">
<p>Lu, Y., Wang, L., Yang, P., Jiang, W., Ma, Z. (2023). "cuZK: Accelerating Zero-Knowledge Proof with A Faster Parallel Multi-Scalar Multiplication Algorithm on GPUs." IACR Cryptology ePrint Archive, 2022/1321: <a href="https://eprint.iacr.org/2022/1321" target="_blank" rel="noopener noreferrer">https://eprint.iacr.org/2022/1321</a> <a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fnref-2-6ee944" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩</a> <a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fnref-2-6ee944-2" data-footnote-backref="" aria-label="Back to reference 2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3-6ee944">
<p>Wang, H., Liu, W., Hou, K., Feng, W. (2016). "Parallel Transposition of Sparse Data Structures." Proceedings of the 2016 International Conference on Supercomputing (ICS '16): <a href="https://synergy.cs.vt.edu/pubs/papers/wang-transposition-ics16.pdf" target="_blank" rel="noopener noreferrer">https://synergy.cs.vt.edu/pubs/papers/wang-transposition-ics16.pdf</a> <a href="https://zkmopro.org/blog/metal-msm-v2#user-content-fnref-3-6ee944" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content>
        <author>
            <name>Moven Tsai</name>
            <uri>https://github.com/moven0831</uri>
        </author>
        <category label="metal" term="metal"/>
        <category label="msm" term="msm"/>
        <category label="gpu" term="gpu"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Mopro x Noir: Powering Mobile Zero-Knowledge Proofs]]></title>
        <id>https://zkmopro.org/blog/noir-integraion</id>
        <link href="https://zkmopro.org/blog/noir-integraion"/>
        <updated>2025-05-12T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithStickyNavbar_wa2S" id="introduction">Introduction<a href="https://zkmopro.org/blog/noir-integraion#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p><a href="https://noir-lang.org/docs" target="_blank" rel="noopener noreferrer">Noir</a> has been gaining significant traction recently, and for good reason. It's renowned for its elegant circuit frontend design and high-performance backend. With robust SDKs that make building web-based zero-knowledge apps seamless—and the ability to verify proofs on-chain—Noir stands out as a powerful and promising framework for developing modern ZKP applications.</p>
<p>However, while Noir offers strong support for web-based applications, native mobile support is still limited within the ecosystem. Most existing resources for mobile development have been contributed by the <a href="https://github.com/zkpassport" target="_blank" rel="noopener noreferrer">zkPassport</a> team, including key projects like</p>
<ul>
<li><a href="https://github.com/zkpassport/noir_rs" target="_blank" rel="noopener noreferrer">noir_rs</a></li>
<li><a href="https://github.com/Swoir/Swoir" target="_blank" rel="noopener noreferrer">Swoir</a></li>
<li><a href="https://github.com/madztheo/noir_android" target="_blank" rel="noopener noreferrer">noir_android</a>.</li>
<li><a href="https://github.com/madztheo/noir-react-native-starter" target="_blank" rel="noopener noreferrer">noir-react-native-starter</a></li>
</ul>
<p>Our work is deeply inspired by the zkPassport team’s contributions, but builds upon them with significant improvements and optimizations.</p>
<p>In this article, we’ll walk through how we integrated Noir into the Mopro project, highlighting the key challenges we faced and the opportunities we uncovered along the way. We'll also introduce <a href="https://github.com/vivianjeng/stealthnote-mobile" target="_blank" rel="noopener noreferrer">Stealthnote Mobile</a>, a fully native mobile application built with Noir, to demonstrate the potential and performance of running zero-knowledge proofs natively on mobile devices.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="build-noir-rs">Build <code>noir-rs</code><a href="https://zkmopro.org/blog/noir-integraion#build-noir-rs" class="hash-link" aria-label="Direct link to build-noir-rs" title="Direct link to build-noir-rs">​</a></h2>
<p>Like many apps with Mopro, our journey began with building a Rust crate. We started by adopting <a href="https://github.com/zkpassport/noir_rs" target="_blank" rel="noopener noreferrer"><code>noir_rs</code></a> from the zkPassport team. However, we quickly ran into some major challenges: <strong>compiling the Barretenberg backend</strong> — the proving system used by most Noir projects — could take up to 20 minutes. On top of that, the build process required a very <em>specific developer environment</em>, and <em>lacked caching</em>, meaning the entire binary had to be rebuilt from scratch even after a successful build.</p>
<p>To address these issues, we introduced a solution in</p>
<ul>
<li><code>noir-rs</code>: <a href="https://github.com/zkmopro/noir-rs" target="_blank" rel="noopener noreferrer">https://github.com/zkmopro/noir-rs</a></li>
</ul>
<p>by <em>prebuilding the backend binaries</em> and hosting them on a server. During the build process, these binaries are downloaded automatically, eliminating the need for local compilation. This approach drastically reduces build time—from 20 minutes to just <strong>8 seconds</strong>—and removes the need for any special environment setup. In fact, you can see from our <a href="https://github.com/zkmopro/noir-rs/blob/main/.github/workflows/test.yml" target="_blank" rel="noopener noreferrer">GitHub Actions YAML file</a> that <strong>no additional dependencies are required</strong>.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="suggestions">Suggestions<a href="https://zkmopro.org/blog/noir-integraion#suggestions" class="hash-link" aria-label="Direct link to Suggestions" title="Direct link to Suggestions">​</a></h3>
<p>Currently, the build process for the backend happens locally, which makes it non-reproducible and difficult to upgrade in CI environments like GitHub Actions. To improve this, we believe the build logic should be moved into CI. However, this process is quite complex and largely duplicated across repositories like the Noir and Aztec packages (See: <a href="https://github.com/AztecProtocol/aztec-packages/blob/46c2ad0b551a37e74118a789a1ea32a2daa1f849/.github/workflows/publish-bb.yml" target="_blank" rel="noopener noreferrer">publish-bb.yml</a>).</p>
<p>Our proposal is that the Noir and Aztec teams consolidate this effort by <strong>building <code>libbarretenberg.a</code> within the same CI pipeline and releasing them</strong>. Since <a href="https://noir-lang.org/docs/dev/getting_started/quick_start#proving-backend-1" target="_blank" rel="noopener noreferrer"><code>bb</code></a> depends on these static libraries, they would naturally be compiled as part of that process. These prebuilt artifacts could then be published alongside the bb binary. This would allow downstream consumers like <code>noir-rs</code> to directly use the published binaries, eliminating the need to maintain custom build scripts or host binaries separately.</p>
<div class="theme-admonition theme-admonition-note admonition_iIk3 alert alert--secondary"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_S87P"><p><code>bb</code> is a CLI tool that generates proofs directly in the user's terminal. Underneath, it relies on <code>libbarretenberg.a</code>—a static library that exposes low-level function APIs for proof generation, e.g.</p><div class="language-cpp codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-cpp codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">acir_prove_ultra_honk</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> acir_vec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                           </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> recursive</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                           </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">const</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> witness_vec</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">                           </span><span class="token keyword" style="font-style:italic">uint8_t</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token plain"> out</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>When properly linked, this library can be used directly within a Rust program, enabling seamless integration of the proving system into native applications.</p></div></div>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="go-mobile-native">Go Mobile-Native<a href="https://zkmopro.org/blog/noir-integraion#go-mobile-native" class="hash-link" aria-label="Direct link to Go Mobile-Native" title="Direct link to Go Mobile-Native">​</a></h2>
<p>Once we have the Rust crate ready, integrating it into a Mopro project allows us to easily generate bindings for both iOS and Android. Mopro significantly improves the mobile integration experience in the following ways:</p>
<ol>
<li>
<p><strong>No additional Swift or Kotlin bindings required</strong></p>
<p>Unlike the zkPassport team's approach—where they built separate <a href="https://github.com/Swoir/Swoir" target="_blank" rel="noopener noreferrer"><code>Swoir</code></a> and <a href="https://github.com/madztheo/noir_android" target="_blank" rel="noopener noreferrer"><code>noir_android</code></a> libraries with custom domain-specific interfaces—Mopro leverages <a href="https://github.com/mozilla/uniffi-rs" target="_blank" rel="noopener noreferrer">uniffi</a> to automatically generate language bindings. This means developers don’t need to manually maintain Swift <sup><a href="https://zkmopro.org/blog/noir-integraion#user-content-fn-1-d2afeb" id="user-content-fnref-1-d2afeb" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup> or Kotlin wrappers <sup><a href="https://zkmopro.org/blog/noir-integraion#user-content-fn-2-d2afeb" id="user-content-fnref-2-d2afeb" data-footnote-ref="true" aria-describedby="footnote-label">2</a></sup>; they can directly import and use the generated bindings in their mobile codebases. e.g.</p>
<p>For swift:</p>
<div class="language-swift codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-swift codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> moproFFI</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> proofData </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">try</span><span class="token operator" style="color:rgb(137, 221, 255)">!</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">generateNoirProof</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">circuitPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> zkemailCircuitPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> srsPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> zkemailSrsPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>For kotlin:</p>
<div class="language-kotlin codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-kotlin codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">import</span><span class="token plain"> uniffi</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">mopro</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token plain">generateNoirProof</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">let proofData </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">generateNoirProof</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">circuitFile</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> srsPath</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> inputs</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
</li>
<li>
<p><strong>Framework-Agnostic Design</strong></p>
<p>Mopro is not tied to any specific mobile framework. By defining reusable templates for each target framework, developers are free to choose the environment they’re most comfortable with. Currently, Mopro supports <em>native iOS (Xcode + Swift)</em>, <em>native Android (Android Studio + Kotlin)</em>, as well as cross-platform frameworks like <em>React Native</em> and <em>Flutter</em>—offering maximum flexibility and accessibility for diverse developer needs.</p>
</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="mopro-support">Mopro Support<a href="https://zkmopro.org/blog/noir-integraion#mopro-support" class="hash-link" aria-label="Direct link to Mopro Support" title="Direct link to Mopro Support">​</a></h3>
<p>We’ve successfully integrated Noir proving into both Mopro-FFI and the Mopro CLI. You can now install the Mopro CLI and follow the steps in the <a href="https://zkmopro.org/docs/getting-started">Getting Started</a> guide to create a Noir project. Simply replace the SRS and circuit files with your own, and provide your custom circuit input — that’s it!</p>
<p>We’ve also provided an example zkEmail repository featuring a Noir circuit</p>
<ul>
<li>mopro-zkemail-nr: <a href="https://github.com/zkmopro/mopro-zkemail-nr" target="_blank" rel="noopener noreferrer">https://github.com/zkmopro/mopro-zkemail-nr</a></li>
</ul>
<p>along with a NoirHack workshop video.</p>
<p align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/UrT2x3JSKFg?si=OdRMi93vIBu9lSRn" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></p>
<p>Feel free to explore the repo, clone it, and follow along to learn how to use Noir with Mopro in a real-world application.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="challenge-cross-platform-support-is-limited">Challenge: Cross-platform support is limited<a href="https://zkmopro.org/blog/noir-integraion#challenge-cross-platform-support-is-limited" class="hash-link" aria-label="Direct link to Challenge: Cross-platform support is limited" title="Direct link to Challenge: Cross-platform support is limited">​</a></h3>
<p>Our current implementation draws heavily from the zkPassport team’s work, which currently supports iOS devices and ARM64 Android devices/emulators. However, there are limitations: iOS simulators—essential for efficient development and testing—are not supported, and many Android developers (especially those using Windows with WSL) rely on x86_64 emulators. Even CI environments like GitHub Actions commonly use x86_64 Android emulators.</p>
<table><thead><tr><th>Platforms</th><th>Current support target</th><th>Support</th></tr></thead><tbody><tr><td>iOS</td><td><code>aarch64-apple-ios</code></td><td>✅</td></tr><tr><td>iOS</td><td><code>aarch64-apple-ios-sim</code></td><td>✅</td></tr><tr><td>iOS</td><td><code>x86_64-apple-ios</code></td><td>✅</td></tr><tr><td>Android</td><td><code>x86_64-linux-android</code></td><td>✅</td></tr><tr><td>Android</td><td><code>i686-linux-android</code></td><td>❌</td></tr><tr><td>Android</td><td><code>armv7-linux-androideabi</code></td><td>❌</td></tr><tr><td>Android</td><td><code>aarch64-linux-androids</code></td><td>✅</td></tr><tr><td>MacOS</td><td><code>stable-aarch64-apple-darwin</code></td><td>✅</td></tr><tr><td>Linux</td><td><code>x86_64-unknown-linux-gnu</code></td><td>✅</td></tr><tr><td>Windows</td><td><code>x86_64-pc-windows-msvc</code></td><td>❌</td></tr></tbody></table>
<p>Expanding support to these platforms is a significant challenge. The <code>barretenberg</code> backend, written in C++ and built with CMake, is large and complex, making cross-compilation non-trivial. While we’re evaluating the effort required to support additional architectures, we’re also hopeful that the Noir or zkPassport teams may address this gap in the future.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="case-study-stealthnote-mobile-app">Case Study: Stealthnote Mobile App<a href="https://zkmopro.org/blog/noir-integraion#case-study-stealthnote-mobile-app" class="hash-link" aria-label="Direct link to Case Study: Stealthnote Mobile App" title="Direct link to Case Study: Stealthnote Mobile App">​</a></h2>
<p>During the NoirHack 2025 (April 14th to May 10th), the Mopro team participated by building a mobile-native Noir application. Our project was inspired by <a href="https://stealthnote.xyz/" target="_blank" rel="noopener noreferrer"><strong>Stealthnote</strong></a>, originally created by Saleel. Stealthnote is a web-based app that allows users to sign in with Google OAuth and prove ownership of their organizational email address. It leverages a Noir circuit to generate a zero-knowledge proof from the JWT issued by Google OAuth. You can read more about the original project in <a href="https://saleel.xyz/blog/stealthnote/" target="_blank" rel="noopener noreferrer">Saleel’s blog post</a>.</p>
<p>We aim to enhance performance and user experience by building a fully native mobile app. At the same time, we want to demonstrate that the current Mopro + Noir stack is fully capable of supporting mobile-native development. Therefore, we decided to build a mobile version of Stealthnote.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="what-we-built">What We Built<a href="https://zkmopro.org/blog/noir-integraion#what-we-built" class="hash-link" aria-label="Direct link to What We Built" title="Direct link to What We Built">​</a></h3>
<p>During the hackathon, we developed</p>
<ul>
<li>Stealthnote Mobile: <a href="https://github.com/vivianjeng/stealthnote-mobile" target="_blank" rel="noopener noreferrer">https://github.com/vivianjeng/stealthnote-mobile</a></li>
<li>Testflight for iOS: <a href="https://testflight.apple.com/join/8hqYwe8C" target="_blank" rel="noopener noreferrer">download</a></li>
<li>Android APK: <a href="https://drive.google.com/file/d/1IMsH0fBpaLGkFgFX0oqnlS6LQk3WCr3t/view?usp=sharing" target="_blank" rel="noopener noreferrer">download</a></li>
</ul>
<p>A mobile-native frontend for the original Stealthnote project. To maintain compatibility, we kept the original Noir circuits and backend logic intact, focusing entirely on building the mobile frontend. Even so, creating a mobile frontend involved tackling many challenges across platforms.</p>
<p>We chose Flutter as our cross-platform framework because of its fast build times compared to React Native and its rich ecosystem of packages for mobile functionalities—such as Google authentication, biometric login, camera access, gallery browsing, and file storage. In our view, <em>Flutter is currently the best choice for building high-performance, cross-platform apps</em>.</p>
<p>Our architecture separates responsibilities clearly:</p>
<ul>
<li>
<p><strong>Flutter</strong> handles:</p>
<ol>
<li>UI/UX design</li>
<li>Google OAuth authentication</li>
<li>Backend API communication</li>
</ol>
</li>
<li>
<p><strong>Rust</strong> handles:</p>
<ol>
<li>Parsing the JWT and converting it into the structured inputs required by the Noir circuit</li>
<li>Generating and verifying Noir proofs</li>
<li>Ephemeral key generation using Ed25519 and Poseidon2<!-- -->
<br>
</li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>To learn how to integrate Mopro Rust bindings into a Flutter project, please refer to the</p><ul>
<li><a href="https://zkmopro.org/docs/setup/flutter-setup">Flutter Setup</a> section</li>
<li><a href="https://github.com/zkmopro/flutter-app" target="_blank" rel="noopener noreferrer">flutter-app</a> example</li>
<li><a href="https://github.com/zkmopro/mopro_flutter_package" target="_blank" rel="noopener noreferrer">mopro_flutter_package</a> plugin example</li>
</ul></div></div>
</li>
</ul>
<p>This clear separation lets us leverage the strengths of both technologies—Flutter’s frontend speed and flexibility, and Rust’s performance and security for core cryptographic logic.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="benchmark">Benchmark<a href="https://zkmopro.org/blog/noir-integraion#benchmark" class="hash-link" aria-label="Direct link to Benchmark" title="Direct link to Benchmark">​</a></h3>
<p>While the Mopro team anticipated the outcome, the benchmark results are still notable: a mobile-native prover delivers performance up to <strong>10 times faster</strong> than running the same proof in a browser environment.</p>
<table><thead><tr><th>JWT Operation</th><th>Prove</th><th>Verify</th></tr></thead><tbody><tr><td>Browser</td><td>37.292 s</td><td>0.286 s</td></tr><tr><td>Desktop (Mac M1 Pro)</td><td>2.02 s</td><td>0.007 s</td></tr><tr><td>Android emulator (Pixel 8)</td><td>4.786 s</td><td>3.013 s</td></tr><tr><td>iPhone 16 Pro</td><td>2.626 s</td><td>1.727 s</td></tr></tbody></table>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="challenge-insufficient-rust-tooling-and-sdks">Challenge: Insufficient Rust tooling and SDKs.<a href="https://zkmopro.org/blog/noir-integraion#challenge-insufficient-rust-tooling-and-sdks" class="hash-link" aria-label="Direct link to Challenge: Insufficient Rust tooling and SDKs." title="Direct link to Challenge: Insufficient Rust tooling and SDKs.">​</a></h3>
<p>One of the main challenges we faced was converting all the TypeScript functions used in StealthNote into their Rust equivalents. For standard cryptographic operations like Ed25519 signatures, this was relatively straightforward — Rust has mature libraries we could rely on.</p>
<p>However, <strong>Poseidon2</strong> presented a bigger challenge. Unlike the original Poseidon hash, Poseidon2 is a newer variant used in the Noir circuits. Unfortunately, there was no direct Rust implementation available. The Noir team primarily supports a TypeScript version via <a href="https://www.npmjs.com/package/@aztec/bb.js" target="_blank" rel="noopener noreferrer"><code>bb.js</code></a>, which is a wrapper around Barretenberg's C++ implementation compiled to WASM. This made it hard to fully understand the logic and port it to Rust.</p>
<p>Eventually, we located the <a href="https://github.com/noir-lang/poseidon/blob/master/src/poseidon2.nr" target="_blank" rel="noopener noreferrer">Poseidon2 Noir circuit</a> and its <a href="https://github.com/noir-lang/noir/blob/master/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs" target="_blank" rel="noopener noreferrer">permutation logic</a> in the noir-lang repository. Using that as a reference, we implemented our own version of</p>
<ul>
<li>poseidon2.rs in Rust: <a href="https://github.com/vivianjeng/stealthnote-mobile/blob/main/src/proof/poseidon2.rs" target="_blank" rel="noopener noreferrer">https://github.com/vivianjeng/stealthnote-mobile/blob/main/src/proof/poseidon2.rs</a></li>
</ul>
<p>aligning it with the Noir circuit to ensure compatibility.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="suggestions-1">Suggestions<a href="https://zkmopro.org/blog/noir-integraion#suggestions-1" class="hash-link" aria-label="Direct link to Suggestions" title="Direct link to Suggestions">​</a></h3>
<p>Since Barretenberg already provides a C++ implementation for most Noir circuit functions, it would actually be more efficient and sustainable for the Noir or Aztec team to maintain a native C++ or Rust SDK for Rust developers — rather than focusing primarily on the JavaScript (<a href="https://www.npmjs.com/package/@aztec/bb.js" target="_blank" rel="noopener noreferrer"><code>bb.js</code></a>) interface.</p>
<p>As we suggested earlier, an ideal solution would be for the team to publish precompiled static binaries (e.g. <code>libbarretenberg.a</code>) alongside versioned releases. This way, other developers — like us — could easily integrate these binaries into their Rust projects using proper FFI bindings.</p>
<p>This approach would remove the need for every developer to rebuild Barretenberg from source or maintain their own custom builds. For example, our <a href="https://github.com/zkmopro/noir-rs/tree/main/bb" target="_blank" rel="noopener noreferrer"><code>bb.rs</code></a> project demonstrates this idea, but it still lacks many of the standard library features available in bb.js, such as: poseidon2, blake2, AES encryption/decryption. Providing official support for these as native libraries would greatly improve the developer experience and adoption for building performant, <strong>mobile-native</strong> or <strong>server-side</strong> ZK applications.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="conclusion">Conclusion<a href="https://zkmopro.org/blog/noir-integraion#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion">​</a></h2>
<p>We began integrating Noir with Mopro in early April, and the process progressed smoothly — thanks in large part to the foundational work done by the zkPassport team. In contrast to our experience with the Circom prover (which took several months to make mobile-compatible), Noir integration was faster and more straightforward.</p>
<p>However, we also discovered significant gaps in Noir’s current support for mobile-native development. While the prover itself is crucial, a complete SDK ecosystem is equally important. At the moment, teams aiming to build mobile-native apps — like zkPassport — must develop many of the components from scratch, rather than being able to rely on official tooling or prebuilt libraries from the Noir team.</p>
<p>This raises a challenge: <em>what if a project doesn’t have the expertise, time, or resources to build and maintain a mobile-native infrastructure?</em> These barriers can prevent great ideas from becoming usable apps.</p>
<p>There’s still a long road ahead — for both the Mopro team and the Noir ecosystem — to provide a full suite of mobile-native infrastructure that can empower ZK developers, whether they're building the next zkPassport or an entirely new kind of app.</p>
<p>Finally, we want to encourage more developers to explore building mobile-native apps. These platforms offer better performance, deeper device integration, and more seamless user experiences — which are essential if you want your ZK project to truly reach users.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="contribution">Contribution<a href="https://zkmopro.org/blog/noir-integraion#contribution" class="hash-link" aria-label="Direct link to Contribution" title="Direct link to Contribution">​</a></h2>
<p>All kinds of contributions are welcome! 🎉</p>
<p>Feel free to check out the current issues on the <a href="https://github.com/zkmopro/mopro/issues" target="_blank" rel="noopener noreferrer">Mopro GitHub repository</a>, or open a new issue if you notice something missing or have ideas to improve the project.</p>
<p>Here are some current Noir-related issues worth exploring:</p>
<ul>
<li><a href="https://github.com/zkmopro/mopro/issues/423" target="_blank" rel="noopener noreferrer">[Noir] Download SRS script #423</a></li>
<li><a href="https://github.com/zkmopro/mopro/issues/422" target="_blank" rel="noopener noreferrer">[Noir] separating proof bytes and public signals bytes #422</a></li>
<li><a href="https://github.com/zkmopro/mopro/issues/410" target="_blank" rel="noopener noreferrer">[Noir] React native template update #410</a></li>
<li><a href="https://github.com/zkmopro/mopro/issues/409" target="_blank" rel="noopener noreferrer">[Noir] Flutter template update #409</a></li>
</ul>
<p>Feel free to reach out to the Mopro team on Telegram <a href="https://t.me/zkmopro" target="_blank" rel="noopener noreferrer">@zkmopro</a> for questions or support, and follow us on X (formerly Twitter) <a href="https://x.com/zkmopro" target="_blank" rel="noopener noreferrer">@zkmopro</a> to stay up to date with our latest progress!</p>
<!-- -->
<section data-footnotes="true" class="footnotes"><h2 class="anchor anchorWithStickyNavbar_wa2S sr-only" id="footnote-label">Footnotes<a href="https://zkmopro.org/blog/noir-integraion#footnote-label" class="hash-link" aria-label="Direct link to Footnotes" title="Direct link to Footnotes">​</a></h2>
<ol>
<li id="user-content-fn-1-d2afeb">
<p>Noir Swift wrapper: <a href="https://github.com/Swoir/Swoir/blob/main/Sources/Swoir/Circuit.swift" target="_blank" rel="noopener noreferrer">https://github.com/Swoir/Swoir/blob/main/Sources/Swoir/Circuit.swift</a> <a href="https://zkmopro.org/blog/noir-integraion#user-content-fnref-1-d2afeb" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2-d2afeb">
<p>Noir Kotlin wrapper: <a href="https://github.com/madztheo/noir_android/blob/main/lib/src/main/java/com/noirandroid/lib/Circuit.kt" target="_blank" rel="noopener noreferrer">https://github.com/madztheo/noir_android/blob/main/lib/src/main/java/com/noirandroid/lib/Circuit.kt</a> <a href="https://zkmopro.org/blog/noir-integraion#user-content-fnref-2-d2afeb" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content>
        <author>
            <name>Vivian Jeng</name>
            <uri>https://github.com/vivianjeng</uri>
        </author>
        <category label="noir" term="noir"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[A Developer Experience Retrospective: Integrating MoPro with Anon Aadhaar]]></title>
        <id>https://zkmopro.org/blog/2025-mopro-integration-retrospective</id>
        <link href="https://zkmopro.org/blog/2025-mopro-integration-retrospective"/>
        <updated>2025-05-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This is a developer experience blog from the Anon Aadhaar team lead, Vikas integrating MoPro.]]></summary>
        <content type="html"><![CDATA[<p><em>This is a developer experience blog from the Anon Aadhaar team lead, Vikas integrating MoPro.</em></p>
<p>For a long time, mobile proving has been tough for developers, especially with zero-knowledge proofs. Developers often spend more time running circuits on mobile than actually building them. A major factor for improving UX will be how quickly we can integrate these proving circuits into mobile apps.</p>
<p><img decoding="async" loading="lazy" src="https://github.com/user-attachments/assets/07b43732-9ce3-42b2-a196-45c88ace3c6d" alt="meme" class="img_KKSl"></p>
<p>Most mobile proving applications have historically used the Rapidsnark/Witnesscal setup, which is quite messy. It requires a good amount of app development experience, raising questions about how many code changes are needed compared to newer setups like Mopro.</p>
<p>In my past experience, it took me 15 days to figure out how to set up proving circuits on Android. One frustrating part of the Witnesscal/Rapidsnark setup is that you need to write native code for each platform. For binding functions in C++, you still have to write Java for Android and Objective-C for iOS. Recently, React Native has gotten some good updates with Turbo Repo architecture, which I thought could be a solution for this — <a href="https://hackmd.io/@0xvikasrushi/BJhNL6g_yl" target="_blank" rel="noopener noreferrer">https://hackmd.io/@0xvikasrushi/BJhNL6g_yl</a></p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="integration-time">Integration Time<a href="https://zkmopro.org/blog/2025-mopro-integration-retrospective#integration-time" class="hash-link" aria-label="Direct link to Integration Time" title="Direct link to Integration Time">​</a></h2>
<p>How long does it take for a developer to integrate and run a circuit on mobile?</p>
<ul>
<li>You still need to write some native code, but it’s minimal. Mopro’s unified Rust bindings are shipped via a Kotlin package, so it’s pretty much plug-and-play. For Android, it’s fairly simple — no need to write any C++ code. :)</li>
<li>Developers should focus on writing circuits rather than building an SDK. I was happy to only need to spend 3–4 days putting together a basic SDK that can generate and verify proofs—no need for a fancy frontend.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="code-changes-required">Code Changes Required<a href="https://zkmopro.org/blog/2025-mopro-integration-retrospective#code-changes-required" class="hash-link" aria-label="Direct link to Code Changes Required" title="Direct link to Code Changes Required">​</a></h2>
<p>How many files need to be modified? Is the integration minimal or does it require significant changes?</p>
<ul>
<li>Previously, we had a <code>cpp/</code> folder filled with obscure, hard-to-maintain code — two types of binding, different binaries for witness calculation, and a complicated setup process. Now, that’s all automated, and integration is much smoother.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="cross-platform-support">Cross-Platform Support<a href="https://zkmopro.org/blog/2025-mopro-integration-retrospective#cross-platform-support" class="hash-link" aria-label="Direct link to Cross-Platform Support" title="Direct link to Cross-Platform Support">​</a></h2>
<p>Does it require writing platform-specific code (e.g., Java for Android, Objective-C for iOS), or is it abstracted away?</p>
<ul>
<li>Yes, but only a very small amount of platform-specific code is needed.</li>
<li>In this particular integration, we were able to go from 700 lines to less than 100 lines of actual code.</li>
<li>These ~100 lines are native <a href="https://github.com/anon-aadhaar/anon-aadhaar-react-native/blob/14dceaa630af5f8679d166b0738feaa97862e2a8/android/src/main/java/com/rapidsnark/RapidsnarkModule.kt" target="_blank" rel="noopener noreferrer">Kotlin logic</a> used to bind native code to JavaScript, and the implementation is clean and generic enough.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="benchmark-performance">Benchmark Performance<a href="https://zkmopro.org/blog/2025-mopro-integration-retrospective#benchmark-performance" class="hash-link" aria-label="Direct link to Benchmark Performance" title="Direct link to Benchmark Performance">​</a></h2>
<p>How do Mopro’s binding benchmarks compare to implementations like Anon Aadhaar?</p>
<ul>
<li>There was approximately a ~7% improvement in Execution time</li>
</ul>
<div class="language-jsx codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-jsx codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token constant" style="color:rgb(130, 170, 255)">LOG</span><span class="token plain">  </span><span class="token maybe-class-name">Mopro</span><span class="token plain"> </span><span class="token maybe-class-name">Execution</span><span class="token plain"> time</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">3957</span><span class="token plain"> milliseconds</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token constant" style="color:rgb(130, 170, 255)">LOG</span><span class="token plain">  </span><span class="token maybe-class-name">Rapidsnark</span><span class="token plain"> </span><span class="token maybe-class-name">Execution</span><span class="token plain"> time</span><span class="token operator" style="color:rgb(137, 221, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(247, 140, 108)">4220</span><span class="token plain"> milliseconds</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h1>Conclusion</h1>
<p>All in all, integrating Mopro with Anon Aadhaar allowed for:</p>
<ul>
<li><strong>Fast proving with latest bindings + RapidSnark:</strong> Uses modern proving setups under the hood, so things run fast and smooth.</li>
<li><strong>Easy to plug in, great dev experience:</strong> Just works out of the box. We didn’t have to fight the SDK—focus stayed on writing circuits.</li>
<li><strong>Cross-platform code:</strong> Local, browser, or cloud—same code works across all platforms.</li>
<li><strong>Less platform-specific code:</strong> Code is clean and portable—no hacks or infra-specific tweaks needed.</li>
<li><strong>Future scaling:</strong> Architecture is solid. Easy to grow, optimize, and plug into bigger systems.</li>
</ul>]]></content>
        <author>
            <name>Vikas Rushi</name>
            <uri>https://github.com/0xvikasrushi</uri>
        </author>
        <category label="mopro" term="mopro"/>
        <category label="mobile-proving" term="mobile-proving"/>
        <category label="zkp" term="zkp"/>
        <category label="developer-experience" term="developer-experience"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Integrating Mopro Native Packages Across Mobile Platforms]]></title>
        <id>https://zkmopro.org/blog/mopro-native-packages</id>
        <link href="https://zkmopro.org/blog/mopro-native-packages"/>
        <updated>2025-05-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[TL; DR Mopro now ships pre-built native packages for Swift (iOS), Kotlin (Android), Flutter, and React Native.]]></summary>
        <content type="html"><![CDATA[<blockquote>
<p><strong>TL; DR</strong> Mopro now ships pre-built native packages for Swift (iOS), Kotlin (Android), Flutter, and React Native.<br>
<!-- -->Just one import and one build. Proving made simple!</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="announcing-mopro-native-packages">Announcing Mopro Native Packages<a href="https://zkmopro.org/blog/mopro-native-packages#announcing-mopro-native-packages" class="hash-link" aria-label="Direct link to Announcing Mopro Native Packages" title="Direct link to Announcing Mopro Native Packages">​</a></h2>
<p>We're excited to launch Mopro native packages, enabling developers to effortlessly generate and verify zero-knowledge proofs (ZKPs) directly on mobile devices. These native packages leverage Rust's performance and seamlessly integrate with popular mobile frameworks. Built using the Mopro CLI, they're available for direct import via each platform's package manager.</p>
<p>You can also easily create your own customized native packages by following <a href="https://zkmopro.org/docs/getting-started" target="_blank" rel="noopener noreferrer">zkmopro-docs</a>.</p>
<table><thead><tr><th>Framework</th><th>Package Manager</th><th>Default Packages</th><th>zkEmail Packages via Mopro</th></tr></thead><tbody><tr><td><strong>Swift (iOS)</strong></td><td>Xcode / SwiftPM / CocoaPods</td><td><a href="https://github.com/zkmopro/mopro-swift-package" target="_blank" rel="noopener noreferrer">mopro-swift-package</a></td><td><a href="https://github.com/zkmopro/zkemail-swift-package" target="_blank" rel="noopener noreferrer">zkemail-swift-package</a></td></tr><tr><td><strong>Kotlin (Android)</strong></td><td>JitPack</td><td><a href="https://github.com/zkmopro/mopro-kotlin-package" target="_blank" rel="noopener noreferrer">mopro-kotlin-package</a></td><td><a href="https://github.com/zkmopro/zkemail-kotlin-package" target="_blank" rel="noopener noreferrer">zkemail-kotlin-package</a></td></tr><tr><td><strong>Flutter</strong></td><td>pub.dev</td><td><a href="https://github.com/zkmopro/mopro_flutter_package" target="_blank" rel="noopener noreferrer">mopro_flutter_package</a></td><td><a href="https://github.com/zkmopro/zkemail_flutter_package" target="_blank" rel="noopener noreferrer">zkemail_flutter_package</a></td></tr><tr><td><strong>React Native</strong></td><td>npm / yarn</td><td><a href="https://github.com/zkmopro/mopro-react-native-package" target="_blank" rel="noopener noreferrer">mopro-react-native-package</a></td><td><a href="https://github.com/zkmopro/zkemail-react-native-package" target="_blank" rel="noopener noreferrer">zkemail-react-native-package</a></td></tr></tbody></table>
<p>This blog provides a quick guide on integrating these packages into your projects, outlines how we built them (so you can customize your own), addresses challenges we overcame, and highlights future developments. Let's get started!</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="import-build-prove---thats-it">Import, Build, Prove - That's It<a href="https://zkmopro.org/blog/mopro-native-packages#import-build-prove---thats-it" class="hash-link" aria-label="Direct link to Import, Build, Prove - That's It" title="Direct link to Import, Build, Prove - That's It">​</a></h2>
<p>Mopro's native packages simplify the integration process dramatically. Unlike the traditional approach that requires crafting APIs, generating bindings, and manually building app templates, these pre-built packages allow developers to import them directly via package managers and immediately begin developing application logic.</p>
<p>For ZK projects, converting your Rust-based solutions into mobile-native packages is straightforward with Mopro. Our guide on <a href="https://github.com/zkmopro/mopro-swift-package?tab=readme-ov-file#how-to-build-the-package" target="_blank" rel="noopener noreferrer">"How to Build the Package"</a> explains the process clearly.</p>
<p>For instance, our zkEmail native packages were created by first <a href="https://github.com/zkmopro/mopro-zkemail-nr/blob/main/src/lib.rs" target="_blank" rel="noopener noreferrer">defining ZK proving and verification APIs in Rust</a>, generating bindings with <code>mopro build</code>, and embedding these into native packages. The circuit is the header-only proof from <a href="https://github.com/Mach-34/zkemail.nr_header_demo" target="_blank" rel="noopener noreferrer">zkemail.nr_header_demo</a>.</p>
<p>Here's how zkEmail performs on Apple M3 chips:</p>
<table><thead><tr><th>zkEmail Operation</th><th>iOS, Time (ms)</th><th>Android, Time (ms)</th></tr></thead><tbody><tr><td>Proof Generation</td><td>1,309</td><td>3,826</td></tr><tr><td>Verification</td><td>962</td><td>2,857</td></tr></tbody></table>
<p align="center"></p><table><tbody><tr><td align="center"><a href="https://zkmopro.org/img/zkemail-flutter-app-ios.png" target="_blank" rel="noopener noreferrer"><img src="https://zkmopro.org/img/zkemail-flutter-app-ios.png" alt="iOS zkEmail App Example" width="300"></a><br><sub><b>iOS</b></sub></td><td align="center"><a href="https://zkmopro.org/img/zkemail-flutter-app-android.png" target="_blank" rel="noopener noreferrer"><img src="https://zkmopro.org/img/zkemail-flutter-app-android.png" alt="Android zkEmail App Example" width="300"></a><br><sub><b>Android</b></sub></td></tr></tbody></table><p></p><p align="center"><em>Flutter App for iOS &amp; Android zkEmail Example</em></p><p></p>
<p>Notice that, with Mopro and the use of <a href="https://github.com/zkmopro/noir-rs" target="_blank" rel="noopener noreferrer">Noir-rs</a>, we port zkEmail into native packages while keeping the proof size align with Noir's Barretenberg backend CLI. It transfers the API logic directly to mobile platforms with no extra work or glue code needed!</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="how-it-worked-before-mopro">How it worked before Mopro<a href="https://zkmopro.org/blog/mopro-native-packages#how-it-worked-before-mopro" class="hash-link" aria-label="Direct link to How it worked before Mopro" title="Direct link to How it worked before Mopro">​</a></h3>
<p>Previously, integrating ZKPs into mobile applications involved more manual work and platform-specific implementations. For example, developers might have used solutions like:</p>
<ul>
<li><strong>Swoir:</strong> <a href="https://github.com/Swoir/Swoir/tree/main" target="_blank" rel="noopener noreferrer">https://github.com/Swoir/Swoir/tree/main</a></li>
<li><strong>noir-android:</strong> <a href="https://github.com/madztheo/noir_android/tree/main" target="_blank" rel="noopener noreferrer">https://github.com/madztheo/noir_android/tree/main</a></li>
</ul>
<p>These approaches often required developers to handle bridging code and manage dependencies separately for each platform, unlike the streamlined process Mopro now offers.</p>
<p>With Mopro, developers can leverage pre-built native packages and import them directly via package managers. This, combined with automated binding generation, significantly reduces the need for manual API crafting and platform-specific glue code.</p>
<p>While developers still write their application logic using platform-specific languages, Mopro simplifies the integration of core ZK functionalities, especially when leveraging Rust's extensive cryptography ecosystem.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="under-the-hood">Under The Hood<a href="https://zkmopro.org/blog/mopro-native-packages#under-the-hood" class="hash-link" aria-label="Direct link to Under The Hood" title="Direct link to Under The Hood">​</a></h2>
<p>Developing native packages involved tackling several technical challenges to ensure smooth and efficient operation across different platforms.</p>
<p>This section dives into two key challenges we addressed:</p>
<ol>
<li>Optimizing static library sizes for iOS to manage package distribution and download speeds.</li>
<li>Ensuring compatibility with Android's release mode to prevent runtime errors due to code shrinking.</li>
</ol>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="optimizing-static-library-sizes-for-ios">Optimizing Static Library Sizes for iOS<a href="https://zkmopro.org/blog/mopro-native-packages#optimizing-static-library-sizes-for-ios" class="hash-link" aria-label="Direct link to Optimizing Static Library Sizes for iOS" title="Direct link to Optimizing Static Library Sizes for iOS">​</a></h3>
<h4 class="anchor anchorWithStickyNavbar_wa2S" id="why-static-linking">Why Static Linking?<a href="https://zkmopro.org/blog/mopro-native-packages#why-static-linking" class="hash-link" aria-label="Direct link to Why Static Linking?" title="Direct link to Why Static Linking?">​</a></h4>
<p>UniFFI exports Swift bindings as a static archive (<code>libmopro_bindings.a</code>). Static linking ensures all Rust symbols are available at link-time, simplifying Xcode integration. However, it bundles all Rust dependencies (Barretenberg Backend, rayon, big-integer math), resulting in larger archive sizes.</p>
<h4 class="anchor anchorWithStickyNavbar_wa2S" id="baseline-size">Baseline Size<a href="https://zkmopro.org/blog/mopro-native-packages#baseline-size" class="hash-link" aria-label="Direct link to Baseline Size" title="Direct link to Baseline Size">​</a></h4>
<p>The full build creates an archive around <strong>≈ 153 MB</strong> in size. When uploading files over 100 MB to GitHub, Git LFS takes over by replacing the file with a text pointer in the repository while storing the actual content on a remote server like GitHub.com. This setup can cause issues for package managers that try to fetch the package directly from a GitHub URL for a release publish.</p>
<p>While uploading large files may be acceptable for some package management platforms or remote servers like Cloudflare R2, the large file size slows down:</p>
<ul>
<li>CocoaPods or SwiftPM downloads</li>
<li>CI cache recovery</li>
<li>Cloning the repository, especially on slower connections</li>
</ul>
<h4 class="anchor anchorWithStickyNavbar_wa2S" id="our-solution-zip--unzip-strategy">Our Solution: Zip &amp; Unzip Strategy<a href="https://zkmopro.org/blog/mopro-native-packages#our-solution-zip--unzip-strategy" class="hash-link" aria-label="Direct link to Our Solution: Zip &amp; Unzip Strategy" title="Direct link to Our Solution: Zip &amp; Unzip Strategy">​</a></h4>
<p>To keep development fast and responsive, we compress the entire <code>MoproBindings.xcframework</code> before uploading it to GitHub and publishing it to CocoaPods, reducing its size to about <strong>≈ 41 MB</strong>.</p>
<p>We also found that by customizing <code>script_phase</code> in the <code>.podspec</code> (check our implementation in <a href="https://github.com/zkmopro/zkemail-swift-package/blob/b5c3a94f8580b0332ced2c8409a1017530a56e38/ZKEmailSwift.podspec#L93-L103" target="_blank" rel="noopener noreferrer"><code>ZKEmailSwift.podspec</code></a>), we can unzip the bindings during pod install. This gives us the best of both worlds: (1) smaller packages for distribution and (2) full compatibility with Xcode linking. The added CPU cost is minor compared to the time saved on downloads.</p>
<h4 class="anchor anchorWithStickyNavbar_wa2S" id="comparison-with-android">Comparison With Android<a href="https://zkmopro.org/blog/mopro-native-packages#comparison-with-android" class="hash-link" aria-label="Direct link to Comparison With Android" title="Direct link to Comparison With Android">​</a></h4>
<p>On Android, dynamic <code>.so</code> libraries (around 22 MB in total) are used, with symbols loaded lazily at runtime to keep the package size small. In contrast, because iOS's constraint on third-party Rust dynamic libraries in App Store builds, static linking with compression is currently the most viable option, to the best of our knowledge.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="ensuring-android-release-mode-compatibility">Ensuring Android Release Mode Compatibility<a href="https://zkmopro.org/blog/mopro-native-packages#ensuring-android-release-mode-compatibility" class="hash-link" aria-label="Direct link to Ensuring Android Release Mode Compatibility" title="Direct link to Ensuring Android Release Mode Compatibility">​</a></h3>
<p>Another challenge we tackled was ensuring compatibility with Android's release mode. By default, Android's release build process applies <a href="https://developer.android.com/build/shrink-code" target="_blank" rel="noopener noreferrer">code shrinking and obfuscation</a> to optimize app size. While beneficial for optimization, this process caused a <code>java.lang.UnsatisfiedLinkError</code> for Mopro apps.</p>
<p>The root cause was that code shrinking interfered with <a href="https://mozilla.github.io/uniffi-rs/latest/kotlin/gradle.html#jna-dependency" target="_blank" rel="noopener noreferrer">JNA (Java Native Access)</a>, a crucial dependency for UniFFI, which we use for Rust-to-Kotlin bindings. The shrinking process was removing or altering parts of JNA that were necessary for the bindings to function correctly, leading to the <code>UnsatisfiedLinkError</code> when the app tried to call the native Rust code.</p>
<h4 class="anchor anchorWithStickyNavbar_wa2S" id="the-fix-adjusting-gradle-build-configurations">The Fix: Adjusting Gradle Build Configurations<a href="https://zkmopro.org/blog/mopro-native-packages#the-fix-adjusting-gradle-build-configurations" class="hash-link" aria-label="Direct link to The Fix: Adjusting Gradle Build Configurations" title="Direct link to The Fix: Adjusting Gradle Build Configurations">​</a></h4>
<p>Our solution, as detailed in <a href="https://github.com/zkmopro/mopro/issues/416" target="_blank" rel="noopener noreferrer">GitHub Issue #416</a>, involves a configuration adjustment in the consuming application's <code>android/build.gradle.kts</code> file (or <code>android/app/build.gradle</code> for older Android projects). Developers using Mopro need to explicitly disable code and resource shrinking for their release builds:</p>
<div class="language-kotlin codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-kotlin codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">android </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    buildTypes </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token function" style="color:rgb(130, 170, 255)">getByName</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string-literal singleline string" style="color:rgb(195, 232, 141)">"release"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Disables code shrinking, obfuscation, and optimization for</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// your project's release build type.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            minifyEnabled </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Disables resource shrinking, which is performed by the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Android Gradle plugin.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            shrinkResources </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 88, 116)">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h4 class="anchor anchorWithStickyNavbar_wa2S" id="impact-and-future-considerations">Impact and Future Considerations<a href="https://zkmopro.org/blog/mopro-native-packages#impact-and-future-considerations" class="hash-link" aria-label="Direct link to Impact and Future Considerations" title="Direct link to Impact and Future Considerations">​</a></h4>
<p>This configuration ensures that JNA and, consequently, the UniFFI bindings remain intact, allowing Mopro-powered Android apps to build and run successfully in release mode. This approach aligns with recommendations found in the official Flutter documentation for handling <a href="https://docs.flutter.dev/deployment/android#shrink-your-code-with-r8" target="_blank" rel="noopener noreferrer">similar issues</a>. While this increases the final app size slightly, it guarantees the stability and functionality of the native ZK operations. We are also actively exploring ways to refine this in the future to allow for optimized builds without compromising JNA's functionality.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="the-road-ahead">The Road Ahead<a href="https://zkmopro.org/blog/mopro-native-packages#the-road-ahead" class="hash-link" aria-label="Direct link to The Road Ahead" title="Direct link to The Road Ahead">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="a-manual-tweaks-for-cross-platform-frameworks">a. Manual Tweaks for Cross-Platform Frameworks<a href="https://zkmopro.org/blog/mopro-native-packages#a-manual-tweaks-for-cross-platform-frameworks" class="hash-link" aria-label="Direct link to a. Manual Tweaks for Cross-Platform Frameworks" title="Direct link to a. Manual Tweaks for Cross-Platform Frameworks">​</a></h3>
<p>Cross-platform frameworks like React Native and Flutter require additional glue code to define modules, as they straddle multiple runtimes. Each layer needs its own integration.</p>
<p>For example, in our <a href="https://github.com/zkmopro/zkemail-react-native-package" target="_blank" rel="noopener noreferrer">zkEmail React Native package</a>, we use three separate wrappers.</p>
<ul>
<li>[TypeScript] <a href="https://github.com/zkmopro/zkemail-react-native-package/blob/main/src/MoproReactNativePackageModule.ts" target="_blank" rel="noopener noreferrer"><code>MoproReactNativePackageModule.ts</code></a>: declares the public API and lets the React Native code-gen load the native module.</li>
<li>[Swift] <a href="https://github.com/zkmopro/zkemail-react-native-package/blob/main/ios/MoproReactNativePackageModule.swift" target="_blank" rel="noopener noreferrer"><code>MoproReactNativePackageModule.swift</code></a>: loads bindings into Objective-C–discoverable classes.</li>
<li>[Kotlin] <a href="https://github.com/zkmopro/zkemail-react-native-package/blob/main/android/src/main/java/expo/modules/moproreactnativepackage/MoproReactNativePackageModule.kt" target="_blank" rel="noopener noreferrer"><code>MoproReactNativePackageModule.kt</code></a>: loads bindings and bridges via JNI.</li>
</ul>
<p>Similarly, for our <a href="https://github.com/zkmopro/zkemail_flutter_package" target="_blank" rel="noopener noreferrer">zkEmail Flutter package</a>, a comparable set of wrappers is employed:</p>
<ul>
<li>[Dart] <a href="https://github.com/zkmopro/zkemail_flutter_package/blob/main/lib/zkemail_flutter_package.dart" target="_blank" rel="noopener noreferrer"><code>zkemail_flutter_package.dart</code></a>: defines the public Dart API for the Flutter plugin, invoking methods on the native side via platform channels.</li>
<li>[Swift] <a href="https://github.com/zkmopro/zkemail_flutter_package/blob/main/ios/Classes/ZkemailFlutterPackagePlugin.swift" target="_blank" rel="noopener noreferrer"><code>ZkemailFlutterPackagePlugin.swift</code></a>: calls the underlying Rust-generated Swift bindings.</li>
<li>[Kotlin] <a href="https://github.com/zkmopro/zkemail_flutter_package/blob/main/android/src/main/kotlin/com/zkmopro/zkemail_flutter_package/ZkemailFlutterPackagePlugin.kt" target="_blank" rel="noopener noreferrer"><code>ZkemailFlutterPackagePlugin.kt</code></a>: bridges Dart calls to the Rust-generated Kotlin bindings.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="b-support-for-custom-package-names">b. Support for Custom Package Names<a href="https://zkmopro.org/blog/mopro-native-packages#b-support-for-custom-package-names" class="hash-link" aria-label="Direct link to b. Support for Custom Package Names" title="Direct link to b. Support for Custom Package Names">​</a></h3>
<p>Initially, we encountered naming conflicts when the same XCFramework was used in multiple Xcode projects. Addressing this to allow fully customizable package names is an ongoing effort.</p>
<p>Initial progress was made with updates in <a href="https://github.com/zkmopro/mopro/issues/387" target="_blank" rel="noopener noreferrer">issue#387</a> and a partial fix in <a href="https://github.com/zkmopro/mopro/pull/404" target="_blank" rel="noopener noreferrer">PR#404</a>. Further work to complete this feature is being tracked in <a href="https://github.com/zkmopro/mopro/issues/413" target="_blank" rel="noopener noreferrer">issue#413</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="whats-next-shaping-mopros-future-together">What's Next: Shaping Mopro's Future Together<a href="https://zkmopro.org/blog/mopro-native-packages#whats-next-shaping-mopros-future-together" class="hash-link" aria-label="Direct link to What's Next: Shaping Mopro's Future Together" title="Direct link to What's Next: Shaping Mopro's Future Together">​</a></h2>
<p>Currently, the Mopro CLI helps you create app templates via the <code>mopro create</code> command, once bindings are generated with <code>mopro build</code>.</p>
<p>Our vision is to enhance this by enabling the automatic generation of fully customized native packages. This would include managing all necessary glue code for cross-platform frameworks, potentially through a new command (maybe like <code>mopro pack</code>) or by extending existing commands. We believe this will significantly streamline the developer workflow. If you're interested in shaping this feature, we invite you to check out the discussion and contribute your ideas in <a href="https://github.com/zkmopro/mopro/issues/419" target="_blank" rel="noopener noreferrer">issue #419</a>.</p>
<p>By achieving this, we aim to unlock seamless mobile proving capabilities, simplifying adoption for developers leveraging existing ZK solutions or porting Rust-based ZK projects. Your contributions can help us make mobile ZK development more accessible for everyone!</p>
<p>If you find it interesting, feel free to reach out to the Mopro team on Telegram: <a href="https://t.me/zkmopro" target="_blank" rel="noopener noreferrer">@zkmopro</a>, or better yet, dive into the codebase and open a PR! We're excited to see what the community builds with Mopro.</p>
<p>Happy proving!</p>]]></content>
        <author>
            <name>Moven Tsai</name>
            <uri>https://github.com/moven0831</uri>
        </author>
        <category label="multi-platform" term="multi-platform"/>
        <category label="native-package" term="native-package"/>
        <category label="zkEmail" term="zkEmail"/>
        <category label="noir" term="noir"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[2025 ETHTaipei Workshop]]></title>
        <id>https://zkmopro.org/blog/2025-ethtaipei-workshop</id>
        <link href="https://zkmopro.org/blog/2025-ethtaipei-workshop"/>
        <updated>2025-03-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Overview]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithStickyNavbar_wa2S" id="overview">Overview<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#overview" class="hash-link" aria-label="Direct link to Overview" title="Direct link to Overview">​</a></h2>
<p>This tutorial guides developers through getting started with Mopro and building a native mobile app from scratch. It covers</p>
<ul>
<li>
<p>Setting up an example <a href="https://github.com/zkmopro/circuit-registry/blob/main/multiplier2/multiplier2.circom" target="_blank" rel="noopener noreferrer">multiplier2</a> Circom circuit</p>
<ul>
<li>Starting from <a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#0-prerequisites">0. Prerequisites</a></li>
</ul>
</li>
<li>
<p>Modifying it to use a different circuit, such as <a href="https://github.com/zkmopro/circuit-registry/blob/main/keccak256/keccak256_256_test.circom" target="_blank" rel="noopener noreferrer">keccak256</a></p>
<ul>
<li>Starting from <a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#6-update-circuits">6. Prerequisites</a></li>
</ul>
</li>
<li>
<p>Additionally, we'll integrate the <a href="https://github.com/worldcoin/semaphore-rs" target="_blank" rel="noopener noreferrer">semaphore-rs</a> Rust crate to generate native bindings and run the implementation on both iOS and Android.</p>
</li>
</ul>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>This is a workshop tutorial from <a href="https://ethtaipei.org/" target="_blank" rel="noopener noreferrer">ETHTaipei</a> 2025 in April. If you'd like to follow along and build a native mobile app, please check out this commit: <a href="https://github.com/zkmopro/mopro/tree/085fa41e2e2d0c76036cffceed3661ecd15c5fe7" target="_blank" rel="noopener noreferrer">085fa41</a>.</p></div></div>
<ul>
<li>
<p>We also offer comprehensive iOS and Android tutorials to guide you through the entire process, ensuring you don’t miss anything!</p>
<ul>
<li><strong>iOS</strong></li>
</ul>
<p align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/qPtKTJU1Xfo?si=1a2sh7t24u17lS4r" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></p>
<ul>
<li><strong>Android</strong></li>
</ul>
<p align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/P2Cwqrir_4o?si=ta30AFEf1yBA6MAP" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="0-prerequisites">0. Prerequisites<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#0-prerequisites" class="hash-link" aria-label="Direct link to 0. Prerequisites" title="Direct link to 0. Prerequisites">​</a></h2>
<ul>
<li>XCode or Android Studio<!-- -->
<ul>
<li>If you're using Android Studio, ensure that you follow the <a href="https://zkmopro.org/docs/prerequisites/#android-configuration" target="_blank" rel="noopener noreferrer">Android configuration</a> steps and set the <code>ANDROID_HOME</code> environment variable.</li>
</ul>
</li>
<li>Rust and CMake</li>
</ul>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>Documentation: <a href="https://zkmopro.org/docs/prerequisites" target="_blank" rel="noopener noreferrer">https://zkmopro.org/docs/prerequisites</a></p></div></div>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="1-download-mopro-cli-tool">1. Download Mopro CLI tool<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#1-download-mopro-cli-tool" class="hash-link" aria-label="Direct link to 1. Download Mopro CLI tool" title="Direct link to 1. Download Mopro CLI tool">​</a></h2>
<p>We offer a convenient command-line tool called <code>mopro</code> to streamline the development process. It functions similarly to tools like <code>npx create-react-app</code> or Foundry, enabling developers to get started quickly and efficiently.</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">git</span><span class="token plain"> clone https://github.com/zkmopro/mopro</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">cd</span><span class="token plain"> mopro/cli</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token function" style="color:rgb(130, 170, 255)">cargo</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">--path</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(255, 203, 107)">cd</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">..</span><span class="token plain">/</span><span class="token punctuation" style="color:rgb(199, 146, 234)">..</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="2-initialize-a-project-with-mopro-cli">2. Initialize a project with Mopro CLI<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#2-initialize-a-project-with-mopro-cli" class="hash-link" aria-label="Direct link to 2. Initialize a project with Mopro CLI" title="Direct link to 2. Initialize a project with Mopro CLI">​</a></h2>
<p>The <code>mopro init</code> command helps you create a Rust project designed to generate bindings for both iOS and Android.
This step is similar to running <code>npx create-react-app</code>, so select the directory where you want to create your new app.</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mopro init</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Start by selecting a name for your project (default: <code>mopro-example-app</code>).</p>
<p>Next, choose the proving system that best fits your needs—Mopro currently supports both <code>circom</code> and <code>halo2</code>. For this example, we’ll be using <code>circom</code>.</p>
<p><img decoding="async" loading="lazy" alt="mopro init" src="https://zkmopro.org/assets/images/mopro-init-15ce2cb3c72622fd11b8638d945b3388.jpg" class="img_KKSl"></p>
<p>Next, navigate to your project directory by running:</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token builtin class-name" style="color:rgb(255, 203, 107)">cd</span><span class="token plain"> mopro-example-app</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="3-build-rust-bindings-with-mopro-cli">3. Build Rust bindings with mopro CLI<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#3-build-rust-bindings-with-mopro-cli" class="hash-link" aria-label="Direct link to 3. Build Rust bindings with mopro CLI" title="Direct link to 3. Build Rust bindings with mopro CLI">​</a></h2>
<p><code>mopro build</code> command can help developers build binaries for mobile targets (e.g. iOS and Android devices).</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mopro build</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<ul>
<li>
<p>Choose <code>debug</code> for faster builds during development or <code>release</code> for optimized performance in production.</p>
</li>
<li>
<p>Select the platforms you want to build for: <code>ios</code>, <code>android</code>, <code>web</code>.</p>
</li>
<li>
<p>Select the architecture for each platform:</p>
<ul>
<li><strong>iOS</strong>:</li>
</ul>
</li>
</ul>
<table><thead><tr><th>Architecture</th><th>Description</th><th>Suggested</th></tr></thead><tbody><tr><td><code>aarch64-apple-ios</code></td><td>For physical iOS devices</td><td>✅</td></tr><tr><td><code>aarch64-apple-ios-sim</code></td><td>For M-series Mac simulators</td><td>✅</td></tr><tr><td><code>x86_64-apple-ios</code></td><td>For Intel-based Mac simulators</td><td>-</td></tr></tbody></table>
<ul>
<li><strong>Android</strong>:</li>
</ul>
<table><thead><tr><th>Architecture</th><th>Description</th><th>Suggested</th></tr></thead><tbody><tr><td><code>x86_64-linux-android</code></td><td>For 64-bit Intel architecture (x86_64)</td><td>✅</td></tr><tr><td><code>i686-linux-android</code></td><td>For 32-bit Intel architecture (x86)</td><td>-</td></tr><tr><td><code>armv7-linux-androideabi</code></td><td>For 32-bit ARM architecture (ARMv7-A)</td><td>-</td></tr><tr><td><code>aarch64-linux-android</code></td><td>For 64-bit ARM architecture (ARMv8-A)</td><td>✅</td></tr></tbody></table>
<p><img decoding="async" loading="lazy" alt="mopro build" src="https://zkmopro.org/assets/images/mopro-build-f1378661def0f1ee18e5d52ee6d65408.jpg" class="img_KKSl"></p>
<div class="theme-admonition theme-admonition-warning admonition_iIk3 alert alert--warning"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_S87P"><p>The build process may take a few minutes to complete.</p></div></div>
<p>Next, you will see the following instructions displayed:</p>
<p><img decoding="async" loading="lazy" alt="mopro-build-finish" src="https://zkmopro.org/assets/images/mopro-build-finish-d1bdf80e44ae5a12658564248cbf430a.jpg" class="img_KKSl"></p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="4-create-templates-for-mobile-development">4. Create templates for mobile development<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#4-create-templates-for-mobile-development" class="hash-link" aria-label="Direct link to 4. Create templates for mobile development" title="Direct link to 4. Create templates for mobile development">​</a></h2>
<p><code>mopro create</code> command generates templates for various platforms and integrates bindings into the specified directories.</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mopro create</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Currently supported platforms:</p>
<ul>
<li>iOS (Xcode project)</li>
<li>Android (Android Studio project)</li>
<li>React Native</li>
<li>Flutter</li>
<li>Web</li>
</ul>
<p>After running the <code>mopro create</code> command, a new folder will be created in the current directory, such as:</p>
<ul>
<li><code>ios</code></li>
<li><code>android</code></li>
<li><code>react-native</code></li>
<li><code>flutter</code></li>
<li><code>web</code> (currently does not support Circom prover)</li>
</ul>
<p>You will then see the following instructions to open the project:</p>
<p><img decoding="async" loading="lazy" alt="mopro-create" src="https://zkmopro.org/assets/images/mopro-create-37df526950ca837bf96f459c00053828.jpg" class="img_KKSl"></p>
<p>If you want to create multiple templates, simply run <code>mopro create</code> again and select a different framework each time.</p>
<p><img decoding="async" loading="lazy" alt="mopro-create-android" src="https://zkmopro.org/assets/images/mopro-create-android-4a7cee681deeaa66291b0dbf93f23796.jpg" class="img_KKSl"></p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="5-run-the-app-on-a-devicesimulator">5. Run the app on a device/simulator<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#5-run-the-app-on-a-devicesimulator" class="hash-link" aria-label="Direct link to 5. Run the app on a device/simulator" title="Direct link to 5. Run the app on a device/simulator">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="ios">iOS<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#ios" class="hash-link" aria-label="Direct link to iOS" title="Direct link to iOS">​</a></h3>
<p>Open the Xcode project by running the following command:</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">open</span><span class="token plain"> ios/MoproApp.xcodeproj</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Select the target device and run the project by pressing <code>cmd</code> + <code>R</code>.</p>
<p>Alternatively, you can watch this video to see how to run the app.</p>
<p align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/6TydXwYMQCU?si=9s3B8OVa_eRLzchU&amp;start=100" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="android">Android<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#android" class="hash-link" aria-label="Direct link to Android" title="Direct link to Android">​</a></h3>
<p>Open the Android Studio project by running the following command:</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token function" style="color:rgb(130, 170, 255)">open</span><span class="token plain"> android </span><span class="token parameter variable" style="color:rgb(191, 199, 213)">-a</span><span class="token plain"> Android</span><span class="token punctuation" style="color:rgb(199, 146, 234)">\</span><span class="token plain"> Studio</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Run the project by pressing <code>^</code> + <code>R</code> or <code>ctrl</code> + <code>R</code>.</p>
<p>Alternatively, you can watch this video to see how to run the app.</p>
<p align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/r6WolEEHuMw?si=Lrvkxt03fnqwaYlK&amp;start=196" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="6-update-circuits">6. Update circuits<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#6-update-circuits" class="hash-link" aria-label="Direct link to 6. Update circuits" title="Direct link to 6. Update circuits">​</a></h2>
<p>This section explains how to update circuits with alternative witness generators and corresponding zkey files. We use the <a href="https://github.com/zkmopro/circuit-registry/blob/main/keccak256/keccak256_256_test.circom" target="_blank" rel="noopener noreferrer">Keccak256 circuit</a> as a reference example here.</p>
<ol>
<li>
<p>Add wasm and zkey file in the <code>test-vectors/circom</code> folder</p>
<ul>
<li>wasm: <a href="https://ci-keys.zkmopro.org/keccak256_256_test.wasm" target="_blank" rel="noopener noreferrer">https://ci-keys.zkmopro.org/keccak256_256_test.wasm</a></li>
<li>zkey: <a href="https://ci-keys.zkmopro.org/keccak256_256_test_final.zkey" target="_blank" rel="noopener noreferrer">https://ci-keys.zkmopro.org/keccak256_256_test_final.zkey</a></li>
</ul>
</li>
<li>
<p>In <code>src/lib.rs</code> file, update the circuit's witness generator function definition.</p>
<div class="language-diff codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-diff codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted prefix deleted" style="color:rgb(255, 85, 114)">-</span><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)">  rust_witness::witness!(multiplier2);</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"></span><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)">  rust_witness::witness!(keccak256256test);</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"></span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">mopro_ffi::set_circom_circuits! {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token deleted-sign deleted prefix deleted" style="color:rgb(255, 85, 114)">-</span><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)">    ("multiplier2_final.zkey", WitnessFn::RustWitness(multiplier2_witness))</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"></span><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)">    ("keccak256_256_test_final.zkey", WitnessFn::RustWitness(keccak256256test_witness))</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"></span><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<div class="theme-admonition theme-admonition-warning admonition_iIk3 alert alert--warning"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_S87P"><p>The name should match the lowercase version of the WASM file, with all special characters removed.<br>
e.g.<br>
<code>multiplier2</code> -&gt; <code>multiplier2</code><br>
<code>keccak_256_256_main</code> -&gt; <code>keccak256256main</code><br>
<code>aadhaar-verifier</code> -&gt; <code>aadhaarverifier</code></p></div></div>
</li>
<li>
<p>Similar to <a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#3-build-rust-bindings-with-mopro-cli">Step 3</a>, regenerate the bindings to reflect the updated circuit.</p>
<div class="language-sh codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-sh codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token plain">mopro build</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
</li>
<li>
<p>Manually update the bindings in the app by replacing the existing ones.</p>
<ul>
<li>
<p><strong>iOS:</strong></p>
<ul>
<li>Replace <code>ios/MoproiOSBindings</code> with <code>MoproiOSBindings</code>.</li>
</ul>
</li>
<li>
<p><strong>Android:</strong></p>
<ul>
<li>
<p>Replace <code>android/app/src/main/jniLibs</code> with <code>MoproAndroidBindings/jniLibs</code></p>
</li>
<li>
<p>Replace <code>android/app/src/main/java/uniffi</code> with <code>MoproAndroidBindings/uniffi</code></p>
</li>
</ul>
</li>
</ul>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>We aim to provide the <code>mopro update</code> CLI tool to assist with updating bindings.
Contributions to this effort are welcome.<a href="https://github.com/zkmopro/mopro/issues/269" target="_blank" rel="noopener noreferrer">https://github.com/zkmopro/mopro/issues/269</a></p></div></div>
</li>
<li>
<p>Copy zkeys to assets</p>
<ul>
<li>
<p><strong>iOS:</strong><br>
Open Xcode, drag in the zkeys you plan to use for proving, then navigate to the project’s <strong>Build Phases</strong>. Under <strong>Copy Bundle Resources</strong>, add each zkey to ensure it's included in the app bundle.</p>
<p>Alternatively, you can watch this video to see how to copy zkey in XCode.</p>
<p align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/6TydXwYMQCU?si=QvOxlfbOOyX3GpcM&amp;start=53" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></p>
</li>
<li>
<p><strong>Android</strong><br>
Paste the zkey in the assets folder: <code>android/app/src/main/assets</code>.</p>
<p>Alternatively, you can watch this video to see how to copy zkey in XCode.</p>
<p align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/r6WolEEHuMw?si=aOs7SPz4ajtE1hMP&amp;start=141" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin"></iframe></p>
</li>
</ul>
</li>
<li>
<p>Update circuit input and zkey path</p>
</li>
</ol>
<ul>
<li>
<p>Update <code>zkeyPath</code> to <code>keccak256_256_test_final</code></p>
<ul>
<li><strong>iOS:</strong><br></li>
</ul>
<div class="language-diff codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-diff codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted prefix deleted" style="color:rgb(255, 85, 114)">-</span><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"> private let zkeyPath = Bundle.main.path(forResource: "multiplier2_final", ofType: "zkey")!</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"></span><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"> private let zkeyPath = Bundle.main.path(forResource: "keccak256_256_test_final", ofType: "zkey")!</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<ul>
<li><strong>Android:</strong><br></li>
</ul>
<div class="language-diff codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-diff codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted prefix deleted" style="color:rgb(255, 85, 114)">-</span><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"> val zkeyPath = getFilePathFromAssets("multiplier2_final.zkey")</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"></span><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"> val zkeyPath = getFilePathFromAssets("keccak256_256_test_final.zkey")</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
</li>
<li>
<p>Update circuit inputs: <a href="https://ci-keys.zkmopro.org/keccak256.json" target="_blank" rel="noopener noreferrer">https://ci-keys.zkmopro.org/keccak256.json</a></p>
<ul>
<li><strong>iOS:</strong><br></li>
</ul>
<div class="language-diff codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-diff codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted prefix deleted" style="color:rgb(255, 85, 114)">-</span><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"> let input_str: String = "{\"b\":[\"5\"],\"a\":[\"3\"]}"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"></span><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"> let input_str: String = "{\"in\":[\"0\",\"0\",\"1\",\"0\",\"1\",\"1\",\"1\",\"0\",\"1\",\"0\",\"1\",\"0\",\"0\",\"1\",\"1\",\"0\",\"1\",\"1\",\"0\",\"0\",\"1\",\"1\",\"1\",\"0\",\"0\",\"0\",\"1\",\"0\",\"1\",\"1\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"]}"</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<ul>
<li><strong>Android:</strong><br></li>
</ul>
<div class="language-diff codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-diff codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted prefix deleted" style="color:rgb(255, 85, 114)">-</span><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"> val input_str: String = "{\"b\":[\"5\"],\"a\":[\"3\"]}"</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token deleted-sign deleted line" style="color:rgb(255, 85, 114)"></span><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"> val input_str: String = "{\"in\":[\"0\",\"0\",\"1\",\"0\",\"1\",\"1\",\"1\",\"0\",\"1\",\"0\",\"1\",\"0\",\"0\",\"1\",\"1\",\"0\",\"1\",\"1\",\"0\",\"0\",\"1\",\"1\",\"1\",\"0\",\"0\",\"0\",\"1\",\"0\",\"1\",\"1\",\"1\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\",\"0\"]}"</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
</li>
</ul>
<ol start="7">
<li>Then Run the app again like in <a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#5-run-the-app-on-a-devicesimulator">step 5</a>.</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="7-update-rust-exported-functions">7. Update Rust exported functions<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#7-update-rust-exported-functions" class="hash-link" aria-label="Direct link to 7. Update Rust exported functions" title="Direct link to 7. Update Rust exported functions">​</a></h2>
<p>Currently, only <code>generateCircomProof</code> and <code>verifyCircomProof</code> are available with the bindings, but the bindings can be extended to support nearly all Rust functions.</p>
<p>Here is an example demonstrating how to use the Semaphore crate.</p>
<ol>
<li>
<p>Update <code>Cargo.toml</code></p>
<p>Import semaphore crate from: <a href="https://github.com/worldcoin/semaphore-rs" target="_blank" rel="noopener noreferrer">https://github.com/worldcoin/semaphore-rs</a></p>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>We are using this specific commit <a href="https://github.com/worldcoin/semaphore-rs/tree/340d4ada82da830b07041cf2185aa6fd1c4e2967" target="_blank" rel="noopener noreferrer">340d4ad</a> for <code>semaphore-rs</code>.</p></div></div>
<div class="language-toml codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_ignD">Cargo.toml</div><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-toml codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token key property">semaphore-rs</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"> </span><span class="token key property">git</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"https://github.com/worldcoin/semaphore-rs"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token key property">features</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token string" style="color:rgb(195, 232, 141)">"depth_16"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token key property">rev</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(195, 232, 141)">"340d4ad"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
</li>
<li>
<p>Define a function to generate semaphore proof</p>
<p><a href="https://github.com/chengggkk/Zuma/blob/master/src/lib.rs" target="_blank" rel="noopener noreferrer">Here</a> is an example to define semaphore <code>prove</code> and <code>verify</code> in <code>src/lib.rs</code></p>
<p>Alternatively, we can create a demo function called <code>semaphore()</code> to run the code from the <a href="https://github.com/worldcoin/semaphore-rs?tab=readme-ov-file#example" target="_blank" rel="noopener noreferrer">README</a>.</p>
<div class="language-rust codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockTitle_ignD">src/lib.rs</div><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-rust codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">use</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">semaphore_rs</span><span class="token namespace punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain">get_supported_depths</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> hash_to_field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">identity</span><span class="token namespace punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token class-name" style="color:rgb(255, 203, 107)">Identity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">            </span><span class="token namespace" style="color:rgb(178, 204, 214)">poseidon_tree</span><span class="token namespace punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token class-name" style="color:rgb(255, 203, 107)">LazyPoseidonTree</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">protocol</span><span class="token namespace punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">pub</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:rgb(130, 170, 255)">semaphore</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// generate identity</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">mut</span><span class="token plain"> secret </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token string" style="color:rgb(195, 232, 141)">b"secret"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> id </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Identity</span><span class="token punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">from_secret</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token keyword" style="font-style:italic">mut</span><span class="token plain"> secret</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">None</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Get the first available tree depth. This is controlled by the crate features.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> depth </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">get_supported_depths</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">[</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">]</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// generate merkle tree</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> leaf </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">from</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">mut</span><span class="token plain"> tree </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">LazyPoseidonTree</span><span class="token punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token function" style="color:rgb(130, 170, 255)">new</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">depth</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> leaf</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">derived</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    tree </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> tree</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">update</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">commitment</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> merkle_proof </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> tree</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">proof</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token number" style="color:rgb(247, 140, 108)">0</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> root </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> tree</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">root</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// change signal and external_nullifier here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> signal_hash </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">hash_to_field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">b"xxx"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> external_nullifier_hash </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">hash_to_field</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token string" style="color:rgb(195, 232, 141)">b"appId"</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> nullifier_hash </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">generate_nullifier_hash</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> external_nullifier_hash</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> proof </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">generate_proof</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">merkle_proof</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> external_nullifier_hash</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> signal_hash</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">unwrap</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> success </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">verify_proof</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">root</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> nullifier_hash</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> signal_hash</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> external_nullifier_hash</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token operator" style="color:rgb(137, 221, 255)">&amp;</span><span class="token plain">proof</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> depth</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">.</span><span class="token function" style="color:rgb(130, 170, 255)">unwrap</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token macro property">assert!</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">success</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<div class="theme-admonition theme-admonition-warning admonition_iIk3 alert alert--warning"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_S87P"><p>You can also try returning a value; otherwise, nothing will happen after execution.
e.g.</p><div class="language-rust codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-rust codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">pub</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:rgb(130, 170, 255)">semaphore</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-&gt;</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">bool</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token keyword" style="font-style:italic">return</span><span class="token plain"> success</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></div>
</li>
<li>
<p>Export the function through UniFFI procedural macros</p>
<p>You can simply use the UniFFI proc-macros (e.g. <code>#[uniffi::export]</code>) to define the function interfaces.</p>
<div class="theme-admonition theme-admonition-info admonition_iIk3 alert alert--info"><div class="admonitionHeading_Z8US"><span class="admonitionIcon_SBrw"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_S87P"><p>For more details, refer to the <a href="https://mozilla.github.io/uniffi-rs/latest/proc_macro/index.html" target="_blank" rel="noopener noreferrer">UniFFI documentation</a>.</p></div></div>
<div class="language-diff codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-diff codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token inserted-sign inserted prefix inserted" style="color:rgb(195, 232, 141)">+</span><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"> #[uniffi::export]</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token inserted-sign inserted line" style="color:rgb(195, 232, 141)"></span><span class="token plain">pub fn semaphore() {</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   // generate identity</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   let mut secret = *b"secret";</span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token unchanged line"></span><span class="token unchanged prefix unchanged"> </span><span class="token unchanged line">   ...</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
</li>
<li>
<p>Run <code>mopro build</code> again and manually update the bindings for iOS and Android as explained in <a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#6-update-circuits">Step 6</a>.</p>
</li>
<li>
<p>You can now call the <code>semaphore()</code> function you just defined on both iOS and Android. 🎉</p>
</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="8-conclusion">8. Conclusion<a href="https://zkmopro.org/blog/2025-ethtaipei-workshop#8-conclusion" class="hash-link" aria-label="Direct link to 8. Conclusion" title="Direct link to 8. Conclusion">​</a></h2>
<ul>
<li>
<p>By following the tutorial, you will learn to create a native mobile ZK app with:</p>
<ul>
<li>A simple circuit</li>
<li>Custom circuits</li>
<li>Custom functions and structs</li>
</ul>
</li>
<li>
<p>Just like with the Semaphore case, this approach can be extended to any Rust crate, as long as you define the input and output types according to the <a href="https://mozilla.github.io/uniffi-rs/latest/proc_macro/index.html" target="_blank" rel="noopener noreferrer">UniFFI documentation</a>.</p>
</li>
<li>
<p>Alternative platforms, such as wasm for web, and frameworks like React Native and Flutter are also supported. Please check out:</p>
<ul>
<li><a href="https://github.com/zkmopro/react-native-app" target="_blank" rel="noopener noreferrer">https://github.com/zkmopro/react-native-app</a></li>
<li><a href="https://github.com/zkmopro/flutter-app" target="_blank" rel="noopener noreferrer">https://github.com/zkmopro/flutter-app</a></li>
</ul>
<p>Or simply run <code>mopro build</code> for these frameworks.</p>
</li>
<li>
<p>There are still many challenges to address, and contributions are highly encouraged. Feel free to explore the issues list.</p>
<ul>
<li><a href="https://github.com/zkmopro/mopro/issues" target="_blank" rel="noopener noreferrer">https://github.com/zkmopro/mopro/issues</a></li>
<li><a href="https://github.com/zkmopro/gpu-acceleration/issues" target="_blank" rel="noopener noreferrer">https://github.com/zkmopro/gpu-acceleration/issues</a></li>
</ul>
</li>
<li>
<p>We encourage developers to build mobile apps, as it helps us enhance the developer experience and gain a deeper understanding of the challenges involved.</p>
</li>
</ul>]]></content>
        <author>
            <name>Vivian Jeng</name>
            <uri>https://github.com/vivianjeng</uri>
        </author>
        <category label="workshop" term="workshop"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Comparison of Circom Provers]]></title>
        <id>https://zkmopro.org/blog/circom-comparison</id>
        <link href="https://zkmopro.org/blog/circom-comparison"/>
        <updated>2025-01-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithStickyNavbar_wa2S" id="introduction">Introduction<a href="https://zkmopro.org/blog/circom-comparison#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p>Throughout 2024, we compared various Groth16 provers for Circom. Our goal was to demonstrate that native provers (written in C++ or Rust) outperform <code>snarkjs</code> in terms of speed. Along the way, we uncovered some fascinating insights, which we’re excited to share with you in this post.</p>
<p>To understand a Groth16 prover, let’s break it down into two main components: <strong>witness generation</strong> and <strong>proof generation</strong>.</p>
<p><strong>Witness Generation:</strong> 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.</p>
<p><strong>Proof Generation:</strong> Once the witness is generated, this step takes the witness and the zkey (generated by <code>snarkjs</code>) to compute the polynomial commitments and produce a succinct zero-knowledge proof.</p>
<p>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.</p>
<p>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.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="witness-generation">Witness Generation<a href="https://zkmopro.org/blog/circom-comparison#witness-generation" class="hash-link" aria-label="Direct link to Witness Generation" title="Direct link to Witness Generation">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="snarkjs"><code>snarkjs</code><a href="https://zkmopro.org/blog/circom-comparison#snarkjs" class="hash-link" aria-label="Direct link to snarkjs" title="Direct link to snarkjs">​</a></h3>
<ul>
<li><a href="https://github.com/iden3/snarkjs" target="_blank" rel="noopener noreferrer">https://github.com/iden3/snarkjs</a></li>
</ul>
<p><code>snarkjs</code> 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.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="witnesscalc"><code>witnesscalc</code><a href="https://zkmopro.org/blog/circom-comparison#witnesscalc" class="hash-link" aria-label="Direct link to witnesscalc" title="Direct link to witnesscalc">​</a></h3>
<ul>
<li><a href="https://github.com/0xPolygonID/witnesscalc" target="_blank" rel="noopener noreferrer">https://github.com/0xPolygonID/witnesscalc</a></li>
</ul>
<p><code>witnesscalc</code> 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 <code>snarkjs</code>. With cross-platform support and compatibility with other ZKP tools, Witnesscalc is ideal for handling performance-sensitive applications and large circuits.</p>
<p>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.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="wasmer"><code>wasmer</code><a href="https://zkmopro.org/blog/circom-comparison#wasmer" class="hash-link" aria-label="Direct link to wasmer" title="Direct link to wasmer">​</a></h3>
<ul>
<li><a href="https://github.com/arkworks-rs/circom-compat" target="_blank" rel="noopener noreferrer">https://github.com/arkworks-rs/circom-compat</a></li>
</ul>
<p>One option available in Rust is <code>circom-compat</code>, maintained by the Arkworks team. This library uses the <code>.wasm</code> file generated by Circom and relies on the Rust crate <a href="https://github.com/wasmerio/wasmer" target="_blank" rel="noopener noreferrer"><code>wasmer</code></a> to execute the witness generation. However, wasmer doesn’t run natively on devices—it creates a WebAssembly execution environment for the <code>.wasm</code> file. As a result, the performance of wasmer is comparable to the WebAssembly performance of <code>snarkjs</code> running in a browser.</p>
<p>Initially, we encountered memory issues with wasmer during implementation (<a href="https://github.com/zkmopro/mopro/issues/1" target="_blank" rel="noopener noreferrer">issue #1</a>). 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 (<a href="https://github.com/zkmopro/mopro/issues/107" target="_blank" rel="noopener noreferrer">issue #107</a>). As a result, we decided to abandon this approach for Mopro.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="circom-witness-rs"><code>circom-witness-rs</code><a href="https://zkmopro.org/blog/circom-comparison#circom-witness-rs" class="hash-link" aria-label="Direct link to circom-witness-rs" title="Direct link to circom-witness-rs">​</a></h3>
<ul>
<li><a href="https://github.com/philsippl/circom-witness-rs" target="_blank" rel="noopener noreferrer">https://github.com/philsippl/circom-witness-rs</a></li>
</ul>
<p>Another Rust-based option is <code>circom-witness-rs</code>, developed by the Worldcoin team. Unlike solutions that rely on WebAssembly (wasm) output from the Circom compiler, this tool directly utilizes <code>.cpp</code> and <code>.dat</code> files generated by Circom. It employs the <a href="https://github.com/dtolnay/cxx" target="_blank" rel="noopener noreferrer"><code>cxx</code></a> crate to execute functions within the <code>.cpp</code> 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.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="circom-witnesscalc"><code>circom-witnesscalc</code><a href="https://zkmopro.org/blog/circom-comparison#circom-witnesscalc" class="hash-link" aria-label="Direct link to circom-witnesscalc" title="Direct link to circom-witnesscalc">​</a></h3>
<ul>
<li><a href="https://github.com/iden3/circom-witnesscalc" target="_blank" rel="noopener noreferrer">https://github.com/iden3/circom-witnesscalc</a></li>
</ul>
<p>The team at iden3 took over this project and began maintaining it under the name <code>circom-witnesscalc</code>. While it heavily draws inspiration from <code>circom-witness-rs</code>, it inherits the same limitation—it does not support RSA circuits. For more details, refer to the "Unimplemented Features" section in the <a href="https://github.com/iden3/circom-witnesscalc?tab=readme-ov-file#unimplemented-features" target="_blank" rel="noopener noreferrer">README</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="rust-witness"><code>rust-witness</code><a href="https://zkmopro.org/blog/circom-comparison#rust-witness" class="hash-link" aria-label="Direct link to rust-witness" title="Direct link to rust-witness">​</a></h3>
<ul>
<li><a href="https://github.com/chancehudson/rust-witness" target="_blank" rel="noopener noreferrer">https://github.com/chancehudson/rust-witness</a></li>
</ul>
<p>Currently, Mopro utilizes a tool called <code>rust-witness</code>, developed by a member of the Mopro team. This tool leverages <a href="https://github.com/turbolent/w2c2" target="_blank" rel="noopener noreferrer"><code>w2c2</code></a> to translate WebAssembly (<code>.wasm</code>) files into portable C code. By transpiling <code>.wasm</code> 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.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="proof-generation">Proof Generation<a href="https://zkmopro.org/blog/circom-comparison#proof-generation" class="hash-link" aria-label="Direct link to Proof Generation" title="Direct link to Proof Generation">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="snarkjs-1"><code>snarkjs</code><a href="https://zkmopro.org/blog/circom-comparison#snarkjs-1" class="hash-link" aria-label="Direct link to snarkjs-1" title="Direct link to snarkjs-1">​</a></h3>
<ul>
<li><a href="https://github.com/iden3/snarkjs" target="_blank" rel="noopener noreferrer">https://github.com/iden3/snarkjs</a></li>
</ul>
<p>As mentioned earlier, <code>snarkjs</code> is the most commonly used tool for generating Groth16 proofs. However, its performance still has room for improvement.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="rapidsnark"><code>rapidsnark</code><a href="https://zkmopro.org/blog/circom-comparison#rapidsnark" class="hash-link" aria-label="Direct link to rapidsnark" title="Direct link to rapidsnark">​</a></h3>
<ul>
<li><a href="https://github.com/iden3/rapidsnark" target="_blank" rel="noopener noreferrer">https://github.com/iden3/rapidsnark</a></li>
</ul>
<p>Rapidsnark, developed by the iden3 team, is an alternative to <code>snarkjs</code> designed to deliver faster Groth16 proof generation. Similar to <code>witnesscalc</code>, it is written in C++. While it shows promising performance, we are still working on integrating it into Mopro.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="ark-works"><code>ark-works</code><a href="https://zkmopro.org/blog/circom-comparison#ark-works" class="hash-link" aria-label="Direct link to ark-works" title="Direct link to ark-works">​</a></h3>
<ul>
<li><a href="https://github.com/arkworks-rs/circom-compat" target="_blank" rel="noopener noreferrer">https://github.com/arkworks-rs/circom-compat</a></li>
</ul>
<p>The primary Rust-based option is <code>circom-compat</code>, maintained by the Arkworks team. Arkworks is a Rust ecosystem designed for programmable cryptography, deliberately avoiding dependencies on native libraries like <code>gmp</code>. 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.</p>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="comparison-table">Comparison Table<a href="https://zkmopro.org/blog/circom-comparison#comparison-table" class="hash-link" aria-label="Direct link to Comparison Table" title="Direct link to Comparison Table">​</a></h2>
<p>Here, we present a table comparing different witness generators and proof generators to provide a clearer understanding of their features and performance.</p>
<p>In this comparison, we use <code>circom-witnesscalc</code> as a representative for both <code>circom-witness-rs</code> and <code>circom-witnesscalc</code>, as they share fundamentally similar implementations and characteristics.</p>
<table><tbody><tr><th>Witness Generator</th><th><a href="https://github.com/iden3/snarkjs" target="_blank" rel="noopener noreferrer"><code>snarkjs</code></a></th><th><a href="https://github.com/0xPolygonID/witnesscalc" target="_blank" rel="noopener noreferrer"><code>witnesscalc</code></a></th><th><a href="https://github.com/arkworks-rs/circom-compat" target="_blank" rel="noopener noreferrer"><code>wasmer</code></a></th><th><a href="https://github.com/iden3/circom-witnesscalc" target="_blank" rel="noopener noreferrer"><code>circom-witnesscalc</code></a></th><th><a href="https://github.com/chancehudson/rust-witness" target="_blank" rel="noopener noreferrer"><code>rust-witness</code></a></th></tr><tr><td><a href="https://zkmopro.org/docs/performance">Performance</a></td><td>slow</td><td>the fastest 🚀</td><td>slow</td><td>sometimes fastest 🚀</td><td>slightly faster than snarkjs</td></tr><tr><td>Supported Circuits</td><td>all</td><td>all</td><td>all</td><td>RSA not supported</td><td>all</td></tr><tr><td>Language</td><td>JavaScript</td><td>C++</td><td>Rust</td><td>Rust</td><td>Rust</td></tr><tr><td>Browser</td><td>✅</td><td>❌</td><td>❌</td><td>❌</td><td>❌</td></tr><tr><td>Desktop</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>iOS</td><td>✅</td><td>✅</td><td>❌</td><td>✅</td><td>✅</td></tr><tr><td>Android</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>Mopro Support</td><td>❌</td><td>⚠️ WIP <sup><a href="https://zkmopro.org/blog/circom-comparison#user-content-fn-1-ca0b0e" id="user-content-fnref-1-ca0b0e" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup></td><td>❌ Abandoned</td><td>⚠️ Possible <sup><a href="https://zkmopro.org/blog/circom-comparison#user-content-fn-2-ca0b0e" id="user-content-fnref-2-ca0b0e" data-footnote-ref="true" aria-describedby="footnote-label">2</a></sup></td><td>✅</td></tr></tbody></table>
<table><tbody><tr><th>Proof Generator</th><th><a href="https://github.com/iden3/snarkjs" target="_blank" rel="noopener noreferrer"><code>snarkjs</code></a></th><th><a href="https://github.com/iden3/rapidsnark" target="_blank" rel="noopener noreferrer"><code>rapidsnark</code></a></th><th><a href="https://github.com/arkworks-rs/circom-compat" target="_blank" rel="noopener noreferrer"><code>arkworks</code></a></th></tr><tr><td><a href="https://zkmopro.org/docs/performance">Performance</a></td><td>slow</td><td>the fastest 🚀</td><td>fast</td></tr><tr><td>Supported Circuits</td><td>all</td><td>all</td><td>all</td></tr><tr><td>Language</td><td>JavaScript</td><td>C++</td><td>Rust</td></tr><tr><td>Browser</td><td>✅</td><td>❌</td><td>✅<sup><a href="https://zkmopro.org/blog/circom-comparison#user-content-fn-3-ca0b0e" id="user-content-fnref-3-ca0b0e" data-footnote-ref="true" aria-describedby="footnote-label">3</a></sup></td></tr><tr><td>Desktop</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>iOS</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>Android</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>Mopro Support</td><td>❌</td><td>⚠️ WIP <sup><a href="https://zkmopro.org/blog/circom-comparison#user-content-fn-4-ca0b0e" id="user-content-fnref-4-ca0b0e" data-footnote-ref="true" aria-describedby="footnote-label">4</a></sup></td><td>✅</td></tr></tbody></table>
<h2 class="anchor anchorWithStickyNavbar_wa2S" id="conclusion">Conclusion<a href="https://zkmopro.org/blog/circom-comparison#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion">​</a></h2>
<p>In conclusion, we found that the <code>witnesscalc</code> and <code>rapidsnark</code> stack offers the best performance, but integrating it into Rust presents significant challenges. These tools rely heavily on C++ and native dependencies like <code>gmp</code>, <code>cmake</code>, and <code>nasm</code>. Our goal is to integrate these tools into Rust to make them more accessible for application development. Similar to how <code>snarkjs</code> 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.</p>
<p>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: <a href="https://t.me/zkmopro" target="_blank" rel="noopener noreferrer">@zkmopro</a>.</p>
<!-- -->
<section data-footnotes="true" class="footnotes"><h2 class="anchor anchorWithStickyNavbar_wa2S sr-only" id="footnote-label">Footnotes<a href="https://zkmopro.org/blog/circom-comparison#footnote-label" class="hash-link" aria-label="Direct link to Footnotes" title="Direct link to Footnotes">​</a></h2>
<ol>
<li id="user-content-fn-1-ca0b0e">
<p>We are actively working on integrating <code>witnesscalc</code> into Mopro. Please refer to <a href="https://github.com/zkmopro/mopro/issues/284" target="_blank" rel="noopener noreferrer">issue #284</a> <a href="https://zkmopro.org/blog/circom-comparison#user-content-fnref-1-ca0b0e" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2-ca0b0e">
<p>Please refer to <a href="https://github.com/zkmopro/mopro/pull/255" target="_blank" rel="noopener noreferrer">PR #255</a> to see how to use <code>circom-witnesscalc</code> with Mopro. <a href="https://zkmopro.org/blog/circom-comparison#user-content-fnref-2-ca0b0e" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3-ca0b0e">
<p><a href="https://github.com/waku-org" target="_blank" rel="noopener noreferrer">waku-org</a> has investigated this approach; however, it does not outperform snarkjs in terms of performance. Please refer to <a href="https://github.com/zkmopro/mopro/issues/202#issuecomment-2236923108" target="_blank" rel="noopener noreferrer">this comment</a> for more details. <a href="https://zkmopro.org/blog/circom-comparison#user-content-fnref-3-ca0b0e" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4-ca0b0e">
<p>We are actively working on integrating <code>rapidsnark</code> into Mopro. Please refer to <a href="https://github.com/zkmopro/mopro/issues/285" target="_blank" rel="noopener noreferrer">issue #285</a> <a href="https://zkmopro.org/blog/circom-comparison#user-content-fnref-4-ca0b0e" data-footnote-backref="" aria-label="Back to reference 4" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content>
        <author>
            <name>Vivian Jeng</name>
            <uri>https://github.com/vivianjeng</uri>
        </author>
        <category label="circom" term="circom"/>
        <category label="comparison" term="comparison"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Reflecting on 2024 - The Mopro Retrospective]]></title>
        <id>https://zkmopro.org/blog/2024-mopro-retrospective</id>
        <link href="https://zkmopro.org/blog/2024-mopro-retrospective"/>
        <updated>2025-01-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[It has been a remarkable year for the Mopro project. We’ve successfully transitioned from a proof of concept to a ready-to-use solution, attracting significant interest from various projects.]]></summary>
        <content type="html"><![CDATA[<p>It has been a remarkable year for the Mopro project. We’ve successfully transitioned from a proof of concept to a ready-to-use solution, attracting significant interest from various projects.</p>
<p>Here are the milestones we’ve achieved this year, along with key reflections on our journey.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="optimizing-developer-workflow">Optimizing Developer Workflow<a href="https://zkmopro.org/blog/2024-mopro-retrospective#optimizing-developer-workflow" class="hash-link" aria-label="Direct link to Optimizing Developer Workflow" title="Direct link to Optimizing Developer Workflow">​</a></h3>
<p>We streamlined the development process through significant codebase refactoring. By merging <code>mopro-core</code> and <code>mopro-ffi</code> into a single <code>mopro-ffi</code> folder and consolidating the iOS, Android, and web apps into a <code>test-e2e</code> folder, we reduced folder depth, making it easier for contributors to locate functions.</p>
<p>Additionally, we removed the circuits compilation and trusted setup processes, along with unused toolchain targets. These optimizations have drastically improved our CI workflow, reducing the peak runtime from around 1 hour to just 10 minutes (10 times faster)!</p>
<p>We also enhanced the Mopro CLI, significantly reducing the time required for setup and usage. As mentioned earlier, users no longer need to install unnecessary toolchains or download unused circuits from the Mopro repository.</p>
<p>Looking ahead to 2025, we plan to make the CLI even more accessible by providing precompiled binaries for download, eliminating the need for git clone during installation.</p>
<p>Users can now quickly clone the repository and build an iOS or Android project in just three commands—<code>mopro init</code>, <code>mopro build</code>, and <code>mopro create</code>—all within 3 minutes! For detailed instructions, check out the <a href="https://zkmopro.org/docs/getting-started">Getting Started</a> section.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="enabling-multi-platform-support">Enabling Multi-Platform Support<a href="https://zkmopro.org/blog/2024-mopro-retrospective#enabling-multi-platform-support" class="hash-link" aria-label="Direct link to Enabling Multi-Platform Support" title="Direct link to Enabling Multi-Platform Support">​</a></h3>
<p>In addition to Swift for iOS and Kotlin for Android, we’ve now created templates for cross-platform frameworks like React Native and Flutter. The CLI has been updated to support these platforms, and the documentation has been refreshed to reflect these enhancements.</p>
<p>Please refer to the following resources:</p>
<ul>
<li><a href="https://zkmopro.org/docs/setup/react-native-setup">React Native Setup</a></li>
<li><a href="https://github.com/zkmopro/react-native-app" target="_blank" rel="noopener noreferrer">React Native App</a></li>
<li><a href="https://zkmopro.org/docs/setup/flutter-setup">Flutter Setup</a></li>
<li><a href="https://github.com/zkmopro/flutter-app" target="_blank" rel="noopener noreferrer">Flutter App</a></li>
</ul>
<p>Additionally, Mopro now supports WASM for web browsers. We provide wasm-bindgen for the Halo2 prover, enabling developers to use the Mopro CLI to generate website templates with bindings. This significantly reduces the time spent navigating the outdated Halo2 tutorial available at <a href="https://zcash.github.io/halo2/user/wasm-port.html" target="_blank" rel="noopener noreferrer">Using halo2 in WASM</a> (It was authored in 2022).</p>
<p>Please refer to the following resources to learn how to use Mopro for building WASM applications for web browsers:</p>
<ul>
<li><a href="https://zkmopro.org/docs/setup/web-wasm-setup">WASM Setup</a></li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="expanding-compatibility-with-general-rust-functions">Expanding Compatibility with General Rust Functions<a href="https://zkmopro.org/blog/2024-mopro-retrospective#expanding-compatibility-with-general-rust-functions" class="hash-link" aria-label="Direct link to Expanding Compatibility with General Rust Functions" title="Direct link to Expanding Compatibility with General Rust Functions">​</a></h3>
<p>We realized that generating and verifying proofs alone isn’t sufficient for application developers. To address this, we made the Mopro template compatible with any Rust crate or function, allowing developer to extend the FFI interface directly through Rust code.</p>
<p>For instance, if a developer needs a Poseidon hash function but neither Swift nor Kotlin provides a Poseidon hash library, they can integrate a Rust Poseidon crate. First, they define the function API in Rust, such as:</p>
<div class="language-rust codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-rust codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token attribute attr-name" style="color:rgb(255, 203, 107)">#[uniffi::export]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">pub</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:rgb(130, 170, 255)">poseidon</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">input</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Vec</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token keyword" style="font-style:italic">u8</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-&gt;</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Vec</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token keyword" style="font-style:italic">u8</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Poseidon hash implementation</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>By annotating your function with <code>#[uniffi::export]</code>, UniFFI automatically declares the appropriate FFI type and generates the necessary bindings for Swift, Kotlin.</p>
<p>Once processed, the generated foreign language bindings expose your custom functions seamlessly. You can then call the Poseidon function in your Swift or Kotlin code as if it were a native API, enabling straightforward cross-language integration.</p>
<p>For more details on how UniFFI processes your Rust code to generate these bindings, please refer to the <a href="https://mozilla.github.io/uniffi-rs/latest/proc_macro/index.html" target="_blank" rel="noopener noreferrer">Uniffi - Procedural Macros</a>.</p>
<p>By running <code>mopro build</code> again, the developer can generate Swift and/or Kotlin bindings for the Poseidon hash function. They can then easily call the function in Swift or Kotlin like this:</p>
<div class="language-swift codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-swift codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">let</span><span class="token plain"> hash </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">poseidon</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">input</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> input</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>or in kotlin</p>
<div class="language-kotlin codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-kotlin codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">val</span><span class="token plain"> hash </span><span class="token operator" style="color:rgb(137, 221, 255)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(130, 170, 255)">poseidon</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">input</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Additionally, this approach is compatible with WASM for browsers. You can define a function in Rust as follows:</p>
<div class="language-rust codeBlockContainer_ZiiY theme-code-block" style="--prism-color:#bfc7d5;--prism-background-color:#292d3e"><div class="codeBlockContent_QMo3"><pre tabindex="0" class="prism-code language-rust codeBlock_jfw7 thin-scrollbar" style="color:#bfc7d5;background-color:#292d3e"><code class="codeBlockLines_jUhv"><span class="token-line" style="color:#bfc7d5"><span class="token keyword" style="font-style:italic">use</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">serde_wasm_bindgen</span><span class="token namespace punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token plain">to_value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">use</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(178, 204, 214)">wasm_bindgen</span><span class="token namespace punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token namespace" style="color:rgb(178, 204, 214)">prelude</span><span class="token namespace punctuation" style="color:rgb(199, 146, 234)">::</span><span class="token operator" style="color:rgb(137, 221, 255)">*</span><span class="token punctuation" style="color:rgb(199, 146, 234)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token attribute attr-name" style="color:rgb(255, 203, 107)">#[wasm_bindgen]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token keyword" style="font-style:italic">pub</span><span class="token plain"> </span><span class="token keyword" style="font-style:italic">fn</span><span class="token plain"> </span><span class="token function-definition function" style="color:rgb(130, 170, 255)">poseidon</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token plain">input</span><span class="token punctuation" style="color:rgb(199, 146, 234)">:</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">JsValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">-&gt;</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">Result</span><span class="token operator" style="color:rgb(137, 221, 255)">&lt;</span><span class="token class-name" style="color:rgb(255, 203, 107)">JsValue</span><span class="token punctuation" style="color:rgb(199, 146, 234)">,</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(255, 203, 107)">JsValue</span><span class="token operator" style="color:rgb(137, 221, 255)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(199, 146, 234)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token comment" style="color:rgb(105, 112, 152);font-style:italic">// Poseidon function implementation</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain">    </span><span class="token function" style="color:rgb(130, 170, 255)">to_value</span><span class="token punctuation" style="color:rgb(199, 146, 234)">(</span><span class="token punctuation" style="color:rgb(199, 146, 234)">...</span><span class="token punctuation" style="color:rgb(199, 146, 234)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#bfc7d5"><span class="token plain"></span><span class="token punctuation" style="color:rgb(199, 146, 234)">}</span><br></span></code></pre><div class="buttonGroup_TGb3"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_DrnI" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_ABK0"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_A0fP"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Then, by running <code>mopro build</code> again with the web target, you can generate the necessary bindings for the web. Once built, you can call the <code>poseidon</code> function directly in JavaScript, making it seamlessly accessible in browser-based applications.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="the-rise-of-new-zk-mobile-apps">The Rise of New ZK Mobile Apps<a href="https://zkmopro.org/blog/2024-mopro-retrospective#the-rise-of-new-zk-mobile-apps" class="hash-link" aria-label="Direct link to The Rise of New ZK Mobile Apps" title="Direct link to The Rise of New ZK Mobile Apps">​</a></h3>
<p>This year, we’ve seen a growing number of ZK mobile apps being developed. Some notable examples include:</p>
<ol>
<li><a href="https://github.com/worldcoin/idkit-swift" target="_blank" rel="noopener noreferrer">World ID</a></li>
<li><a href="https://github.com/anon-aadhaar/anon-aadhaar-react-native" target="_blank" rel="noopener noreferrer">Anon Aadhaar</a></li>
<li><a href="https://github.com/zk-passport/openpassport" target="_blank" rel="noopener noreferrer">Open Passport</a></li>
<li><a href="https://github.com/mynawallet" target="_blank" rel="noopener noreferrer">Myna Wallet</a></li>
<li><a href="https://github.com/rarimo/FreedomTool" target="_blank" rel="noopener noreferrer">FreedomTool</a></li>
</ol>
<p>These apps benefit significantly from mobile-native proving compared to using tools like snarkjs. For instance, Anon Aadhaar achieves up to <em>8x</em> faster performance with <a href="https://github.com/iden3/rapidsnark" target="_blank" rel="noopener noreferrer">rapidsnark</a> compared to <a href="https://github.com/iden3/snarkjs" target="_blank" rel="noopener noreferrer">snarkjs</a>.</p>
<p>For more details on the benchmarks, please refer to the <a href="https://zkmopro.org/docs/performance">benchmark section</a>.</p>
<p>While we’ve provided the Mopro stack with <code>rust-witness</code> and <code>ark-works</code>, most applications are leveraging the <code>witnesscalc</code> and <code>rapidsnark</code> stack for faster proving, particularly with RSA circuits.</p>
<p>Given the adoption trends and benchmark results, we've recognized the need to prioritize improving <code>rapidsnark</code> integration and further enhancing the developer experience. This will be a key focus in Q1 of 2025.</p>
<p>We’re excited to see even more ZK mobile-native apps emerge in the near future, delivering <strong>improved performance</strong> and <strong>enhanced user experiences</strong>.</p>
<h3 class="anchor anchorWithStickyNavbar_wa2S" id="final-thoughts-and-looking-ahead">Final Thoughts and Looking Ahead<a href="https://zkmopro.org/blog/2024-mopro-retrospective#final-thoughts-and-looking-ahead" class="hash-link" aria-label="Direct link to Final Thoughts and Looking Ahead" title="Direct link to Final Thoughts and Looking Ahead">​</a></h3>
<p>The Mopro tool has become more robust, now supporting multiple platforms. However, our vision extends further—we aim to develop a mobile-native ecosystem as comprehensive and developer-friendly as the JavaScript/TypeScript ecosystem, empowering developers to seamlessly build innovative apps.</p>
<p>As we look to the future, we encourage developers to explore the opportunities in building ZK mobile applications. By leveraging mobile-native proving, you can create apps that are not only faster but also more accessible to users worldwide. Let’s work together to shape the next wave of ZK technology and bring its benefits to mobile platforms!</p>]]></content>
        <author>
            <name>Vivian Jeng</name>
            <uri>https://github.com/vivianjeng</uri>
        </author>
        <category label="retrospective" term="retrospective"/>
    </entry>
</feed>