| Crates.io | fp-zk-verifier |
| lib.rs | fp-zk-verifier |
| version | 0.1.2 |
| created_at | 2026-01-23 23:57:44.188785+00 |
| updated_at | 2026-01-24 21:33:38.266522+00 |
| description | Zero-Knowledge proof verification primitives for Orbinum Network - Groth16 verification on BN254 |
| homepage | https://orbinum.network |
| repository | https://github.com/orbinum/node |
| max_upload_size | |
| id | 2065725 |
| size | 111,780 |
Groth16 proof verifier primitives for Zero-Knowledge circuits in Orbinum Network.
This crate provides the core verification infrastructure for validating Zero-Knowledge proofs on-chain. It is designed to be used by:
┌─────────────────────────────────────────────────────────────┐
│ ZK VERIFIER │
│ (3-Layer Architecture) │
├─────────────────────────────────────────────────────────────┤
│ │
│ Layer 1: CORE (Foundational Types) │
│ ──────────────────────────────── │
│ • types.rs │
│ - Proof: Groth16 proof structure │
│ - VerifyingKey: Circuit verification key │
│ - PublicInputs: Public circuit inputs │
│ • constants.rs │
│ - CIRCUIT_ID_TRANSFER = 1 │
│ - CIRCUIT_ID_UNSHIELD = 2 │
│ - TRANSFER_PUBLIC_INPUTS = 5 │
│ - UNSHIELD_PUBLIC_INPUTS = 4 │
│ - BASE_VERIFICATION_COST, PER_INPUT_COST │
│ • error.rs │
│ - VerifierError: Error types │
│ │
│ Layer 2: CRYPTO (Cryptographic Verification) │
│ ───────────────────────────────────────── │
│ • groth16.rs: Proof verification │
│ - Groth16Verifier::verify(vk, inputs, proof) │
│ - estimate_verification_cost(input_count) │
│ • utils.rs: Field conversions │
│ - bytes_to_field_elements(bytes) │
│ - field_elements_to_bytes(elements) │
│ │
│ Layer 3: COMPAT (Compatibility & Interoperability) │
│ ─────────────────────────────────────────────────────── │
│ • snarkjs.rs: SnarkJS format support │
│ - parse_proof_from_snarkjs(json) │
│ - parse_public_inputs_from_snarkjs(json) │
│ │
│ VK Registry (Verification Keys) │
│ ──────────────────────────────── │
│ • vk/transfer.rs: Transfer circuit VK (5 inputs) │
│ • vk/unshield.rs: Unshield circuit VK (4 inputs) │
│ • vk/registry.rs: Runtime lookup by circuit ID │
│ - get_vk_by_circuit_id(id) │
│ - get_public_input_count(id) │
│ - validate_public_input_count(id, count) │
│ │
└─────────────────────────────────────────────────────────────┘
DO NOT use wildcards (*). Import specific functions with full paths:
// CORRECT - Explicit paths
use fp_zk_verifier::core::types::{Proof, VerifyingKey, PublicInputs};
use fp_zk_verifier::core::constants::{CIRCUIT_ID_TRANSFER, CIRCUIT_ID_UNSHIELD};
use fp_zk_verifier::crypto::groth16::Groth16Verifier;
use fp_zk_verifier::vk::registry::{get_vk_by_circuit_id, validate_public_input_count};
use fp_zk_verifier::compat::snarkjs::{parse_proof_from_snarkjs, parse_public_inputs_from_snarkjs};
// INCORRECT - Don't use wildcards
use fp_zk_verifier::crypto::*;
use fp_zk_verifier::vk::*;
use fp_zk_verifier::core::constants::CIRCUIT_ID_TRANSFER;
use fp_zk_verifier::crypto::groth16::Groth16Verifier;
use fp_zk_verifier::vk::registry::get_vk_by_circuit_id;
// Get verification key for transfer circuit
let vk = get_vk_by_circuit_id(CIRCUIT_ID_TRANSFER)?;
// Verify proof
let result = Groth16Verifier::verify(&vk, &public_inputs, &proof);
assert!(result.is_ok());
use fp_zk_verifier::core::constants::{CIRCUIT_ID_TRANSFER, CIRCUIT_ID_UNSHIELD};
use fp_zk_verifier::vk::registry::{
get_vk_by_circuit_id,
get_public_input_count,
validate_public_input_count
};
// Dynamic VK lookup
let circuit_id = CIRCUIT_ID_TRANSFER;
let vk = get_vk_by_circuit_id(circuit_id)?;
// Get expected input count
let expected = get_public_input_count(circuit_id)?;
assert_eq!(expected, 5); // Transfer has 5 inputs
// Validate inputs before verification
validate_public_input_count(circuit_id, public_inputs.len())?;
use fp_zk_verifier::compat::snarkjs::{
parse_proof_from_snarkjs,
parse_public_inputs_from_snarkjs
};
// Parse proof from SnarkJS JSON
let snarkjs_proof = r#"{"pi_a": [...], "pi_b": [...], "pi_c": [...]}"#;
let proof = parse_proof_from_snarkjs(snarkjs_proof)?;
// Parse public inputs
let snarkjs_inputs = r#"["123", "456", ...]"#;
let inputs = parse_public_inputs_from_snarkjs(snarkjs_inputs)?;
use fp_zk_verifier::crypto::groth16::Groth16Verifier;
// Estimate verification cost (for fee calculation)
let input_count = 5; // Transfer circuit
let cost = Groth16Verifier::estimate_verification_cost(input_count);
std (default): Enable standard library supportsubstrate: Enable Substrate runtime integration (sp-core, sp-runtime, sp-std)[dependencies]
fp-zk-verifier = { version = "0.1", default-features = false }
# For no_std runtime
fp-zk-verifier = { version = "0.1", default-features = false, features = ["substrate"] }
# Run all tests
cargo test --features std
# Run specific test file
cargo test --test vk_tests
# Run with output
cargo test -- --nocapture
# Check compilation
cargo check --all-features
tests/
├── crypto_tests.rs # Groth16 verification and utils (6 tests)
├── vk_tests.rs # VK registry and structure (11 tests)
└── snarkjs_compat_tests.rs # SnarkJS parsing (4 tests)
use fp_zk_verifier::vk::registry::validate_public_input_count;
// Always validate input count before verification
validate_public_input_count(circuit_id, inputs.len())?;
| Operation | Time (on-chain) | Gas Cost |
|---|---|---|
| Transfer verification | ~8-10ms | ~100k |
| Unshield verification | ~6-8ms | ~80k |
| VK lookup | <1ms | ~1k |
| Input validation | <1ms | ~500 |
Benchmarked on Substrate parachain with BN254 curve
use frame_support::pallet_prelude::*;
use fp_zk_verifier::core::constants::CIRCUIT_ID_TRANSFER;
use fp_zk_verifier::core::types::{Proof, PublicInputs};
use fp_zk_verifier::crypto::groth16::Groth16Verifier;
use fp_zk_verifier::vk::registry::{get_vk_by_circuit_id, validate_public_input_count};
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(100_000)]
pub fn verify_transfer_proof(
origin: OriginFor<T>,
proof: Proof,
public_inputs: PublicInputs,
) -> DispatchResult {
ensure_signed(origin)?;
// Validate input count
validate_public_input_count(CIRCUIT_ID_TRANSFER, public_inputs.len())
.map_err(|_| Error::<T>::InvalidInputCount)?;
// Get verification key
let vk = get_vk_by_circuit_id(CIRCUIT_ID_TRANSFER)
.map_err(|_| Error::<T>::InvalidCircuitId)?;
// Verify proof
Groth16Verifier::verify(&vk, &public_inputs, &proof)
.map_err(|_| Error::<T>::ProofVerificationFailed)?;
Ok(())
}
}
Generate VK from trusted setup:
snarkjs groth16 setup circuit.r1cs pot.ptau circuit.zkey
snarkjs zkey export verificationkey circuit.zkey vk.json
Convert to Rust:
# Use scripts/convert_vk.rs from circuits/ directory
cargo run --bin convert_vk -- vk.json > vk_bytes.txt
Create new VK module:
// src/vk/new_circuit.rs
pub const CIRCUIT_ID: u8 = 3;
pub const PUBLIC_INPUT_COUNT: usize = 6;
pub fn get_vk() -> VerifyingKey {
// ... paste VK bytes
}
Register in vk/registry.rs:
pub fn get_vk_by_circuit_id(id: u8) -> Result<VerifyingKey, VerifierError> {
match id {
CIRCUIT_ID_TRANSFER => Ok(transfer::get_vk()),
CIRCUIT_ID_UNSHIELD => Ok(unshield::get_vk()),
3 => Ok(new_circuit::get_vk()), // Add here
_ => Err(VerifierError::InvalidCircuitId(id)),
}
}
Licensed under Apache 2.0 or GPL-3.0.