Skip to content

ConorNethermind/PrivateX402

This is a Proof-of-Concept, and is not intended for in-production use. Nethermind and the authors take no responsibility for the use of the protocol/code in-production.

Private X402

A privacy-preserving payment channel protocol that enables users to allocate budgets to multiple agents without revealing individual amounts on-chain.

Overview

Private X402 lets a user open a payment channel and allocate budgets to many agents inside a single Merkle tree. Payments happen off-chain via signed cumulative receipts. Settlement is on-chain: only the total funding and encrypted data are visible. Agents claim funds by proving membership and decryption correctness.

The protocol supports two interchangeable trust backends:

  • ZK proofs (Noir circuits + Barretenberg): cryptographic settlement verification via SNARKs (planned, not yet integrated)
  • TEE (Intel SGX via Gramine): trusted execution replaces circuit constraints (current MVP)

For the formal protocol specification with mathematical definitions and circuit routines, see docs/protocol-specification.md.

Architecture

Protocol Introduction

User Library ──────── Agent Server ──────── Smart Contracts ──────── TEE / ZK
(crates/user)         (crates/agent)        (contracts/)              (crates/proofs, crates/tee, circuits/)
1. Setup Channel    User → Contract       Fund channel, commit Merkle root, submit P_alloc proof
2. Register         User → Agent          Send Merkle proof + leaf to prove budget allocation
3. Make Payments    User → Agent          Signed cumulative receipts (off-chain)
4. Settle           User → Contract       Submit encrypted amounts + P_settle proof, reclaim remainder
5. Finalize         Contract              Dispute window expires, channel data accumulated into epoch hash
6. Dispute (opt)    Agent → Contract      Challenge incorrect settlement with receipt + Merkle proof
7. Claim            Agent → Contract      Submit P_claim proof to withdraw from finalized epoch

See docs/protocol-specification.md for formal protocol details.

Project Structure

private-x402/
├── circuits/              # Noir ZK circuits
│   ├── lib/              # Shared circuit library
│   ├── user_setup/       # Channel setup circuit (P_alloc)
│   ├── settlement/       # Settlement verification circuit (P_settle)
│   └── agent_claim/      # Claim aggregation circuit (P_claim)
├── contracts/            # Solidity smart contracts (Foundry)
│   ├── src/             # Contract source code
│   ├── test/            # Foundry tests + cross-language fixtures
│   └── script/          # Deployment scripts
├── crates/              # Rust workspace
│   ├── agent/           # Agent HTTP server
│   ├── common/          # Merkle trees, hash chain, blinding, signatures, CLF
│   ├── ethereum/        # Typed blockchain clients + contract bindings
│   ├── proof/           # Proof trait, ProofClient, TEE implementations
│   ├── user/            # User-side channel management
│   └── tee/             # TEE attestation backend
│       ├── guest/       # Enclave binary (runs inside Gramine)
│       ├── host/        # HTTP server + library (registration, renewal)
│       └── config/      # Gramine manifests and mock attestation data
├── docs/                # Protocol specification and roadmap
└── scripts/             # Build automation

Current State & Limitations

Component Status Notes
Smart Contracts Working Full lifecycle, pluggable verifiers
TEE Backend Working (mock) HTTP server, registration, renewal, proof dispatch
Ethereum Crate Working Typed clients for User, Agent, TEE interactions
Agent Server Working Registration with agent_id validation, EIP-191 payment verification
User Library Working Generic over ProofClient, Anvil integration tests. Currently no CLI
Noir Circuits Exist, not integrated Poseidon2 inconsistency, SC stack-too-deep
TEE on real SGX Not tested Only mock mode validated
CLI / UI None Library API only
HTTP 402 flow Not implemented Coinbase integration out of scope
Dispute E2E Not tested Contract unit tests only

See docs/next-steps.md for detailed blockers and roadmap.

Try it out:

We currently provide the following E2E test (crates/user/tests/e2e_integration.rs), which covers the full User-Agent-Blockchain lifecycle:

  1. Deploy contracts on Anvil
  2. Register TEE key
  3. Create channel (setup proof)
  4. Register with agent + make payments
  5. Settle channel
  6. Finalize after dispute window
  7. Verify channel is closed

Requires anvil in PATH; runs with mock TEE mode.

Prerequisites

Rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

The project includes rust-toolchain.toml which automatically selects the correct toolchain (nightly, edition 2024).

Foundry (Forge, Anvil, Cast)

curl -L https://foundry.paradigm.xyz | bash
foundryup

Quick Start

git clone https://github.com/NethermindEth/private-x402.git
cd private-x402
git submodule update --init

# Build contracts
cd contracts && forge install && forge build && cd ..

# Build Rust workspace
cargo build --workspace

# Run all tests (requires anvil in PATH)
cargo test --workspace

Key Abstractions

Proof System

Proof trait (crates/proof/src/proof.rs): Generic over PublicInput, PrivateInput, ProverContext, and Output. Each proof type has a selector (keccak256(flow_name)) enabling dispatch without knowing the concrete type. Implementations: TeeUserSetup<H>, TeeSettlement<H>, TeeAgentClaim.

ProofClient trait (crates/proof/src/io/client.rs): Async prove() method, Send + Sync. This is the integration point; the same application code works with different backends:

  • TeeHttpClient: remote TEE server (production)
  • In-process TEE: synchronous proving wrapped in async (development)
  • NoopProofClient: always returns error (unit tests)

ProofClient uses native async traits (Rust 2024) and is not dyn-compatible. All usage sites use concrete types via generics.

TEE Architecture

The TEE subsystem separates concerns into Guest and Host:

  • Guest (tee/guest): Runs inside the enclave, holds a signing key, dispatches proofs via a selector registry ( PROVERS map). The guest does not depend on proof internals; any Proof implementation can be registered.
  • Host (tee/host): Wraps Guest in a Mutex for thread safety, manages on-chain TEE key registration and attestation renewal. Exposes /prove HTTP endpoint.
  • TeeHttpClient (tee/host): Implements ProofClient trait for remote proof delegation over HTTP.

Cryptographic Primitives

Hasher (crates/common/src/hash/): Keccak256Hasher (default) and Poseidon2Hasher (feature-gated). The same BalanceTree<H> works with either backend via the MerkleHash trait.

BlindingScheme (crates/common/src/blinding/): Hides settlement amounts. Keccak256BlindingScheme wraps uint256 XOR blinding; Poseidon2BlindingScheme operates over BN254 field elements.

SignatureScheme (crates/common/src/signature/): EIP-191 currently, pluggable via SignatureScheme trait.

User Library

UserApp<P: ProofClient> and Channel<P: ProofClient> are generic over the proof backend. The proof client is injected at construction via Arc<P>; the same code works for TEE, ZK, or mock testing. Channel IDs are contract-assigned ( returned from the setup transaction), not client-generated.

Smart Contracts

PrivateX402.sol manages the full channel lifecycle: setup, settlement, finalize/dispute, and agent claim.

Pluggable components (all injected at deployment):

  • Verifiers via IVerifier: TeeVerifier (MVP), MockVerifier (tests), future ZK verifiers
  • Blinding via IBlindingScheme: Keccak256BlindingScheme, Poseidon2BlindingScheme
  • Hashing via IHasher: KeccakHasher, Poseidon2Hasher
  • Signatures via ISignatureVerifier: Eip191Verifier

Epoch hash chain: Finalized channels are accumulated into a rolling hash per epoch. Agents batch-claim across multiple channels in a single proof, reducing gas costs.

Rust Codebase

Crate Purpose Key Types
common Merkle trees, hash chain, blinding, signatures, CLF BalanceTree<H>, MerklePath, HashChain<H>, Eip191Scheme
proof Proving abstractions + TEE implementations Proof, ProofClient, TeeUserSetup<H>, ProveRequest
ethereum Typed blockchain clients + contract bindings UserClient, AgentClient, TeeClient
user User-side channel management UserApp<P>, Channel<P>
agent Agent HTTP server (axum) AppState, registration/payment handlers
tee/guest Enclave binary TeeGuest, PROVERS registry
tee/host TEE orchestrator + HTTP server TeeHost, TeeHttpClient

Dependency graph:

common
  ├── proof
  │     └── tee/guest
  │           └── tee/host ──── ethereum
  ├── ethereum
  ├── user ────── proof, ethereum, common
  └── agent ───── common

Cross-Language Fixtures

Rust, Solidity, and Noir must agree on cryptographic encodings. A golden fixture pattern ensures consistency:

  • Fixture file: contracts/test/fixtures/cross_language_vectors.json (committed, auto-generated)
  • Rust side: Each module owns its vectors via rstest tests that call clf::write(tag, value); the fixture is rewritten on every cargo test -p common
  • Solidity side: CrossLanguageFixtures.sol provides typed stdJson accessors
  • CI drift detection: The strict-fixtures feature asserts values match before overwriting

Coverage: Keccak hashing, hash chains, blinding, payment signatures, TEE proofs, u64 encoding.

To add a new vector: Write an rstest in the Rust module's #[cfg(test)] block that computes the value and calls clf::write(tag, json!({...})), then add a Solidity accessor and commit the updated JSON.

License

Apache 2.0. See LICENSE for details.

About

Privacy-preserving payment channels for multi-agent AI systems

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors