5/5
## Verifying ECDSA Signatures On-Chain with Noir and ZKPs This lesson recaps our journey into leveraging Zero-Knowledge Proofs (ZKPs) with the Noir programming language to verify Elliptic Curve Digital Signature Algorithm (ECDSA) signatures. We'll explore how this verification process can be seamlessly integrated with on-chain smart contracts, bridging the gap between private computation and public blockchain transparency. ## Mastering Noir for Zero-Knowledge Circuits To effectively build our ZK circuits for signature verification, we first familiarized ourselves with key Noir concepts: * **Managing Dependencies:** Noir allows for the use of external libraries or modules, known as dependencies. For instance, to perform ECDSA-related operations, we might use a dependency like `dep::ecrecover`. This modularity simplifies development by allowing us to leverage pre-built, audited functionalities. * **Working with Array Types:** When defining functions in Noir, especially those interacting with cryptographic data, fixed-size arrays are crucial. We learned to specify array inputs by defining both the element type (e.g., `u8` for a byte) and the fixed size (e.g., `[u8; 32]` for a 32-byte array). This explicit typing is essential for the deterministic nature of ZK circuits. ## The ECDSA Signature Verification Workflow with Noir Verifying an ECDSA signature using a Noir circuit involves a meticulous workflow, from preparing inputs to defining the core circuit logic. **1. Public Key Handling:** The process begins with obtaining the public key associated with the signature. * **Source:** For development and testing, public keys can be extracted from a local development environment like a Foundry keystore. * **Decomposition:** An ECDSA public key is typically represented by its X and Y coordinates on the elliptic curve. These coordinates need to be separated as they will be individual inputs to our Noir circuit. **2. Input Preparation for the Noir Circuit:** Raw cryptographic data (public key coordinates, the signature itself, and the hashed message) must be formatted into `u8` arrays of specific, fixed sizes. * **External Scripting:** This conversion is often handled by an external script, perhaps written in JavaScript or TypeScript. This script takes the raw byte data and transforms it into the array format expected by Noir. * **Prover Configuration:** These formatted arrays are then supplied as inputs to the Noir circuit, commonly facilitated through a `Prover.toml` file, which specifies the input values for the proof generation process. **3. Crafting the Noir Verification Circuit (`main.nr`):** The heart of our off-chain logic resides in the `main.nr` file, which houses the Noir circuit designed to verify an ECDSA signature. * **Circuit Inputs:** The `main` function in our Noir circuit is designed to accept the following inputs: * `pub_key_x: [u8; 32]`: The 32-byte X-coordinate of the public key. * `pub_key_y: [u8; 32]`: The 32-byte Y-coordinate of the public key. * `signature: [u8; 64]`: The 64-byte ECDSA signature, typically the r and s components concatenated. * `hashed_message: [u8; 32]`: The 32-byte hash of the message that was originally signed. * `expected_address: Field`: The Ethereum address that is expected to be derived from the provided public key. This is a public input used to confirm the public key's authenticity. * **Circuit Logic:** 1. The circuit utilizes an `ecrecover` function (imported from a dependency) to attempt to recover an Ethereum address. This function typically takes the `pub_key_x`, `pub_key_y`, `signature`, and `hashed_message` as its arguments. 2. Crucially, the circuit then asserts that the `address` recovered by `ecrecover` matches the `expected_address` that was passed in as a public input. 3. If this assertion holds true, it cryptographically proves that the provided signature is valid for the given hashed message and corresponds to the public key from which the `expected_address` is derived. * **Example `main.nr` Snippet:** ```noir // Dependency for ecrecover is implied through its usage use dep::ecrecover; fn main( pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64], hashed_message: [u8; 32], expected_address: Field ) { // Recover the address from the signature and message let recovered_address: Field = ecrecover::ecrecover(pub_key_x, pub_key_y, signature, hashed_message); // Assert that the recovered address matches the expected address assert(recovered_address == expected_address, "Address does not match expected address"); } ``` ## Generating and Verifying Zero-Knowledge Proofs Once the Noir circuit is defined and the inputs are prepared: 1. **Proof Generation (Off-Chain):** A Zero-Knowledge proof is generated based on the circuit and the specific input values. This computation happens off-chain and proves that the circuit's logic was executed correctly with the given inputs, without revealing any private inputs (if any were designated as such). 2. **Off-Chain Verification (Optional):** Before moving to on-chain verification, the generated proof can be verified off-chain. This is a good practice to ensure the circuit behaves as expected and the proof system is functioning correctly. ## Bringing ZK Proofs On-Chain: The Verifier Smart Contract The true power of this system lies in its ability to verify these ZK proofs on a blockchain. * **Circuit Compilation to `Verifier.sol`:** When the Noir circuit (`main.nr`) is compiled, one of the key outputs is a Solidity smart contract, typically named `Verifier.sol`. * **The Verifier Contract's Role:** This `Verifier.sol` contract contains the necessary cryptographic logic to verify proofs that were generated from its corresponding Noir circuit. It essentially embeds the verification part of the ZK-SNARK into a smart contract. * **Enabling On-Chain Verification:** By deploying this `Verifier.sol` contract to a blockchain, we create an on-chain mechanism to validate ZK proofs. A user can generate a proof off-chain (e.g., proving they correctly executed the ECDSA signature verification logic) and submit this compact proof to the deployed Verifier contract. The contract can then efficiently verify the proof's validity on-chain. ## Demystifying the End-to-End ZK Workflow The entire process, from circuit conception to on-chain verification, can be summarized in these steps: 1. **Circuit Development (Off-Chain):** Design, write, and thoroughly test the Noir ZK circuit (e.g., our ECDSA signature verifier). 2. **Verifier Deployment (On-Chain):** Compile the Noir circuit to produce the `Verifier.sol` smart contract and deploy this contract to your target blockchain. 3. **Proof Generation (Off-Chain):** A user (the prover) takes their private inputs (like the signature itself, if kept private) and public inputs, executes the Noir circuit locally using these inputs, and generates a ZK proof. 4. **Proof Submission & Verification (On-Chain):** The user submits the generated ZK proof to the deployed Verifier smart contract on the blockchain. The contract then executes its verification logic to confirm the proof's authenticity. ## Key Insights for ZK Development As you delve deeper into ZKPs and their blockchain integration, keep these crucial points in mind: * **Pace Yourself:** The concepts and steps involved are dense and multifaceted. Don't hesitate to take breaks and revisit material to ensure a solid understanding. * **Off-Chain vs. On-Chain Distinction:** * **Off-Chain Operations:** Writing the ZK circuit (e.g., in Noir) and generating the ZK proof are *always* performed off-chain. These operations can be computationally intensive and are best suited for environments where resources are less constrained than on a blockchain. * **On-Chain Operations:** The *verification* of the ZK proof is what can be efficiently performed **on-chain** using the generated Verifier smart contract. This step is designed to be relatively cheap and fast on the blockchain. * **Bridging ZKPs and Blockchain:** This entire workflow elegantly "melds together" or "meshes" the powerful privacy-preserving capabilities of zero-knowledge proofs with the transparent and auditable nature of on-chain smart contracts. * **Core Use Case – Verifiable Private Computation:** The fundamental advantage is the ability to prove knowledge of a fact (e.g., "I know the private key corresponding to this public key that validly signed this message," or "This signature is indeed valid for this public key and message") without revealing the sensitive underlying information (like the private key or, in some scenarios, even the signature itself if it's treated as a private witness). ## What's Next? Building with On-Chain ZK Verification The skills you've acquired lay the foundation for exciting developments: * **Building Advanced Protocols:** Future lessons will focus on constructing sophisticated protocols that leverage these Verifier smart contracts. Imagine systems where users can prove they signed a message without revealing the message content or the signature directly on-chain, enabling new forms of private interactions. * **Course Challenge – Practical Application:** The main challenge presented is to integrate signature verification into an on-chain project using the ZK techniques learned. This will solidify your understanding and showcase the practical utility of these concepts. * **Upcoming: The ZK Game:** The next section will introduce a "ZK Game." This engaging project will intentionally include a bug, providing a valuable learning opportunity related to a specific ZK concept. By mastering the creation of Noir circuits for tasks like ECDSA signature verification and understanding how to deploy and use their corresponding Verifier smart contracts, you are unlocking a powerful paradigm for building applications that require private computations to be verified in a public, trustless manner on blockchains.
A detailed exploration of Verifying ECDSA Signatures On-Chain with Noir and ZKPs - Delve into building Noir ZK circuits for ECDSA signature verification, covering key concepts like input preparation, array types, and the `ecrecover` function. This lesson guides you through generating proofs off-chain and deploying Verifier smart contracts for on-chain validation, bridging private computation with blockchain transparency.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Noir syntax
Create a witness, a proof, and Solidity verifier contracts
Use the Poseidon commitment scheme
Create ZK circuits and build a full ZK protocol
ZK Merkle trees and hashing in Noir
Verify signatures without revealing the signer
Build the backend for a full-stack ZK application with noir.js and bb.js
How to create proofs and verify them in a front-end
Last updated on June 12, 2025
Duration: 6min
Duration: 1h 11min
Duration: 2h 12min
Duration: 3h 19min
Course Overview
About the course
Noir syntax
Create a witness, a proof, and Solidity verifier contracts
Use the Poseidon commitment scheme
Create ZK circuits and build a full ZK protocol
ZK Merkle trees and hashing in Noir
Verify signatures without revealing the signer
Build the backend for a full-stack ZK application with noir.js and bb.js
How to create proofs and verify them in a front-end
Last updated on June 12, 2025