Skip to main content

Zero-Knowledge Proofs

Orbinum uses Groth16 proofs over the BN254 curve for efficient on-chain verification of private transactions.


Table of Contents


Proving System

Groth16

Orbinum uses Groth16, a zkSNARK proving system chosen for:

PropertyBenefit
Constant proof size192 bytes regardless of circuit complexity
Fast verification~3 pairings, O(1) verification time
EVM compatibilityNative precompiles on Ethereum
Mature toolingSnarkJS, Circom, arkworks support

BN254 Curve

The BN254 (alt_bn128) curve provides:

  • 254-bit prime field
  • ~100 bits of security
  • Ethereum precompile support (EIP-196, EIP-197)
  • Efficient pairing operations

Cryptographic Primitives

Poseidon Hash

Orbinum uses Poseidon as the primary hash function:

// From fp-zk-primitives
pub fn poseidon_hash_2(left: [u8; 32], right: [u8; 32]) -> [u8; 32]
pub fn poseidon_hash_4(inputs: [[u8; 32]; 4]) -> [u8; 32]

Why Poseidon?

PropertyPoseidonSHA256Pedersen
R1CS constraints~300~25,000~750
ZK-friendlyYesNoYes
StandardizedYesYesNo

Commitment Scheme

Notes are committed using Poseidon:

pub fn create_commitment(
value: u128,
pk_d: [u8; 32],
blinding: [u8; 32],
) -> [u8; 32] {
poseidon_hash_4([
value.to_le_bytes().try_into()?,
pk_d,
blinding,
[0u8; 32], // padding
])
}

Nullifier Derivation

Nullifiers prevent double-spending:

pub fn compute_nullifier(
commitment: [u8; 32],
spending_key: [u8; 32],
) -> [u8; 32] {
poseidon_hash_2(commitment, spending_key)
}

Merkle Tree

Binary Merkle tree with Poseidon hashing:

pub fn compute_merkle_root(
leaves: &[[u8; 32]],
) -> [u8; 32]

pub fn verify_merkle_proof(
leaf: [u8; 32],
proof: &MerkleProof,
root: [u8; 32],
) -> bool

Circuit Architecture

Directory Structure

circuits/
├── circuits/
│ ├── main.circom # Entry point
│ ├── merkle.circom # Merkle proof verification
│ ├── poseidon.circom # Poseidon hash
│ └── transfer.circom # Transfer constraints
├── scripts/
│ ├── compile.sh # Compile circuits
│ └── setup.sh # Trusted setup
└── test/
└── transfer.test.js # Circuit tests

Transfer Circuit

The main circuit verifies private transfers:

Private Inputs:

  • value_a, value_b: Input note values
  • blinding_a, blinding_b: Input blinding factors
  • path_a, path_b: Merkle proofs
  • sk: Spending key

Public Inputs:

  • nullifier_a, nullifier_b: Computed nullifiers
  • commitment_c, commitment_d: Output commitments
  • merkle_root: Tree root

Constraints:

// 1. Verify input notes exist
merkle_verify(commitment_a, path_a, root) == 1
merkle_verify(commitment_b, path_b, root) == 1

// 2. Verify nullifiers
nullifier_a == poseidon(commitment_a, sk)
nullifier_b == poseidon(commitment_b, sk)

// 3. Verify value conservation
value_a + value_b == value_c + value_d

// 4. Verify output commitments
commitment_c == poseidon(value_c, pk_c, blinding_c)
commitment_d == poseidon(value_d, pk_d, blinding_d)

Circuit Metrics

CircuitConstraintsProving TimeProof Size
Shield~5,000~1s192 bytes
Transfer (2-in-2-out)~50,000~10s192 bytes
Unshield~25,000~5s192 bytes

Verification

On-Chain Verifier

The pallet-zk-verifier handles proof verification:

// From frame/zk-verifier/src/lib.rs
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn verify_proof(
origin: OriginFor<T>,
circuit_id: CircuitId,
proof: BoundedVec<u8, T::MaxProofSize>,
public_inputs: BoundedVec<[u8; 32], T::MaxPublicInputs>,
) -> DispatchResult
}

Verification Process

Verification Key Management

#[pallet::storage]
pub type VerificationKeys<T> = StorageMap<
_,
Blake2_128Concat,
CircuitId,
VerificationKey,
OptionQuery,
>;

Keys are registered by governance:

pub fn register_verification_key(
origin: OriginFor<T>,
circuit_id: CircuitId,
vk: VerificationKey,
) -> DispatchResult

Trusted Setup

Ceremony

Groth16 requires a trusted setup per circuit:

Setup Scripts

# Compile circuit
./scripts/compile.sh transfer

# Generate proving/verification keys
./scripts/setup.sh transfer

Security Requirements

RequirementDescription
1-of-N honestAt least one participant must be honest
Toxic wasteRandom values must be destroyed
VerifiabilitySetup can be audited
WIP - Ceremony Pending

Production trusted setup ceremony has not been performed. Current keys are for testing only.


Performance

Benchmarks

OperationTime (Client)Time (On-chain)
Proof generation~10sN/A
Proof verification~15ms~50ms
Commitment creation<1ms<1ms
Merkle proof<10ms<10ms

Optimization Targets

AreaCurrentTarget
Proof generation10s<5s
Circuit constraints50k30k
Verification gas~200k~150k