A First Look at Collaborative Noir

Collaborative SNARKs bring together ZK and MPC allowing multiple parties to compute on private data without revealing it. With coCircom, we took the first step by combining MPC with Circom circuits, making private computation accessible to developers using ZK.

We started with coCircom because it was a good fit for the ZK circuit builders—it met developers where they were. Circom provided a solid foundation to mix MPC and ZK practically for current projects. But the space is advancing. With the rise of ZK smart contracts on networks like Aztec and a growing interest in privacy, new DSLs like Noir are becoming increasingly interesting to ZK developers.

For many privacy-focused applications, computing on private data is essential. However, ZK alone can’t handle everything—we need MPC to collaborate securely. This is why we’ve started developing coNoir, extending our tooling to combine the power of Noir with the collaborative approach of coSNARKs.

Here’s a first look at coNoir—why we built it, how it expands private computation, and what it unlocks for real-world applications.

Why Noir?

First off, we want to explain why we decided to extend our coSNARK tooling with Noir. It really comes down to three reasons:

We’re big fans of Noir

Even before we started working on coSNARKs, we were already experimenting with Noir. Back then, coSNARKs were more of a "this is a really cool theoretical concept" kind of thing for us. Since we already had a strong background in MPC, the idea of mixing MPC with ZK seemed like a fun theoretical experiment. Anyways, at that point in time we were already hooked on Noir, and how easy it was to write some ZK friendly hash functions with it. In case you didn't know, we implemented some of our favorite hash functions in Noir and bundled them to a library.

So, it was only natural that when we took the next step in our plan to take over the world to enable private compute with coSNARKs to look at Noir. In fact, we initially thought about starting out with Noir over circom, but circom came out on top at the end due to the reasons we mentioned earlier.

Noir's composable and rich ecosystem

Noir, the programming language, is by design agnostic to the underlying proof system. It achieves this by borrowing some design philosophy from LLVM (I actually never confirmed this with someone from Aztec, but it is the way I personally think about it, because I played with the LLVM compiler architecture for a little bit over 6 months). The compilation chain from Noir looks like this:

Noir compile chain

As we can see in the graphic, there is an intermediate representation (IR) point in the compilation chain, called Abstract Circuit Intermediate Representation (ACIR). The idea is to provide a common lowering point that is backend agnostic, so that languages can compile to ACIR and ACIR in turn lowers to specific proof systems (maybe now you see why I usually think about LLVM in that context). At the time of writing this post, I do not know whether there are actually other zkDSLs that lower to ACIR though. Nevertheless, as you can see the compile chain is composable by design, which means we can attach our coSNARK tooling in a part of the compilation chain which is most suitable for us and reuse all the other tools that come before us in the chain!

In our specific case, we step in right after the lowering step to ACIR, which is used as an instruction set to compute the extended witness in MPC. After we obtained the secret shared witness, we provide the witness to our custom proving backend, coBarretenberg, which at the moment is compatible with Barretenberg's Honk prover!

MPC-friendliness of Proof System

Last but not least, we of course also need to investigate the MPC-friendliness of the underlying proof system. As described earlier, we are free to write any proof system that parses ACIR and outputs a proof, because of the composability of the compilation chain. This means, theoretically, even if Barretenberg would not be MPC friendly, we could build something that is MPC-friendly and attach it in the last step of the chain as dedicated proving backend. Luckily for us, Barretenberg supports multiple variants of PLONK and Honk, which are based on Elliptic Curves and the SumCheck protocol and use the KZG polynomial commitment scheme, which are perfect for MPC.

With this approach, we can go down the same way as we did with circom. By MPC-ifying Barretenberg (rewrite everything in Rust and put MPC on top of it), we are fully compatible with Barretenberg's verifier and therefore with Aztec's tool chain. Any verifier is not able to distinguish between a proof from Barretenberg and coBarretenberg!

What can we do so far with coNoir

Now that we’ve covered our building blocks and reasoning for building coNoir, let's dive a bit deeper into what our tooling is currently capable of.

In order to be able to generate proofs in an MPC setting you have to be able to secret share the inputs or witnesses among the computing parties. So, consequently, our tooling enables you to take an input, generate secret shares of these inputs which you can then share to the parties.

Depending on the desired use case, you either do the extended witness generation by yourself and then secret share it to the parties, or you start the whole process one step earlier, namely you secret share the inputs and the nodes do the witness extension in an MPC manner. Furthermore it also implements the option to translate witness shares from REP3 to 3-party Shamir secret shares.

Coming to more concrete instances of Noir programs, our coNoir tooling currently supports basic field addition and multiplication over the scalar field of the BN254 curve, as well as any operations the compiler can reduce to these basic operations. So far, any Noir programs that consist purely of field arithmetic are supported, but of course, we plan to add more functionality soon!

To get an idea of what's possible so far, we can, for instance, compute a Poseidon hash from two different inputs, as shown in the following example:

fn main(x: Field, y: Field) -> pub Field {
    poseidon::bn254::hash_2([x, y])
}

Roadmap

So, what's next? We already hit a pretty big milestone by renaming our GitHub repository from collaborative-circom to co-snarks, which we wanted to do for some time now!

On a more serious note, we, of course, need a lot more functionality in our MPC version of the witness extension and coBarretenberg. Doing field arithmetic is nice and all, but to catch the full beauty of Noir, we also need integer arithmetic, blackbox functions, the powerful Brillig-VM for running unconstrained code, and more. To highlight what is still ahead of us and were we are at the moment, we prepared a Roadmap:

coNoir Roadmap

As we can see, the witness extension is currently one step ahead of coBarretenberg. In contrast to coCircom though, the witness extension in coNoir is less complex and easier to build. This is partly to the fact, that ACIR is already in an easy to execute form for MPC, and we can reuse a lot of the compilation. Circom does not have an IR. It simply parsed the circom file to some Abstract-Syntax-Tree (AST) which then was turned into C++ or WASM. Therefore, if we didn't want to build an MPC-VM that is able to execute WASM or C++, we had to do something else. Therefore, we wrote a custom compiler, that lowered from the circom AST to a proprietary instruction set for a dedicated MPC-VM. That VM then computed the extended witness.

Why am I telling you all this? Because all that steps were not necessary for coNoir, showing again the beauty of the composability of the Noir compile chain. We use all compiler steps until we have ACIR and execute it. This saved us a lot of engineering effort. The next intermediate step is adding integer support (e.g., u32, u64 data types) and adding support for Range checks in the backend. Additionally, we have one team working on the witness extension currently adding support for the first blackbox functions, namely Poseidon2 and SHA256. So stay tuned for further updates on our progress of coNoir!

Conclusion

To summarize, our decision to integrate Noir into our coSNARK tooling stems from our enthusiasm for the language, its rich and composable ecosystem, and its MPC-friendly proof system. Noir's design allows us to leverage the benefits of a robust compilation chain, providing flexibility and compatibility with various proof systems, especially with our implementation of coBarretenberg. This integration not only enables us to generate and verify proofs efficiently but also enhances our ability to handle complex computations securely in a multiparty computation setup.

Through the development of coNoir, we have successfully adapted the Ultrahonk prover and verifier for MPC applications, maintaining compatibility with Barretenberg's infrastructure. The ability to perform both extended witness generation as well as the proof generation within an MPC framework opens new avenues for secure computing, empowering developers to create privacy-preserving applications.

As we continue to evolve our coSNARK tooling, we look forward to exploring further capabilities of coNoir and contributing to the growth of the zero-knowledge ecosystem. The potential of Noir, combined with our expertise in MPC, sets the stage for innovative solutions in the realm of secure and private computation.

Keep an eye on our X or join our Discord for updates in the futures!