diff --git a/ledger/src/generators/zkapp_command.rs b/ledger/src/generators/zkapp_command.rs index 474cba65d..910ca5927 100644 --- a/ledger/src/generators/zkapp_command.rs +++ b/ledger/src/generators/zkapp_command.rs @@ -1,3 +1,19 @@ +//! zkApp Command Generation for Testing +//! +//! This module provides utilities for generating zkApp commands for testing purposes. +//! It includes functions to create valid and invalid zkApp transactions with various +//! configurations, account updates, and preconditions. +//! +//! The generators support: +//! - Creating arbitrary zkApp commands with configurable parameters +//! - Generating both valid and invalid transactions for testing edge cases +//! - Setting up complex account update hierarchies +//! - Testing various authorization scenarios +//! - Generating invalid preconditions for failure testing +//! +//! These generators are essential for comprehensive testing of the zkApp +//! implementation against the Mina Protocol specification. + use std::{ collections::{ hash_map::Entry::{Occupied, Vacant}, @@ -50,7 +66,12 @@ use super::{Failure, NotPermitedOf, Role}; // /// Value when we run `dune runtest src/lib/staged_ledger -f` // const ACCOUNT_CREATION_FEE: Fee = Fee::from_u64(1000000000); -/// +/// Generates invalid protocol state preconditions for testing failure cases. +/// +/// This function creates preconditions that are guaranteed to fail validation +/// against the current protocol state, allowing testing of zkApp rejection scenarios. +/// +/// Reference: fn gen_invalid_protocol_state_precondition(psv: &ProtocolStateView) -> ZkAppPreconditions { enum Tamperable { BlockchainLength, @@ -1312,16 +1333,30 @@ fn gen_fee_payer( } } +/// Parameters for generating zkApp commands in tests. +/// +/// This struct contains all the configuration needed to generate +/// realistic zkApp commands for testing various scenarios. pub struct GenZkappCommandParams<'a> { + /// Global slot for the transaction (affects timing preconditions) pub global_slot: Option, + /// Optional failure mode to generate invalid transactions pub failure: Option<&'a Failure>, + /// Maximum number of account updates to generate pub max_account_updates: Option, + /// Maximum number of token-related updates to generate pub max_token_updates: Option, + /// Keypair for the fee payer account pub fee_payer_keypair: &'a Keypair, + /// Map of public keys to keypairs for signing pub keymap: &'a HashMap, + /// Optional table tracking account states across generations pub account_state_tbl: Option<&'a mut HashMap>, + /// Ledger state for account lookups and modifications pub ledger: Mask, + /// Protocol state view for precondition generation pub protocol_state_view: Option<&'a ProtocolStateView>, + /// Optional verification key for zkApp accounts pub vk: Option<&'a VerificationKeyWire>, } diff --git a/ledger/src/scan_state/transaction_logic.rs b/ledger/src/scan_state/transaction_logic.rs index 3658a1a2c..8a7d85f14 100644 --- a/ledger/src/scan_state/transaction_logic.rs +++ b/ledger/src/scan_state/transaction_logic.rs @@ -1224,10 +1224,20 @@ pub mod zkapp_command { } } - /// + /// Represents a field update in a zkApp account update. + /// + /// This enum allows zkApp account updates to either: + /// - Set a field to a new value + /// - Keep the current value unchanged + /// + /// This is used throughout zkApp account updates for optional field modifications. + /// + /// Reference: #[derive(Debug, Clone, PartialEq, Eq)] pub enum SetOrKeep { + /// Set the field to this new value Set(T), + /// Keep the current value unchanged Keep, } @@ -1879,15 +1889,28 @@ pub mod zkapp_command { } } - /// + /// Preconditions on the protocol state that must hold for a zkApp to execute. + /// + /// These preconditions allow zkApps to ensure certain blockchain state conditions + /// are met before their transaction executes. All specified conditions must be + /// satisfied or the transaction will fail. + /// + /// Reference: #[derive(Debug, Clone, PartialEq)] pub struct ZkAppPreconditions { + /// Required hash of the snarked ledger pub snarked_ledger_hash: Hash, + /// Required range for blockchain length pub blockchain_length: Numeric, + /// Required range for minimum window density pub min_window_density: Numeric, + /// Required range for total currency in circulation pub total_currency: Numeric, + /// Required range for global slot since genesis pub global_slot_since_genesis: Numeric, + /// Required conditions on staking epoch data pub staking_epoch_data: EpochData, + /// Required conditions on next epoch data pub next_epoch_data: EpochData, } @@ -3582,11 +3605,24 @@ pub mod zkapp_command { pub authorization: Signature, } - /// + /// A zkApp command represents a complete zkApp transaction. + /// + /// This contains all the information needed to execute a zkApp transaction: + /// - The fee payer who pays transaction fees and signs the transaction + /// - A forest of account updates to be applied atomically + /// - An optional memo for the transaction + /// + /// The account updates are processed in a specific order using a call stack, + /// ensuring that permissions and authorization are properly enforced. + /// + /// Reference: #[derive(Debug, Clone, PartialEq)] pub struct ZkAppCommand { + /// Account that pays fees and provides base authorization pub fee_payer: FeePayer, + /// Tree of account updates to be processed pub account_updates: CallForest, + /// Optional memo attached to the transaction pub memo: Memo, } diff --git a/ledger/src/zkapps/intefaces.rs b/ledger/src/zkapps/intefaces.rs index 39b1f946f..78c8bdcad 100644 --- a/ledger/src/zkapps/intefaces.rs +++ b/ledger/src/zkapps/intefaces.rs @@ -1,3 +1,20 @@ +//! zkApp Application Interfaces +//! +//! This module defines the trait interfaces that abstract over the different +//! implementations of zkApp processing (SNARK vs non-SNARK modes). +//! +//! The interfaces follow the Mina Protocol zkApp specification and provide +//! clean abstractions for: +//! - Witness generation and constraint handling +//! - Account operations and state management +//! - Ledger operations and inclusion proofs +//! - Boolean logic and arithmetic operations +//! - Authorization and permission checking +//! +//! These interfaces allow the same zkApp logic to work in both: +//! - Circuit mode: Generating constraints for SNARK proofs +//! - Non-circuit mode: Direct execution for validation + use std::marker::PhantomData; use mina_hasher::Fp; @@ -23,16 +40,23 @@ use crate::{ AccountId, AuthRequired, MyCow, ReceiptChainHash, TokenId, ZkAppAccount, }; +/// Core trait for witness generation in zkApp circuits. +/// +/// This trait abstracts the process of generating witnesses (private inputs) +/// for the zkApp circuit. In circuit mode, this tracks variables and adds +/// constraints. In non-circuit mode, this is essentially a no-op. pub trait WitnessGenerator where Self: Sized, { type Bool: BoolInterface; + /// Add a value to the witness with constraint checking fn exists(&mut self, data: T) -> T where T: ToFieldElements + Check; + /// Add a value to the witness without constraint checking fn exists_no_check(&mut self, data: T) -> T where T: ToFieldElements; @@ -62,6 +86,10 @@ where } } +/// Handler trait for zkApp-specific operations. +/// +/// This trait defines the operations that are specific to zkApp processing +/// and may have different implementations in SNARK vs non-SNARK modes. pub trait ZkappHandler { type Z: ZkappApplication; type AccountUpdate: AccountUpdateInterface; @@ -283,6 +311,10 @@ where type StackFrame: StackFrameInterface; } +/// Interface for global blockchain state operations. +/// +/// This trait provides access to global state that persists across +/// zkApp command processing, including ledger states and fee tracking. pub trait GlobalStateInterface { type Ledger; type W: WitnessGenerator; @@ -329,6 +361,11 @@ pub trait LocalStateInterface { fn add_new_failure_status_bucket(local: &mut zkapp_logic::LocalState); } +/// Interface for zkApp account updates. +/// +/// An account update represents a single modification to an account +/// within a zkApp transaction. This trait provides access to the +/// account update data and methods for authorization checking. pub trait AccountUpdateInterface where Self: Sized, @@ -406,6 +443,11 @@ pub trait TxnVersionInterface { fn older_than_current(version: TxnVersion, w: &mut Self::W) -> Self::Bool; } +/// Interface for boolean operations in zkApp circuits. +/// +/// This trait abstracts boolean logic operations that work in both +/// circuit and non-circuit modes. In circuit mode, these operations +/// generate constraints. In non-circuit mode, they perform direct evaluation. pub trait BoolInterface where Self: Sized, @@ -444,6 +486,10 @@ pub trait TransactionCommitmentInterface { ) -> Fp; } +/// Interface for account data and operations. +/// +/// This trait provides access to account state and methods for +/// modifying account properties like balance, permissions, app state, etc. pub trait AccountInterface where Self: Sized, @@ -480,6 +526,10 @@ where fn set_last_action_slot(&mut self, slot: Self::GlobalSlot); } +/// Interface for ledger operations in zkApp processing. +/// +/// This trait provides the ledger operations needed for zkApp execution, +/// including account retrieval, modification, and inclusion proof verification. pub trait LedgerInterface: LedgerIntf + Clone { type W: WitnessGenerator; type AccountUpdate: AccountUpdateInterface; @@ -574,6 +624,12 @@ pub trait BranchInterface { F: FnOnce(&mut Self::W) -> T; } +/// Main trait that ties together all zkApp interfaces. +/// +/// This trait defines the complete type system for zkApp processing, +/// specifying all the associated types and their relationships. +/// It serves as the central abstraction that allows the same zkApp +/// logic to work in both circuit and non-circuit modes. pub trait ZkappApplication where Self: Sized, diff --git a/ledger/src/zkapps/zkapp_logic.rs b/ledger/src/zkapps/zkapp_logic.rs index ff1743f75..621989e1f 100644 --- a/ledger/src/zkapps/zkapp_logic.rs +++ b/ledger/src/zkapps/zkapp_logic.rs @@ -1,3 +1,18 @@ +//! zkApp Command Logic Implementation +//! +//! This module implements the core logic for processing zkApp commands according to +//! the Mina Protocol specification. It handles account updates, authorization checking, +//! precondition validation, and state transitions. +//! +//! The implementation follows the Redux-style state machine pattern where: +//! - Actions flow through the system as events +//! - Enabling conditions check if actions are valid +//! - Reducers process actions to update state +//! - Effects trigger service calls when needed +//! +//! Reference: Mina Protocol zkApp RFCs and OCaml implementation +//! + use mina_hasher::Fp; use mina_signer::CompressedPubKey; use openmina_core::constants::constraint_constants; @@ -20,29 +35,56 @@ use crate::proofs::{ zkapp::StartDataSkeleton, }; +/// Represents whether we're processing the first account update in a zkApp command. +/// This affects how the transaction commitment and ledger state are handled. +/// +/// - `Yes(T)`: This is definitely the first account update (fee payer) +/// - `No`: This is definitely not the first account update +/// - `Compute(T)`: Dynamically determine based on call stack state pub enum IsStart { Yes(T), No, Compute(T), } +/// Result of retrieving the next account update from the call stack. +/// This represents the state after popping an account update and its children +/// from the current execution frame. struct GetNextAccountUpdateResult { + /// The account update to process account_update: Z::AccountUpdate, + /// Token ID of the caller (used for token permission checks) caller_id: TokenId, + /// Child account updates that will be processed recursively account_update_forest: Z::CallForest, + /// Updated call stack after processing new_call_stack: Z::CallStack, + /// New stack frame for the next iteration new_frame: Z::StackFrame, } +/// Elements that can be added to a zkApp command's receipt chain. +/// This is used to build the cryptographic commitment chain that proves +/// the sequence of account updates in a zkApp transaction. #[derive(Clone)] pub enum ZkAppCommandElt { + /// A commitment hash representing a zkApp command in the receipt chain ZkAppCommandCommitment(crate::ReceiptChainHash), } +/// Assert that a boolean condition holds during zkApp processing. +/// This is used for invariant checking and constraint generation in the circuit. +/// +/// In circuit mode, this adds constraints to ensure the condition is true. +/// In non-circuit mode, this performs runtime validation. +/// +/// # Arguments +/// * `b` - The boolean condition to check +/// * `s` - Error message if the condition fails +/// +/// # Reference +/// fn assert_(b: Z::Bool, s: &str) -> Result<(), String> { - // Used only for circuit generation (add constraints) - // - if let Boolean::False = b.as_boolean() { return Err(s.to_string()); } @@ -257,6 +299,23 @@ fn get_next_account_update( } } +/// Updates the action state for a zkApp account based on new actions. +/// +/// The action state maintains a rolling hash of the last 5 actions performed +/// by the zkApp. This function implements the action state update logic: +/// - If actions is empty, no changes are made +/// - If this is the same slot as the last action, replace the first element +/// - If this is a new slot, shift all elements and add new hash to position 0 +/// +/// # Arguments +/// * `action_state` - Current 5-element action state array +/// * `actions` - New actions to be added +/// * `txn_global_slot` - Current transaction's global slot +/// * `last_action_slot` - Slot when the last action was performed +/// * `w` - Witness generator for circuit constraints +/// +/// # Returns +/// Updated action state array and the new last action slot pub fn update_action_state( action_state: &[Fp; 5], actions: &Actions, @@ -298,39 +357,88 @@ pub fn update_action_state( ([s1_new, s2, s3, s4, s5], last_action_slot) } +/// Local state maintained during zkApp command processing. +/// This tracks the execution state as we iterate through account updates. #[derive(Debug, Clone)] pub struct LocalState { + /// Current execution frame containing account updates to process pub stack_frame: Z::StackFrame, + /// Stack of frames for nested account update calls pub call_stack: Z::CallStack, + /// Commitment hash for the current transaction (excluding fee payer) pub transaction_commitment: Fp, + /// Full commitment hash including fee payer (used for authorization) pub full_transaction_commitment: Fp, + /// Running balance of fees/excess in this transaction pub excess: Z::SignedAmount, + /// Running balance of supply changes (account creation fees) pub supply_increase: Z::SignedAmount, + /// Current ledger state being modified pub ledger: Z::Ledger, + /// Whether the current account update succeeded pub success: Z::Bool, + /// Index of the current account update being processed pub account_update_index: Z::Index, + /// Table tracking failure statuses for error reporting pub failure_status_tbl: Z::FailureStatusTable, + /// Whether the overall transaction will succeed pub will_succeed: Z::Bool, } +/// Global state that persists across zkApp command processing. +/// This represents the overall blockchain state being modified. pub type GlobalState = GlobalStateSkeleton< ::Ledger, // ledger ::SignedAmount, // fee_excess & supply_increase ::GlobalSlotSinceGenesis, // block_global_slot >; +/// Data provided when starting zkApp command processing. +/// This contains the initial account updates and success prediction. pub type StartData = StartDataSkeleton< ::CallForest, // account_updates ::Bool, // will_succeed >; +/// Parameters for applying a single zkApp account update. +/// This contains all the context needed to process one account update. pub struct ApplyZkappParams<'a, Z: ZkappApplication> { + /// Whether this is the first account update (fee payer) pub is_start: IsStart>, + /// Mutable reference to global blockchain state pub global_state: &'a mut Z::GlobalState, + /// Mutable reference to local transaction state pub local_state: &'a mut LocalState, + /// Additional data needed for this specific account update pub single_data: Z::SingleData, } +/// Core function for applying a single zkApp account update. +/// +/// This is the main entry point for processing zkApp commands according to the +/// Mina Protocol specification. It performs the complete validation and application +/// logic for one account update in a zkApp transaction. +/// +/// The function handles: +/// - Authorization checking (signatures and proofs) +/// - Precondition validation (account and protocol state) +/// - Balance changes and fee handling +/// - Account state updates (app state, permissions, etc.) +/// - Token permission enforcement +/// - Action state updates +/// - Receipt chain updates +/// +/// # Arguments +/// * `params` - All parameters needed for processing this account update +/// * `w` - Witness generator for constraint generation in circuits +/// +/// # Returns +/// `Ok(())` if the account update is valid and successfully applied, +/// `Err(String)` with an error message if validation fails. +/// +/// # Reference +/// This implements the zkApp command logic from the Mina Protocol: +/// pub fn apply(params: ApplyZkappParams<'_, Z>, w: &mut Z::WitnessGenerator) -> Result<(), String> where Z: ZkappApplication,