- 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.
537 lines
16 KiB
TypeScript
537 lines
16 KiB
TypeScript
/**
|
|
* Synor ZK SDK Examples for JavaScript/TypeScript
|
|
*
|
|
* Demonstrates Zero-Knowledge proof operations:
|
|
* - Circuit compilation
|
|
* - Proof generation and verification
|
|
* - Groth16, PLONK, and STARK proving systems
|
|
* - Recursive proofs
|
|
* - On-chain verification
|
|
*/
|
|
|
|
import {
|
|
SynorZk,
|
|
ZkConfig,
|
|
ProvingSystem,
|
|
CircuitFormat,
|
|
ProofStatus,
|
|
} from '../src/zk';
|
|
|
|
async function main() {
|
|
// Initialize client
|
|
const config: ZkConfig = {
|
|
apiKey: process.env.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,
|
|
};
|
|
|
|
const zk = new SynorZk(config);
|
|
|
|
try {
|
|
// Check service health
|
|
const healthy = await zk.healthCheck();
|
|
console.log(`Service healthy: ${healthy}\n`);
|
|
|
|
// Example 1: Circuit compilation
|
|
await circuitExample(zk);
|
|
|
|
// Example 2: Proof generation
|
|
await proofExample(zk);
|
|
|
|
// Example 3: Different proving systems
|
|
await provingSystemsExample(zk);
|
|
|
|
// Example 4: Recursive proofs
|
|
await recursiveProofExample(zk);
|
|
|
|
// Example 5: On-chain verification
|
|
await onChainVerificationExample(zk);
|
|
|
|
// Example 6: Trusted setup
|
|
await setupExample(zk);
|
|
} finally {
|
|
zk.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Circuit compilation
|
|
*/
|
|
async function circuitExample(zk: SynorZk): Promise<void> {
|
|
console.log('=== 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
|
|
console.log('Compiling Circom circuit...');
|
|
const compiled = await zk.circuits.compile({
|
|
code: circomCircuit,
|
|
format: CircuitFormat.Circom,
|
|
name: 'hash_preimage',
|
|
provingSystem: ProvingSystem.Groth16,
|
|
});
|
|
|
|
console.log(`Circuit compiled:`);
|
|
console.log(` Circuit ID: ${compiled.circuitId}`);
|
|
console.log(` Constraints: ${compiled.constraintCount}`);
|
|
console.log(` Public inputs: ${compiled.publicInputCount}`);
|
|
console.log(` Private inputs: ${compiled.privateInputCount}`);
|
|
console.log(` Proving key size: ${compiled.provingKeySize} bytes`);
|
|
console.log(` Verification key size: ${compiled.verificationKeySize} bytes`);
|
|
|
|
// List circuits
|
|
const circuits = await zk.circuits.list();
|
|
console.log(`\nYour circuits: ${circuits.length}`);
|
|
for (const circuit of circuits) {
|
|
console.log(` ${circuit.name} (${circuit.circuitId})`);
|
|
}
|
|
|
|
// Get circuit details
|
|
const details = await zk.circuits.get(compiled.circuitId);
|
|
console.log(`\nCircuit details:`);
|
|
console.log(` Format: ${details.format}`);
|
|
console.log(` Proving system: ${details.provingSystem}`);
|
|
console.log(` Created: ${new Date(details.createdAt).toISOString()}`);
|
|
|
|
// Download proving key
|
|
const provingKey = await zk.circuits.getProvingKey(compiled.circuitId);
|
|
console.log(`\nProving key downloaded: ${provingKey.length} bytes`);
|
|
|
|
// Download verification key
|
|
const verificationKey = await zk.circuits.getVerificationKey(compiled.circuitId);
|
|
console.log(`Verification key downloaded: ${verificationKey.length} bytes`);
|
|
|
|
console.log('');
|
|
}
|
|
|
|
/**
|
|
* Proof generation and verification
|
|
*/
|
|
async function proofExample(zk: SynorZk): Promise<void> {
|
|
console.log('=== 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();
|
|
`;
|
|
|
|
const compiled = await zk.circuits.compile({
|
|
code: circuit,
|
|
format: CircuitFormat.Circom,
|
|
name: 'multiplier',
|
|
provingSystem: ProvingSystem.Groth16,
|
|
});
|
|
|
|
// Generate proof
|
|
console.log('Generating proof...');
|
|
const startTime = Date.now();
|
|
|
|
const proof = await zk.proofs.generate({
|
|
circuitId: compiled.circuitId,
|
|
inputs: {
|
|
a: '3',
|
|
b: '7',
|
|
},
|
|
});
|
|
|
|
const proofTime = Date.now() - startTime;
|
|
console.log(`Proof generated in ${proofTime}ms`);
|
|
console.log(` Proof ID: ${proof.proofId}`);
|
|
console.log(` Proof size: ${proof.proof.length} bytes`);
|
|
console.log(` Public signals: ${proof.publicSignals}`);
|
|
|
|
// Verify proof
|
|
console.log('\nVerifying proof...');
|
|
const verifyStart = Date.now();
|
|
|
|
const isValid = await zk.proofs.verify({
|
|
circuitId: compiled.circuitId,
|
|
proof: proof.proof,
|
|
publicSignals: proof.publicSignals,
|
|
});
|
|
|
|
const verifyTime = Date.now() - verifyStart;
|
|
console.log(`Verification completed in ${verifyTime}ms`);
|
|
console.log(` Valid: ${isValid}`);
|
|
|
|
// Verify with wrong public signals (should fail)
|
|
console.log('\nVerifying with wrong signals...');
|
|
const invalidResult = await zk.proofs.verify({
|
|
circuitId: compiled.circuitId,
|
|
proof: proof.proof,
|
|
publicSignals: ['42'], // Wrong answer
|
|
});
|
|
console.log(` Valid: ${invalidResult} (expected false)`);
|
|
|
|
// Get proof status
|
|
const status = await zk.proofs.getStatus(proof.proofId);
|
|
console.log(`\nProof status:`);
|
|
console.log(` State: ${status.state}`);
|
|
console.log(` Verified: ${status.verified}`);
|
|
console.log(` Created: ${new Date(status.createdAt).toISOString()}`);
|
|
|
|
// List proofs
|
|
const proofs = await zk.proofs.list({ circuitId: compiled.circuitId });
|
|
console.log(`\nProofs for circuit: ${proofs.length}`);
|
|
|
|
console.log('');
|
|
}
|
|
|
|
/**
|
|
* Different proving systems comparison
|
|
*/
|
|
async function provingSystemsExample(zk: SynorZk): Promise<void> {
|
|
console.log('=== 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();
|
|
`;
|
|
|
|
const systems = [
|
|
ProvingSystem.Groth16,
|
|
ProvingSystem.Plonk,
|
|
ProvingSystem.Stark,
|
|
];
|
|
|
|
console.log('Comparing proving systems:\n');
|
|
|
|
for (const system of systems) {
|
|
console.log(`${system}:`);
|
|
|
|
// Compile for this system
|
|
const compiled = await zk.circuits.compile({
|
|
code: circuit,
|
|
format: CircuitFormat.Circom,
|
|
name: `comparison_${system.toLowerCase()}`,
|
|
provingSystem: system,
|
|
});
|
|
|
|
// Generate proof
|
|
const proofStart = Date.now();
|
|
const proof = await zk.proofs.generate({
|
|
circuitId: compiled.circuitId,
|
|
inputs: { x: '10', y: '20' },
|
|
});
|
|
const proofTime = Date.now() - proofStart;
|
|
|
|
// Verify proof
|
|
const verifyStart = Date.now();
|
|
await zk.proofs.verify({
|
|
circuitId: compiled.circuitId,
|
|
proof: proof.proof,
|
|
publicSignals: proof.publicSignals,
|
|
});
|
|
const verifyTime = Date.now() - verifyStart;
|
|
|
|
console.log(` Setup: ${compiled.setupTime}ms`);
|
|
console.log(` Proof time: ${proofTime}ms`);
|
|
console.log(` Verify time: ${verifyTime}ms`);
|
|
console.log(` Proof size: ${proof.proof.length} bytes`);
|
|
console.log(` Verification key: ${compiled.verificationKeySize} bytes`);
|
|
console.log('');
|
|
}
|
|
|
|
console.log('Summary:');
|
|
console.log(' Groth16: Smallest proofs, fast verification, trusted setup required');
|
|
console.log(' PLONK: Universal setup, flexible, moderate proof size');
|
|
console.log(' STARK: No trusted setup, largest proofs, quantum resistant');
|
|
|
|
console.log('');
|
|
}
|
|
|
|
/**
|
|
* Recursive proof aggregation
|
|
*/
|
|
async function recursiveProofExample(zk: SynorZk): Promise<void> {
|
|
console.log('=== 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
|
|
const inner = await zk.circuits.compile({
|
|
code: innerCircuit,
|
|
format: CircuitFormat.Circom,
|
|
name: 'inner_circuit',
|
|
provingSystem: ProvingSystem.Groth16,
|
|
});
|
|
|
|
// Generate multiple proofs to aggregate
|
|
console.log('Generating proofs to aggregate...');
|
|
const proofsToAggregate = [];
|
|
for (let i = 1; i <= 4; i++) {
|
|
const proof = await zk.proofs.generate({
|
|
circuitId: inner.circuitId,
|
|
inputs: { x: i.toString() },
|
|
});
|
|
proofsToAggregate.push({
|
|
proof: proof.proof,
|
|
publicSignals: proof.publicSignals,
|
|
});
|
|
console.log(` Proof ${i}: y = ${proof.publicSignals[0]}`);
|
|
}
|
|
|
|
// Aggregate proofs recursively
|
|
console.log('\nAggregating proofs...');
|
|
const aggregated = await zk.proofs.aggregate({
|
|
circuitId: inner.circuitId,
|
|
proofs: proofsToAggregate,
|
|
aggregationType: 'recursive',
|
|
});
|
|
|
|
console.log(`Aggregated proof:`);
|
|
console.log(` Proof ID: ${aggregated.proofId}`);
|
|
console.log(` Aggregated count: ${aggregated.aggregatedCount}`);
|
|
console.log(` Proof size: ${aggregated.proof.length} bytes`);
|
|
console.log(` Size reduction: ${(1 - aggregated.proof.length / (proofsToAggregate.length * proofsToAggregate[0].proof.length)) * 100}%`);
|
|
|
|
// Verify aggregated proof
|
|
const isValid = await zk.proofs.verifyAggregated({
|
|
circuitId: inner.circuitId,
|
|
proof: aggregated.proof,
|
|
publicSignalsList: proofsToAggregate.map(p => p.publicSignals),
|
|
});
|
|
console.log(`\nAggregated proof valid: ${isValid}`);
|
|
|
|
// Batch verification (verify multiple proofs in one operation)
|
|
console.log('\nBatch verification...');
|
|
const batchResult = await zk.proofs.batchVerify({
|
|
circuitId: inner.circuitId,
|
|
proofs: proofsToAggregate,
|
|
});
|
|
console.log(` All valid: ${batchResult.allValid}`);
|
|
console.log(` Results: ${batchResult.results.join(', ')}`);
|
|
|
|
console.log('');
|
|
}
|
|
|
|
/**
|
|
* On-chain verification
|
|
*/
|
|
async function onChainVerificationExample(zk: SynorZk): Promise<void> {
|
|
console.log('=== 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();
|
|
`;
|
|
|
|
const compiled = await zk.circuits.compile({
|
|
code: circuit,
|
|
format: CircuitFormat.Circom,
|
|
name: 'vote_commitment',
|
|
provingSystem: ProvingSystem.Groth16,
|
|
});
|
|
|
|
// Generate Solidity verifier
|
|
console.log('Generating Solidity verifier...');
|
|
const solidityVerifier = await zk.contracts.generateVerifier({
|
|
circuitId: compiled.circuitId,
|
|
language: 'solidity',
|
|
optimized: true,
|
|
});
|
|
console.log(`Solidity verifier generated: ${solidityVerifier.code.length} bytes`);
|
|
console.log(` Contract name: ${solidityVerifier.contractName}`);
|
|
console.log(` Gas estimate: ${solidityVerifier.gasEstimate}`);
|
|
|
|
// Generate proof
|
|
const proof = await zk.proofs.generate({
|
|
circuitId: compiled.circuitId,
|
|
inputs: {
|
|
vote: '1', // Vote YES (1) or NO (0)
|
|
nullifier: '12345',
|
|
commitment: '12345', // 1 * 12345
|
|
},
|
|
});
|
|
|
|
// Format proof for on-chain verification
|
|
console.log('\nFormatting proof for on-chain...');
|
|
const onChainProof = await zk.contracts.formatProof({
|
|
circuitId: compiled.circuitId,
|
|
proof: proof.proof,
|
|
publicSignals: proof.publicSignals,
|
|
format: 'calldata',
|
|
});
|
|
console.log(` Calldata: ${onChainProof.calldata.slice(0, 100)}...`);
|
|
console.log(` Estimated gas: ${onChainProof.gasEstimate}`);
|
|
|
|
// Deploy verifier contract (simulation)
|
|
console.log('\nDeploying verifier contract...');
|
|
const deployment = await zk.contracts.deployVerifier({
|
|
circuitId: compiled.circuitId,
|
|
network: 'synor-testnet',
|
|
});
|
|
console.log(` Contract address: ${deployment.address}`);
|
|
console.log(` TX hash: ${deployment.txHash}`);
|
|
console.log(` Gas used: ${deployment.gasUsed}`);
|
|
|
|
// Verify on-chain
|
|
console.log('\nVerifying on-chain...');
|
|
const onChainResult = await zk.contracts.verifyOnChain({
|
|
contractAddress: deployment.address,
|
|
proof: proof.proof,
|
|
publicSignals: proof.publicSignals,
|
|
network: 'synor-testnet',
|
|
});
|
|
console.log(` TX hash: ${onChainResult.txHash}`);
|
|
console.log(` Verified: ${onChainResult.verified}`);
|
|
console.log(` Gas used: ${onChainResult.gasUsed}`);
|
|
|
|
// Generate verifier for other targets
|
|
console.log('\nGenerating verifiers for other targets:');
|
|
const targets = ['cairo', 'noir', 'ink'];
|
|
for (const target of targets) {
|
|
const verifier = await zk.contracts.generateVerifier({
|
|
circuitId: compiled.circuitId,
|
|
language: target,
|
|
});
|
|
console.log(` ${target}: ${verifier.code.length} bytes`);
|
|
}
|
|
|
|
console.log('');
|
|
}
|
|
|
|
/**
|
|
* Trusted setup ceremonies
|
|
*/
|
|
async function setupExample(zk: SynorZk): Promise<void> {
|
|
console.log('=== Trusted Setup ===');
|
|
|
|
// Get available ceremonies
|
|
const ceremonies = await zk.setup.listCeremonies();
|
|
console.log(`Active ceremonies: ${ceremonies.length}`);
|
|
for (const ceremony of ceremonies) {
|
|
console.log(` ${ceremony.name}:`);
|
|
console.log(` Status: ${ceremony.status}`);
|
|
console.log(` Participants: ${ceremony.participantCount}`);
|
|
console.log(` 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();
|
|
`;
|
|
|
|
console.log('\nInitializing setup for new circuit...');
|
|
const setup = await zk.setup.initialize({
|
|
circuit,
|
|
format: CircuitFormat.Circom,
|
|
name: 'new_circuit_setup',
|
|
provingSystem: ProvingSystem.Groth16,
|
|
ceremonyType: 'powers_of_tau', // or 'phase2'
|
|
});
|
|
console.log(`Setup initialized:`);
|
|
console.log(` Ceremony ID: ${setup.ceremonyId}`);
|
|
console.log(` Powers of Tau required: ${setup.powersRequired}`);
|
|
console.log(` Current phase: ${setup.phase}`);
|
|
|
|
// Contribute to ceremony (in practice, generates random entropy)
|
|
console.log('\nContributing to ceremony...');
|
|
const contribution = await zk.setup.contribute({
|
|
ceremonyId: setup.ceremonyId,
|
|
entropy: Buffer.from('random-entropy-from-user').toString('hex'),
|
|
});
|
|
console.log(`Contribution submitted:`);
|
|
console.log(` Participant: ${contribution.participantId}`);
|
|
console.log(` Contribution hash: ${contribution.hash}`);
|
|
console.log(` Verification status: ${contribution.verified}`);
|
|
|
|
// Get ceremony status
|
|
const status = await zk.setup.getStatus(setup.ceremonyId);
|
|
console.log(`\nCeremony status:`);
|
|
console.log(` Phase: ${status.phase}`);
|
|
console.log(` Total contributions: ${status.totalContributions}`);
|
|
console.log(` Verified contributions: ${status.verifiedContributions}`);
|
|
console.log(` Ready for finalization: ${status.readyForFinalization}`);
|
|
|
|
// Finalize setup (when enough contributions)
|
|
if (status.readyForFinalization) {
|
|
console.log('\nFinalizing setup...');
|
|
const finalized = await zk.setup.finalize(setup.ceremonyId);
|
|
console.log(`Setup finalized:`);
|
|
console.log(` Proving key hash: ${finalized.provingKeyHash}`);
|
|
console.log(` Verification key hash: ${finalized.verificationKeyHash}`);
|
|
console.log(` Contribution transcript: ${finalized.transcriptCid}`);
|
|
}
|
|
|
|
// Download ceremony transcript
|
|
const transcript = await zk.setup.getTranscript(setup.ceremonyId);
|
|
console.log(`\nCeremony transcript: ${transcript.length} bytes`);
|
|
|
|
console.log('');
|
|
}
|
|
|
|
// Run examples
|
|
main().catch(console.error);
|