|
| 1 | +//! Two-phase transaction application |
| 2 | +//! |
| 3 | +//! This module implements the two-phase transaction application model used in Mina. |
| 4 | +//! This approach enables efficient proof generation, particularly for zkApp commands. |
| 5 | +//! |
| 6 | +//! # Application Phases |
| 7 | +//! |
| 8 | +//! ## First Pass |
| 9 | +//! |
| 10 | +//! The first pass ([`apply_transaction_first_pass`]) performs: |
| 11 | +//! - Transaction validation (nonces, balances, permissions) |
| 12 | +//! - Fee payment |
| 13 | +//! - For zkApp commands: applies fee payer and begins account update processing |
| 14 | +//! - For other transactions: completes the entire application |
| 15 | +//! - Records the previous ledger hash |
| 16 | +//! |
| 17 | +//! ## Second Pass |
| 18 | +//! |
| 19 | +//! The second pass ([`apply_transaction_second_pass`]) performs: |
| 20 | +//! - For zkApp commands: completes account update processing and emits events/actions |
| 21 | +//! - For other transactions: simply packages the results from first pass |
| 22 | +//! |
| 23 | +//! # Key Types |
| 24 | +//! |
| 25 | +//! - [`TransactionPartiallyApplied`]: Intermediate state between passes |
| 26 | +//! - [`ZkappCommandPartiallyApplied`]: zkApp-specific intermediate state |
| 27 | +//! - [`FullyApplied`]: Wrapper for non-zkApp transactions that complete in first pass |
| 28 | +//! |
| 29 | +//! # Fee Transfers and Coinbase |
| 30 | +//! |
| 31 | +//! Fee transfers and coinbase transactions also use helper functions in this module: |
| 32 | +//! - [`apply_fee_transfer`]: Distributes fees to block producers |
| 33 | +//! - [`apply_coinbase`]: Handles block rewards and optional fee transfers |
| 34 | +//! - [`process_fee_transfer`]: Core logic for fee distribution with permission checks |
| 35 | +//! |
| 36 | +//! Both transactions have structured failure status to indicate which part failed: |
| 37 | +//! - Single transfer: `[[failure]]` |
| 38 | +//! - Two transfers both fail: `[[failure1]; [failure2]]` |
| 39 | +//! - First succeeds, second fails: `[[]; [failure2]]` |
| 40 | +//! - First fails, second succeeds: `[[failure1]; []]` |
| 41 | +
|
1 | 42 | use super::{ |
2 | 43 | transaction_applied::{CoinbaseApplied, FeeTransferApplied}, |
3 | 44 | *, |
@@ -46,6 +87,106 @@ where |
46 | 87 | } |
47 | 88 | } |
48 | 89 |
|
| 90 | +/// Applies the first pass of transaction application. |
| 91 | +/// |
| 92 | +/// This function performs the initial phase of transaction processing, which includes |
| 93 | +/// validation, fee payment, and partial application. The behavior differs based on |
| 94 | +/// transaction type: |
| 95 | +/// |
| 96 | +/// # Transaction Type Handling |
| 97 | +/// |
| 98 | +/// - **Signed Commands** (payments, stake delegations): Fully applied in the first pass. |
| 99 | +/// The result is wrapped in [`FullyApplied`] since no second pass work is needed. |
| 100 | +/// |
| 101 | +/// - **zkApp Commands**: Partially applied. The first pass: |
| 102 | +/// - Validates the zkApp command structure and permissions |
| 103 | +/// - Applies the fee payer account update |
| 104 | +/// - Begins processing the first phase of account updates |
| 105 | +/// - Records intermediate state in [`ZkappCommandPartiallyApplied`] |
| 106 | +/// |
| 107 | +/// - **Fee Transfers**: Fully applied in the first pass, distributing fees to block |
| 108 | +/// producers according to the protocol rules. |
| 109 | +/// |
| 110 | +/// - **Coinbase**: Fully applied in the first pass, crediting block rewards and any |
| 111 | +/// associated fee transfers to the designated accounts. |
| 112 | +/// |
| 113 | +/// # Ledger State Changes |
| 114 | +/// |
| 115 | +/// The ledger is mutated during the first pass as follows: |
| 116 | +/// |
| 117 | +/// - **Signed Commands**: |
| 118 | +/// - Fee payer balance decreased by fee amount |
| 119 | +/// - Fee payer nonce incremented |
| 120 | +/// - Fee payer receipt chain hash updated |
| 121 | +/// - Fee payer timing updated based on vesting schedule |
| 122 | +/// - For payments: sender balance decreased, receiver balance increased |
| 123 | +/// - For payments: new account created if receiver doesn't exist |
| 124 | +/// - For stake delegations: delegate field updated |
| 125 | +/// |
| 126 | +/// - **zkApp Commands**: |
| 127 | +/// - Fee payer account fully updated (balance, nonce, receipt chain, timing) |
| 128 | +/// - First phase account updates applied to ledger |
| 129 | +/// - New accounts may be created |
| 130 | +/// |
| 131 | +/// - **Fee Transfers**: |
| 132 | +/// - Receiver account balances increased by fee amounts |
| 133 | +/// - Timing updated when balances increase |
| 134 | +/// - New accounts created if receivers don't exist |
| 135 | +/// |
| 136 | +/// - **Coinbase**: |
| 137 | +/// - Block producer balance increased by reward amount |
| 138 | +/// - Fee transfer recipient balance increased (if applicable) |
| 139 | +/// - Timing updated when balances increase |
| 140 | +/// - New accounts created if recipients don't exist |
| 141 | +/// |
| 142 | +/// # Parameters |
| 143 | +/// |
| 144 | +/// - `constraint_constants`: Protocol constants including account creation fees and limits |
| 145 | +/// - `global_slot`: Current global slot number for timing validation |
| 146 | +/// - `txn_state_view`: View of the protocol state for validating transaction preconditions |
| 147 | +/// - `ledger`: Mutable reference to the ledger being updated |
| 148 | +/// - `transaction`: The transaction to apply |
| 149 | +/// |
| 150 | +/// # Returns |
| 151 | +/// |
| 152 | +/// Returns a [`TransactionPartiallyApplied`] containing either: |
| 153 | +/// - [`FullyApplied`] result for transactions that complete in first pass |
| 154 | +/// - [`ZkappCommandPartiallyApplied`] for zkApp commands needing second pass |
| 155 | +/// |
| 156 | +/// # Errors |
| 157 | +/// |
| 158 | +/// Returns an error if: |
| 159 | +/// - Transaction validation fails (invalid nonce, insufficient balance, etc.) |
| 160 | +/// - Fee payment fails |
| 161 | +/// - Account permissions are insufficient |
| 162 | +/// - Timing constraints are violated |
| 163 | +/// |
| 164 | +/// ## Error Side Effects |
| 165 | +/// |
| 166 | +/// When an error occurs, the ledger state depends on where the error occurred: |
| 167 | +/// |
| 168 | +/// - **Errors during fee payment** (invalid nonce, nonexistent fee payer): Ledger |
| 169 | +/// remains completely unchanged. |
| 170 | +/// |
| 171 | +/// - **Errors after fee payment** (insufficient balance for payment, permission |
| 172 | +/// errors): The fee has already been charged to ensure network compensation. The |
| 173 | +/// fee payer's account will have: balance decreased by fee, nonce incremented, |
| 174 | +/// receipt chain hash updated. However, the actual payment/operation is NOT |
| 175 | +/// performed. |
| 176 | +/// |
| 177 | +/// # Tests |
| 178 | +/// |
| 179 | +/// Test coverage (in `ledger/tests/test_transaction_logic_first_pass.rs`): |
| 180 | +/// |
| 181 | +/// - [`test_apply_payment_success`]: successful payment with ledger state verification |
| 182 | +/// - [`test_apply_payment_insufficient_balance`]: payment exceeding sender balance |
| 183 | +/// - [`test_apply_payment_invalid_nonce`]: payment with incorrect nonce |
| 184 | +/// - [`test_apply_payment_nonexistent_fee_payer`]: payment from nonexistent account |
| 185 | +/// |
| 186 | +/// [`test_apply_payment_success`]: ../../tests/test_transaction_logic_first_pass.rs |
| 187 | +/// [`test_apply_payment_insufficient_balance`]: ../../tests/test_transaction_logic_first_pass.rs |
| 188 | +/// [`test_apply_payment_invalid_nonce`]: ../../tests/test_transaction_logic_first_pass.rs |
| 189 | +/// [`test_apply_payment_nonexistent_fee_payer`]: ../../tests/test_transaction_logic_first_pass.rs |
49 | 190 | pub fn apply_transaction_first_pass<L>( |
50 | 191 | constraint_constants: &ConstraintConstants, |
51 | 192 | global_slot: Slot, |
@@ -108,6 +249,71 @@ where |
108 | 249 | } |
109 | 250 | } |
110 | 251 |
|
| 252 | +/// Completes the second pass of transaction application. |
| 253 | +/// |
| 254 | +/// This function finalizes transaction processing by completing any remaining work |
| 255 | +/// from the first pass. The behavior differs based on transaction type: |
| 256 | +/// |
| 257 | +/// # Transaction Type Handling |
| 258 | +/// |
| 259 | +/// - **Signed Commands**: No additional work needed. Simply unwraps the [`FullyApplied`] |
| 260 | +/// result from the first pass and packages it into a [`TransactionApplied`]. |
| 261 | +/// |
| 262 | +/// - **zkApp Commands**: Completes the second phase of application: |
| 263 | +/// - Processes the second phase of account updates |
| 264 | +/// - Emits events and actions from the zkApp execution |
| 265 | +/// - Updates the zkApp's on-chain state |
| 266 | +/// - Validates all preconditions are satisfied |
| 267 | +/// |
| 268 | +/// - **Fee Transfers**: No additional work needed. Simply packages the first pass result. |
| 269 | +/// |
| 270 | +/// - **Coinbase**: No additional work needed. Simply packages the first pass result. |
| 271 | +/// |
| 272 | +/// # Ledger State Changes |
| 273 | +/// |
| 274 | +/// The ledger is mutated during the second pass only for zkApp commands: |
| 275 | +/// |
| 276 | +/// - **Signed Commands**: No ledger changes (all modifications completed in first pass) |
| 277 | +/// |
| 278 | +/// - **zkApp Commands**: |
| 279 | +/// - Second phase account updates applied |
| 280 | +/// - Account balances modified based on zkApp logic |
| 281 | +/// - Account app state fields updated |
| 282 | +/// - Account permissions may be modified |
| 283 | +/// - Action state and event sequence numbers updated |
| 284 | +/// - New accounts may be created |
| 285 | +/// |
| 286 | +/// - **Fee Transfers**: No ledger changes (all modifications completed in first pass) |
| 287 | +/// |
| 288 | +/// - **Coinbase**: No ledger changes (all modifications completed in first pass) |
| 289 | +/// |
| 290 | +/// # Parameters |
| 291 | +/// |
| 292 | +/// - `constraint_constants`: Protocol constants including account creation fees and limits |
| 293 | +/// - `ledger`: Mutable reference to the ledger being updated |
| 294 | +/// - `partial_transaction`: The partially applied transaction from the first pass |
| 295 | +/// |
| 296 | +/// # Returns |
| 297 | +/// |
| 298 | +/// Returns a [`TransactionApplied`] containing the complete application result with: |
| 299 | +/// - Previous ledger hash (recorded during first pass) |
| 300 | +/// - Full transaction status (Applied or Failed with specific error codes) |
| 301 | +/// - Account updates and new account information |
| 302 | +/// - Events and actions (for zkApp commands) |
| 303 | +/// |
| 304 | +/// # Errors |
| 305 | +/// |
| 306 | +/// Returns an error if: |
| 307 | +/// - Second phase zkApp account updates fail |
| 308 | +/// - zkApp preconditions fail during second pass |
| 309 | +/// - Account permissions are insufficient |
| 310 | +/// |
| 311 | +/// # Notes |
| 312 | +/// |
| 313 | +/// For non-zkApp transactions, this function performs minimal work since the first |
| 314 | +/// pass already completed the application. The two-phase model exists primarily to |
| 315 | +/// enable efficient zkApp proof generation where different account updates can be |
| 316 | +/// processed in separate circuit phases. |
111 | 317 | pub fn apply_transaction_second_pass<L>( |
112 | 318 | constraint_constants: &ConstraintConstants, |
113 | 319 | ledger: &mut L, |
|
0 commit comments