> For the complete documentation index, see [llms.txt](https://q3labs.gitbook.io/polypay-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://q3labs.gitbook.io/polypay-docs/zero-knowledge-implementation.md).

# 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)
