A flexible and secure system for managing zero-knowledge proof verifications on Solana. This project provides a router-based verification system with non-upgradeable verifiers that have emergency controls, and comprehensive administration tools.
The Verifier Router system enables:
- Dynamic registration and routing of ZK proof verifiers
- Emergency stop mechanisms with both centralized and decentralized triggers
- Flexible ownership controls with two-step transfers
Comprehensive script support for program management:
- Supports both local key signers and Fireblocks HSM
- Supports adding additional verifiers beyond the currently supported Groth_16 program
- Ownership management and transfer
- Emergency Stop by owner
- Rust and Cargo
- Solana Tool Suite
- Node.js and Yarn
- Anchor Framework
- Clone the repository:
git clone <repository-url>
cd verifier-router
- Install dependencies:
yarn install
- Configure environment:
cp example.env .env
# Edit .env with your configuration
Note: Deployment accounts need at minimum a 6 SOL balance by default and any non-deployment actions require an account with a 1 SOL minimum balance.
- Deploy the router and initial verifier:
anchor keys sync
anchor build
yarn run client
yarn run deploy
- (Optional) Add additional verifiers programs:
yarn run add
- Router Program: Central registry and routing system
- Verifier Programs: Individual verifier implementations (e.g., Groth16)
- Client Programs: Programs that use the router for proof verification
#[derive(Accounts)]
pub struct IncrementNonce<'info> {
#[account(mut)]
pub program_data: Account<'info, ProgramData>,
pub router: Program<'info, VerifierRouterProgram>,
pub router_account: Account<'info, VerifierRouter>,
#[account(
seeds = [
b"verifier",
&program_data.selector.to_le_bytes()
],
bump,
seeds::program = verifier_router::ID,
)]
pub verifier_entry: Account<'info, VerifierEntry>,
pub verifier_program: UncheckedAccount<'info>,
pub system_program: Program<'info, System>,
}
pub fn increment_nonce(
ctx: Context<IncrementNonce>,
proof: Proof,
journal_nonce: u32,
) -> Result<()> {
// Your programs initial code...
// Next we collect the accounts necessary for making the CPI call to the RISC Zero Proof Verifier program
let cpi_accounts = Verify {
router: ctx.accounts.router_account.to_account_info(),
verifier_entry: ctx.accounts.verifier_entry.to_account_info(),
verifier_program: ctx.accounts.verifier_program.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
};
// We hash our journal outputs that we used for our earlier requirements to get a journal digest
let journal_digest = hashv(&[&journal_nonce.to_le_bytes()]).to_bytes();
// We collect the image ID that our program is expecting our proof to match so that an attacker cannot use
// a proof generated from a modified program
let image_id = ctx.accounts.program_data.image_id;
// We pass the selector for the proof verifier that we are currently using
let selector = ctx.accounts.program_data.selector;
// We setup our CPI context for the router
let cpi_ctx = CpiContext::new(ctx.accounts.router.to_account_info(), cpi_accounts);
// We make the CPI call to the RISC Zero Verifier Router which if it returns means the proof is valid
// In Solana you cannot recover from a CPI call which returns an error, to make this clear I explicitly unwrap although
// behavior would be the same if I ignored the result.
verifier_router::cpi::verify(cpi_ctx, selector, proof, image_id, journal_digest).unwrap();
// Your programs code after a successful verification...
}
- Transfer Ownership:
NEW_OWNER=<pubkey> yarn run transfer
yarn run accept # Run on new owner's machine
- Add Verifier:
VERIFIER_ADDRESS=<address> yarn run add
- Emergency Stop:
yarn run estop # Follow prompts
All scripts have values set in the environment, see example.env
for a full
list of options.
yarn run deploy
: Deploy programsyarn run add
: Add new verifieryarn run estop
: Emergency stop by owneryarn run transfer
: Transfer ownershipyarn run accept
: Accept ownershipyarn run renounce
: Renounce ownershipyarn run client
: Generate TypeScript clients
See example.env
for full configuration options including:
- Network endpoints
- Account addresses
- Deployment settings
- Fireblocks integration (optional)
Open a terminal and run a local validator:
solana-test-validator -r
Then in another terminal run:
anchor test --skip-local-validator