This document summarizes how to integrate Aztec Protocol's privacy features with our Hidden Garden skill leaderboard system. Aztec enables private smart contracts with confidential transactions while maintaining composability with Ethereum.
Private Functions:
- Executed client-side on user devices
- Generate zero-knowledge proofs locally
- Transaction details remain confidential
- Cannot read current public state (only historical)
Public Functions:
- Executed on Aztec Virtual Machine (by sequencers)
- Similar to Ethereum's execution model
- Can read current public state
- Cannot access private state directly
Utility Execution:
- Local queries that don't affect network state
- No proofs required
- Useful for read-only operations
Private State (UTXO Model):
- Stored as Unspent Transaction Outputs (UTXOs)
- Encrypted notes that only the owner can decrypt
- Each note contains: value, owner, randomness
- Supports confidential balances and data
Public State (Merkle Tree):
- Stored in a public Merkle tree
- Accessible to all contracts
- Similar to Ethereum's storage model
- Used for transparent operations
State Separation:
- Private functions can read historical public state
- Public functions cannot access private state
- Cross-domain interactions require explicit messaging
Purpose:
- Domain-specific language for writing zero-knowledge circuits
- Compiles to smart contracts on Aztec
- Similar syntax to Rust for developer familiarity
Key Features:
- Private and public function definitions
- Note structures for private state
- Cryptographic primitives (Poseidon hashing, etc.)
- Contract composability
-
User Initiates Transaction:
- Frontend calls private function
- User's device executes function locally
- Generates zero-knowledge proof
-
Proof Generation:
- Proves transaction validity without revealing details
- Includes note commitments and nullifiers
- Validates private state transitions
-
Transaction Submission:
- Submit proof + encrypted notes to sequencer
- Sequencer validates proof
- Updates private state tree
-
State Update:
- Old notes are nullified (spent)
- New notes are created (unspent)
- Public state can be updated via public functions
-
Function Call:
- Similar to Ethereum transactions
- Executed by sequencers
- Updates public Merkle tree
-
State Access:
- Can read current public state
- Can emit public events
- Cannot access private notes directly
L1 ↔ L2 Communication:
- Public messages: Transparent, verifiable
- Private messages: Encrypted, confidential
- Bridge contracts on both sides
- Message ordering and finality guarantees
Integration Flow for Hidden Garden
Our current SkillLeaderboard contract:
- Public skill tier submissions
- Transparent leaderboard queries
- No privacy for user skills or rankings
Private Skill Submissions:
- Users submit skill tiers privately
- Only aggregate statistics are public
- Individual rankings remain confidential
Hybrid Approach:
- Keep public contract for backward compatibility
- Add private Aztec contract for privacy-preserving submissions
- Bridge between public and private states
Contract Structure:
contract SkillLeaderboardPrivate {
// Private state: encrypted notes
struct SkillNote {
owner: AztecAddress,
skill_hash: Field,
tier: u8,
secret: Field,
}
// Public state: aggregate statistics
struct PublicStats {
total_submissions: u64,
skill_count: u64,
}
// Private function: submit skill tier
#[private]
fn submit_skill_tier(
skill_hash: Field,
tier: u8,
secret: Field
) -> Note<SkillNote> {
// Create private note
// Validate tier range
// Emit encrypted event
}
// Public function: update aggregate stats
#[public]
fn update_stats(skill_hash: Field) {
// Increment public counters
// No access to private notes
}
}Purpose:
- Connect Ethereum L1 with Aztec L2
- Handle message passing
- Maintain state synchronization
Key Functions:
contract SkillLeaderboardBridge {
// Receive messages from Aztec
function receiveFromAztec(bytes calldata message) external;
// Send messages to Aztec
function sendToAztec(bytes calldata message) external;
// Query aggregated stats
function getAggregateStats(bytes32 skillHash) external view returns (uint256);
}Aztec.js SDK:
- Initialize Aztec client
- Create user accounts
- Send private transactions
- Query private state (with user's key)
Example:
import { createAztecClient } from '@aztec/aztec.js';
const client = await createAztecClient();
const user = await client.createUser();
const contract = await client.getContract('SkillLeaderboardPrivate');
// Submit private skill tier
await contract.methods.submit_skill_tier(skillHash, tier, secret)
.send({ from: user });-
Aztec Development Environment
- Install Aztec CLI:
npm install -g @aztec/cli - Start local sandbox:
aztec start - Deploy contracts:
aztec deploy
- Install Aztec CLI:
-
Noir Contract
- Create contract in
packages/circuits-aztec/ - Define private note structure
- Implement private submission function
- Add public aggregation functions
- Create contract in
-
Bridge Contract
- Deploy on Ethereum L1
- Deploy on Aztec L2
- Implement message passing
-
Frontend Integration
- Install
@aztec/aztec.js - Initialize Aztec client
- Handle private transactions
- Display aggregate stats (public)
- Install
-
State Synchronization
- Keep public contract for v1 compatibility
- Bridge private submissions to public aggregates
- Maintain dual state (public + private)
// packages/circuits-aztec/src/skill_leaderboard.nr
contract SkillLeaderboardPrivate {
use dep::aztec::{
state_vars::Map,
note::Note,
};
// Private note structure
struct SkillNote {
owner: AztecAddress,
skill_hash: Field,
tier: u8,
secret: Field,
}
// Private state: user's skill notes
#[private]
fn submit_skill_tier(
skill_hash: Field,
tier: u8,
secret: Field
) -> Note<SkillNote> {
require(tier > 0 && tier <= 10, "Invalid tier");
let note = SkillNote {
owner: context.msg_sender(),
skill_hash,
tier,
secret,
};
Note::new(note)
}
// Public function: get aggregate stats (no private data)
#[public]
fn get_aggregate_stats(skill_hash: Field) -> u64 {
// Return public aggregate statistics
// No access to individual private notes
}
}- Individual skill tier submissions
- User's skill profile
- Ranking positions
- Submission timestamps (if desired)
- Aggregate statistics (total submissions per skill)
- Skill hash identifiers
- Public events (encrypted or unencrypted)
- Contract addresses
- Prove skill tier submission without revealing tier value
- Prove ownership of skill note without revealing identity
- Prove aggregate statistics are computed correctly
- Nullifiers: Prevent double-spending of notes
- Commitments: Hide note contents while proving existence
- Secrets: User-controlled randomness for privacy
- Private State: Only accessible by note owner
- Public State: Verifiable by all, but no private data leakage
- Cross-Domain: Message ordering and finality guarantees
- Users manage their own private keys
- Keys never leave user's device
- Proof generation happens client-side
- Private functions can call public functions
- Public functions cannot call private functions directly
- Use events and messages for cross-domain communication
- Private contracts can call other private contracts
- Public contracts can call other public contracts
- Cross-domain calls require explicit messaging
- Bridge contracts on both L1 and L2
- Message passing with ordering guarantees
- State synchronization mechanisms
# Start Aztec sandbox
aztec start
# Compile Noir contract
cd packages/circuits-aztec
nargo compile
# Deploy contract
aztec deploy SkillLeaderboardPrivate
# Run tests
aztec test- Unit tests for private functions (local execution)
- Integration tests with Aztec sandbox
- Cross-domain message testing
- Privacy verification (ensure no data leakage)
- Deploy to Aztec testnet
- Deploy bridge contracts to Ethereum testnet
- Test end-to-end flow
- Deploy to mainnet after thorough testing
- Keep existing public
SkillLeaderboardcontract - Deploy private Aztec contract
- Users can choose public or private submissions
- Implement bridge between public and private
- Aggregate private submissions to public stats
- Maintain backward compatibility
- Deprecate public submissions
- Migrate to fully private system
- Maintain public aggregates only
struct SkillNote {
owner: AztecAddress, // Note owner (private)
skill_hash: Field, // Skill identifier
tier: u8, // Skill tier (1-10)
secret: Field, // Randomness for privacy
}struct PublicStats {
skill_hash: Field,
total_submissions: u64,
average_tier: u64, // Computed from private notes
last_updated: u64,
}