Privacy Technologies
Protocol 01 combines cutting-edge cryptography to deliver true financial privacy on Solana.
Every component is built from scratch for maximum security and efficiency.
System Architecture
System Architecture
Protocol Stack
Core Technologies
Stealth Addresses (ECDH)
Stealth addresses allow recipients to receive funds without revealing their public address. Each payment creates a unique one-time address using Elliptic Curve Diffie-Hellman key exchange.
Key Features
- Sender generates ephemeral keypair for each transaction
- Shared secret computed using ECDH between sender ephemeral key and recipient's public key
- One-time address derived: P = H(shared_secret) * G + recipient_pubkey
- Only the recipient can detect and spend funds using their private key
- Implemented using Curve25519 for efficiency on Solana
Code Example
// Generate stealth address const ephemeral = Keypair.generate(); const sharedSecret = x25519(ephemeral.secretKey, recipientPubkey); const stealthKey = deriveStealthKey(sharedSecret, recipientPubkey);
Zero-Knowledge Proofs (Groth16)
ZK-SNARKs enable private transfers where amounts and participants are hidden. We use Groth16 proofs verified on-chain using Solana's native alt_bn128 syscalls.
Key Features
- Circom circuits with ~12,000 constraints for efficient proving
- Poseidon hash function (ZK-friendly) for commitments and Merkle trees
- Groth16 verification using Solana's native BN254 pairing precompiles
- Proof generation takes <2 seconds on modern devices
- Trusted setup completed with secure MPC ceremony
Code Example
// Generate ZK proof for private transfer
const { proof, publicSignals } = await snarkjs.groth16.fullProve(
{ inputs, merkle_path, nullifiers },
"transfer.wasm",
"transfer.zkey"
);Shielded Pool Architecture
The shielded pool stores encrypted notes in a Merkle tree. Users can deposit (shield), transfer privately, and withdraw (unshield) while maintaining complete privacy.
Key Features
- Note = Hash(amount, owner_pubkey, randomness, token_mint)
- Sparse Merkle tree with depth 20 (~1M notes capacity)
- Nullifiers prevent double-spending without revealing which note was spent
- Historical roots accepted for concurrent transactions
- Supports SOL and any SPL token
Code Example
// Shielded note structure
Note = {
amount: u64, // Hidden from observers
owner_pubkey: [u8; 32], // Derived from spending key
randomness: [u8; 32], // Blinding factor
commitment: Poseidon(amount, owner, randomness, mint)
}Poseidon Hash Function
Poseidon is a ZK-friendly hash function designed specifically for use inside arithmetic circuits. It's significantly more efficient than traditional hashes like SHA-256 or Keccak in ZK contexts.
Key Features
- Operates natively over prime fields (BN254 scalar field)
- ~300x fewer constraints than Keccak in Circom circuits
- Used for note commitments and Merkle tree hashing
- Parameters: BN254 curve, x^5 S-box, 8 full rounds
- Compatible with circomlib implementation
Code Example
// Poseidon hash in circuit
template NoteCommitment() {
signal input amount;
signal input ownerPubkey;
signal input randomness;
signal output commitment;
component hash = Poseidon(4);
hash.inputs[0] <== amount;
hash.inputs[1] <== ownerPubkey;
hash.inputs[2] <== randomness;
hash.inputs[3] <== tokenMint;
commitment <== hash.out;
}Merkle Tree Proofs
A Merkle tree stores all note commitments, allowing users to prove membership without revealing which note they own. The root is stored on-chain and updated with each deposit.
Key Features
- Binary tree with Poseidon hash at each node
- Depth 20 = 2^20 = ~1 million notes capacity
- Proof size: 20 siblings + 20 path indices
- Incremental insertions for gas efficiency
- Precomputed zero values for empty subtrees
Code Example
// Merkle proof verification in circuit
for (var i = 0; i < TREE_DEPTH; i++) {
left = pathIndices[i] == 0 ? current : siblings[i];
right = pathIndices[i] == 0 ? siblings[i] : current;
current = Poseidon(left, right);
}
root === computedRoot; // Must match on-chain rootNullifier Mechanism
Nullifiers are unique identifiers that prevent double-spending without revealing which note was spent. Each note can only produce one valid nullifier.
Key Features
- Nullifier = Poseidon(commitment, spending_key_hash)
- Stored on-chain in a set (prevents reuse)
- Cannot be linked back to the original note
- Spending key proves ownership without revealing identity
- Bloom filter optimization for gas efficiency
Code Example
// Nullifier computation const spendingKeyHash = Poseidon([spendingKey]); const nullifier = Poseidon([commitment, spendingKeyHash]); // On-chain: require(!nullifierSet.contains(nullifier))
Solana On-Chain Verification
Protocol 01 leverages Solana's native cryptographic syscalls for efficient on-chain ZK proof verification, achieving verification in under 200K compute units.
Key Features
- alt_bn128 syscalls for BN254 curve operations
- Native pairing checks for Groth16 verification
- Anchor framework for type-safe program development
- Compute budget: ~200K CU for full proof verification
- Cross-program invocations for token transfers
Code Example
// On-chain Groth16 verification
let pairing_result = sol_alt_bn128_pairing(
&[pi_a_neg, vk_alpha, pi_b, vk_beta, pi_c, vk_gamma, ...]
);
require!(pairing_result == 1, "Invalid proof");Private Relay Architecture
User-funded relayer that verifies ZK proofs and executes private transfers to stealth addresses, breaking the on-chain link between sender and recipient.
Key Features
- User funds relayer with transfer amount + fee (0.5%) + gas + rent
- Relayer verifies ZK proof server-side (Groth16 snarkjs verification)
- Relayer sends to stealth address — no on-chain link to the sender
- Currently a self-hosted backend service (Node.js)
- Roadmap: on-chain Solana program for fully decentralized relay
Code Example
// Private relay flow
// 1. User generates ZK proof client-side
const { proof, publicSignals } = await generateProof(inputs);
// 2. User funds relayer with amount + fee + gas
const fundTx = await fundRelayer(amount, fee, gasCost, rentCost);
// 3. Relayer verifies proof and sends to stealth address
// POST /api/private-send
{
proof, // Groth16 ZK proof
publicSignals, // Nullifier + commitments
recipient, // Stealth address (unlinkable)
amount // Transfer amount
}
// Result: recipient receives funds with no link to senderClient SDK Architecture
Two SDKs for different privacy levels. SpecterClient for stealth addresses, ShieldedClient for ZK proofs. Both run client-side for maximum privacy.
Key Features
- TypeScript/JavaScript SDK with full type definitions
- WASM-compiled circuits for browser compatibility
- Web Worker isolation for proof generation
- Automatic note management and encryption
- Streaming payments with SPL token support
Code Example
// === P-01 SDK - Stealth Addresses ===
import { P01Client } from '@p01/sdk';
const client = new P01Client({ cluster: 'devnet' });
const wallet = await P01Client.createWallet();
await client.connect(wallet);
// Send private transfer (stealth address)
await client.sendPrivate(recipientStealthAddress, 1.5);
// Create payment stream
await client.createStream(recipient, 10, 30); // 10 SOL over 30 days
// === P-01 SDK - ZK Shielded Pool ===
import { ShieldedClient } from '@p01/sdk/zk';
const zkClient = new ShieldedClient({ connection, wallet });
await zkClient.initialize(seedPhrase);
// Shield tokens (deposit to private pool)
await zkClient.shield(1_000_000_000n); // 1 SOL
// Private ZK transfer
const zkAddress = ShieldedClient.decodeZkAddress("zk:...");
await zkClient.transfer(zkAddress, 500_000_000n);
// Unshield (withdraw to public)
await zkClient.unshield(publicKey, 500_000_000n);Security Model
Threat Model
- Blockchain observers cannot link senders and recipients
- Transaction amounts are hidden in shielded transfers
- Spending patterns cannot be analyzed
- Balance tracking is impossible for third parties
Guarantees
- Sound: Invalid proofs cannot be generated
- Complete: Valid spends always produce valid proofs
- Zero-knowledge: Proofs reveal nothing beyond validity
- No double-spending: Nullifiers are unique per note