Security and Transparency

UPA Security and Transparency

NEBRA's Universal Proof Aggregation (UPA) protocol enables applications on Ethereum to outsource the task of verifying zero-knowledge proofs (ZKPs) to an off-chain aggregator. The UPA verifies ZKPs at a lower cost than direct verification in a smart contract, while maintaining the same security and soundness guarantees.

NEBRA is committed to security and transparency, and we believe that users of the UPA have a right to examine the code and read the audit reports themselves before entrusting us with their proofs. This page serves as a security-oriented guide to the UPA protocol and its current implementation. We begin with an overview of the UPA's guarantees and potential concerns, then provide details on the on- and off-chain components of the UPA.

UPA Guarantees

At a high level, the security guarantees of the UPA are:

  • Proof validity: If the UPA marks a given proof ID as "verified" then the user must have submitted a valid proof to the UPA.

  • Equal privacy: the UPA does not require additional information beyond the proof and public inputs. It therefore maintains the existing level of privacy offered by each application.

Additionally, the UPA offers a censorship-resistance guarantee via on-chain proof submission:

  • Proofs submitted on-chain are indexed by the UPA contract and must be verified in this order (assuming they are valid). Failure to include valid proofs or failure to respect the ordering of proofs exposes the aggregator to a penalty.

Application Developer Responsibilities

The UPA cannot address the following security concerns. It is the application developer's responsibility to prevent:

  • Replay attacks: once a proof has been verified by the UPA, its inputs are forever marked as "verified." Application developers must decide whether it should be possible to reuse these inputs for multiple transactions. When reuse is undesirable the application itself must prevent this with some mechanism such as a nullifier. (This should already be the case, because even without the UPA it is possible to generate multiple valid proofs for the same set of inputs!)

  • Under-constrained circuits: it is the application developer's responsibility to write circuits that correctly enforce the in-circuit portion of their application logic.

Liveness and Redundancy

Developers should understand that the UPA protocol has an off-chain component, and therefore cannot guarantee liveness equal to that of Ethereum. NEBRA will strive to match Ethereum's liveness by building redundancy into the prover network that powers the UPA's off-chain component. Nonetheless, we recommend that application developers build in resilience to any potential outages the UPA may experience.

This simply means that application smart contracts should maintain the ability to directly verify users' proofs, as they would in the absence of the UPA. Then, in the event of UPA downtime, users can choose to submit their transaction with "direct verification" rather than "UPA verification," albeit at a higher cost.

UPA On-Chain Component

The UPA's on-chain component is a collection of smart contracts on Ethereum. Its responsibilities include

  • Verifying Key Registration: the UPA records verifying keys submitted by developers and assigns to each key a unique circuit ID.

  • Proof Submission: the UPA accepts (circuit ID, proof, inputs) tuples and

    • Ensures that the circuit ID belongs to a previously-registered VK

    • Computes a proof ID from the circuit ID and inputs

    • Indexes submissions, determining the order in which the aggregator must verify them

    • Records a "proof digest," identifying the proof for potential censorship challenges

  • Aggregated Proof Verification: the UPA checks proofs submitted by the aggregator, ensuring that only valid application proofs will be marked as verified

  • Verification Queries: allowing applications to query whether a given proof ID has been marked as verified

  • Fee Collection: determining and collecting the fee due for proof aggregation

  • Fee Disbursement: ensuring that the aggregator can collect the fee only after aggregating proofs

  • Censorship resistance: handling censorship challenges and punishing the aggregator for censoring valid proofs

The on-chain portion of the protocol is specified here and the smart contracts implementing the protocol can be found here. All contracts are written in Solidity (with one exception, see below). A list of current deployments can be found here.

The on-chain protocol and its implementation have been audited by ABDK and Zellic. Their reports may be found here.

An open-source SDK for integrating with the UPA is available here. It is unaudited, and NEBRA assumes no responsibility for its behavior. Please submit any bug reports or feature requests in NEBRA's Telegram channel.

UPA Off-Chain Component

The UPA's off-chain component is a collection of zero-knowledge circuits that ensure that only valid application proofs will be marked as verified by the UPA. More precisely, the soundness guarantee is that if a given proof ID is an input to a valid aggregated proof, then the aggregator has knowledge of a valid application proof for the verifying key and public inputs corresponding to that proof ID.

Applications using the UPA therefore receive a cryptographic guarantee that their users submitted valid application proofs for any inputs that the UPA marks as verified.

The off-chain portion of the protocol is specified here and its open-source implementation as Halo2 circuits can be found here. The off-chain protocol and its implementation have been audited by ABDK and Zellic. Their reports may be found here.

Proofs for these circuits are produced by a permissioned off-chain aggregator. This aggregator is operated by NEBRA (though the protocol allows for the role to eventually be shared or even decentralized). We emphasize that although the aggregator is centralized, it is not trusted. Even a malicious aggregator cannot aggregate invalid application proofs, thanks to the cryptographic soundness guarantee of the circuits. We explain below how the UPA smart contracts verify the work done by the aggregator.

Because the aggregator is untrusted, there is technically no need to inspect its source code. Nonetheless, NEBRA has open-sourced a prover tool capable of performing all the core functionality of the aggregator.

AggregatedProofVerifier Contract

The AggregatedProofVerifier contract is the on-chain component of the UPA that is responsible for verifying the aggregated proofs produced by the aggregator. This contract links the on- and off-chain parts of the protocol, allowing the UPA contract to verify the aggregator's claim that a list of application inputs have valid proofs. It plays an essential role in the overall soundness of the UPA.

Unlike the other UPA contracts, the AggregatedProofVerifier is not written in Solidity. It is generated by the open-source snark-verifier library from the verifying key of the Halo2 circuit that produces aggregated proofs (the "outer" circuit). This library generates an on-chain verifier in the form of Yul code, a low-level assembly language that is not really human-readable (at least not easily).

Auditing the AggregatedProofVerifier contract is therefore more challenging than the main UPA contracts, but not impossible. Because the contract is generated deterministically using open-source tools and data, any third party is free to repeat the procedure and check that the resulting bytecode matches the actual deployment. This is similar to how ordinary smart contracts written in Solidity can have their source code verified by blockchain explorers like Etherscan; the blockchain explorer compiles the provided Solidity code and checks that it matches the contract's EVM bytecode.

In the AggregatedProofVerifier's case, the compilation pipeline is more complex. To ensure the UPA protocol's transparency, we provide a script that runs through this pipeline and compares the result to our on-chain deployment. The pipeline that produces this bytecode is:

  1. Trusted Input: The pipeline begins with a KZG structured reference string (SRS). The UPA uses an SRS produced by the Perpetual Powers of Tau Ceremony (PPoT), a secure multiparty computation orchestrated by the Ethereum Foundation's Privacy and Scaling Explorations group.

  2. Transform the SRS to a Halo2-compatible format. The PPoT SRS is in a format that is not immediately usable by Halo2 circuits. An open-source tool (written by Kobi Gurkan and modified by Axiom Crypto and NEBRA) converts the SRS to the appropriate format.

  3. Compute Halo2 Verifying Key. This step takes the description of the outer circuit as a Halo2 circuit and produces a verifying key from the SRS produced by the previous step. The open-source tool that performs this step is NEBRA's prover tool, which relies on the halo2-proofs library for key generation.

  4. Generate verifier Yul code. This step takes the Outer Circuit verifying key produced above and the SRS from Step 2 and produces Yul code that verifies the proofs submitted by the aggregator to the UPA contract. The open-source tool that performs this step is the snark-verifier library.

  5. Compile to EVM bytecode. Finally, solc compiles the Yul verifier generated in the previous step to EVM bytecode. For the sake of comparison to the deployed contract code, it is important to use the same solc version (v0.8.17) used by NEBRA during deployment.

Last updated