synor/sdk/go/examples/zk_example.go
Gulshan Yadav 9416d76108 Add IBC and ZK SDK examples for Rust
- 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.
2026-01-28 14:15:51 +05:30

662 lines
19 KiB
Go

// Package main demonstrates the Synor ZK SDK for Go
//
// This example covers Zero-Knowledge proof operations:
// - Circuit compilation
// - Proof generation and verification
// - Groth16, PLONK, and STARK proving systems
// - Recursive proofs
// - On-chain verification
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/synor/sdk-go/zk"
)
func main() {
// Initialize client
config := zk.Config{
APIKey: getEnv("SYNOR_API_KEY", "your-api-key"),
Endpoint: "https://zk.synor.io/v1",
Timeout: 120 * time.Second, // ZK ops can be slow
Retries: 3,
Debug: false,
DefaultProvingSystem: zk.ProvingSystemGroth16,
}
client, err := zk.NewClient(config)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
ctx := context.Background()
// Check service health
healthy, err := client.HealthCheck(ctx)
if err != nil {
log.Printf("Health check failed: %v", err)
} else {
fmt.Printf("Service healthy: %v\n\n", healthy)
}
// Run examples
circuitExample(ctx, client)
proofExample(ctx, client)
provingSystemsExample(ctx, client)
recursiveProofExample(ctx, client)
onChainVerificationExample(ctx, client)
setupExample(ctx, client)
}
func circuitExample(ctx context.Context, client *zk.Client) {
fmt.Println("=== Circuit Compilation ===")
// Circom circuit example: prove knowledge of preimage
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
fmt.Println("Compiling Circom circuit...")
compiled, err := client.Circuits.Compile(ctx, &zk.CompileRequest{
Code: circomCircuit,
Format: zk.CircuitFormatCircom,
Name: "hash_preimage",
ProvingSystem: zk.ProvingSystemGroth16,
})
if err != nil {
log.Printf("Failed to compile circuit: %v", err)
return
}
fmt.Println("Circuit compiled:")
fmt.Printf(" Circuit ID: %s\n", compiled.CircuitID)
fmt.Printf(" Constraints: %d\n", compiled.ConstraintCount)
fmt.Printf(" Public inputs: %d\n", compiled.PublicInputCount)
fmt.Printf(" Private inputs: %d\n", compiled.PrivateInputCount)
fmt.Printf(" Proving key size: %d bytes\n", compiled.ProvingKeySize)
fmt.Printf(" Verification key size: %d bytes\n", compiled.VerificationKeySize)
// List circuits
circuits, err := client.Circuits.List(ctx)
if err != nil {
log.Printf("Failed to list circuits: %v", err)
return
}
fmt.Printf("\nYour circuits: %d\n", len(circuits))
for _, circuit := range circuits {
fmt.Printf(" %s (%s)\n", circuit.Name, circuit.CircuitID)
}
// Get circuit details
details, err := client.Circuits.Get(ctx, compiled.CircuitID)
if err != nil {
log.Printf("Failed to get circuit details: %v", err)
return
}
fmt.Println("\nCircuit details:")
fmt.Printf(" Format: %s\n", details.Format)
fmt.Printf(" Proving system: %s\n", details.ProvingSystem)
fmt.Printf(" Created: %s\n", details.CreatedAt)
// Download proving key
provingKey, err := client.Circuits.GetProvingKey(ctx, compiled.CircuitID)
if err != nil {
log.Printf("Failed to get proving key: %v", err)
return
}
fmt.Printf("\nProving key downloaded: %d bytes\n", len(provingKey))
// Download verification key
verificationKey, err := client.Circuits.GetVerificationKey(ctx, compiled.CircuitID)
if err != nil {
log.Printf("Failed to get verification key: %v", err)
return
}
fmt.Printf("Verification key downloaded: %d bytes\n", len(verificationKey))
fmt.Println()
}
func proofExample(ctx context.Context, client *zk.Client) {
fmt.Println("=== 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, err := client.Circuits.Compile(ctx, &zk.CompileRequest{
Code: circuit,
Format: zk.CircuitFormatCircom,
Name: "multiplier",
ProvingSystem: zk.ProvingSystemGroth16,
})
if err != nil {
log.Printf("Failed to compile circuit: %v", err)
return
}
// Generate proof
fmt.Println("Generating proof...")
startTime := time.Now()
proof, err := client.Proofs.Generate(ctx, &zk.GenerateProofRequest{
CircuitID: compiled.CircuitID,
Inputs: map[string]string{"a": "3", "b": "7"},
})
if err != nil {
log.Printf("Failed to generate proof: %v", err)
return
}
proofTime := time.Since(startTime)
fmt.Printf("Proof generated in %v\n", proofTime)
fmt.Printf(" Proof ID: %s\n", proof.ProofID)
fmt.Printf(" Proof size: %d bytes\n", len(proof.Proof))
fmt.Printf(" Public signals: %v\n", proof.PublicSignals)
// Verify proof
fmt.Println("\nVerifying proof...")
verifyStart := time.Now()
isValid, err := client.Proofs.Verify(ctx, &zk.VerifyRequest{
CircuitID: compiled.CircuitID,
Proof: proof.Proof,
PublicSignals: proof.PublicSignals,
})
if err != nil {
log.Printf("Failed to verify proof: %v", err)
return
}
verifyTime := time.Since(verifyStart)
fmt.Printf("Verification completed in %v\n", verifyTime)
fmt.Printf(" Valid: %v\n", isValid)
// Verify with wrong public signals (should fail)
fmt.Println("\nVerifying with wrong signals...")
invalidResult, err := client.Proofs.Verify(ctx, &zk.VerifyRequest{
CircuitID: compiled.CircuitID,
Proof: proof.Proof,
PublicSignals: []string{"42"}, // Wrong answer
})
if err != nil {
log.Printf("Failed to verify with wrong signals: %v", err)
return
}
fmt.Printf(" Valid: %v (expected false)\n", invalidResult)
// Get proof status
status, err := client.Proofs.GetStatus(ctx, proof.ProofID)
if err != nil {
log.Printf("Failed to get proof status: %v", err)
return
}
fmt.Println("\nProof status:")
fmt.Printf(" State: %s\n", status.State)
fmt.Printf(" Verified: %v\n", status.Verified)
fmt.Printf(" Created: %s\n", status.CreatedAt)
// List proofs
proofs, err := client.Proofs.List(ctx, compiled.CircuitID)
if err != nil {
log.Printf("Failed to list proofs: %v", err)
return
}
fmt.Printf("\nProofs for circuit: %d\n", len(proofs))
fmt.Println()
}
func provingSystemsExample(ctx context.Context, client *zk.Client) {
fmt.Println("=== 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 := []struct {
system zk.ProvingSystem
name string
}{
{zk.ProvingSystemGroth16, "GROTH16"},
{zk.ProvingSystemPLONK, "PLONK"},
{zk.ProvingSystemSTARK, "STARK"},
}
fmt.Println("Comparing proving systems:\n")
for _, s := range systems {
fmt.Printf("%s:\n", s.name)
// Compile for this system
compiled, err := client.Circuits.Compile(ctx, &zk.CompileRequest{
Code: circuit,
Format: zk.CircuitFormatCircom,
Name: fmt.Sprintf("comparison_%s", s.name),
ProvingSystem: s.system,
})
if err != nil {
log.Printf("Failed to compile for %s: %v", s.name, err)
continue
}
// Generate proof
proofStart := time.Now()
proof, err := client.Proofs.Generate(ctx, &zk.GenerateProofRequest{
CircuitID: compiled.CircuitID,
Inputs: map[string]string{"x": "10", "y": "20"},
})
if err != nil {
log.Printf("Failed to generate proof for %s: %v", s.name, err)
continue
}
proofTime := time.Since(proofStart)
// Verify proof
verifyStart := time.Now()
_, err = client.Proofs.Verify(ctx, &zk.VerifyRequest{
CircuitID: compiled.CircuitID,
Proof: proof.Proof,
PublicSignals: proof.PublicSignals,
})
if err != nil {
log.Printf("Failed to verify proof for %s: %v", s.name, err)
continue
}
verifyTime := time.Since(verifyStart)
fmt.Printf(" Setup: %dms\n", compiled.SetupTime)
fmt.Printf(" Proof time: %v\n", proofTime)
fmt.Printf(" Verify time: %v\n", verifyTime)
fmt.Printf(" Proof size: %d bytes\n", len(proof.Proof))
fmt.Printf(" Verification key: %d bytes\n", compiled.VerificationKeySize)
fmt.Println()
}
fmt.Println("Summary:")
fmt.Println(" Groth16: Smallest proofs, fast verification, trusted setup required")
fmt.Println(" PLONK: Universal setup, flexible, moderate proof size")
fmt.Println(" STARK: No trusted setup, largest proofs, quantum resistant")
fmt.Println()
}
func recursiveProofExample(ctx context.Context, client *zk.Client) {
fmt.Println("=== Recursive Proofs ===")
// Inner circuit
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
inner, err := client.Circuits.Compile(ctx, &zk.CompileRequest{
Code: innerCircuit,
Format: zk.CircuitFormatCircom,
Name: "inner_circuit",
ProvingSystem: zk.ProvingSystemGroth16,
})
if err != nil {
log.Printf("Failed to compile inner circuit: %v", err)
return
}
// Generate multiple proofs to aggregate
fmt.Println("Generating proofs to aggregate...")
var proofsToAggregate []zk.ProofData
for i := 1; i <= 4; i++ {
proof, err := client.Proofs.Generate(ctx, &zk.GenerateProofRequest{
CircuitID: inner.CircuitID,
Inputs: map[string]string{"x": fmt.Sprintf("%d", i)},
})
if err != nil {
log.Printf("Failed to generate proof %d: %v", i, err)
continue
}
proofsToAggregate = append(proofsToAggregate, zk.ProofData{
Proof: proof.Proof,
PublicSignals: proof.PublicSignals,
})
fmt.Printf(" Proof %d: y = %s\n", i, proof.PublicSignals[0])
}
// Aggregate proofs recursively
fmt.Println("\nAggregating proofs...")
aggregated, err := client.Proofs.Aggregate(ctx, &zk.AggregateRequest{
CircuitID: inner.CircuitID,
Proofs: proofsToAggregate,
AggregationType: "recursive",
})
if err != nil {
log.Printf("Failed to aggregate proofs: %v", err)
return
}
originalSize := 0
for _, p := range proofsToAggregate {
originalSize += len(p.Proof)
}
fmt.Println("Aggregated proof:")
fmt.Printf(" Proof ID: %s\n", aggregated.ProofID)
fmt.Printf(" Aggregated count: %d\n", aggregated.AggregatedCount)
fmt.Printf(" Proof size: %d bytes\n", len(aggregated.Proof))
fmt.Printf(" Size reduction: %.1f%%\n", (1-float64(len(aggregated.Proof))/float64(originalSize))*100)
// Verify aggregated proof
publicSignalsList := make([][]string, len(proofsToAggregate))
for i, p := range proofsToAggregate {
publicSignalsList[i] = p.PublicSignals
}
isValid, err := client.Proofs.VerifyAggregated(ctx, &zk.VerifyAggregatedRequest{
CircuitID: inner.CircuitID,
Proof: aggregated.Proof,
PublicSignalsList: publicSignalsList,
})
if err != nil {
log.Printf("Failed to verify aggregated proof: %v", err)
return
}
fmt.Printf("\nAggregated proof valid: %v\n", isValid)
// Batch verification (verify multiple proofs in one operation)
fmt.Println("\nBatch verification...")
batchResult, err := client.Proofs.BatchVerify(ctx, &zk.BatchVerifyRequest{
CircuitID: inner.CircuitID,
Proofs: proofsToAggregate,
})
if err != nil {
log.Printf("Failed to batch verify: %v", err)
return
}
fmt.Printf(" All valid: %v\n", batchResult.AllValid)
fmt.Printf(" Results: %v\n", batchResult.Results)
fmt.Println()
}
func onChainVerificationExample(ctx context.Context, client *zk.Client) {
fmt.Println("=== 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, err := client.Circuits.Compile(ctx, &zk.CompileRequest{
Code: circuit,
Format: zk.CircuitFormatCircom,
Name: "vote_commitment",
ProvingSystem: zk.ProvingSystemGroth16,
})
if err != nil {
log.Printf("Failed to compile circuit: %v", err)
return
}
// Generate Solidity verifier
fmt.Println("Generating Solidity verifier...")
solidityVerifier, err := client.Contracts.GenerateVerifier(ctx, &zk.GenerateVerifierRequest{
CircuitID: compiled.CircuitID,
Language: "solidity",
Optimized: true,
})
if err != nil {
log.Printf("Failed to generate verifier: %v", err)
return
}
fmt.Printf("Solidity verifier generated: %d bytes\n", len(solidityVerifier.Code))
fmt.Printf(" Contract name: %s\n", solidityVerifier.ContractName)
fmt.Printf(" Gas estimate: %d\n", solidityVerifier.GasEstimate)
// Generate proof
proof, err := client.Proofs.Generate(ctx, &zk.GenerateProofRequest{
CircuitID: compiled.CircuitID,
Inputs: map[string]string{
"vote": "1", // Vote YES (1) or NO (0)
"nullifier": "12345",
"commitment": "12345", // 1 * 12345
},
})
if err != nil {
log.Printf("Failed to generate proof: %v", err)
return
}
// Format proof for on-chain verification
fmt.Println("\nFormatting proof for on-chain...")
onChainProof, err := client.Contracts.FormatProof(ctx, &zk.FormatProofRequest{
CircuitID: compiled.CircuitID,
Proof: proof.Proof,
PublicSignals: proof.PublicSignals,
Format: "calldata",
})
if err != nil {
log.Printf("Failed to format proof: %v", err)
return
}
calldataPreview := onChainProof.Calldata
if len(calldataPreview) > 100 {
calldataPreview = calldataPreview[:100]
}
fmt.Printf(" Calldata: %s...\n", calldataPreview)
fmt.Printf(" Estimated gas: %d\n", onChainProof.GasEstimate)
// Deploy verifier contract (simulation)
fmt.Println("\nDeploying verifier contract...")
deployment, err := client.Contracts.DeployVerifier(ctx, &zk.DeployRequest{
CircuitID: compiled.CircuitID,
Network: "synor-testnet",
})
if err != nil {
log.Printf("Failed to deploy verifier: %v", err)
return
}
fmt.Printf(" Contract address: %s\n", deployment.Address)
fmt.Printf(" TX hash: %s\n", deployment.TxHash)
fmt.Printf(" Gas used: %d\n", deployment.GasUsed)
// Verify on-chain
fmt.Println("\nVerifying on-chain...")
onChainResult, err := client.Contracts.VerifyOnChain(ctx, &zk.OnChainVerifyRequest{
ContractAddress: deployment.Address,
Proof: proof.Proof,
PublicSignals: proof.PublicSignals,
Network: "synor-testnet",
})
if err != nil {
log.Printf("Failed to verify on-chain: %v", err)
return
}
fmt.Printf(" TX hash: %s\n", onChainResult.TxHash)
fmt.Printf(" Verified: %v\n", onChainResult.Verified)
fmt.Printf(" Gas used: %d\n", onChainResult.GasUsed)
// Generate verifier for other targets
fmt.Println("\nGenerating verifiers for other targets:")
targets := []string{"cairo", "noir", "ink"}
for _, target := range targets {
verifier, err := client.Contracts.GenerateVerifier(ctx, &zk.GenerateVerifierRequest{
CircuitID: compiled.CircuitID,
Language: target,
})
if err != nil {
log.Printf("Failed to generate %s verifier: %v", target, err)
continue
}
fmt.Printf(" %s: %d bytes\n", target, len(verifier.Code))
}
fmt.Println()
}
func setupExample(ctx context.Context, client *zk.Client) {
fmt.Println("=== Trusted Setup ===")
// Get available ceremonies
ceremonies, err := client.Setup.ListCeremonies(ctx)
if err != nil {
log.Printf("Failed to list ceremonies: %v", err)
return
}
fmt.Printf("Active ceremonies: %d\n", len(ceremonies))
for _, ceremony := range ceremonies {
fmt.Printf(" %s:\n", ceremony.Name)
fmt.Printf(" Status: %s\n", ceremony.Status)
fmt.Printf(" Participants: %d\n", ceremony.ParticipantCount)
fmt.Printf(" Current round: %d\n", ceremony.CurrentRound)
}
// 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();
`
fmt.Println("\nInitializing setup for new circuit...")
setup, err := client.Setup.Initialize(ctx, &zk.SetupInitRequest{
Circuit: circuit,
Format: zk.CircuitFormatCircom,
Name: "new_circuit_setup",
ProvingSystem: zk.ProvingSystemGroth16,
CeremonyType: "powers_of_tau", // or 'phase2'
})
if err != nil {
log.Printf("Failed to initialize setup: %v", err)
return
}
fmt.Println("Setup initialized:")
fmt.Printf(" Ceremony ID: %s\n", setup.CeremonyID)
fmt.Printf(" Powers of Tau required: %d\n", setup.PowersRequired)
fmt.Printf(" Current phase: %s\n", setup.Phase)
// Contribute to ceremony (in practice, generates random entropy)
fmt.Println("\nContributing to ceremony...")
contribution, err := client.Setup.Contribute(ctx, &zk.ContributeRequest{
CeremonyID: setup.CeremonyID,
Entropy: fmt.Sprintf("%x", []byte("random-entropy-from-user")),
})
if err != nil {
log.Printf("Failed to contribute: %v", err)
return
}
fmt.Println("Contribution submitted:")
fmt.Printf(" Participant: %s\n", contribution.ParticipantID)
fmt.Printf(" Contribution hash: %s\n", contribution.Hash)
fmt.Printf(" Verification status: %v\n", contribution.Verified)
// Get ceremony status
status, err := client.Setup.GetStatus(ctx, setup.CeremonyID)
if err != nil {
log.Printf("Failed to get ceremony status: %v", err)
return
}
fmt.Println("\nCeremony status:")
fmt.Printf(" Phase: %s\n", status.Phase)
fmt.Printf(" Total contributions: %d\n", status.TotalContributions)
fmt.Printf(" Verified contributions: %d\n", status.VerifiedContributions)
fmt.Printf(" Ready for finalization: %v\n", status.ReadyForFinalization)
// Finalize setup (when enough contributions)
if status.ReadyForFinalization {
fmt.Println("\nFinalizing setup...")
finalized, err := client.Setup.Finalize(ctx, setup.CeremonyID)
if err != nil {
log.Printf("Failed to finalize setup: %v", err)
return
}
fmt.Println("Setup finalized:")
fmt.Printf(" Proving key hash: %s\n", finalized.ProvingKeyHash)
fmt.Printf(" Verification key hash: %s\n", finalized.VerificationKeyHash)
fmt.Printf(" Contribution transcript: %s\n", finalized.TranscriptCID)
}
// Download ceremony transcript
transcript, err := client.Setup.GetTranscript(ctx, setup.CeremonyID)
if err != nil {
log.Printf("Failed to get transcript: %v", err)
return
}
fmt.Printf("\nCeremony transcript: %d bytes\n", len(transcript))
fmt.Println()
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}