State Model
Comprehensive account structures and state management for ZKVAULT on Solana.
Account Architecture
ZKVAULT uses a hierarchical account structure with Program Derived Addresses (PDAs) for deterministic account discovery.
Account Hierarchy
Vault (PDA)
├── ProofSubmission[] (PDAs)
├── EncryptedData[] (PDAs)
├── VerificationKey (PDA)
└── AccessControl (PDA)Core Account Structures
Vault Account
#[account]
pub struct Vault {
pub vault_id: [u8; 32], // Unique identifier
pub authority: Pubkey, // Owner/admin
pub bump: u8, // PDA bump seed
// Proof system configuration
pub proof_protocol: ProofProtocol,
pub verification_key: Pubkey, // Reference to VK account
pub vk_hash: [u8; 32], // VK commitment
// Encryption settings
pub encryption_algorithm: EncryptionAlgorithm,
pub max_data_size: u64,
// Access control
pub access_control: AccessControlType,
pub access_policy: Pubkey, // Reference to policy account
// Statistics
pub total_proofs_submitted: u64,
pub total_proofs_verified: u64,
pub total_data_stored: u64,
// Timestamps
pub created_at: i64,
pub last_updated: i64,
// Flags
pub is_active: bool,
pub is_frozen: bool, // Emergency freeze
}
impl Vault {
pub const SIZE: usize = 8 // discriminator
+ 32 // vault_id
+ 32 // authority
+ 1 // bump
+ 1 // proof_protocol
+ 32 // verification_key
+ 32 // vk_hash
+ 1 // encryption_algorithm
+ 8 // max_data_size
+ 1 // access_control
+ 32 // access_policy
+ 8 * 3 // statistics
+ 8 * 2 // timestamps
+ 1 * 2; // flags
}ProofSubmission Account
#[account]
pub struct ProofSubmission {
pub vault: Pubkey, // Parent vault
pub proof_id: [u8; 32], // Unique proof identifier
pub bump: u8,
// Proof data
pub proof_bytes: Vec<u8>, // Compressed proof (192-768 bytes)
pub public_inputs: Vec<u64>, // Public circuit inputs
pub circuit_id: [u8; 32], // Circuit identifier
// Metadata
pub prover: Pubkey, // Submitter address
pub submitted_at: i64,
pub nonce: u64, // Replay protection
// Verification status
pub verification_status: VerificationStatus,
pub verified_at: Option<i64>,
pub compute_units_used: u32,
// Results
pub verification_result: Option<bool>,
pub error_code: Option<u32>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
pub enum VerificationStatus {
Pending,
InProgress,
Verified,
Failed,
Expired,
}
impl ProofSubmission {
pub const MAX_SIZE: usize = 8 // discriminator
+ 32 // vault
+ 32 // proof_id
+ 1 // bump
+ 4 + 768 // proof_bytes (vec)
+ 4 + (8 * 32) // public_inputs (max 32)
+ 32 // circuit_id
+ 32 // prover
+ 8 // submitted_at
+ 8 // nonce
+ 1 // verification_status
+ 9 // verified_at (Option<i64>)
+ 4 // compute_units_used
+ 2 // verification_result (Option<bool>)
+ 5; // error_code (Option<u32>)
}EncryptedData Account
#[account]
pub struct EncryptedData {
pub vault: Pubkey,
pub data_id: [u8; 32],
pub bump: u8,
// Encrypted content
pub ciphertext: Vec<u8>, // Max 10KB per account
pub nonce: [u8; 12], // ChaCha20 nonce
pub tag: [u8; 16], // Poly1305 authentication tag
// Commitments
pub data_commitment: [u8; 32], // Pedersen commitment to plaintext
pub encryption_proof: Vec<u8>, // Proof of correct encryption
// Metadata
pub uploader: Pubkey,
pub content_type: String, // MIME type
pub uploaded_at: i64,
pub size_bytes: u64,
// Chunking (for large files)
pub is_chunked: bool,
pub chunk_index: Option<u32>,
pub total_chunks: Option<u32>,
pub parent_data_id: Option<[u8; 32]>,
// Access control
pub access_proof_required: bool,
pub allowed_readers: Vec<Pubkey>,
}
impl EncryptedData {
pub const MAX_SIZE: usize = 8
+ 32 + 32 + 1
+ 4 + 10_240 // ciphertext (10KB)
+ 12 + 16
+ 32 + 4 + 256
+ 32 + 4 + 64 + 8 + 8
+ 1 + 5 + 5 + 33
+ 1 + 4 + (32 * 10); // max 10 allowed readers
}VerificationKey Account
#[account]
pub struct VerificationKey {
pub key_id: [u8; 32],
pub bump: u8,
// Key data
pub protocol: ProofProtocol,
pub curve: Curve,
pub key_data: Vec<u8>, // Serialized VK
pub key_hash: [u8; 32], // Integrity check
// For Groth16
pub alpha_g1: Option<[u8; 64]>,
pub beta_g2: Option<[u8; 128]>,
pub gamma_g2: Option<[u8; 128]>,
pub delta_g2: Option<[u8; 128]>,
pub ic_len: u32, // Input commitment count
// Metadata
pub authority: Pubkey,
pub uploaded_at: i64,
pub version: u32,
pub is_active: bool,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy)]
pub enum ProofProtocol {
Groth16,
PLONK,
Halo2,
Marlin,
Custom(u8),
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy)]
pub enum Curve {
BN254,
BLS12_381,
BLS12_377,
}PDA Derivation Rules
Vault PDA
let (vault_pda, bump) = Pubkey::find_program_address(
&[
b"vault",
authority.as_ref(),
vault_id.as_ref(),
],
&zkvault_program_id,
);ProofSubmission PDA
let (proof_pda, bump) = Pubkey::find_program_address(
&[
b"proof",
vault.as_ref(),
&nonce.to_le_bytes(),
],
&zkvault_program_id,
);EncryptedData PDA
let (data_pda, bump) = Pubkey::find_program_address(
&[
b"encrypted_data",
vault.as_ref(),
data_id.as_ref(),
],
&zkvault_program_id,
);VerificationKey PDA
let (vk_pda, bump) = Pubkey::find_program_address(
&[
b"verification_key",
key_id.as_ref(),
],
&zkvault_program_id,
);State Transitions
Vault Lifecycle
Created → Active → [Frozen] → Closed
// State validation
impl Vault {
pub fn can_submit_proof(&self) -> bool {
self.is_active && !self.is_frozen
}
pub fn can_store_data(&self) -> bool {
self.is_active && !self.is_frozen
&& self.total_data_stored < self.max_data_size
}
pub fn can_update_vk(&self) -> bool {
self.is_active && self.total_proofs_verified == 0
}
}ProofSubmission Lifecycle
Pending → InProgress → Verified/Failed → [Expired]
impl ProofSubmission {
pub fn is_expired(&self, current_time: i64) -> bool {
let max_age = 300; // 5 minutes
current_time - self.submitted_at > max_age
}
pub fn can_verify(&self) -> bool {
matches!(self.verification_status, VerificationStatus::Pending)
&& !self.is_expired(Clock::get().unwrap().unix_timestamp)
}
}Access Control Models
AccessControl Types
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub enum AccessControlType {
Owner, // Only vault authority
Whitelist, // Explicit address list
Public, // Anyone
ProofBased, // Requires ZK proof of authorization
TokenGated, // Requires specific token holdings
Multisig { threshold: u8, signers: Vec<Pubkey> },
}AccessPolicy Account
#[account]
pub struct AccessPolicy {
pub vault: Pubkey,
pub policy_type: AccessControlType,
// For Whitelist
pub whitelist: Vec<Pubkey>,
// For ProofBased
pub required_circuit: Option<[u8; 32]>,
pub policy_vk: Option<Pubkey>,
// For TokenGated
pub required_token_mint: Option<Pubkey>,
pub minimum_balance: Option<u64>,
// For Multisig
pub signers: Vec<Pubkey>,
pub threshold: u8,
pub pending_signatures: Vec<Signature>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct Signature {
pub signer: Pubkey,
pub signed_at: i64,
pub signature: [u8; 64],
}Indexing and Querying
Event Emission
#[event]
pub struct VaultCreated {
pub vault: Pubkey,
pub authority: Pubkey,
pub vault_id: [u8; 32],
pub timestamp: i64,
}
#[event]
pub struct ProofSubmitted {
pub vault: Pubkey,
pub proof_id: [u8; 32],
pub prover: Pubkey,
pub circuit_id: [u8; 32],
pub timestamp: i64,
}
#[event]
pub struct ProofVerified {
pub vault: Pubkey,
pub proof_id: [u8; 32],
pub result: bool,
pub compute_units: u32,
pub timestamp: i64,
}
#[event]
pub struct DataStored {
pub vault: Pubkey,
pub data_id: [u8; 32],
pub size: u64,
pub is_chunked: bool,
pub timestamp: i64,
}Off-chain Indexing
ZKVAULT recommends using The Graph or similar indexers for efficient querying.
// Example GraphQL schema
type Vault @entity {
id: ID!
vaultId: Bytes!
authority: Bytes!
protocol: String!
totalProofs: BigInt!
totalVerified: BigInt!
totalData: BigInt!
createdAt: BigInt!
proofs: [ProofSubmission!]! @derivedFrom(field: "vault")
encryptedData: [EncryptedData!]! @derivedFrom(field: "vault")
}
type ProofSubmission @entity {
id: ID!
vault: Vault!
proofId: Bytes!
prover: Bytes!
status: VerificationStatus!
result: Boolean
submittedAt: BigInt!
verifiedAt: BigInt
}