zkREPL: An Online Development Environment for zkSNARKs

June 5, 2022

zkREPL: An Online Development Environment for zkSNARKs

by antimatter15

Try out zkREPL here! Also see the repository, and check out circom2 on npm.

In the Paleolithic age of computing, computers were great electromechanical beasts that could execute a few dozen operations a second at the cost of millions of dollars a year. Programs were developed by meticulous meditation, by plugging wires in switchboards, and by punching cards. At the time, bugs were literal insects caught nibbling on wires, occasionally causing electrical shorts which threatened to literally burn everything down.

Back then, programming a computer was an arduous activity filled with ritual and ceremony. But in the late 1950s, a new system of programming called LISP had been invented which was capable of reasoning about its own implementation—through a construct called "eval", programs could call upon their own maker to spawn new programs. Upon this, researchers came up with a simple program which would read some input from the keyboard, send it to the "eval" construct, print the result, and loop back to the start to await further input.

This one neat trick, the REPL, set into motion the democratization of programming—from something only used by nation states to steer rockets, to something used by teenagers to make video games.

ZK

In recent years we have seen the rise of a new age of computing. We have blockchains like Ethereum and Bitcoin, processing a few dozen operations a second, at the cost of millions of dollars a year in gas prices. We regularly see bugs that burn down entire protocols.

What drives this new age of computing, are new systems capable of reasoning about their own trust: from auditable centralized oracles, content-addressable storage, byzantine consensus, multi-party computation, and perhaps the most interesting one of all—zero knowledge proofs.

The current world of zero knowledge is filled ritual and ceremony. Sorcerers enlist thousands of people in their struggle to exorcise demons—to credibly dispose of toxic waste generated in a trusted setup.

But with this infancy comes the opportunity to rhyme with history, and democratize ZK.

Read

In democratizing ZK, we have to first establish a lingua franca—-a language that can be used and understood by experts and amateurs alike. To that end we were fortunate to be able to build on Circom 2.0, a simple language with syntax that should be familiar to anyone who has seen JavaScript, while exposing enough of the underlying abstraction that people can still get a grasp of the low-level circuit structure being built.

Beyond the language, we need its medium. We'd like to be able to dive into the world of ZK as easily as clicking a link in a web browser, so we built zkREPL on top of Monaco: the same text editing component that powers Visual Studio Code. But we extended it for our particular purposes—custom syntax highlighting, and tooltips with links to external includes.

In the future, it might be nice to switch to CodeMirror 6, as Monaco doesn't work particularly well on mobile devices, and we can leverage Joel Gustafson's lezer-circom project to add a proper incremental syntax parser and enable all sorts of new capabilities for exploring, navigating, and visualizing Circom code.

On a deeper level, in building systems which explicitly reason about their own trust, a ZK system is only as good as the ability for its users to be able to convince themselves that the code which allegedly underlies a given contract is actually what is running. To that end, there’s a lot more that could be done to provide a system for enabling users to verify the source code for a circuit.

Eval

Traditionally, to play with writing zero knowledge programs in Circom, you would have to first install the entire Rust toolchain and compile the compiler from source. The first step to building zkREPL was to get the Circom compiler to compile into WebAssembly so it could run portably inside a browser.

This involved a few hurdles along the way—of varying degrees of annoyance. There were a few relatively simple changes, like adapting it to support 32 bit architectures like WASM, and disabling the multithreading features which are not currently supported by Rust WASM.

Then came a few of the the more obnoxious hurdles: Circom was statically linked with an external C++ library called WABT, the WebAssembly Binary Toolkit, whose job it was to translate a text-based WAT format into the binary WASM format. The Rust WASM compiler backend doesn't support linking with modules written in different languages, but the WABT project had been separately compiled to WASM using Emscripten. To get around this, we first stripped Circom of all the code that would invoke WABT and created a wrapper script, which when asked to compile Circom to WASM, would ask launch a WABT-free Circom and ask for a WAT file and immediately feed it into another WABT module to generate the resulting WASM.

Later on, Blaine Bublitz came across the WAST library, a pure Rust implementation of the WASM binary format, and submitted a set of upstream changes to Circom to replace WABT with this pure Rust implementation, allowing us to once and for all get rid of that nasty hack.

WebAssembly is a very lightweight specification which essentially represents pure computation- it doesn't have a concept of files, time, or even an output console. This is where WASI comes in- it defines a standard environment that WASM programs can interface with in order to read files, accept input, and print to an output console. However, WASM is a new technology, and WASI even more so.

At this point our Circom/WASM implementation was ostensibly working well: it was compiling our code and generating valid WASM witness generators. However, when it came time to test things end-to-end with SnarkJS, we found that the requisite R1CS files came out to be a few bytes larger than we had expected. After a bit of sleuthing, a culprit was identified: under certain circumstances, a particular sequence of writing to a file, jumping back to a previous position, and continuing to write to that file, would provoke a bug in certain implementations of the WASI runtime that would write things to the wrong place. We ended up getting around this by vendoring and patching a version of the WASI Filesystem runtime.

At this point we had a working version of Circom 2.0 compiled to WebAssembly that would run in the browser. But beyond that, we packed it up as a NodeJS module so anyone can develop with Circom simply by running npx circom2 on their computer. Additionally, Blaine contributed a programmatic API which enabled integration with build tools such as hardhat-circom.

On top of the standard Circom language, we have also dabbled with a few extensions that go beyond the core language. We've added the ability to import code directly from external URLs, and a way to specify inputs to your witness generation program directly as a comment in the bottom of the file.

In the future, it would be interesting to extend the language further with support for custom gates in PLONK.

Print

Snarks are delicate creature; leave a single wire dangling and you may have introduced a critical bug that might burn your entire protocol to the ground. With the stakes involved, it becomes ever more critical that the environment doesn't merely print output, but interleaves it with the code in such a way that developers can truly understand every inch of the code's surface.

In zkREPL you can simply hover over signals in your source code and see a tooltip that shows the values of those signals extracted from your witness file—Look ma! No print statements!

Tools for bringing awareness to the nature of the code which is running can get much more advanced over time. For example, we might at some point be able to integrate some of the static analysis capabilities of tools like Franklyn's Ecne in order to prove the uniqueness of witnesses for circuit implementations.

In the future, we could also add new modes for visualizing and exploring circuits as block diagrams with the ability to zoom into individual blocks and see the underlying wires, as suggested by Steven Hao.

Loop

Like Bilbo Baggins' journey in the Hobbit, the story of a REPL begins and ends with going "there and back again". By returning to the start, developers can iterate on their implementations, adding features, refining algorithms, and fixing bugs. Computers have evolved quite a bit since the 1950s, and programming doesn't need to the solitary meditation that it once was. With the advent of the internet, the process of looping is no longer synchronous- it's parallel.

Efforts like implementing ECDSA and RSA in Circom are fundamentally collaborative efforts, and so zkREPL endeavors to support that by making it easy to share and collaborate on circuits. As such you can simply hit "Ctrl-S" and save a copy of your circuit as a Github Gist, and copy the URL from your browser bar to share it with a friend, or embed it into a tutorial page as an iframe.

In the future, we can shorten the loop even further with better integration with Git, or the ability to view and edit code with realtime multiplayer.

Conclusion

As 0xPARC draws from the rich mythology of Xerox PARC, this project is named in homage of that neat little trick that shifted programming from an elite ritual to a ubiquitous democratic rite.

The entire ecosystem is in its infancy, and tools like zkREPL are still quite primitive. If you'd be interested in helping out, either by implementing any of the ideas laid out above, or by contributing your own vision, come hang out on our Github Repo.

Acknowledgements

Thanks to gubsheep, Albert Ni and the rest of the 0xPARC Learning Group #1 for inspiring and subsequently testing and using this project. Thanks to Jordi Baylina and iden3 for the Circom language and SnarkJS ecosystem which powers it. Thanks to Blaine Bublitz for contributions to the WASM port of Circom. Thanks to Joel Gustafson, Lily Jordan, Steven Hao, Uma Roy and Guillermo Webster for helpful conversations about the design of the project. Also thanks to Alex Klarfeld for first introducing me to the concept of zk-SNARKs in 2017.