Privacy-First Application Framework combining Mina zkApps with Zcash Shielded Transactions
Built for the Privacy Infrastructure & Developer Tools Bounty ($3,000)
A complete privacy-first application framework that combines:
- Mina Protocol's zkApps for zero-knowledge proofs
- Zcash's shielded transactions for private value transfers
- Privacy primitives (Merkle trees, nullifiers, Semaphore)
- Two working PoC applications with real ZK proofs
| Requirement | Status | Evidence |
|---|---|---|
| Privacy primitives (Merkle, nullifiers, Semaphore) | β Complete | 9/9 tests passing |
| Framework for on-chain privacy apps | β Complete | 2 zkApp contracts |
| Shielded Zcash + Mina zkApps integration | β Complete | 11/11 tests passing |
| User data self-custody design | β Complete | Commitment-nullifier patterns |
| At least 1 PoC application | β 2 PoCs | Private Voting + Anonymous Credentials |
cd joi
npm install
npm testExpected Output:
Test Suites: 6 passed, 6 total
Tests: 40 passed, 40 total
Time: ~200 seconds
The framework includes real Zcash integration tested against an actual zcashd node. Here's how to set it up:
# Create config directory
mkdir -p /tmp/zcash-data
# Create config file with all required settings
cat > /tmp/zcash-data/zcash.conf << 'EOF'
regtest=1
rpcuser=zcashuser
rpcpassword=zcashpass
rpcallowip=0.0.0.0/0
rpcbind=0.0.0.0
nuparams=5ba81b19:1
nuparams=76b809bb:1
nuparams=2bb40e60:1
nuparams=f5b9230b:1
nuparams=e9ff75a6:1
nuparams=c2d6d0b4:1
i-am-aware-zcashd-will-be-replaced-by-zebrad-and-zallet-in-2025=1
allowdeprecated=z_getnewaddress
allowdeprecated=z_getbalance
allowdeprecated=getnewaddress
exportdir=/srv/zcashd/.zcash/exports
txindex=1
EOF# Start zcashd container (regtest mode)
docker run -d --name zcashd-regtest \
--platform linux/amd64 \
-p 18232:18232 \
-v /tmp/zcash-data:/srv/zcashd/.zcash \
electriccoinco/zcashd:latest
# Wait for node to start (30-45 seconds)
echo "Waiting for zcashd to start..."
sleep 35
# Verify node is running
curl -s -X POST http://localhost:18232 \
-u zcashuser:zcashpass \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"1","method":"getblockchaininfo","params":[]}' | jq '.result.chain'
# Should output: "regtest"# Set transaction fee (required for NU5)
curl -s -X POST http://localhost:18232 \
-u zcashuser:zcashpass \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"1","method":"settxfee","params":[0.001]}'
# Mine 101 blocks to get mature coins (regtest only)
curl -s -X POST http://localhost:18232 \
-u zcashuser:zcashpass \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"1","method":"generate","params":[101]}' | jq '.result | length'
# Should output: 101cd joi
# Set environment variables
export ZCASH_RPC_URL=http://localhost:18232
export ZCASH_RPC_USER=zcashuser
export ZCASH_RPC_PASSWORD=zcashpass
# Run Zcash-specific tests
npm test -- src/integration/zcash-rpc-integration.test.ts
# Expected: 11/11 tests passing# With Zcash node running, all 40 tests will pass
npm test
# Expected: 40/40 tests passingIf tests fail to connect:
- Wait longer for zcashd to start (can take 45+ seconds on first run)
- Check logs:
docker logs zcashd-regtest - Verify RPC is accessible:
curl http://localhost:18232(should return error, not connection refused)
If you see "tx unpaid action limit exceeded":
- The fee was already set in Step 3, but if needed:
curl -X POST http://localhost:18232 \ -u zcashuser:zcashpass \ -d '{"method":"settxfee","params":[0.001]}'
For ARM64 Macs:
- The
--platform linux/amd64flag enables x86 emulation (slower but works)
To stop the node:
docker stop zcashd-regtest
docker rm zcashd-regtestcd ui
npm install
npm run dev
# Open http://localhost:3000mina/
βββ joi/ # Core Framework
β βββ src/
β β βββ primitives/ # Privacy building blocks
β β β βββ MerkleTree.ts # 8-level Merkle trees with proofs
β β β βββ Nullifier.ts # Poseidon-based nullifiers
β β β βββ Semaphore.ts # Full Semaphore protocol
β β βββ voting/ # PoC #1: Private Voting
β β β βββ PrivateVoting.ts # zkApp with real proofs
β β βββ credentials/ # PoC #2: Anonymous Credentials
β β β βββ AnonymousCredentials.ts
β β βββ zcash/ # Zcash Integration
β β β βββ ZcashRpcClient.ts # Full RPC wrapper (277 lines)
β β β βββ ShieldedPool.ts # Shielded tx manager
β β βββ integration/ # Integration tests with REAL proofs
β β βββ voting-integration.test.ts
β β βββ credentials-integration.test.ts
β β βββ zcash-rpc-integration.test.ts
β βββ INTEGRATION_TEST_RESULTS.md # Detailed test evidence
β
βββ ui/ # Next.js Demo UI
β βββ app/
β βββ page.tsx # Landing page
β βββ voting/page.tsx # Voting demo
β βββ credentials/page.tsx # Credentials demo
β
βββ resources/
βββ bounty.md # Bounty requirements
- 8-level trees with efficient membership proofs
- Prove membership without revealing position
- Poseidon hash for zkSNARK compatibility
const tree = new SimpleMerkleTree(8);
tree.addLeaf(commitment);
tree.build();
const proof = tree.getProof(0); // Proves membership anonymously- Prevent double-spending/voting
- Collision-resistant via Poseidon
- No identity linkage
const nullifier = generateNullifier(secret, pollId);
// Public nullifier, secret identity stays private- Anonymous group signaling
- Identity commitments + nullifiers
- External nullifier scope
Location: joi/src/voting/PrivateVoting.ts
- β Anonymous voter registration
- β Merkle-proof eligibility verification
- β Nullifier-based double-vote prevention
- β Public results, private individual votes
1. Voter creates identity commitment (secret stays private)
2. Admin adds commitment to voter Merkle tree
3. Voter generates Merkle proof of eligibility
4. Voter creates nullifier (prevents double-voting)
5. Vote is cast with ZK proof - no identity disclosed!
npm run test:integration:voting
# Compilation: 43.26 seconds
# Full flow with 3 voters: 86 seconds
# All proofs are REAL (proofsEnabled: true)Location: joi/src/credentials/AnonymousCredentials.ts
- β Age verification (age β₯ 18) without revealing exact age
- β Threshold proofs for any attribute
- β Multiple credential holders
- β No identity disclosure
1. User has credential with value (e.g., age = 25)
2. User proves value β₯ threshold (e.g., 18)
3. Verifier confirms eligibility
4. Exact value (25) is NEVER revealed
npm run test:integration:credentials
# Compilation: 49.25 seconds
# 4/4 tests passing with real proofsLocation: joi/src/zcash/
-
ZcashRpcClient.ts (277 lines) - Full RPC wrapper
z_getnewaddress/z_getaddressforaccountz_sendmanyfor shielded transactionsz_getbalancefor balance queriesz_getoperationstatusfor async tracking
-
ShieldedPool.ts - High-level shielded transaction manager
- Address generation
- Shield/unshield operations
- Cross-chain proof structure
We executed real shielded transactions on zcashd regtest:
-
Transparent β Shielded (Shield)
- Amount: 0.9 ZEC
- TXID:
565d329a167730b8b70874cc793d81a63c66ba4153d4c3cd456ef85596e0333b
-
Shielded β Shielded (with memo)
- Amount: 0.3 ZEC
- Memo: "Test shielded tx from Mina Privacy Framework"
- TXID:
0ae9ffaa6cb4fb49aaf0578313589cdaac442a53a124b8f8a7b13d616a20f887
| Suite | Tests | Description |
|---|---|---|
| Privacy Primitives | 9 | Merkle trees, nullifiers, Semaphore |
| Private Voting (Unit) | 9 | Contract logic tests |
| Anonymous Credentials | 4 | Threshold proof tests |
| Voting Integration | 3 | Real ZK proofs (~3 min) |
| Credentials Integration | 4 | Real ZK proofs (~3 min) |
| Zcash RPC Integration | 11 | Real zcashd node |
# All tests
npm test
# Just privacy primitives
npm test -- src/primitives/Primitives.test.ts
# Integration tests with real proofs (slower)
npm run test:integration:voting # ~3 minutes
npm run test:integration:credentials # ~3 minutes
# Zcash integration (requires running node)
npm test -- src/integration/zcash-rpc-integration.test.tsβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User Applications β
β (Private Voting, Anonymous Credentials) β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β Privacy Framework Core β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Primitives β β Zcash Pool β β zkApps β β
β β β’ Merkle β β β’ Shielded β β β’ Voting β β
β β β’ Nullifier β β β’ RPC β β β’ Creds β β
β β β’ Semaphore β β β’ Bridge β β β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β Blockchain Layer β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β Mina Protocol ββββββββββββΊβ Zcash Network β β
β β (zkApps) β Bridge β (Shielded Pool) β β
β ββββββββββββββββββββ ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
User commits to identity β Uses nullifier to act β System prevents reuse
(public) (public) (no identity link)
User proves set membership β Without revealing position β Verifier confirms
(zero-knowledge) (anonymity) (trust)
User proves attribute β₯ threshold β Without revealing exact value
(age β₯ 18) (privacy preserved)
- Node.js 18+
- npm
- Docker (for Zcash testing)
git clone <repo>
cd mina/joi
npm install
npm run build# Private Voting Demo
npm run demo
# Anonymous Credentials Demo
npm run demo:credentialscd ../ui
npm install
npm run dev
# Open http://localhost:3000| File | Lines | Purpose |
|---|---|---|
src/primitives/MerkleTree.ts |
154 | Merkle tree implementation |
src/primitives/Nullifier.ts |
55 | Nullifier/commitment generation |
src/voting/PrivateVoting.ts |
148 | Voting zkApp contract |
src/credentials/AnonymousCredentials.ts |
194 | Credentials zkApp |
src/zcash/ZcashRpcClient.ts |
302 | Zcash RPC wrapper |
src/zcash/ShieldedPool.ts |
481 | Shielded pool manager |
src/integration/*.test.ts |
~400 | Integration tests with real proofs |
-
Real ZK Proofs - Integration tests run with
proofsEnabled: true(most projects mock this) -
Real Zcash Integration - Tested against actual zcashd node with real transactions
-
Two PoC Applications - Exceeds requirement of "at least 1 PoC"
-
40/40 Tests Passing - No mocks, no stubs, no skipped tests
-
Production-Quality Code - TypeScript, well-documented, clean architecture
-
Working UI - Visual demo for judges
Apache 2.0 - See LICENSE
Privacy by Design, Privacy by Default
Built with β€οΈ for the Mina zkApps Hackathon