A minimal zk id Solana program that uses zero-knowledge proofs for identity verification with compressed accounts. Note this is an example how to verify a zk inclusion proof, not a full zk identity protocol and not production-ready.
For examples of zk identity protocols, see:
- Iden3 - Full decentralized identity protocol with claims, revocation, and recovery
- Semaphore - Privacy-preserving group signaling with nullifiers
Creates a compressed account for an issuer entity who can credential other users, storing their pubkey and initializing their credential issuance counter.
Issues a new credential by creating a compressed account that binds a user's pubkey to an issuer, incrementing the issuer's credential counter in the process.
Verifies a zero-knowledge proof of credential ownership using Groth16 verification and creates an encrypted event account to store the verification result on-chain.
Properties:
- Credential verification is private. The credential is not exposed during zk proof verification. (The transaction payer is not private, for full privacy a relayer or freshly funded keypair should be used.)
- Each credential can only be used once per
verification_id. (The event account address serves as a nullifier.) - Only the credential owner can produce a valid proof.
- Rust (1.90.0 or later)
- Node.js (v22 or later) and npm
- Solana CLI (2.3.11 or later)
- Light CLI: Install with
npm install -g @lightprotocol/zk-compression-cli
- Circom (v2.2.2): Zero-knowledge circuit compiler
- SnarkJS: JavaScript library for generating and verifying ZK proofs
To install circom and snarkjs:
# Install circom (Linux/macOS)
wget https://github.com/iden3/circom/releases/download/v2.2.2/circom-linux-amd64
chmod +x circom-linux-amd64
sudo mv circom-linux-amd64 /usr/local/bin/circom
# For macOS, replace with circom-macos-amd64
# Install snarkjs globally
npm install -g snarkjsBefore building and testing, you need to compile the ZK circuits and generate the proving/verification keys:
# Run the setup script to compile circuits and generate keys
./scripts/setup.shThis script will:
- Install npm dependencies
- Download the Powers of Tau ceremony file
- Compile the circom circuit
- Generate the proving key (zkey)
- Export the verification key
From the parent zk/ directory:
# Build, deploy, and test this example
make zk-id
# Or run individual steps
make build # Build all programs
make deploy # Deploy to local validator
make test-ts # Run TypeScript testsBuild:
cargo build-sbfRust tests (full ZK verification flow):
RUST_BACKTRACE=1 cargo test-sbf -- --nocaptureTypeScript tests:
Requires a running local validator with Light Protocol:
light test-validator # In separate terminal
npm install
npm run test:tszk-id/
├── circuits/ # Circom circuit definitions
│ └── compressed_account_merkle_proof.circom
├── build/ # Generated circuit artifacts (after setup)
│ ├── verification_key.json
│ └── *.zkey, *.wasm, etc.
├── scripts/
│ └── setup.sh # Circuit compilation and setup script
├── src/
│ ├── lib.rs # Solana program implementation
│ └── verifying_key.rs # Generated Groth16 verifying key
├── tests/
│ └── test.rs # Rust integration tests
└── ts-tests/
└── zk-id.test.ts # TypeScript tests
This example uses Light SDK v0.17+ with the V2 accounts layout:
system_accounts_offsetparameter to locate system accounts in remaining accountsCpiAccounts::new()fromlight_sdk::cpi::v2into_new_address_params_assigned_packed(seed, Some(index))for address parameterssha::LightAccountfor accounts with Vec fields (uses SHA256 flat hashing)poseidon::LightAccountfor accounts with fixed-size fields (uses Poseidon hashing)
To clean generated circuit files:
./scripts/clean.sh