Turbocharged Zero-Knowledge Proofs for Mobile

Abdul Rashid Reshamwala
Sep 18, 2024
Turbocharged Zero-Knowledge Proofs for Mobile
engineeringOSSgnarkmobile

Introduction

Reclaim Protocol empowers users to securely own and share their online data through zero-knowledge proofs generated entirely on the client side. This allows users to prove aspects of their identity or reputation from any website without revealing sensitive information. For example, a user can demonstrate they are a 5-star driver without disclosing their license plate number, or confirm excellent credit without sharing bank account details.

The Role of Zero-Knowledge Proofs in Reclaim Protocol

Zero-knowledge proofs are central to the Reclaim Protocol, enabling users to securely prove claims about their identity or data without revealing any sensitive information. By generating these proofs entirely on the client side, users maintain complete control over what they share. We utilize techniques like TLS Request Selective Reveal and TLS Response Selective Reveal to allow users to prove they have accessed specific data on a website without exposing private details like passwords or cookies.

An essential component in this process is the Witness, which helps verify the integrity of the user's interactions without accessing plaintext data. For those interested in the technical specifics of these cryptographic methods, please refer to our whitepaper for more detailed information.

Zero-Knowledge Proof Diagram

Figure 1: Simplified diagram of proof generation in Reclaim Protocol

Our Initial Setup with Circom and snarkjs

When we first developed the Reclaim Protocol, we chose Circom and snarkjs for generating zero-knowledge proofs because snarkjs was easy to integrate with our TypeScript and JavaScript codebase.

To run snarkjs on our mobile apps built with React Native and Flutter, we used webviews to embed a web environment within our apps. We developed a custom RPC mechanism to enable communication between the mobile app and the webview running snarkjs, handling tasks like proof generation and communication with the witness.

Despite setting up this infrastructure, we faced significant challenges:

We attempted to reduce memory usage by applying various WebAssembly (WASM) optimization techniques. However, these optimizations weren't enough to overcome the limitations. The memory issues became a critical bottleneck, affecting the reliability, performance, and scalability of our mobile applications. It became clear that to improve user experience and support more complex proofs, we needed to find an alternative solution.

change gif

Exploring Alternatives: Why We Chose gnark

Facing significant challenges with Circom and snarkjs—especially memory limitations and performance issues on mobile devices—we began exploring alternative solutions for zero-knowledge proof generation. Our goals were clear:

Evaluating RapidSNARK

One of the first alternatives we evaluated was RapidSNARK. About six months ago, we conducted initial tests to assess its performance and suitability for our needs. Unfortunately, we didn't observe significant improvements over snarkjs at that time. In fact, we encountered more crashes, which compounded our existing challenges rather than alleviating them.

However, we've heard that RapidSNARK has made substantial advancements in recent months. The development team has reportedly addressed many of the issues we experienced, and performance has improved considerably. We remain open to revisiting RapidSNARK in the future to see if it can offer benefits that align with our requirements.

swipe

Why We Chose gnark

After evaluating various options, we ultimately chose gnark for several compelling reasons:

Switching to gnark addressed the critical bottlenecks we were experiencing, enabling us to enhance the performance, reliability, and scalability of Reclaim Protocol. gnark currently provides the optimal balance of efficiency and compatibility for our needs.

Transitioning from snarkjs to gnark involved several steps, and gnark's efficiency made the effort worthwhile.

Migrating Circuits

Our first step in transitioning to gnark was rewriting the zero-knowledge circuits that were initially built in Circom. This re-implementation in gnark's Go-based framework allowed us to optimize for better performance and reduce memory consumption. The ChaCha20 circuits worked great right out of the box, providing a performance boost. However, the AES circuits initially performed worse than in snarkjs.

To address this, we optimized the AES implementation by utilizing gnark's built-in lookup tables, which significantly improved both performance and memory efficiency. This optimization allowed us to maintain the high performance we needed for AES encryption.

We've already open-sourced these optimized circuits! You can check them out and contribute at https://github.com/reclaimprotocol/gnark-symmetric-crypto.

swipe

Adjusting Mobile Architecture

gnarkprover_explaination

In this setup, proof generation has been moved from the webview to native libraries for better performance and efficiency:

The Reclaim In-App Webview handles user interactions and sends HTTP requests via the Attestor SDK, which operates through a Headless Webview. When zero-knowledge proof generation is required, the Reclaim app first checks if the gnarkprover Plugin is available. If it's present, the app uses it to generate proofs natively. Otherwise, it defaults to the webview for proof generation. By using the gnarkprover Plugin, the Attestor SDK offloads proof generation to the gnark Prover FFI, which runs natively on the device.

The gnarkProver Flutter Plugin then communicates with the gnark Prover FFI (Foreign Function Interface), which is a native library compiled from Go directly for each mobile platform. This allows proof generation to happen natively on the device, making it faster and more efficient. This approach is also very modular in nature and can be easily extended to support other zero-knowledge proof systems in the future.

By moving the heavy lifting of proof generation to the native gnark Prover, we reduce the overhead of running it in the webview, improving performance and optimizing resource usage.

Challenges and Solutions AKA The Hard Parts 😬

problems

Throughout our transition to gnark, we encountered several significant technical challenges. Here's a few of them:

1. Native Library Size and App Clip Limitations

size

In our initial implementation, we embedded the cryptographic circuits directly into the native library. While this worked functionally, it caused the library size to grow to around 100MB—well beyond the 50MB limit imposed by Apple's App Clip feature, which we use for seamless integrations. This posed a significant challenge, as maintaining App Clip compatibility was crucial for our integration strategy.

Solution

To solve this, we shifted from embedding the circuits within the native library to downloading them dynamically from the frontend. We also pre-initialized the circuits on the client side, allowing the proof generation process to begin as quickly as possible. This approach reduced the native library size dramatically, bringing us well within the App Clip size limits. All our assets are hosted on global CDNs for fast and efficient delivery.

2. Debug Build Freezing on iOS 🥶

Another challenge arose during development on iOS. Whenever we attempted to call the native Go library in a debug build, the app would freeze. This issue took considerable time and effort to diagnose, as it wasn't immediately clear what was causing the freeze.

freeze

Solution

After countless hours of debugging and researching, we traced the problem to the Go runtime itself, specifically related to asynchronous preemption. The Go runtime sends OS signals frequently to preempt long-running goroutines. However, iOS's debugger struggled to handle these signals quickly enough, leading to severe slowdowns or complete freezes. The issue was documented in golang/go#57651.

To resolve this, we applied a simple fix: setting the runtime flag GODEBUG=asyncpreemptoff=1. This disables asynchronous preemption in the Go runtime, effectively preventing the flood of signals that caused the debugger to freeze. After applying this fix, our iOS builds ran smoothly, with no further issues.

Performance Testing 🧪

Our tests revealed significant improvements in memory efficiency and proof generation speed, particularly on mobile devices. We observed a dramatic reduction in average proof generation time, cutting it down from 40 seconds with snarkjs to just 4-5 seconds with gnark, making the process much faster and more efficient.

You can see some of our benchmarks in the video below!

Comparison of proof generation speed: snarkjs vs gnark

Github username claim in less than 3 seconds

We also achieved much greater stability in proof generation. Previously, after generating 20-30 proofs, the app would slow down or even crash in some cases. Now, we can generate proofs for up to 100 blocks without significant performance degradation or increased memory usage. This improvement unlocks many new use cases that were previously not possible due to the limitations of our old setup.

Charts for Nerds

chart chart

Looking Ahead

We're excited about the future of zero-knowledge proofs on mobile devices and the opportunities they present. As we continue to refine and enhance our implementation, we're committed to pushing the boundaries of what's possible on mobile platforms.

We plan to do more such deep dive into our stack. You can follow me @abdul_rashid_r and @reclaimprotocol on twitter to get updates on our latest work. We will be open-sourcing more of our tech in the near future including our internal sdk and flutter gnark prover plugin so keep an eye out for that!

Checkout our Github Repos

Copyright © 2024 Reclaim Protocol. All rights reserved.