- Introduced `ibc_example.rs` demonstrating Inter-Blockchain Communication operations including cross-chain transfers, channel management, packet handling, and relayer operations. - Introduced `zk_example.rs` showcasing Zero-Knowledge proof operations such as circuit compilation, proof generation and verification, and on-chain verification with multiple proving systems.
607 lines
19 KiB
Rust
607 lines
19 KiB
Rust
//! Synor ZK SDK Examples for Rust
|
|
//!
|
|
//! Demonstrates Zero-Knowledge proof operations:
|
|
//! - Circuit compilation
|
|
//! - Proof generation and verification
|
|
//! - Groth16, PLONK, and STARK proving systems
|
|
//! - Recursive proofs
|
|
//! - On-chain verification
|
|
|
|
use std::collections::HashMap;
|
|
use std::env;
|
|
use std::time::Instant;
|
|
use synor_zk::{
|
|
AggregateRequest, BatchVerifyRequest, CircuitFormat, CompileRequest, ContributeRequest,
|
|
DeployRequest, FormatProofRequest, GenerateProofRequest, GenerateVerifierRequest,
|
|
OnChainVerifyRequest, ProofData, ProvingSystem, SetupInitRequest, SynorZk, VerifyAggregatedRequest,
|
|
VerifyRequest, ZkConfig,
|
|
};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Initialize client
|
|
let config = ZkConfig {
|
|
api_key: env::var("SYNOR_API_KEY").unwrap_or_else(|_| "your-api-key".to_string()),
|
|
endpoint: "https://zk.synor.io/v1".to_string(),
|
|
timeout: 120000, // ZK ops can be slow
|
|
retries: 3,
|
|
debug: false,
|
|
default_proving_system: ProvingSystem::Groth16,
|
|
};
|
|
|
|
let zk = SynorZk::new(config)?;
|
|
|
|
// Check service health
|
|
let healthy = zk.health_check().await?;
|
|
println!("Service healthy: {}\n", healthy);
|
|
|
|
// Run examples
|
|
circuit_example(&zk).await?;
|
|
proof_example(&zk).await?;
|
|
proving_systems_example(&zk).await?;
|
|
recursive_proof_example(&zk).await?;
|
|
on_chain_verification_example(&zk).await?;
|
|
setup_example(&zk).await?;
|
|
|
|
zk.close().await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn circuit_example(zk: &SynorZk) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Circuit Compilation ===");
|
|
|
|
// Circom circuit example: prove knowledge of preimage
|
|
let circom_circuit = r#"
|
|
pragma circom 2.1.0;
|
|
|
|
template HashPreimage() {
|
|
signal input preimage;
|
|
signal input hash;
|
|
|
|
// Simplified hash computation (in reality, use proper hash)
|
|
signal preimageSquared;
|
|
preimageSquared <== preimage * preimage;
|
|
|
|
// Constrain that hash matches
|
|
hash === preimageSquared;
|
|
}
|
|
|
|
component main {public [hash]} = HashPreimage();
|
|
"#;
|
|
|
|
// Compile circuit
|
|
println!("Compiling Circom circuit...");
|
|
let compiled = zk
|
|
.circuits
|
|
.compile(CompileRequest {
|
|
code: circom_circuit.to_string(),
|
|
format: CircuitFormat::Circom,
|
|
name: "hash_preimage".to_string(),
|
|
proving_system: ProvingSystem::Groth16,
|
|
})
|
|
.await?;
|
|
|
|
println!("Circuit compiled:");
|
|
println!(" Circuit ID: {}", compiled.circuit_id);
|
|
println!(" Constraints: {}", compiled.constraint_count);
|
|
println!(" Public inputs: {}", compiled.public_input_count);
|
|
println!(" Private inputs: {}", compiled.private_input_count);
|
|
println!(" Proving key size: {} bytes", compiled.proving_key_size);
|
|
println!(" Verification key size: {} bytes", compiled.verification_key_size);
|
|
|
|
// List circuits
|
|
let circuits = zk.circuits.list().await?;
|
|
println!("\nYour circuits: {}", circuits.len());
|
|
for circuit in &circuits {
|
|
println!(" {} ({})", circuit.name, circuit.circuit_id);
|
|
}
|
|
|
|
// Get circuit details
|
|
let details = zk.circuits.get(&compiled.circuit_id).await?;
|
|
println!("\nCircuit details:");
|
|
println!(" Format: {:?}", details.format);
|
|
println!(" Proving system: {:?}", details.proving_system);
|
|
println!(" Created: {}", details.created_at);
|
|
|
|
// Download proving key
|
|
let proving_key = zk.circuits.get_proving_key(&compiled.circuit_id).await?;
|
|
println!("\nProving key downloaded: {} bytes", proving_key.len());
|
|
|
|
// Download verification key
|
|
let verification_key = zk.circuits.get_verification_key(&compiled.circuit_id).await?;
|
|
println!("Verification key downloaded: {} bytes", verification_key.len());
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn proof_example(zk: &SynorZk) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Proof Generation ===");
|
|
|
|
// First, compile a simple circuit
|
|
let circuit = r#"
|
|
pragma circom 2.1.0;
|
|
|
|
template Multiplier() {
|
|
signal input a;
|
|
signal input b;
|
|
signal output c;
|
|
|
|
c <== a * b;
|
|
}
|
|
|
|
component main {public [c]} = Multiplier();
|
|
"#;
|
|
|
|
let compiled = zk
|
|
.circuits
|
|
.compile(CompileRequest {
|
|
code: circuit.to_string(),
|
|
format: CircuitFormat::Circom,
|
|
name: "multiplier".to_string(),
|
|
proving_system: ProvingSystem::Groth16,
|
|
})
|
|
.await?;
|
|
|
|
// Generate proof
|
|
println!("Generating proof...");
|
|
let start_time = Instant::now();
|
|
|
|
let mut inputs = HashMap::new();
|
|
inputs.insert("a".to_string(), "3".to_string());
|
|
inputs.insert("b".to_string(), "7".to_string());
|
|
|
|
let proof = zk
|
|
.proofs
|
|
.generate(GenerateProofRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
inputs,
|
|
})
|
|
.await?;
|
|
|
|
let proof_time = start_time.elapsed();
|
|
println!("Proof generated in {:?}", proof_time);
|
|
println!(" Proof ID: {}", proof.proof_id);
|
|
println!(" Proof size: {} bytes", proof.proof.len());
|
|
println!(" Public signals: {:?}", proof.public_signals);
|
|
|
|
// Verify proof
|
|
println!("\nVerifying proof...");
|
|
let verify_start = Instant::now();
|
|
|
|
let is_valid = zk
|
|
.proofs
|
|
.verify(VerifyRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
proof: proof.proof.clone(),
|
|
public_signals: proof.public_signals.clone(),
|
|
})
|
|
.await?;
|
|
|
|
let verify_time = verify_start.elapsed();
|
|
println!("Verification completed in {:?}", verify_time);
|
|
println!(" Valid: {}", is_valid);
|
|
|
|
// Verify with wrong public signals (should fail)
|
|
println!("\nVerifying with wrong signals...");
|
|
let invalid_result = zk
|
|
.proofs
|
|
.verify(VerifyRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
proof: proof.proof.clone(),
|
|
public_signals: vec!["42".to_string()], // Wrong answer
|
|
})
|
|
.await?;
|
|
println!(" Valid: {} (expected false)", invalid_result);
|
|
|
|
// Get proof status
|
|
let status = zk.proofs.get_status(&proof.proof_id).await?;
|
|
println!("\nProof status:");
|
|
println!(" State: {}", status.state);
|
|
println!(" Verified: {}", status.verified);
|
|
println!(" Created: {}", status.created_at);
|
|
|
|
// List proofs
|
|
let proofs = zk.proofs.list(&compiled.circuit_id).await?;
|
|
println!("\nProofs for circuit: {}", proofs.len());
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn proving_systems_example(zk: &SynorZk) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Proving Systems Comparison ===");
|
|
|
|
// Simple circuit for comparison
|
|
let circuit = r#"
|
|
pragma circom 2.1.0;
|
|
|
|
template Comparison() {
|
|
signal input x;
|
|
signal input y;
|
|
signal output sum;
|
|
|
|
sum <== x + y;
|
|
}
|
|
|
|
component main {public [sum]} = Comparison();
|
|
"#;
|
|
|
|
let systems = [
|
|
(ProvingSystem::Groth16, "GROTH16"),
|
|
(ProvingSystem::PLONK, "PLONK"),
|
|
(ProvingSystem::STARK, "STARK"),
|
|
];
|
|
|
|
println!("Comparing proving systems:\n");
|
|
|
|
for (system, name) in systems {
|
|
println!("{}:", name);
|
|
|
|
// Compile for this system
|
|
let compiled = zk
|
|
.circuits
|
|
.compile(CompileRequest {
|
|
code: circuit.to_string(),
|
|
format: CircuitFormat::Circom,
|
|
name: format!("comparison_{}", name.to_lowercase()),
|
|
proving_system: system,
|
|
})
|
|
.await?;
|
|
|
|
// Generate proof
|
|
let proof_start = Instant::now();
|
|
let mut inputs = HashMap::new();
|
|
inputs.insert("x".to_string(), "10".to_string());
|
|
inputs.insert("y".to_string(), "20".to_string());
|
|
|
|
let proof = zk
|
|
.proofs
|
|
.generate(GenerateProofRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
inputs,
|
|
})
|
|
.await?;
|
|
let proof_time = proof_start.elapsed();
|
|
|
|
// Verify proof
|
|
let verify_start = Instant::now();
|
|
zk.proofs
|
|
.verify(VerifyRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
proof: proof.proof.clone(),
|
|
public_signals: proof.public_signals.clone(),
|
|
})
|
|
.await?;
|
|
let verify_time = verify_start.elapsed();
|
|
|
|
println!(" Setup: {}ms", compiled.setup_time);
|
|
println!(" Proof time: {:?}", proof_time);
|
|
println!(" Verify time: {:?}", verify_time);
|
|
println!(" Proof size: {} bytes", proof.proof.len());
|
|
println!(" Verification key: {} bytes", compiled.verification_key_size);
|
|
println!();
|
|
}
|
|
|
|
println!("Summary:");
|
|
println!(" Groth16: Smallest proofs, fast verification, trusted setup required");
|
|
println!(" PLONK: Universal setup, flexible, moderate proof size");
|
|
println!(" STARK: No trusted setup, largest proofs, quantum resistant");
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn recursive_proof_example(zk: &SynorZk) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Recursive Proofs ===");
|
|
|
|
// Inner circuit
|
|
let inner_circuit = r#"
|
|
pragma circom 2.1.0;
|
|
|
|
template Inner() {
|
|
signal input x;
|
|
signal output y;
|
|
y <== x * x;
|
|
}
|
|
|
|
component main {public [y]} = Inner();
|
|
"#;
|
|
|
|
// Compile inner circuit
|
|
let inner = zk
|
|
.circuits
|
|
.compile(CompileRequest {
|
|
code: inner_circuit.to_string(),
|
|
format: CircuitFormat::Circom,
|
|
name: "inner_circuit".to_string(),
|
|
proving_system: ProvingSystem::Groth16,
|
|
})
|
|
.await?;
|
|
|
|
// Generate multiple proofs to aggregate
|
|
println!("Generating proofs to aggregate...");
|
|
let mut proofs_to_aggregate = Vec::new();
|
|
for i in 1..=4 {
|
|
let mut inputs = HashMap::new();
|
|
inputs.insert("x".to_string(), i.to_string());
|
|
|
|
let proof = zk
|
|
.proofs
|
|
.generate(GenerateProofRequest {
|
|
circuit_id: inner.circuit_id.clone(),
|
|
inputs,
|
|
})
|
|
.await?;
|
|
|
|
proofs_to_aggregate.push(ProofData {
|
|
proof: proof.proof,
|
|
public_signals: proof.public_signals.clone(),
|
|
});
|
|
println!(" Proof {}: y = {}", i, proof.public_signals[0]);
|
|
}
|
|
|
|
// Aggregate proofs recursively
|
|
println!("\nAggregating proofs...");
|
|
let aggregated = zk
|
|
.proofs
|
|
.aggregate(AggregateRequest {
|
|
circuit_id: inner.circuit_id.clone(),
|
|
proofs: proofs_to_aggregate.clone(),
|
|
aggregation_type: "recursive".to_string(),
|
|
})
|
|
.await?;
|
|
|
|
let original_size: usize = proofs_to_aggregate.iter().map(|p| p.proof.len()).sum();
|
|
|
|
println!("Aggregated proof:");
|
|
println!(" Proof ID: {}", aggregated.proof_id);
|
|
println!(" Aggregated count: {}", aggregated.aggregated_count);
|
|
println!(" Proof size: {} bytes", aggregated.proof.len());
|
|
println!(
|
|
" Size reduction: {:.1}%",
|
|
(1.0 - aggregated.proof.len() as f64 / original_size as f64) * 100.0
|
|
);
|
|
|
|
// Verify aggregated proof
|
|
let public_signals_list: Vec<Vec<String>> = proofs_to_aggregate
|
|
.iter()
|
|
.map(|p| p.public_signals.clone())
|
|
.collect();
|
|
|
|
let is_valid = zk
|
|
.proofs
|
|
.verify_aggregated(VerifyAggregatedRequest {
|
|
circuit_id: inner.circuit_id.clone(),
|
|
proof: aggregated.proof,
|
|
public_signals_list,
|
|
})
|
|
.await?;
|
|
println!("\nAggregated proof valid: {}", is_valid);
|
|
|
|
// Batch verification (verify multiple proofs in one operation)
|
|
println!("\nBatch verification...");
|
|
let batch_result = zk
|
|
.proofs
|
|
.batch_verify(BatchVerifyRequest {
|
|
circuit_id: inner.circuit_id.clone(),
|
|
proofs: proofs_to_aggregate,
|
|
})
|
|
.await?;
|
|
println!(" All valid: {}", batch_result.all_valid);
|
|
println!(" Results: {:?}", batch_result.results);
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn on_chain_verification_example(zk: &SynorZk) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== On-Chain Verification ===");
|
|
|
|
// Compile circuit
|
|
let circuit = r#"
|
|
pragma circom 2.1.0;
|
|
|
|
template VoteCommitment() {
|
|
signal input vote; // Private: actual vote
|
|
signal input nullifier; // Private: unique identifier
|
|
signal input commitment; // Public: commitment to verify
|
|
|
|
// Simplified commitment (in practice, use Poseidon hash)
|
|
signal computed;
|
|
computed <== vote * nullifier;
|
|
commitment === computed;
|
|
}
|
|
|
|
component main {public [commitment]} = VoteCommitment();
|
|
"#;
|
|
|
|
let compiled = zk
|
|
.circuits
|
|
.compile(CompileRequest {
|
|
code: circuit.to_string(),
|
|
format: CircuitFormat::Circom,
|
|
name: "vote_commitment".to_string(),
|
|
proving_system: ProvingSystem::Groth16,
|
|
})
|
|
.await?;
|
|
|
|
// Generate Solidity verifier
|
|
println!("Generating Solidity verifier...");
|
|
let solidity_verifier = zk
|
|
.contracts
|
|
.generate_verifier(GenerateVerifierRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
language: "solidity".to_string(),
|
|
optimized: true,
|
|
})
|
|
.await?;
|
|
println!(
|
|
"Solidity verifier generated: {} bytes",
|
|
solidity_verifier.code.len()
|
|
);
|
|
println!(" Contract name: {}", solidity_verifier.contract_name);
|
|
println!(" Gas estimate: {}", solidity_verifier.gas_estimate);
|
|
|
|
// Generate proof
|
|
let mut inputs = HashMap::new();
|
|
inputs.insert("vote".to_string(), "1".to_string()); // Vote YES (1) or NO (0)
|
|
inputs.insert("nullifier".to_string(), "12345".to_string());
|
|
inputs.insert("commitment".to_string(), "12345".to_string()); // 1 * 12345
|
|
|
|
let proof = zk
|
|
.proofs
|
|
.generate(GenerateProofRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
inputs,
|
|
})
|
|
.await?;
|
|
|
|
// Format proof for on-chain verification
|
|
println!("\nFormatting proof for on-chain...");
|
|
let on_chain_proof = zk
|
|
.contracts
|
|
.format_proof(FormatProofRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
proof: proof.proof.clone(),
|
|
public_signals: proof.public_signals.clone(),
|
|
format: "calldata".to_string(),
|
|
})
|
|
.await?;
|
|
|
|
let calldata_preview = if on_chain_proof.calldata.len() > 100 {
|
|
&on_chain_proof.calldata[..100]
|
|
} else {
|
|
&on_chain_proof.calldata
|
|
};
|
|
println!(" Calldata: {}...", calldata_preview);
|
|
println!(" Estimated gas: {}", on_chain_proof.gas_estimate);
|
|
|
|
// Deploy verifier contract (simulation)
|
|
println!("\nDeploying verifier contract...");
|
|
let deployment = zk
|
|
.contracts
|
|
.deploy_verifier(DeployRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
network: "synor-testnet".to_string(),
|
|
})
|
|
.await?;
|
|
println!(" Contract address: {}", deployment.address);
|
|
println!(" TX hash: {}", deployment.tx_hash);
|
|
println!(" Gas used: {}", deployment.gas_used);
|
|
|
|
// Verify on-chain
|
|
println!("\nVerifying on-chain...");
|
|
let on_chain_result = zk
|
|
.contracts
|
|
.verify_on_chain(OnChainVerifyRequest {
|
|
contract_address: deployment.address,
|
|
proof: proof.proof,
|
|
public_signals: proof.public_signals,
|
|
network: "synor-testnet".to_string(),
|
|
})
|
|
.await?;
|
|
println!(" TX hash: {}", on_chain_result.tx_hash);
|
|
println!(" Verified: {}", on_chain_result.verified);
|
|
println!(" Gas used: {}", on_chain_result.gas_used);
|
|
|
|
// Generate verifier for other targets
|
|
println!("\nGenerating verifiers for other targets:");
|
|
let targets = ["cairo", "noir", "ink"];
|
|
for target in targets {
|
|
let verifier = zk
|
|
.contracts
|
|
.generate_verifier(GenerateVerifierRequest {
|
|
circuit_id: compiled.circuit_id.clone(),
|
|
language: target.to_string(),
|
|
optimized: false,
|
|
})
|
|
.await?;
|
|
println!(" {}: {} bytes", target, verifier.code.len());
|
|
}
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|
|
|
|
async fn setup_example(zk: &SynorZk) -> Result<(), Box<dyn std::error::Error>> {
|
|
println!("=== Trusted Setup ===");
|
|
|
|
// Get available ceremonies
|
|
let ceremonies = zk.setup.list_ceremonies().await?;
|
|
println!("Active ceremonies: {}", ceremonies.len());
|
|
for ceremony in &ceremonies {
|
|
println!(" {}:", ceremony.name);
|
|
println!(" Status: {}", ceremony.status);
|
|
println!(" Participants: {}", ceremony.participant_count);
|
|
println!(" Current round: {}", ceremony.current_round);
|
|
}
|
|
|
|
// Create a new circuit setup
|
|
let circuit = r#"
|
|
pragma circom 2.1.0;
|
|
|
|
template NewCircuit() {
|
|
signal input a;
|
|
signal output b;
|
|
b <== a + 1;
|
|
}
|
|
|
|
component main {public [b]} = NewCircuit();
|
|
"#;
|
|
|
|
println!("\nInitializing setup for new circuit...");
|
|
let setup = zk
|
|
.setup
|
|
.initialize(SetupInitRequest {
|
|
circuit: circuit.to_string(),
|
|
format: CircuitFormat::Circom,
|
|
name: "new_circuit_setup".to_string(),
|
|
proving_system: ProvingSystem::Groth16,
|
|
ceremony_type: "powers_of_tau".to_string(), // or 'phase2'
|
|
})
|
|
.await?;
|
|
println!("Setup initialized:");
|
|
println!(" Ceremony ID: {}", setup.ceremony_id);
|
|
println!(" Powers of Tau required: {}", setup.powers_required);
|
|
println!(" Current phase: {}", setup.phase);
|
|
|
|
// Contribute to ceremony (in practice, generates random entropy)
|
|
println!("\nContributing to ceremony...");
|
|
let contribution = zk
|
|
.setup
|
|
.contribute(ContributeRequest {
|
|
ceremony_id: setup.ceremony_id.clone(),
|
|
entropy: hex::encode(b"random-entropy-from-user"),
|
|
})
|
|
.await?;
|
|
println!("Contribution submitted:");
|
|
println!(" Participant: {}", contribution.participant_id);
|
|
println!(" Contribution hash: {}", contribution.hash);
|
|
println!(" Verification status: {}", contribution.verified);
|
|
|
|
// Get ceremony status
|
|
let status = zk.setup.get_status(&setup.ceremony_id).await?;
|
|
println!("\nCeremony status:");
|
|
println!(" Phase: {}", status.phase);
|
|
println!(" Total contributions: {}", status.total_contributions);
|
|
println!(" Verified contributions: {}", status.verified_contributions);
|
|
println!(" Ready for finalization: {}", status.ready_for_finalization);
|
|
|
|
// Finalize setup (when enough contributions)
|
|
if status.ready_for_finalization {
|
|
println!("\nFinalizing setup...");
|
|
let finalized = zk.setup.finalize(&setup.ceremony_id).await?;
|
|
println!("Setup finalized:");
|
|
println!(" Proving key hash: {}", finalized.proving_key_hash);
|
|
println!(" Verification key hash: {}", finalized.verification_key_hash);
|
|
println!(" Contribution transcript: {}", finalized.transcript_cid);
|
|
}
|
|
|
|
// Download ceremony transcript
|
|
let transcript = zk.setup.get_transcript(&setup.ceremony_id).await?;
|
|
println!("\nCeremony transcript: {} bytes", transcript.len());
|
|
|
|
println!();
|
|
Ok(())
|
|
}
|