505 lines
15 KiB
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('');
|
|
}
|