synor/sdk/flutter/example/zk_example.dart

505 lines
15 KiB
Dart

/// Synor ZK SDK Examples for Flutter/Dart
///
/// Demonstrates Zero-Knowledge proof operations:
/// - Circuit compilation
/// - Proof generation and verification
/// - Groth16, PLONK, and STARK proving systems
/// - Recursive proofs
/// - On-chain verification
library;
import 'dart:io';
import 'package:synor_zk/synor_zk.dart';
Future<void> main() async {
// Initialize client
final config = ZkConfig(
apiKey: Platform.environment['SYNOR_API_KEY'] ?? 'your-api-key',
endpoint: 'https://zk.synor.io/v1',
timeout: const Duration(seconds: 120), // ZK ops can be slow
retries: 3,
debug: false,
defaultProvingSystem: ProvingSystem.groth16,
);
final zk = SynorZk(config);
try {
// Check service health
final healthy = await zk.healthCheck();
print('Service healthy: $healthy\n');
// Run examples
await circuitExample(zk);
await proofExample(zk);
await provingSystemsExample(zk);
await recursiveProofExample(zk);
await onChainVerificationExample(zk);
await setupExample(zk);
} finally {
await zk.close();
}
}
Future<void> circuitExample(SynorZk zk) async {
print('=== Circuit Compilation ===');
// Circom circuit example: prove knowledge of preimage
const 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
print('Compiling Circom circuit...');
final compiled = await zk.circuits.compile(CompileRequest(
code: circomCircuit,
format: CircuitFormat.circom,
name: 'hash_preimage',
provingSystem: ProvingSystem.groth16,
));
print('Circuit compiled:');
print(' Circuit ID: ${compiled.circuitId}');
print(' Constraints: ${compiled.constraintCount}');
print(' Public inputs: ${compiled.publicInputCount}');
print(' Private inputs: ${compiled.privateInputCount}');
print(' Proving key size: ${compiled.provingKeySize} bytes');
print(' Verification key size: ${compiled.verificationKeySize} bytes');
// List circuits
final circuits = await zk.circuits.list();
print('\nYour circuits: ${circuits.length}');
for (final circuit in circuits) {
print(' ${circuit.name} (${circuit.circuitId})');
}
// Get circuit details
final details = await zk.circuits.get(compiled.circuitId);
print('\nCircuit details:');
print(' Format: ${details.format}');
print(' Proving system: ${details.provingSystem}');
print(' Created: ${details.createdAt}');
// Download proving key
final provingKey = await zk.circuits.getProvingKey(compiled.circuitId);
print('\nProving key downloaded: ${provingKey.length} bytes');
// Download verification key
final verificationKey = await zk.circuits.getVerificationKey(compiled.circuitId);
print('Verification key downloaded: ${verificationKey.length} bytes');
print('');
}
Future<void> proofExample(SynorZk zk) async {
print('=== Proof Generation ===');
// First, compile a simple circuit
const 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();
''';
final compiled = await zk.circuits.compile(CompileRequest(
code: circuit,
format: CircuitFormat.circom,
name: 'multiplier',
provingSystem: ProvingSystem.groth16,
));
// Generate proof
print('Generating proof...');
final startTime = DateTime.now();
final proof = await zk.proofs.generate(GenerateProofRequest(
circuitId: compiled.circuitId,
inputs: {'a': '3', 'b': '7'},
));
final proofTime = DateTime.now().difference(startTime);
print('Proof generated in ${proofTime.inMilliseconds}ms');
print(' Proof ID: ${proof.proofId}');
print(' Proof size: ${proof.proof.length} bytes');
print(' Public signals: ${proof.publicSignals}');
// Verify proof
print('\nVerifying proof...');
final verifyStart = DateTime.now();
final isValid = await zk.proofs.verify(VerifyRequest(
circuitId: compiled.circuitId,
proof: proof.proof,
publicSignals: proof.publicSignals,
));
final verifyTime = DateTime.now().difference(verifyStart);
print('Verification completed in ${verifyTime.inMilliseconds}ms');
print(' Valid: $isValid');
// Verify with wrong public signals (should fail)
print('\nVerifying with wrong signals...');
final invalidResult = await zk.proofs.verify(VerifyRequest(
circuitId: compiled.circuitId,
proof: proof.proof,
publicSignals: ['42'], // Wrong answer
));
print(' Valid: $invalidResult (expected false)');
// Get proof status
final status = await zk.proofs.getStatus(proof.proofId);
print('\nProof status:');
print(' State: ${status.state}');
print(' Verified: ${status.verified}');
print(' Created: ${status.createdAt}');
// List proofs
final proofs = await zk.proofs.list(compiled.circuitId);
print('\nProofs for circuit: ${proofs.length}');
print('');
}
Future<void> provingSystemsExample(SynorZk zk) async {
print('=== Proving Systems Comparison ===');
// Simple circuit for comparison
const 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();
''';
final systems = [
(ProvingSystem.groth16, 'GROTH16'),
(ProvingSystem.plonk, 'PLONK'),
(ProvingSystem.stark, 'STARK'),
];
print('Comparing proving systems:\n');
for (final (system, name) in systems) {
print('$name:');
// Compile for this system
final compiled = await zk.circuits.compile(CompileRequest(
code: circuit,
format: CircuitFormat.circom,
name: 'comparison_${name.toLowerCase()}',
provingSystem: system,
));
// Generate proof
final proofStart = DateTime.now();
final proof = await zk.proofs.generate(GenerateProofRequest(
circuitId: compiled.circuitId,
inputs: {'x': '10', 'y': '20'},
));
final proofTime = DateTime.now().difference(proofStart);
// Verify proof
final verifyStart = DateTime.now();
await zk.proofs.verify(VerifyRequest(
circuitId: compiled.circuitId,
proof: proof.proof,
publicSignals: proof.publicSignals,
));
final verifyTime = DateTime.now().difference(verifyStart);
print(' Setup: ${compiled.setupTime}ms');
print(' Proof time: ${proofTime.inMilliseconds}ms');
print(' Verify time: ${verifyTime.inMilliseconds}ms');
print(' Proof size: ${proof.proof.length} bytes');
print(' Verification key: ${compiled.verificationKeySize} 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('');
}
Future<void> recursiveProofExample(SynorZk zk) async {
print('=== Recursive Proofs ===');
// Inner circuit
const 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
final inner = await zk.circuits.compile(CompileRequest(
code: innerCircuit,
format: CircuitFormat.circom,
name: 'inner_circuit',
provingSystem: ProvingSystem.groth16,
));
// Generate multiple proofs to aggregate
print('Generating proofs to aggregate...');
final proofsToAggregate = <ProofData>[];
for (var i = 1; i <= 4; i++) {
final proof = await zk.proofs.generate(GenerateProofRequest(
circuitId: inner.circuitId,
inputs: {'x': '$i'},
));
proofsToAggregate.add(ProofData(
proof: proof.proof,
publicSignals: proof.publicSignals,
));
print(' Proof $i: y = ${proof.publicSignals[0]}');
}
// Aggregate proofs recursively
print('\nAggregating proofs...');
final aggregated = await zk.proofs.aggregate(AggregateRequest(
circuitId: inner.circuitId,
proofs: proofsToAggregate,
aggregationType: 'recursive',
));
final originalSize = proofsToAggregate.fold<int>(
0,
(sum, p) => sum + p.proof.length,
);
print('Aggregated proof:');
print(' Proof ID: ${aggregated.proofId}');
print(' Aggregated count: ${aggregated.aggregatedCount}');
print(' Proof size: ${aggregated.proof.length} bytes');
print(' Size reduction: ${((1 - aggregated.proof.length / originalSize) * 100).toStringAsFixed(1)}%');
// Verify aggregated proof
final isValid = await zk.proofs.verifyAggregated(VerifyAggregatedRequest(
circuitId: inner.circuitId,
proof: aggregated.proof,
publicSignalsList: proofsToAggregate.map((p) => p.publicSignals).toList(),
));
print('\nAggregated proof valid: $isValid');
// Batch verification (verify multiple proofs in one operation)
print('\nBatch verification...');
final batchResult = await zk.proofs.batchVerify(BatchVerifyRequest(
circuitId: inner.circuitId,
proofs: proofsToAggregate,
));
print(' All valid: ${batchResult.allValid}');
print(' Results: ${batchResult.results.join(", ")}');
print('');
}
Future<void> onChainVerificationExample(SynorZk zk) async {
print('=== On-Chain Verification ===');
// Compile circuit
const 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();
''';
final compiled = await zk.circuits.compile(CompileRequest(
code: circuit,
format: CircuitFormat.circom,
name: 'vote_commitment',
provingSystem: ProvingSystem.groth16,
));
// Generate Solidity verifier
print('Generating Solidity verifier...');
final solidityVerifier = await zk.contracts.generateVerifier(GenerateVerifierRequest(
circuitId: compiled.circuitId,
language: 'solidity',
optimized: true,
));
print('Solidity verifier generated: ${solidityVerifier.code.length} bytes');
print(' Contract name: ${solidityVerifier.contractName}');
print(' Gas estimate: ${solidityVerifier.gasEstimate}');
// Generate proof
final proof = await zk.proofs.generate(GenerateProofRequest(
circuitId: compiled.circuitId,
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...');
final onChainProof = await zk.contracts.formatProof(FormatProofRequest(
circuitId: compiled.circuitId,
proof: proof.proof,
publicSignals: proof.publicSignals,
format: 'calldata',
));
final calldataPreview = onChainProof.calldata.length > 100
? onChainProof.calldata.substring(0, 100)
: onChainProof.calldata;
print(' Calldata: $calldataPreview...');
print(' Estimated gas: ${onChainProof.gasEstimate}');
// Deploy verifier contract (simulation)
print('\nDeploying verifier contract...');
final deployment = await zk.contracts.deployVerifier(DeployRequest(
circuitId: compiled.circuitId,
network: 'synor-testnet',
));
print(' Contract address: ${deployment.address}');
print(' TX hash: ${deployment.txHash}');
print(' Gas used: ${deployment.gasUsed}');
// Verify on-chain
print('\nVerifying on-chain...');
final onChainResult = await zk.contracts.verifyOnChain(OnChainVerifyRequest(
contractAddress: deployment.address,
proof: proof.proof,
publicSignals: proof.publicSignals,
network: 'synor-testnet',
));
print(' TX hash: ${onChainResult.txHash}');
print(' Verified: ${onChainResult.verified}');
print(' Gas used: ${onChainResult.gasUsed}');
// Generate verifier for other targets
print('\nGenerating verifiers for other targets:');
final targets = ['cairo', 'noir', 'ink'];
for (final target in targets) {
final verifier = await zk.contracts.generateVerifier(GenerateVerifierRequest(
circuitId: compiled.circuitId,
language: target,
));
print(' $target: ${verifier.code.length} bytes');
}
print('');
}
Future<void> setupExample(SynorZk zk) async {
print('=== Trusted Setup ===');
// Get available ceremonies
final ceremonies = await zk.setup.listCeremonies();
print('Active ceremonies: ${ceremonies.length}');
for (final ceremony in ceremonies) {
print(' ${ceremony.name}:');
print(' Status: ${ceremony.status}');
print(' Participants: ${ceremony.participantCount}');
print(' Current round: ${ceremony.currentRound}');
}
// Create a new circuit setup
const 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...');
final setup = await zk.setup.initialize(SetupInitRequest(
circuit: circuit,
format: CircuitFormat.circom,
name: 'new_circuit_setup',
provingSystem: ProvingSystem.groth16,
ceremonyType: 'powers_of_tau', // or 'phase2'
));
print('Setup initialized:');
print(' Ceremony ID: ${setup.ceremonyId}');
print(' Powers of Tau required: ${setup.powersRequired}');
print(' Current phase: ${setup.phase}');
// Contribute to ceremony (in practice, generates random entropy)
print('\nContributing to ceremony...');
final contribution = await zk.setup.contribute(ContributeRequest(
ceremonyId: setup.ceremonyId,
entropy: 'random-entropy-from-user'.codeUnits.map((b) => b.toRadixString(16).padLeft(2, '0')).join(),
));
print('Contribution submitted:');
print(' Participant: ${contribution.participantId}');
print(' Contribution hash: ${contribution.hash}');
print(' Verification status: ${contribution.verified}');
// Get ceremony status
final status = await zk.setup.getStatus(setup.ceremonyId);
print('\nCeremony status:');
print(' Phase: ${status.phase}');
print(' Total contributions: ${status.totalContributions}');
print(' Verified contributions: ${status.verifiedContributions}');
print(' Ready for finalization: ${status.readyForFinalization}');
// Finalize setup (when enough contributions)
if (status.readyForFinalization) {
print('\nFinalizing setup...');
final finalized = await zk.setup.finalize(setup.ceremonyId);
print('Setup finalized:');
print(' Proving key hash: ${finalized.provingKeyHash}');
print(' Verification key hash: ${finalized.verificationKeyHash}');
print(' Contribution transcript: ${finalized.transcriptCid}');
}
// Download ceremony transcript
final transcript = await zk.setup.getTranscript(setup.ceremonyId);
print('\nCeremony transcript: ${transcript.length} bytes');
print('');
}