package io.synor.examples; import io.synor.zk.*; import io.synor.zk.types.*; import java.util.*; import java.util.concurrent.CompletableFuture; /** * Synor ZK SDK Examples for Java * * Demonstrates Zero-Knowledge proof operations: * - Circuit compilation * - Proof generation and verification * - Groth16, PLONK, and STARK proving systems * - Recursive proofs * - On-chain verification */ public class ZkExample { public static void main(String[] args) throws Exception { // Initialize client ZkConfig config = ZkConfig.builder() .apiKey(System.getenv("SYNOR_API_KEY") != null ? System.getenv("SYNOR_API_KEY") : "your-api-key") .endpoint("https://zk.synor.io/v1") .timeout(120000) // ZK ops can be slow .retries(3) .debug(false) .defaultProvingSystem(ProvingSystem.GROTH16) .build(); SynorZk zk = new SynorZk(config); try { // Check service health boolean healthy = zk.healthCheck().get(); System.out.println("Service healthy: " + healthy + "\n"); // Run examples circuitExample(zk); proofExample(zk); provingSystemsExample(zk); recursiveProofExample(zk); onChainVerificationExample(zk); setupExample(zk); } finally { zk.close(); } } static void circuitExample(SynorZk zk) throws Exception { System.out.println("=== Circuit Compilation ==="); // Circom circuit example: prove knowledge of preimage String circomCircuit = """ 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 System.out.println("Compiling Circom circuit..."); CompiledCircuit compiled = zk.circuits().compile(CompileRequest.builder() .code(circomCircuit) .format(CircuitFormat.CIRCOM) .name("hash_preimage") .provingSystem(ProvingSystem.GROTH16) .build()).get(); System.out.println("Circuit compiled:"); System.out.println(" Circuit ID: " + compiled.getCircuitId()); System.out.println(" Constraints: " + compiled.getConstraintCount()); System.out.println(" Public inputs: " + compiled.getPublicInputCount()); System.out.println(" Private inputs: " + compiled.getPrivateInputCount()); System.out.println(" Proving key size: " + compiled.getProvingKeySize() + " bytes"); System.out.println(" Verification key size: " + compiled.getVerificationKeySize() + " bytes"); // List circuits List circuits = zk.circuits().list().get(); System.out.println("\nYour circuits: " + circuits.size()); for (Circuit circuit : circuits) { System.out.println(" " + circuit.getName() + " (" + circuit.getCircuitId() + ")"); } // Get circuit details CircuitDetails details = zk.circuits().get(compiled.getCircuitId()).get(); System.out.println("\nCircuit details:"); System.out.println(" Format: " + details.getFormat()); System.out.println(" Proving system: " + details.getProvingSystem()); System.out.println(" Created: " + details.getCreatedAt()); // Download proving key byte[] provingKey = zk.circuits().getProvingKey(compiled.getCircuitId()).get(); System.out.println("\nProving key downloaded: " + provingKey.length + " bytes"); // Download verification key byte[] verificationKey = zk.circuits().getVerificationKey(compiled.getCircuitId()).get(); System.out.println("Verification key downloaded: " + verificationKey.length + " bytes"); System.out.println(); } static void proofExample(SynorZk zk) throws Exception { System.out.println("=== Proof Generation ==="); // First, compile a simple circuit String 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(); """; CompiledCircuit compiled = zk.circuits().compile(CompileRequest.builder() .code(circuit) .format(CircuitFormat.CIRCOM) .name("multiplier") .provingSystem(ProvingSystem.GROTH16) .build()).get(); // Generate proof System.out.println("Generating proof..."); long startTime = System.currentTimeMillis(); Map inputs = new HashMap<>(); inputs.put("a", "3"); inputs.put("b", "7"); Proof proof = zk.proofs().generate(GenerateProofRequest.builder() .circuitId(compiled.getCircuitId()) .inputs(inputs) .build()).get(); long proofTime = System.currentTimeMillis() - startTime; System.out.println("Proof generated in " + proofTime + "ms"); System.out.println(" Proof ID: " + proof.getProofId()); System.out.println(" Proof size: " + proof.getProof().length() + " bytes"); System.out.println(" Public signals: " + proof.getPublicSignals()); // Verify proof System.out.println("\nVerifying proof..."); long verifyStart = System.currentTimeMillis(); boolean isValid = zk.proofs().verify(VerifyRequest.builder() .circuitId(compiled.getCircuitId()) .proof(proof.getProof()) .publicSignals(proof.getPublicSignals()) .build()).get(); long verifyTime = System.currentTimeMillis() - verifyStart; System.out.println("Verification completed in " + verifyTime + "ms"); System.out.println(" Valid: " + isValid); // Verify with wrong public signals (should fail) System.out.println("\nVerifying with wrong signals..."); boolean invalidResult = zk.proofs().verify(VerifyRequest.builder() .circuitId(compiled.getCircuitId()) .proof(proof.getProof()) .publicSignals(List.of("42")) // Wrong answer .build()).get(); System.out.println(" Valid: " + invalidResult + " (expected false)"); // Get proof status ProofStatus status = zk.proofs().getStatus(proof.getProofId()).get(); System.out.println("\nProof status:"); System.out.println(" State: " + status.getState()); System.out.println(" Verified: " + status.isVerified()); System.out.println(" Created: " + status.getCreatedAt()); // List proofs List proofs = zk.proofs().list(compiled.getCircuitId()).get(); System.out.println("\nProofs for circuit: " + proofs.size()); System.out.println(); } static void provingSystemsExample(SynorZk zk) throws Exception { System.out.println("=== Proving Systems Comparison ==="); // Simple circuit for comparison String 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(); """; ProvingSystem[] systems = { ProvingSystem.GROTH16, ProvingSystem.PLONK, ProvingSystem.STARK }; System.out.println("Comparing proving systems:\n"); for (ProvingSystem system : systems) { System.out.println(system + ":"); // Compile for this system CompiledCircuit compiled = zk.circuits().compile(CompileRequest.builder() .code(circuit) .format(CircuitFormat.CIRCOM) .name("comparison_" + system.toString().toLowerCase()) .provingSystem(system) .build()).get(); // Generate proof long proofStart = System.currentTimeMillis(); Map inputs = new HashMap<>(); inputs.put("x", "10"); inputs.put("y", "20"); Proof proof = zk.proofs().generate(GenerateProofRequest.builder() .circuitId(compiled.getCircuitId()) .inputs(inputs) .build()).get(); long proofTime = System.currentTimeMillis() - proofStart; // Verify proof long verifyStart = System.currentTimeMillis(); zk.proofs().verify(VerifyRequest.builder() .circuitId(compiled.getCircuitId()) .proof(proof.getProof()) .publicSignals(proof.getPublicSignals()) .build()).get(); long verifyTime = System.currentTimeMillis() - verifyStart; System.out.println(" Setup: " + compiled.getSetupTime() + "ms"); System.out.println(" Proof time: " + proofTime + "ms"); System.out.println(" Verify time: " + verifyTime + "ms"); System.out.println(" Proof size: " + proof.getProof().length() + " bytes"); System.out.println(" Verification key: " + compiled.getVerificationKeySize() + " bytes"); System.out.println(); } System.out.println("Summary:"); System.out.println(" Groth16: Smallest proofs, fast verification, trusted setup required"); System.out.println(" PLONK: Universal setup, flexible, moderate proof size"); System.out.println(" STARK: No trusted setup, largest proofs, quantum resistant"); System.out.println(); } static void recursiveProofExample(SynorZk zk) throws Exception { System.out.println("=== Recursive Proofs ==="); // Inner circuit String innerCircuit = """ pragma circom 2.1.0; template Inner() { signal input x; signal output y; y <== x * x; } component main {public [y]} = Inner(); """; // Compile inner circuit CompiledCircuit inner = zk.circuits().compile(CompileRequest.builder() .code(innerCircuit) .format(CircuitFormat.CIRCOM) .name("inner_circuit") .provingSystem(ProvingSystem.GROTH16) .build()).get(); // Generate multiple proofs to aggregate System.out.println("Generating proofs to aggregate..."); List proofsToAggregate = new ArrayList<>(); for (int i = 1; i <= 4; i++) { Map inputs = new HashMap<>(); inputs.put("x", String.valueOf(i)); Proof proof = zk.proofs().generate(GenerateProofRequest.builder() .circuitId(inner.getCircuitId()) .inputs(inputs) .build()).get(); proofsToAggregate.add(ProofData.builder() .proof(proof.getProof()) .publicSignals(proof.getPublicSignals()) .build()); System.out.println(" Proof " + i + ": y = " + proof.getPublicSignals().get(0)); } // Aggregate proofs recursively System.out.println("\nAggregating proofs..."); AggregatedProof aggregated = zk.proofs().aggregate(AggregateRequest.builder() .circuitId(inner.getCircuitId()) .proofs(proofsToAggregate) .aggregationType("recursive") .build()).get(); int originalSize = proofsToAggregate.stream() .mapToInt(p -> p.getProof().length()) .sum(); System.out.println("Aggregated proof:"); System.out.println(" Proof ID: " + aggregated.getProofId()); System.out.println(" Aggregated count: " + aggregated.getAggregatedCount()); System.out.println(" Proof size: " + aggregated.getProof().length() + " bytes"); System.out.println(" Size reduction: " + String.format("%.1f", (1 - (double) aggregated.getProof().length() / originalSize) * 100) + "%"); // Verify aggregated proof List> publicSignalsList = proofsToAggregate.stream() .map(ProofData::getPublicSignals) .toList(); boolean isValid = zk.proofs().verifyAggregated(VerifyAggregatedRequest.builder() .circuitId(inner.getCircuitId()) .proof(aggregated.getProof()) .publicSignalsList(publicSignalsList) .build()).get(); System.out.println("\nAggregated proof valid: " + isValid); // Batch verification System.out.println("\nBatch verification..."); BatchVerifyResult batchResult = zk.proofs().batchVerify(BatchVerifyRequest.builder() .circuitId(inner.getCircuitId()) .proofs(proofsToAggregate) .build()).get(); System.out.println(" All valid: " + batchResult.isAllValid()); System.out.println(" Results: " + batchResult.getResults()); System.out.println(); } static void onChainVerificationExample(SynorZk zk) throws Exception { System.out.println("=== On-Chain Verification ==="); // Compile circuit String circuit = """ pragma circom 2.1.0; template VoteCommitment() { signal input vote; signal input nullifier; signal input commitment; signal computed; computed <== vote * nullifier; commitment === computed; } component main {public [commitment]} = VoteCommitment(); """; CompiledCircuit compiled = zk.circuits().compile(CompileRequest.builder() .code(circuit) .format(CircuitFormat.CIRCOM) .name("vote_commitment") .provingSystem(ProvingSystem.GROTH16) .build()).get(); // Generate Solidity verifier System.out.println("Generating Solidity verifier..."); GeneratedVerifier solidityVerifier = zk.contracts().generateVerifier(GenerateVerifierRequest.builder() .circuitId(compiled.getCircuitId()) .language("solidity") .optimized(true) .build()).get(); System.out.println("Solidity verifier generated: " + solidityVerifier.getCode().length() + " bytes"); System.out.println(" Contract name: " + solidityVerifier.getContractName()); System.out.println(" Gas estimate: " + solidityVerifier.getGasEstimate()); // Generate proof Map inputs = new HashMap<>(); inputs.put("vote", "1"); inputs.put("nullifier", "12345"); inputs.put("commitment", "12345"); Proof proof = zk.proofs().generate(GenerateProofRequest.builder() .circuitId(compiled.getCircuitId()) .inputs(inputs) .build()).get(); // Format proof for on-chain verification System.out.println("\nFormatting proof for on-chain..."); FormattedProof onChainProof = zk.contracts().formatProof(FormatProofRequest.builder() .circuitId(compiled.getCircuitId()) .proof(proof.getProof()) .publicSignals(proof.getPublicSignals()) .format("calldata") .build()).get(); String calldataPreview = onChainProof.getCalldata().substring(0, Math.min(100, onChainProof.getCalldata().length())); System.out.println(" Calldata: " + calldataPreview + "..."); System.out.println(" Estimated gas: " + onChainProof.getGasEstimate()); // Deploy verifier contract System.out.println("\nDeploying verifier contract..."); Deployment deployment = zk.contracts().deployVerifier(DeployRequest.builder() .circuitId(compiled.getCircuitId()) .network("synor-testnet") .build()).get(); System.out.println(" Contract address: " + deployment.getAddress()); System.out.println(" TX hash: " + deployment.getTxHash()); System.out.println(" Gas used: " + deployment.getGasUsed()); // Verify on-chain System.out.println("\nVerifying on-chain..."); OnChainVerifyResult onChainResult = zk.contracts().verifyOnChain(OnChainVerifyRequest.builder() .contractAddress(deployment.getAddress()) .proof(proof.getProof()) .publicSignals(proof.getPublicSignals()) .network("synor-testnet") .build()).get(); System.out.println(" TX hash: " + onChainResult.getTxHash()); System.out.println(" Verified: " + onChainResult.isVerified()); System.out.println(" Gas used: " + onChainResult.getGasUsed()); // Generate verifier for other targets System.out.println("\nGenerating verifiers for other targets:"); String[] targets = {"cairo", "noir", "ink"}; for (String target : targets) { GeneratedVerifier verifier = zk.contracts().generateVerifier(GenerateVerifierRequest.builder() .circuitId(compiled.getCircuitId()) .language(target) .build()).get(); System.out.println(" " + target + ": " + verifier.getCode().length() + " bytes"); } System.out.println(); } static void setupExample(SynorZk zk) throws Exception { System.out.println("=== Trusted Setup ==="); // Get available ceremonies List ceremonies = zk.setup().listCeremonies().get(); System.out.println("Active ceremonies: " + ceremonies.size()); for (Ceremony ceremony : ceremonies) { System.out.println(" " + ceremony.getName() + ":"); System.out.println(" Status: " + ceremony.getStatus()); System.out.println(" Participants: " + ceremony.getParticipantCount()); System.out.println(" Current round: " + ceremony.getCurrentRound()); } // Create a new circuit setup String circuit = """ pragma circom 2.1.0; template NewCircuit() { signal input a; signal output b; b <== a + 1; } component main {public [b]} = NewCircuit(); """; System.out.println("\nInitializing setup for new circuit..."); SetupResult setup = zk.setup().initialize(SetupInitRequest.builder() .circuit(circuit) .format(CircuitFormat.CIRCOM) .name("new_circuit_setup") .provingSystem(ProvingSystem.GROTH16) .ceremonyType("powers_of_tau") .build()).get(); System.out.println("Setup initialized:"); System.out.println(" Ceremony ID: " + setup.getCeremonyId()); System.out.println(" Powers of Tau required: " + setup.getPowersRequired()); System.out.println(" Current phase: " + setup.getPhase()); // Contribute to ceremony System.out.println("\nContributing to ceremony..."); Contribution contribution = zk.setup().contribute(ContributeRequest.builder() .ceremonyId(setup.getCeremonyId()) .entropy(bytesToHex("random-entropy-from-user".getBytes())) .build()).get(); System.out.println("Contribution submitted:"); System.out.println(" Participant: " + contribution.getParticipantId()); System.out.println(" Contribution hash: " + contribution.getHash()); System.out.println(" Verification status: " + contribution.isVerified()); // Get ceremony status CeremonyStatus status = zk.setup().getStatus(setup.getCeremonyId()).get(); System.out.println("\nCeremony status:"); System.out.println(" Phase: " + status.getPhase()); System.out.println(" Total contributions: " + status.getTotalContributions()); System.out.println(" Verified contributions: " + status.getVerifiedContributions()); System.out.println(" Ready for finalization: " + status.isReadyForFinalization()); // Finalize setup (when enough contributions) if (status.isReadyForFinalization()) { System.out.println("\nFinalizing setup..."); FinalizedSetup finalized = zk.setup().finalize(setup.getCeremonyId()).get(); System.out.println("Setup finalized:"); System.out.println(" Proving key hash: " + finalized.getProvingKeyHash()); System.out.println(" Verification key hash: " + finalized.getVerificationKeyHash()); System.out.println(" Contribution transcript: " + finalized.getTranscriptCid()); } // Download ceremony transcript byte[] transcript = zk.setup().getTranscript(setup.getCeremonyId()).get(); System.out.println("\nCeremony transcript: " + transcript.length + " bytes"); System.out.println(); } private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } }