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
}