ZK/SEC Research notes from zkSecurity
All posts
noname · Part 3 of 4

noname 2.0: Unlocking Numeric Generics, Folding Schemes, and a Playground

landing

We are thrilled to unveil the upcoming release of noname 2.0 preview; a major leap forward in the noname language! This release will introduce powerful enhancements that enable developers to write more sophisticated ZK circuits. The notable enhancements include:

  • Generic-Sized Arrays: Unlock greater flexibility and effectiveness in your circuit code by enabling functions to handle arrays of varying sizes.
  • Folding Schemes via Sonobe Integration: Seamlessly create your circuits for IVC (incremental verifiable computation). Particularly useful for applications like: Bitcoin light clients on EVM, rollups, and zkVMs.
  • Interactive Online Playground: Experiment, learn, and share your code effortlessly with a new web-based platform.
  • R1CS Optimization: Improve performance by reducing the number of constraints generated from R1CS backend.
  • Bugfixes and Life improvements: A number of community contributions have significantly improved the language's usability and stability.

These enhancements, coupled with the invaluable contributions from our vibrant open-source community, have transformed noname into an even more versatile and user-friendly language. In this post, we will discuss the specifics of these updates, outline our journey in bringing them to life, and acknowledge the significant contributions from our community that have shaped this progress.

Generic-Sized Arrays

One of our ongoing efforts is enhancing the standard library, making it easier for developers to get started with noname. A key challenge we faced was enabling the language to handle functions that accept and return arrays of varying sizes.

Initially, we considered managing without explicit generic features, inspired by GoLang's long history without them. However, given that noname is a typed DSL with a strong emphasis on type checking, we realized the necessity of supporting generic types.

To address this, we introduced generic syntax into the language. Initially, we used the conventional syntax <P> next to the function name to denote generic parameters. For example:

fn to_bits<LEN>(val: Field) -> [Field; LEN]
fn from_bits<LEN>(bits: [Field; LEN]) -> Field

Our primary goal was to minimize codebase changes while implementing the generic-sized array feature.

We initially planed to relax certain type checks for generic types, deferring them to the circuit synthesizer phase. This approach is similar to the concept dynamic dispatch, retaining and managing the underlying structures behind a pointer during the synthesizer phase to propagate constant values and resolve generic types.

However, this approach doesn't resolve the generic types before proceeding to the synthesizer phase after the compilation pipeline. This required significant changes to the synthesizer's assumptions, which are heavily based on the concrete type.

Eventually, we adopted another approach called monomorphization. Instead of heavily refactor the cicuit synthesizer, the generic types are processed and resolved into concrete types by the monomorphization phase in the pipeline before being passed to the circuit synthesizer phase. The following diagram depicts the pipeline.

pipeline

With monomorphization, the synthesizer remains unchanged and doesn't need to accommodate the new generic syntax. This approach significantly reduces complexity and potential risks compared to type erasure.

Let's take a look at how the monomorphization works in the code level:

fn main() {
    // As to_bits / from_bits are generic functions,
    // this will create a new function AST instance for bit length of 3
    let bits = to_bits::<3>(2);
    let val = from_bits::<3>(bits);

    assert_eq(val, 2);
}

After the pass of monomorphization in the pipeline, it instantiates generic functions based on different constant values for the generic parameters. These two function calls will point to the new function instances instead of the original generic function AST.

Function instantiation process will convert the generic types to concrete types, such that:

to_bits::<3>(2);

// the function call above will instantiate the to_bits function as following 
fn to_bits(val: Field) -> [Field; 3]

So the generic type for the return [Field; LEN] became concrete type [Field; 3], after monomorphization.

We also realized that the turbofish syntax was unnecessarily verbose. The detailed reasons are explained in the RFC.

It ends up having the following syntax to express generic function, which is less verbose and more natural to use:

fn to_bits(const LEN: Field, val: Field) -> [Field; LEN]
fn from_bits(bits: [Field; LEN]) -> Field

This allows the function calls to be simplified as:

fn main() {
    let bits = to_bits(3, 2);
    let val = from_bits(bits);

    assert_eq(val, 2);
}

For more details on the monomorphization design process, please refer to our first RFC. We are still looking for feedback on the design and implementation, so feel free to share your thoughts and suggestions.

Folding via Sonobe

Sonobe is an IVC (Incremental Verifiable Computation) framework that simplifies the development of circuits for folding schemes. It effectively abstracts the complexities of folding schemes, allowing developers to primarily focus on implementing the core IVC logic.

folding

Sonobe facilitates the integration of IVC circuits by handling the surrounding infrastructure. Developers can concentrate on defining the circuit for the function 𝐹, as illustrated in the diagram. It then manages the sequencing of computations and generates a proof to verify the correctness of the IVC process.

The potential application involves computing a proof for a series of Bitcoin blocks' transitions off-chain and then verifying it on the Ethereum Virtual Machine (EVM). This means that it's possible to implement a Bitcoin light client on the EVM. Another interesting application could be proving computations for a virtual machine (VM). Since the computation logic for opcodes in the VM is fixed, feeding opcodes along with outputs from an initial state consistently leads to a deterministic final state.

To enable noname to support folding schemes via Sonobe, we identified two straightforward requirements:

  • Compatibility with the arkwork R1CS format.
  • Integration of noname as a new frontend within Sonobe.

While noname already supports generating R1CS circuits compatible with the SnarkJs toolkit, it currently relies on the Circom Rust library rather than the arkwork R1CS library. Therefore, it was necessary to create a port for the latter.

Pierre, a core contributor of Sonobe, initiated researching for noname integration. He suggested noname to support array public output, an important feature. Remarkably, erhant from the community quickly implemented the suggestion, and finiteprods extended the feature to support all types as public outputs.

A special thanks to Pierre and all the contributors who played a crucial role in the Sonobe integration development. Now, you can write circuits in noname that incorporate folding schemes seamlessly.

Online Playground

The online playground is an invaluable tool for quickly sharing and testing code snippets. Logan identified this need and created a suggestion to propose a development, subsequently taking the initiative to implement the online playground.

playground1

As shown in the screenshot, the web UI offers a much more intuitive experience for navigating features compared to the terminal interface. It supports compiling and running noname code with user inputs. Additionally, it provides options to select the backend for displaying the assembly output, which is useful for checking and debugging constraints.

playground2

Thanks to Logan's efforts, the online playground represents a significant advancement in enriching the development experience, particularly in prototyping, testing, and debugging noname code. Even in the absence of a language server for linting and syntax highlighting in editors, this playground addresses existing gaps by providing an additional debugging tool with user-friendly UI. It potentially lays the groundwork for future enhancements that will continue to improve the developer experience and streamline the development process.

R1CS Optimization

After the refactor to support multiple backends, we have added an R1CS (Rank-1 Constraint System) backend to noname as mentioned in our previous blog post. Initially, each operation generated a constraint in R1CS, which was a straightforward but naive approach. The performance of the proving system is significantly impacted by the size and number of constraints, making optimization crucial.

To enhance performance, we optimized R1CS constraint generation by accumulating linear combinations and only adding constraints when necessary. Specifically, constraints are required in cases of comparing equality or arithmetic multiplication operations.

This optimization has led to a significant reduction in the number of constraints generated. For example, consider the constraints generated for the iterate.no example:

Before optimization:

v_3 == (v_1 + -1) * (1)
v_4 == (v_3 + 3) * (1)
v_4 == (v_2) * (1)

After optimization:

v_1 + 2 == (v_2) * (1)

The first two constraints are generated for the addition and subtraction operations, which can be accumulated as an intermediate linear combination. Then, when asserting equality, the accumulated linear combination is used to generate the necessary constraint.

While this optimization significantly improves performance, it presents a challenge for debugging since constraints no longer map one-to-one with the code. To address this, we are planning to retain all intermediate linear combinations so that corresponding operations can be traced back to the code. This debug information could be exported as a JSON file and displayed in the playground, aiding developers in debugging and ensuring the accuracy of constraints.

Other Contributions from the Community

In addition to these major updates, numerous community contributions have significantly enhanced noname. We would like to pay tribute to the following invaluable efforts by the community members. We are immensely grateful for their support.

Enhancements

Fixes

Documentation

Conclusion

We hope these organic enhancements provide developers with greater flexibility, efficiency, and a more intuitive development experience. Again, we extend our heartfelt gratitude to all contributors who have been a part of this journey.

In case you are interested in knowing more about how the noname internal works, you can check out our more recent code walkthrough video.

More resources:

Keep reading
Recommended

noname: ZK app developers should be able to see down to the constraints

Zero-knowledge apps are evolving, and we've been diving into their two main forms: VM instructions and arithmetic circuits. Understanding the "assembly" layer is crucial for developers, especially when optimizing and ensuring security. We’ve played around with a new toy language called **noname**, blending Golang and Rust vibes to make zkApps more understandable. With **noname**, you get detailed insights about how your code translates into gates, offering a clearer picture of the underlying "assembly" and helping pinpoint compiler bugs. If you're curious about enhancing your low-level programming skills or peeking into circuit construction, check out our experiments and see if this inspires you to create better debugging tools!

David Wong · June 03, 2023

The zero-knowledge attack of the year might just have happened, or how Nova got broken

Last week, a surprising paper revealed a major vulnerability in Microsoft's Nova, a leading zero-knowledge proof (ZKP) system, that showcased a false computation with a valid proof. This discovery is especially intriguing given the complexity and robustness typically associated with ZKP systems. We had just launched our company to tackle bugs in ZKP frameworks, and while we anticipated issues, complete breakdowns of systems themselves are rare. Nova's issue revolved around cycles of elliptic curves, showing how small implementation oversights in complex cryptographic systems can lead to significant errors. This post dives into the intricate mechanics of Nova and the discovery process, offering a fascinating glimpse into why rigorous specifications are crucial in cryptography.

David Wong · July 02, 2023

Public report of auditing Penumbra's circuits

We conducted an audit of Penumbra's main circuits and found eight issues, including the critical "double spend" and "double vote" bugs, which the Penumbra team promptly fixed. Our findings highlight Penumbra's robust documentation and code testing. Readers will get insights into how Penumbra uses zero-knowledge proofs for privacy, its decentralized exchange features, and its governance model. The post also provides detailed pseudocode for various cryptographic protocols, emphasizing how Penumbra ensures secure and private transactions. It's a deep dive into the technical details for those intrigued by privacy-focused blockchain technologies.

ZK/SEC · August 24, 2023
More to explore

Circle STARKs: Part IV, Arithmetizing Circles

In this final part of our series on Circle STARKs, we tie together concepts from Mersenne prime fields, circle curves and bivariate polynomials to showcase a comprehensive Circle STARK protocol. We detail the process of arithmetization over the circle curve, introducing Circle FRI as a low-degree test and walk through the complete Circle STARK construction. Readers will explore how trace commitments, constraint batching and low-degree proofs combine for efficient verifiable computation, delving into the nuances of proof validity and security analysis.

Varun Thakore · November 17, 2025

Ditch the Pump & Dump Drama: Your ZK Tech Hub Awaits

We're excited to share our latest project, zkNews, your go-to hub for all things zero-knowledge. This link aggregator keeps you updated with the latest ZK news, groundbreaking research, and cutting-edge projects. Dive into the ZK world, stay informed with breaking announcements, and explore innovative tools and applications. Join our beta and help shape the future of zkNews with your feedback. Big plans are in the works, and we'd love you to be a part of it!

ZK/SEC · May 31, 2024

Proximity Gaps: What Happened and How Does It Affect our SNARKs

A series of recent papers just disproved the proximity gaps conjecture, which has everyone wondering if hash-based SNARKs are in trouble. We break down what actually happened using some helpful visuals. Think of it as understanding which parameter choices are safe versus which ones are now confirmed to be risky. The post walks you through the different security zones (proven safe, conjectured safe, and definitely not safe), explains how these new results connect to other open math problems, and shows what it means for real-world SNARKs in terms of proof sizes and performance trade-offs.

Nicolas Mohnblatt · November 14, 2025