
Company News
Socket Named Top Sales Organization by RepVue
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.
@cheny56/zk-voting-native
Advanced tools
ZK Voting with Native PQC-Quorum Integration - Optimized for Groth16 precompile at 0x16
Zero-Knowledge Voting with Native PQC-Quorum Integration. Optimized for the Groth16 precompile at address 0x16.
0x16gnark-based proof generationnpm install @pqc-quorum/zk-voting-native
# Build the Go prover
cd go && go build -o ../bin/zk-prover .
const { VotingClient, ProofGenerator } = require('@pqc-quorum/zk-voting-native');
// Connect to PQC-Quorum node
const client = new VotingClient('http://localhost:8545', privateKey);
// Cast a vote with native ZK proof
await client.castVote(voterSecret, leafIndex, voteChoice);
This package provides a ZK voting system optimized for PQC-Quorum nodes with native Groth16 verification:
┌─────────────────────────────────────────────────────────────────────────────┐
│ NATIVE ZK VOTING FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────────────┐ │
│ │ JavaScript │ │ Go Prover │ │ PQC-Quorum Node │ │
│ │ Client │ │ (gnark) │ │ │ │
│ └───────┬────────┘ └───────┬────────┘ └───────────┬────────────┘ │
│ │ │ │ │
│ │ 1. Prepare inputs │ │ │
│ ├─────────────────────>│ │ │
│ │ │ 2. Generate proof │ │
│ │ │ (gnark Groth16) │ │
│ │<─────────────────────┤ │ │
│ │ 3. Proof bytes │ │ │
│ │ │ │ │
│ │ 4. Submit tx ──────────────────────────────────>│ │
│ │ │ │ │
│ │ │ 5. Verify via 0x16 ──>│ │
│ │ │ │ (Precompile) │
│ │<─────────────────────────────────────────────────│ │
│ │ 6. Vote recorded │ │ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
| Feature | snarkjs (Solidity) | Native (Precompile) |
|---|---|---|
| Verification Gas | ~5,000,000 | ~200,000 |
| Proof Gen Speed | ~2-5 seconds | ~200ms |
| Trusted Setup | Required | Pre-deployed |
| Hash Function | Poseidon | MiMC |
| Best For | Any EVM chain | PQC-Quorum |
| Contract | Address | Description |
|---|---|---|
| VoterRegistry | Deployed | Merkle tree of eligible voters |
| ZKBallot | Deployed | Main voting with precompile verification |
| TallyManager | Deployed | Tallying orchestration |
| IZKPrecompile | 0x16 | Native Groth16 verifier |
// Register voters (admin only)
function registerVoter(bytes32 commitment) external;
function registerVotersBatch(bytes32[] calldata commitments) external;
function closeRegistration() external;
// Query state
function merkleRoot() external view returns (bytes32);
function getVoterCount() external view returns (uint256);
// Cast vote with native ZK verification
function castVote(
bytes calldata proof, // Groth16 proof bytes
bytes calldata publicInputs, // [merkleRoot, nullifier, commitment, count]
bytes[] calldata encryptedVote // For homomorphic tally (optional)
) external;
// End voting
function endVoting() external;
// Query
function getTotalVotes() external view returns (uint256);
function nullifierUsed(bytes32) external view returns (bool);
The precompile at 0x16 provides efficient Groth16 verification:
interface IZKPrecompile {
function verifyGroth16(
bytes32 vkHash, // Hash of verification key
bytes calldata proof, // 8 G1/G2 elements (256 bytes)
bytes calldata inputs // Public inputs (32 bytes each)
) external view returns (bool);
}
The Go prover uses gnark for Groth16 proof generation.
// go/circuit/vote_circuit.go
type VoteCircuit struct {
// Private inputs
VoterSecret frontend.Variable
VoterLeafIndex frontend.Variable
MerklePath [20]frontend.Variable
PathIndices [20]frontend.Variable
VoteChoice frontend.Variable
VoteSalt frontend.Variable
// Public inputs
MerkleRoot frontend.Variable `gnark:",public"`
Nullifier frontend.Variable `gnark:",public"`
VoteCommitment frontend.Variable `gnark:",public"`
CandidateCount frontend.Variable `gnark:",public"`
}
cd go
go mod tidy
go build -o ../bin/zk-prover .
# Generate proof
./bin/zk-prover prove \
--secret 0x1234... \
--leaf-index 5 \
--merkle-path 0xabc...,0xdef...,... \
--path-indices 0,1,0,... \
--vote-choice 2 \
--vote-salt 0x5678... \
--merkle-root 0x9abc... \
--candidate-count 4
# Outputs: proof.bin, public_inputs.json
High-level client for the complete voting flow:
const { VotingClient } = require('@pqc-quorum/zk-voting-native');
const client = new VotingClient('http://localhost:8545', privateKey);
// Deploy contracts
await client.deployVoterRegistry();
await client.deployZKBallot(candidates, duration, vkHash);
await client.deployTallyManager();
// Admin: Register voters
await client.registerVoters([commitment1, commitment2, ...]);
await client.closeRegistration();
// Voter: Cast vote
await client.castVote({
voterSecret: BigInt('0x...'),
leafIndex: 5,
voteChoice: 2,
candidateCount: 4,
});
// Query state
const totalVotes = await client.getTotalVotes();
const candidates = await client.getCandidates();
JavaScript wrapper for the Go prover:
const { ProofGenerator } = require('@pqc-quorum/zk-voting-native');
const prover = new ProofGenerator({
proverPath: './bin/zk-prover',
circuitPath: './circuits/vote.r1cs',
pkPath: './keys/proving.key',
});
// Generate proof
const { proof, publicInputs } = await prover.generateProof({
voterSecret,
leafIndex,
merklePath,
pathIndices,
voteChoice,
voteSalt,
merkleRoot,
candidateCount,
});
class VotingClient {
constructor(rpcUrl: string, privateKey: string);
// Deployment
deployVoterRegistry(): Promise<Contract>;
deployZKBallot(
candidates: string[],
durationSeconds: number,
vkHash: string
): Promise<Contract>;
deployTallyManager(): Promise<Contract>;
// Connect to existing
connectVoterRegistry(address: string): void;
connectZKBallot(address: string): void;
connectTallyManager(address: string): void;
// Admin functions
registerVoter(commitment: string): Promise<TransactionReceipt>;
registerVoters(commitments: string[]): Promise<TransactionReceipt>;
closeRegistration(): Promise<TransactionReceipt>;
// Voting
castVote(params: CastVoteParams): Promise<{
nullifier: string;
voteCommitment: string;
txHash: string;
}>;
// Queries
getMerkleRoot(): Promise<string>;
getTotalVotes(): Promise<bigint>;
getCandidates(): Promise<Candidate[]>;
isNullifierUsed(nullifier: string): Promise<boolean>;
getVoteCommitments(): Promise<string[]>;
// Tallying
endVoting(): Promise<TransactionReceipt>;
submitHomomorphicTally(tally: number[]): Promise<TransactionReceipt>;
}
interface CastVoteParams {
voterSecret: bigint;
leafIndex: number;
voteChoice: number;
candidateCount: number;
}
const {
// Crypto
mimcHash,
bigIntToHex32,
hex32ToBigInt,
// Merkle Tree
MerkleTree,
// Vote Proof
generateVoteProofInputs,
TREE_DEPTH,
// Tally
RevealTally,
HomomorphicTally,
PaillierCrypto,
} = require('@pqc-quorum/zk-voting-native/lib');
const { MerkleTree, mimcHash } = require('@pqc-quorum/zk-voting-native/lib');
// Create tree
const tree = new MerkleTree(20); // depth 20
// Add leaves
await tree.addLeaf(commitment1);
await tree.addLeaf(commitment2);
// Get root
const root = tree.getRoot();
// Generate proof
const { pathElements, pathIndices } = await tree.generateProof(commitment, index);
const {
VotingClient,
ProofGenerator,
MerkleTree,
mimcHash,
bigIntToHex32,
} = require('@pqc-quorum/zk-voting-native');
async function completeVotingExample() {
// Setup
const rpcUrl = 'http://localhost:8545';
const adminKey = process.env.ADMIN_PRIVATE_KEY;
const candidates = ['Alice', 'Bob', 'Charlie'];
// Initialize client
const client = new VotingClient(rpcUrl, adminKey);
// Deploy contracts
console.log('Deploying contracts...');
await client.deployVoterRegistry();
await client.deployZKBallot(candidates, 3600, VK_HASH);
await client.deployTallyManager();
// Register voters
const voters = [];
const tree = new MerkleTree(20);
for (let i = 0; i < 10; i++) {
const secret = BigInt(crypto.randomBytes(32).toString('hex'), 16);
const commitment = await mimcHash(secret);
voters.push({ secret, commitment, index: i });
await tree.addLeaf(commitment);
}
const commitments = voters.map(v => bigIntToHex32(v.commitment));
await client.registerVoters(commitments);
await client.closeRegistration();
console.log(`Registered ${voters.length} voters`);
console.log(`Merkle root: ${bigIntToHex32(tree.getRoot())}`);
// Cast votes
for (const voter of voters) {
const voteChoice = Math.floor(Math.random() * candidates.length);
const result = await client.castVote({
voterSecret: voter.secret,
leafIndex: voter.index,
voteChoice,
candidateCount: candidates.length,
});
console.log(`Vote cast: ${result.txHash}`);
}
// End voting and get results
await client.endVoting();
const totalVotes = await client.getTotalVotes();
console.log(`Total votes: ${totalVotes}`);
}
const { VotingClient, MockProofGenerator } = require('@pqc-quorum/zk-voting-native');
// Use mock prover for testing (no Go required)
const client = new VotingClient(rpcUrl, privateKey, {
proofGenerator: new MockProofGenerator(),
});
Run benchmarks with:
npm run benchmark
| Operation | Time | Gas |
|---|---|---|
| Proof Generation | ~200ms | - |
| On-chain Verification | - | ~200,000 |
| Vote Registration | ~50ms | ~45,000 |
| Full Vote Cast | ~300ms | ~250,000 |
| Metric | Native (gnark) | snarkjs |
|---|---|---|
| Proof Gen | 200ms | 2-5s |
| Verify Gas | 200k | 5M+ |
| Setup | Pre-deployed | Custom |
| Use Case | Recommended Package |
|---|---|
| PQC-Quorum node | @pqc-quorum/zk-voting-native |
| Any EVM chain | @pqc/zk-voting |
| Maximum portability | @pqc/zk-voting |
| Maximum performance | @pqc-quorum/zk-voting-native |
| No Go environment | @pqc/zk-voting |
| Feature | @pqc/zk-voting | @pqc-quorum/zk-voting-native |
|---|---|---|
| ZK System | Circom/snarkjs | gnark/Groth16 |
| Hash Function | Poseidon | MiMC |
| Verification | Solidity | Precompile (0x16) |
| Proof Generation | JavaScript | Go |
| EVM Compatibility | Any | PQC-Quorum |
| Gas Cost | ~5M | ~200k |
| Trusted Setup | Custom | Pre-deployed |
| Homomorphic Tally | ✓ | ✓ |
| Reveal-Based Tally | ✓ | ✓ |
# Install dependencies
npm install
# Build Go prover
npm run compile-go
# Run example
npm run example
# Run benchmarks
npm run benchmark
MIT
FAQs
ZK Voting with Native PQC-Quorum Integration - Optimized for Groth16 precompile at 0x16
We found that @cheny56/zk-voting-native demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.

Company News
/Security News
Socket is an initial recipient of OpenAI's Cybersecurity Grant Program, which commits $10M in API credits to defenders securing open source software.