---
# System prepended metadata

title: 'Umbra: Private UTXO Payment System'

---

# Umbra: Private UTXO Payment System

## Technical Overview

### Table of Contents
1. [Architecture Overview](#architecture-overview)
2. [System Components](#system-components)
3. [Privacy Model](#privacy-model)
4. [Cryptographic Primitives](#cryptographic-primitives)
5. [Zero-Knowledge Proofs](#zero-knowledge-proofs)
6. [Encrypted Storage](#encrypted-storage)
7. [Security Model](#security-model)

---

## Architecture Overview

Umbra is a privacy-preserving payment system built on Ethereum (Sepolia) using a UTXO model with zero-knowledge proofs. It enables private transfers of USDC where transaction amounts and relationships between sender/receiver are hidden from observers.

### High-Level Architecture

```
                    ┌──────────────────────────────────────────────────────────┐
                    │              ETHEREUM (Sepolia) - Source of Truth        │
                    │  ┌────────────────────────────────────────────────────┐  │
                    │  │            PrivateUTXOLedger Contract              │  │
                    │  │  - SP1 Groth16 Verifier (on-chain proof verify)    │  │
                    │  │  - UTXO Merkle Tree State                          │  │
                    │  │  - Nullifier Registry (double-spend prevention)    │  │
                    │  │  - USDC Custody                                    │  │
                    │  └────────────────────────────────────────────────────┘  │
                    │  ┌─────────────────────┐  ┌────────────────────────────┐ │
                    │  │EncryptedContacts    │  │PrivatePaymentRequests      │ │
                    │  │- Tag-based lookup   │  │- Encrypted payment reqs    │ │
                    │  │- ECIES encryption   │  │- Tag-based recipient IDs   │ │
                    │  └─────────────────────┘  └────────────────────────────┘ │
                    └──────────────────────────────────────────────────────────┘
                                              ▲
                                              │ Submit proven transactions
                                              │ (Gasless via Account Abstraction)
                    ┌─────────────────────────┴─────────────────────────────┐
                    │                                                       │
                    ▼                                                       │
          ┌──────────────────┐                                              │
          │  Relayer Server  │                                              │
          │  - Alchemy Smart │                                              │
          │    Account (AA)  │                                              │
          │  - Gas Sponsored │                                              │
          │                  │                                              │
          └──────────────────┘                                              │
                    ▲                                                       │
                    │ Proof + encrypted notes                               │
                    │                                                       │
┌─────────────────────────────────────────────────────────────────┐         │
│                         Wallet UI (Next.js)                     │         │
│  - Key derivation from MetaMask signature                       │─────────┘
│  - UTXO scanning and decryption (client-side)                   │  Read state
│  - Transaction construction                                     │  (events, roots)
│  - Dual signature model (NullifierSig + TxSig)                  │
│  - All cryptography happens client-side                         │
└─────────────────────────────────────────────────────────────────┘
                    │
                    │ Witness data (notes, signatures, indices)
                    ▼
          ┌──────────────────┐
          │  Prover Server   │
          │  - Express.js    │
          │  - Spawns Rust   │
          │    prover-host   │
          └──────────────────┘
                    │
                    │ Proof request via SP1 SDK
                    ▼
          ┌──────────────────┐
          │ Succinct Prover  │
          │ Network          │
          │ - zkVM Execution │
          │ - Groth16 Proof  │
          │ (~30-60 seconds) │
          └──────────────────┘
```

### Data Flow

1. **Login**: User signs a fixed message with MetaMask → signature hashed → secp256k1 keypair derived
2. **Scan**: Wallet reads `OutputCommitted` events from contract, attempts ECIES decryption with private key
3. **Send**: User constructs transaction, signs commitments (NullifierSig + TxSig), sends witness to Prover Server
4. **Prove**: Prover Server precomputes values, submits to Succinct Network for Groth16 proof generation
5. **Submit**: Wallet receives proof, sends to Relayer (gasless)
6. **Verify**: Contract verifies SP1 proof on-chain, checks nullifiers unused, updates Merkle state

---

## System Components

| Component | Role | Trust Level |
|-----------|------|-------------|
| **PrivateUTXOLedger** | On-chain source of truth. Verifies SP1 proofs, manages UTXO state, prevents double-spends | Trustless (code is law) |
| **EncryptedContacts** | On-chain encrypted address book storage | Trustless (encryption is client-side) |
| **PaymentRequests** | On-chain encrypted payment request storage | Trustless (encryption is client-side) |
| **Wallet UI** | All client-side cryptography: key derivation, UTXO scanning, encryption, signing | Self-custody (keys never leave browser) |
| **Prover Server** | Orchestrates proof generation via Succinct Network | Not trusted - proofs verified on-chain |
| **Relayer** | Submits transactions via Alchemy Smart Account, pays gas | Cannot steal funds or modify proofs |
| **Succinct Network** | Generates Groth16 proofs from SP1 zkVM execution | Not trusted - proofs verified on-chain |

### Contract Addresses (Sepolia)

| Contract | Address |
|----------|---------|
| PrivateUTXOLedger | `0x42ae920DFD0d25Ac014DFd751bd2ff2D2fBa0443` |
| EncryptedContacts | `0x813e453D13dE769922aFc40780FADeF3AC6d939D` |
| PaymentRequests | `0x3c4d73f028d99eC10eB15fED99AC5080C99A4a4d` |

---

## Privacy Model

### What's Hidden vs. Public

| Data | On-Chain Visibility | Who Can Decrypt |
|------|---------------------|-----------------|
| Note commitment | Public | No one (hash) |
| Encrypted note data | Public (in events) | Only recipient |
| Nullifier | Public (when spent) | No one (hash of signature) |
| Transaction amount | Hidden | Sender & Recipient |
| Sender identity | Hidden | Recipient only (via contact name in note) |
| Recipient identity | Hidden | Sender only |
| Contacts | Encrypted on-chain | Only owner |
| Payment requests | Encrypted on-chain | Only recipient |

### Key Privacy Properties

- **Amount Privacy**: Transaction values are never revealed on-chain
- **Sender Privacy**: Cannot determine who sent a payment from on-chain data
- **Recipient Privacy**: Cannot determine who received a payment
- **Unlinkability**: Cannot link inputs to outputs in a transaction (nullifiers hide source notes)

---

## Cryptographic Primitives

### Key Derivation

Users derive a deterministic keypair from their Ethereum wallet signature:

```
User signs fixed message → SHA-256(domain + signature) → secp256k1 Private Key → Public Key
```

```typescript
const domain = 'utxo-prototype-v1-key-derivation:'
const privateKey = sha256(domain + signature)
const publicKey = secp256k1.getPublicKey(privateKey, true) // compressed
const address = '0x' + hex(publicKey) // "Private Address"
```

- Keys exist only in browser memory - never transmitted
- Same MetaMask signature always produces same keypair (deterministic)

### Note Structure

A note represents a private UTXO:

```rust
Note {
    amount: u64,              // Value in USDC micro-units (6 decimals)
    owner_pubkey: [u8; 32],   // Recipient's X-coordinate (from compressed pubkey)
    blinding: [u8; 32]        // Random factor for commitment uniqueness
}
```

### Note Commitment

Domain-separated Blake3 hash ensuring collision resistance:

```
Commitment = Blake3(DOMAIN_NOTE_COMMITMENT || amount_le(8) || owner_pubkey(32) || blinding(32))
```

Where:
- `DOMAIN_NOTE_COMMITMENT = b"NOTE_COMMITMENT_v1"`
- `amount_le` = 8-byte little-endian encoding
- Commitment is stored on-chain in Merkle tree
- Blinding factor ensures two notes with same amount/owner have different commitments

### Nullifier Construction

Prevents double-spending while hiding which note is being spent:

```
Nullifier = Blake3(DOMAIN_NULLIFIER || NullifierSignature)
```

Where:
- `DOMAIN_NULLIFIER = b"NULLIFIER_v1"`
- `NullifierSignature` = ECDSA signature over `ethSignedMessage(keccak256(commitment))`

**Security Properties:**
- Only the owner can compute the nullifier (requires private key to sign)
- Cannot be computed from commitment alone
- Published on-chain when note is spent → prevents double-spend
- Deterministic: same note + same key always produces same nullifier

### Dual Signature Model

Each input note requires TWO signatures for security:

1. **NullifierSig**: Used to derive the nullifier (privacy)
   - Signs: `keccak256(commitment)` with Ethereum prefix
   - Purpose: Deterministic nullifier derivation

2. **TxSig**: Proves ownership for the ZK circuit (anti-theft)
   - Signs: `keccak256(commitment)` with Ethereum prefix
   - Purpose: Recovered pubkey must match note's `owner_pubkey`

Both signatures use the same message but serve different purposes in the security model.

### ECIES Encryption (Notes)

Notes are encrypted to the recipient's public key using ECIES:

```
1. Generate ephemeral keypair (r, R = r*G)
2. Compute shared secret: S = ECDH(r, recipient_pubkey)
3. Derive AES key: K = HKDF-SHA256(S, info="utxo-prototype-v1-encryption")
4. Encrypt: AES-256-GCM(K, nonce, plaintext)
5. Output: (R || nonce || ciphertext)
```

- Ephemeral pubkey (R) is stored on-chain in `OutputCommitted` events
- Only recipient can decrypt using their private key
- Forward secrecy via ephemeral keys

### ECIES Encryption (Contacts & Payment Requests)

Contacts and payment requests use a similar ECIES scheme:

```
1. Generate ephemeral keypair
2. ECDH shared secret with owner/recipient pubkey
3. HKDF-SHA256 key derivation (domain: "utxo-contacts-v1" or "utxo-requests-v1")
4. AES-256-GCM encryption
5. Store: ephemeralPub(33) || nonce(12) || ciphertext
```

---

## Zero-Knowledge Proofs

### SP1 zkVM

We use Succinct's SP1 zkVM which allows writing ZK circuits in Rust. The circuit is compiled to a RISC-V ELF binary and executed in a zkVM that generates Groth16 proofs verifiable on Ethereum.

### Optimized Proving Path

The system uses an **optimized path** where expensive ECDSA operations are performed on the host (prover server) rather than inside the zkVM:

```
Host (Rust):
  - Verify signatures via k256 library
  - Compute nullifiers from signatures
  - Compute commitments from note data
  - Pass precomputed values to zkVM

zkVM (SP1):
  - Verify precomputed commitments match note data (Blake3)
  - Use precomputed nullifiers (no ECDSA in circuit)
  - Verify value conservation
  - Output ABI-encoded public values
```

This reduces proving time by 40-60% compared to in-circuit ECDSA.

### What the Circuit Proves

| Statement | What It Proves | Why It Matters |
|-----------|----------------|----------------|
| Commitment Validity | Input commitments match `Hash(note)` | Cannot spend notes that don't exist |
| Nullifier Correctness | Nullifiers derived from valid signatures | Deterministic spend tracking |
| Output Validity | Output commitments properly formed | New notes are correctly structured |
| Value Conservation | `sum(inputs) >= sum(outputs)` | Cannot create money from nothing |

### Circuit Logic (Simplified)

```rust
// VERIFY precomputed values match note data
for (note, precomputed_commitment) in inputs {
    assert!(commit(note) == precomputed_commitment);
}

// VERIFY value conservation
assert!(sum(input_amounts) >= sum(output_amounts));

// OUTPUT (public, ABI-encoded):
//   - old_root (contract verifies this matches current state)
//   - new_root (new Merkle root after adding outputs)
//   - nullifiers (marks inputs as spent)
//   - output_commitments (new notes added to tree)
```

### Security Note

The standard path (in-circuit ECDSA) is **disabled** for security. The system requires precomputed values where:
- Host verifies signatures before proving
- zkVM verifies precomputed values via hash matching
- Contract verifies the proof and checks nullifiers haven't been used

---

## Encrypted Storage

### EncryptedContacts Contract

Stores encrypted address book entries on-chain:

```solidity
mapping(bytes8 => uint256[]) public contactsByOwner;  // ownerTag => contactIds
mapping(uint256 => Contact) public contacts;

struct Contact {
    bytes8 ownerTag;      // First 8 bytes of keccak256(owner_pubkey)
    bytes encryptedData;  // ECIES-encrypted contact data
    uint256 timestamp;
}
```

**Owner Tag Computation:**
```typescript
ownerTag = keccak256(publicKeyHex).slice(0, 8)  // First 8 bytes
```

### PaymentRequests Contract

Stores encrypted payment requests:

```solidity
mapping(bytes8 => uint256[]) public requestsByRecipient;  // recipientTag => requestIds
mapping(uint256 => Request) public requests;

struct Request {
    bytes8 recipientTag;
    bytes encryptedPayload;  // ECIES-encrypted: {requesterName, requesterAddress, amount, reference, message}
    uint256 timestamp;
    RequestStatus status;    // Pending, Approved, Rejected, Expired
}
```

---

## Security Model

### Threat Model

| Threat | Mitigation |
|--------|------------|
| Double-spend | Nullifier registry on-chain - each nullifier used once |
| Forged ownership | ECDSA signature verification (host + zkVM hash check) |
| Invalid amounts | Value conservation check in ZK circuit |
| Malicious prover | All proofs verified on-chain by SP1 Verifier |
| Front-running | Nullifiers hide which specific note is being spent |
| Key theft | Keys derived from wallet signature, exist only in browser |
| Replay attacks | Nullifiers are one-time use, nonces in Permit2 |
| Proof-binding bypass | Contract decodes outputs from `publicValues` (not separate params) |

### Trust Assumptions

| Component | Security Trust | Liveness Trust |
|-----------|----------------|----------------|
| Smart Contract | Full (verified on-chain) | Ethereum availability |
| Succinct Network | None (proofs verified on-chain) | Required for proof generation |
| Relayer | None (cannot modify proofs) | Required for gasless tx submission |
| Wallet UI | Self-custody | User's device |

### Cryptographic Assumptions

- **secp256k1 ECDSA**: Discrete log hardness
- **Blake3**: Collision resistance, preimage resistance
- **Groth16**: Knowledge-of-exponent assumption, q-PKE
- **AES-256-GCM**: Standard symmetric encryption security
- **HKDF-SHA256**: PRF security of HMAC-SHA256

### Key Security Properties

- **Soundness**: Cannot create valid proof without knowing private keys
- **Zero-Knowledge**: Proof reveals nothing about transaction details
- **Unlinkability**: Cannot link sender to recipient from on-chain data
- **Non-Malleability**: Proofs cannot be modified without invalidation

---

## Gasless Transactions

All user-facing transactions are gasless via Alchemy Account Abstraction:

### Relayer Architecture

```javascript
// Alchemy Smart Account with Gas Policy
const smartAccountClient = await createLightAccountAlchemyClient({
    chain: sepolia,
    signer: LocalAccountSigner.privateKeyToAccountSigner(RELAYER_PRIVATE_KEY),
    policyId: GAS_POLICY_ID,  // Alchemy gas sponsorship
});
```

### Supported Gasless Operations

| Endpoint | Operation |
|----------|-----------|
| `/api/deposit-with-permit` | Deposit USDC (Permit2 signature) |
| `/api/submit-tx` | Private transfer (proof required) |
| `/api/withdraw` | Withdraw to public address (proof required) |
| `/api/save-contact` | Save encrypted contact |
| `/api/create-payment-request` | Create encrypted payment request |

---

## References

- [SP1 Documentation](https://docs.succinct.xyz/)
- [Groth16 Paper](https://eprint.iacr.org/2016/260)
- [ECIES Specification](https://cryptopp.com/wiki/Elliptic_Curve_Integrated_Encryption_Scheme)
- [Blake3 Specification](https://github.com/BLAKE3-team/BLAKE3-specs)
- [Alchemy Account Kit](https://accountkit.alchemy.com/)
- [Permit2](https://github.com/Uniswap/permit2)
