# Zero-Knowledge Implementation

## Why Zero-Knowledge?

Imagine you want to enter a bar that requires you to be 18+.

**Traditional way:** Show your ID card → Bartender sees your name, address, exact birthday.

**Zero-Knowledge way:** Prove "I am 18+" without revealing anything else.

### How PolyPay Uses This

In a traditional multisig account:

* Everyone sees WHO signed each transaction
* Signer addresses are public on blockchain

In PolyPay:

* You prove "I know the secret for an authorized membership ID" without revealing your EOA address
* Your Ethereum address stays private, only the membership ID is visible

## The Four Proofs

When you sign a transaction in PolyPay, the ZK circuit proves four things simultaneously. The circuit is written in [Noir](https://noir-lang.org), a domain-specific language for zero-knowledge proofs. New accounts use [UltraHonk](https://docs.zkverify.io/architecture/verification_pallets/ultrahonk) as the proving backend, while legacy accounts (contractVersion 1) continue using UltraPlonk.

### Proof 1: "I know the transaction"

**Problem:** We need to verify you're signing the correct transaction, not a fake one.

**Solution:** You provide a "fingerprint" (hash) of the transaction using [Poseidon Hash](https://www.poseidon-hash.info). The circuit checks this fingerprint matches.

**Analogy:** Like a sealed envelope - you prove the content inside matches what's expected.

**Why Poseidon?** [zkVerify](https://docs.zkverify.io) limits public inputs to 32 fields. Since transaction hash is 32 bytes, we compress it into a single field using Poseidon hash.

### Proof 2: "I signed it"

**Problem:** Anyone could claim they signed a transaction.

**Solution:** The circuit verifies your [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) signature is valid - the same signature standard Ethereum uses.

**Analogy:** Like your handwritten signature - unique to you and verifiable.

**Technical note:** Ethereum's `personal_sign` adds a prefix `"\x19Ethereum Signed Message:\n32"` before signing. The circuit reconstructs this prefixed message and hashes it with [Keccak256](https://keccak.team/keccak.html) before verification.

### Proof 3: "I am authorized"

**Problem:** How to prove you're in the signers list?

**Solution:**

* Each signer has a membership ID stored as: `commitment = hash(secret, secret)` (the field is named `commitment` on-chain and in the circuit)
* The circuit proves you know the secret for a given membership ID
* The smart contract checks if that membership ID exists in the signers list

**Analogy:** Imagine a club membership list. You prove "I know the password for one of these memberships" and the club verifies that membership is on the list.

**How it works:**

The circuit verifies: `hash(secret, secret) == commitment`

Then the smart contract checks: `commitment in signers list?`

This two-step verification ensures only authorized signers can sign transactions while keeping their Ethereum addresses private.

### Proof 4: "I haven't signed before"

**Problem:** Same signer could submit multiple proofs for one transaction.

**Solution:**

* Each signature generates a unique "nullifier": `nullifier = hash(secret, tx_hash)`
* Smart contract stores used nullifiers
* Same person + same transaction = same nullifier = rejected

**Analogy:** Like a voting ballot with a unique barcode - you can only use it once.

## Complete Flow

1. **User Signs:** User signs tx\_hash with their Ethereum wallet → Produces signature, pub\_key\_x, pub\_key\_y
2. **Frontend Generates Proof:** [Noir](https://noir-lang.org) circuit receives private inputs (signature, pub\_key, secret, tx\_hash) and public inputs (tx\_hash\_commitment, commitment, nullifier) → Outputs ZK Proof
3. **Backend Verifies via zkVerify:** Proof submitted to [zkVerify](https://docs.zkverify.io) for verification → Returns aggregation\_id, attestation
4. **Smart Contract Executes:** When threshold signatures reached, contract verifies all proofs on-chain, checks nullifiers not used, checks each membership ID is in the current signers list, then executes transaction

## Circuit Inputs Reference

### Private Inputs (Hidden from everyone)

| Input           | Type      | Description                                    |
| --------------- | --------- | ---------------------------------------------- |
| signature       | \[u8; 64] | ECDSA signature (r, s) without recovery byte   |
| pub\_key\_x     | \[u8; 32] | Public key X coordinate                        |
| pub\_key\_y     | \[u8; 32] | Public key Y coordinate                        |
| secret          | Field     | Signer's secret (from signing "noir-identity") |
| tx\_hash\_bytes | \[u8; 32] | Transaction hash to sign                       |

### Public Inputs (Visible on-chain)

| Input                | Type  | Description                                                                   |
| -------------------- | ----- | ----------------------------------------------------------------------------- |
| tx\_hash\_commitment | Field | Poseidon hash of tx\_hash                                                     |
| commitment           | Field | hash(secret, secret) - the user's membership ID, checked against signers list |
| nullifier            | Field | Prevents double-signing                                                       |

## More Detail

* [Circuit Code Walkthrough](/polypay-docs/developer-documentation/circuit-code-walkthrough.md)

## Learn More

* [Noir Language Documentation](https://noir-lang.org/docs)
* [UltraHonk Proving System](https://docs.zkverify.io/architecture/verification_pallets/ultrahonk)
* [zkVerify Documentation](https://docs.zkverify.io)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://q3labs.gitbook.io/polypay-docs/zero-knowledge-implementation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
