#!/usr/bin/env python3 """ Synor ZK SDK Examples for Python Demonstrates Zero-Knowledge proof operations: - Circuit compilation - Proof generation and verification - Groth16, PLONK, and STARK proving systems - Recursive proofs - On-chain verification """ import asyncio import os import time from synor_zk import ( SynorZk, ZkConfig, ProvingSystem, CircuitFormat, ) async def main(): """Main entry point.""" # Initialize client config = ZkConfig( api_key=os.environ.get("SYNOR_API_KEY", "your-api-key"), endpoint="https://zk.synor.io/v1", timeout=120000, # ZK ops can be slow retries=3, debug=False, default_proving_system=ProvingSystem.GROTH16, ) zk = SynorZk(config) try: # Check service health healthy = await zk.health_check() print(f"Service healthy: {healthy}\n") # Example 1: Circuit compilation await circuit_example(zk) # Example 2: Proof generation await proof_example(zk) # Example 3: Different proving systems await proving_systems_example(zk) # Example 4: Recursive proofs await recursive_proof_example(zk) # Example 5: On-chain verification await on_chain_verification_example(zk) # Example 6: Trusted setup await setup_example(zk) finally: await zk.close() async def circuit_example(zk: SynorZk): """Circuit compilation.""" print("=== Circuit Compilation ===") # Circom circuit example: prove knowledge of preimage circom_circuit = """ 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 print("Compiling Circom circuit...") compiled = await zk.circuits.compile( code=circom_circuit, format=CircuitFormat.CIRCOM, name="hash_preimage", proving_system=ProvingSystem.GROTH16, ) print(f"Circuit compiled:") print(f" Circuit ID: {compiled.circuit_id}") print(f" Constraints: {compiled.constraint_count}") print(f" Public inputs: {compiled.public_input_count}") print(f" Private inputs: {compiled.private_input_count}") print(f" Proving key size: {compiled.proving_key_size} bytes") print(f" Verification key size: {compiled.verification_key_size} bytes") # List circuits circuits = await zk.circuits.list() print(f"\nYour circuits: {len(circuits)}") for circuit in circuits: print(f" {circuit.name} ({circuit.circuit_id})") # Get circuit details details = await zk.circuits.get(compiled.circuit_id) print(f"\nCircuit details:") print(f" Format: {details.format}") print(f" Proving system: {details.proving_system}") print(f" Created: {details.created_at}") # Download proving key proving_key = await zk.circuits.get_proving_key(compiled.circuit_id) print(f"\nProving key downloaded: {len(proving_key)} bytes") # Download verification key verification_key = await zk.circuits.get_verification_key(compiled.circuit_id) print(f"Verification key downloaded: {len(verification_key)} bytes") print() async def proof_example(zk: SynorZk): """Proof generation and verification.""" print("=== Proof Generation ===") # First, compile a simple circuit circuit = """ pragma circom 2.1.0; template Multiplier() { signal input a; signal input b; signal output c; c <== a * b; } component main {public [c]} = Multiplier(); """ compiled = await zk.circuits.compile( code=circuit, format=CircuitFormat.CIRCOM, name="multiplier", proving_system=ProvingSystem.GROTH16, ) # Generate proof print("Generating proof...") start_time = time.time() proof = await zk.proofs.generate( circuit_id=compiled.circuit_id, inputs={"a": "3", "b": "7"}, ) proof_time = (time.time() - start_time) * 1000 print(f"Proof generated in {proof_time:.0f}ms") print(f" Proof ID: {proof.proof_id}") print(f" Proof size: {len(proof.proof)} bytes") print(f" Public signals: {proof.public_signals}") # Verify proof print("\nVerifying proof...") verify_start = time.time() is_valid = await zk.proofs.verify( circuit_id=compiled.circuit_id, proof=proof.proof, public_signals=proof.public_signals, ) verify_time = (time.time() - verify_start) * 1000 print(f"Verification completed in {verify_time:.0f}ms") print(f" Valid: {is_valid}") # Verify with wrong public signals (should fail) print("\nVerifying with wrong signals...") invalid_result = await zk.proofs.verify( circuit_id=compiled.circuit_id, proof=proof.proof, public_signals=["42"], # Wrong answer ) print(f" Valid: {invalid_result} (expected False)") # Get proof status status = await zk.proofs.get_status(proof.proof_id) print(f"\nProof status:") print(f" State: {status.state}") print(f" Verified: {status.verified}") print(f" Created: {status.created_at}") # List proofs proofs = await zk.proofs.list(circuit_id=compiled.circuit_id) print(f"\nProofs for circuit: {len(proofs)}") print() async def proving_systems_example(zk: SynorZk): """Different proving systems comparison.""" print("=== Proving Systems Comparison ===") # Simple circuit for comparison circuit = """ pragma circom 2.1.0; template Comparison() { signal input x; signal input y; signal output sum; sum <== x + y; } component main {public [sum]} = Comparison(); """ systems = [ ProvingSystem.GROTH16, ProvingSystem.PLONK, ProvingSystem.STARK, ] print("Comparing proving systems:\n") for system in systems: print(f"{system.value}:") # Compile for this system compiled = await zk.circuits.compile( code=circuit, format=CircuitFormat.CIRCOM, name=f"comparison_{system.value.lower()}", proving_system=system, ) # Generate proof proof_start = time.time() proof = await zk.proofs.generate( circuit_id=compiled.circuit_id, inputs={"x": "10", "y": "20"}, ) proof_time = (time.time() - proof_start) * 1000 # Verify proof verify_start = time.time() await zk.proofs.verify( circuit_id=compiled.circuit_id, proof=proof.proof, public_signals=proof.public_signals, ) verify_time = (time.time() - verify_start) * 1000 print(f" Setup: {compiled.setup_time}ms") print(f" Proof time: {proof_time:.0f}ms") print(f" Verify time: {verify_time:.0f}ms") print(f" Proof size: {len(proof.proof)} bytes") print(f" Verification key: {compiled.verification_key_size} bytes") print() print("Summary:") print(" Groth16: Smallest proofs, fast verification, trusted setup required") print(" PLONK: Universal setup, flexible, moderate proof size") print(" STARK: No trusted setup, largest proofs, quantum resistant") print() async def recursive_proof_example(zk: SynorZk): """Recursive proof aggregation.""" print("=== Recursive Proofs ===") # Inner circuit inner_circuit = """ pragma circom 2.1.0; template Inner() { signal input x; signal output y; y <== x * x; } component main {public [y]} = Inner(); """ # Compile inner circuit inner = await zk.circuits.compile( code=inner_circuit, format=CircuitFormat.CIRCOM, name="inner_circuit", proving_system=ProvingSystem.GROTH16, ) # Generate multiple proofs to aggregate print("Generating proofs to aggregate...") proofs_to_aggregate = [] for i in range(1, 5): proof = await zk.proofs.generate( circuit_id=inner.circuit_id, inputs={"x": str(i)}, ) proofs_to_aggregate.append({ "proof": proof.proof, "public_signals": proof.public_signals, }) print(f" Proof {i}: y = {proof.public_signals[0]}") # Aggregate proofs recursively print("\nAggregating proofs...") aggregated = await zk.proofs.aggregate( circuit_id=inner.circuit_id, proofs=proofs_to_aggregate, aggregation_type="recursive", ) original_size = sum(len(p["proof"]) for p in proofs_to_aggregate) print(f"Aggregated proof:") print(f" Proof ID: {aggregated.proof_id}") print(f" Aggregated count: {aggregated.aggregated_count}") print(f" Proof size: {len(aggregated.proof)} bytes") print(f" Size reduction: {(1 - len(aggregated.proof) / original_size) * 100:.1f}%") # Verify aggregated proof is_valid = await zk.proofs.verify_aggregated( circuit_id=inner.circuit_id, proof=aggregated.proof, public_signals_list=[p["public_signals"] for p in proofs_to_aggregate], ) print(f"\nAggregated proof valid: {is_valid}") # Batch verification (verify multiple proofs in one operation) print("\nBatch verification...") batch_result = await zk.proofs.batch_verify( circuit_id=inner.circuit_id, proofs=proofs_to_aggregate, ) print(f" All valid: {batch_result.all_valid}") print(f" Results: {', '.join(str(r) for r in batch_result.results)}") print() async def on_chain_verification_example(zk: SynorZk): """On-chain verification.""" print("=== On-Chain Verification ===") # Compile circuit circuit = """ 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(); """ compiled = await zk.circuits.compile( code=circuit, format=CircuitFormat.CIRCOM, name="vote_commitment", proving_system=ProvingSystem.GROTH16, ) # Generate Solidity verifier print("Generating Solidity verifier...") solidity_verifier = await zk.contracts.generate_verifier( circuit_id=compiled.circuit_id, language="solidity", optimized=True, ) print(f"Solidity verifier generated: {len(solidity_verifier.code)} bytes") print(f" Contract name: {solidity_verifier.contract_name}") print(f" Gas estimate: {solidity_verifier.gas_estimate}") # Generate proof proof = await zk.proofs.generate( circuit_id=compiled.circuit_id, inputs={ "vote": "1", # Vote YES (1) or NO (0) "nullifier": "12345", "commitment": "12345", # 1 * 12345 }, ) # Format proof for on-chain verification print("\nFormatting proof for on-chain...") on_chain_proof = await zk.contracts.format_proof( circuit_id=compiled.circuit_id, proof=proof.proof, public_signals=proof.public_signals, format="calldata", ) print(f" Calldata: {on_chain_proof.calldata[:100]}...") print(f" Estimated gas: {on_chain_proof.gas_estimate}") # Deploy verifier contract (simulation) print("\nDeploying verifier contract...") deployment = await zk.contracts.deploy_verifier( circuit_id=compiled.circuit_id, network="synor-testnet", ) print(f" Contract address: {deployment.address}") print(f" TX hash: {deployment.tx_hash}") print(f" Gas used: {deployment.gas_used}") # Verify on-chain print("\nVerifying on-chain...") on_chain_result = await zk.contracts.verify_on_chain( contract_address=deployment.address, proof=proof.proof, public_signals=proof.public_signals, network="synor-testnet", ) print(f" TX hash: {on_chain_result.tx_hash}") print(f" Verified: {on_chain_result.verified}") print(f" Gas used: {on_chain_result.gas_used}") # Generate verifier for other targets print("\nGenerating verifiers for other targets:") targets = ["cairo", "noir", "ink"] for target in targets: verifier = await zk.contracts.generate_verifier( circuit_id=compiled.circuit_id, language=target, ) print(f" {target}: {len(verifier.code)} bytes") print() async def setup_example(zk: SynorZk): """Trusted setup ceremonies.""" print("=== Trusted Setup ===") # Get available ceremonies ceremonies = await zk.setup.list_ceremonies() print(f"Active ceremonies: {len(ceremonies)}") for ceremony in ceremonies: print(f" {ceremony.name}:") print(f" Status: {ceremony.status}") print(f" Participants: {ceremony.participant_count}") print(f" Current round: {ceremony.current_round}") # Create a new circuit setup circuit = """ pragma circom 2.1.0; template NewCircuit() { signal input a; signal output b; b <== a + 1; } component main {public [b]} = NewCircuit(); """ print("\nInitializing setup for new circuit...") setup = await zk.setup.initialize( circuit=circuit, format=CircuitFormat.CIRCOM, name="new_circuit_setup", proving_system=ProvingSystem.GROTH16, ceremony_type="powers_of_tau", # or 'phase2' ) print(f"Setup initialized:") print(f" Ceremony ID: {setup.ceremony_id}") print(f" Powers of Tau required: {setup.powers_required}") print(f" Current phase: {setup.phase}") # Contribute to ceremony (in practice, generates random entropy) print("\nContributing to ceremony...") contribution = await zk.setup.contribute( ceremony_id=setup.ceremony_id, entropy=b"random-entropy-from-user".hex(), ) print(f"Contribution submitted:") print(f" Participant: {contribution.participant_id}") print(f" Contribution hash: {contribution.hash}") print(f" Verification status: {contribution.verified}") # Get ceremony status status = await zk.setup.get_status(setup.ceremony_id) print(f"\nCeremony status:") print(f" Phase: {status.phase}") print(f" Total contributions: {status.total_contributions}") print(f" Verified contributions: {status.verified_contributions}") print(f" Ready for finalization: {status.ready_for_finalization}") # Finalize setup (when enough contributions) if status.ready_for_finalization: print("\nFinalizing setup...") finalized = await zk.setup.finalize(setup.ceremony_id) print(f"Setup finalized:") print(f" Proving key hash: {finalized.proving_key_hash}") print(f" Verification key hash: {finalized.verification_key_hash}") print(f" Contribution transcript: {finalized.transcript_cid}") # Download ceremony transcript transcript = await zk.setup.get_transcript(setup.ceremony_id) print(f"\nCeremony transcript: {len(transcript)} bytes") print() if __name__ == "__main__": asyncio.run(main())