Skip to main content

neo_devpack_solidity/runtime/state/state_impl/
batch.rs

1use super::*;
2
3impl StateManager {
4    /// Execute state batch
5    pub fn execute_batch(&mut self, batch: StateBatch) -> Result<(), RuntimeError> {
6        if batch.atomic {
7            // Create snapshot for rollback
8            let snapshot_id = self.create_snapshot("Batch execution".to_string());
9
10            // Execute all changes
11            for change in &batch.changes {
12                match self.apply_change(change) {
13                    Ok(_) => {}
14                    Err(e) => {
15                        // Rollback on error
16                        if let Some(snapshot) = self.snapshots.get(snapshot_id as usize) {
17                            self.restore_snapshot(snapshot.clone())?;
18                        }
19                        return Err(e);
20                    }
21                }
22            }
23        } else {
24            // Execute changes non-atomically (best-effort apply, analogous to
25            // Ethereum's `Multicall3.tryAggregate(requireSuccess = false, ...)`).
26            // Each malformed change is silently skipped; subsequent changes
27            // continue to apply. Callers that need all-or-nothing semantics
28            // must set `atomic: true` (which uses a snapshot for rollback).
29            // See `tests/runtime_state_batch_tests.rs` for the canonical
30            // contract and rationale.
31            for change in &batch.changes {
32                let _ = self.apply_change(change);
33            }
34        }
35
36        Ok(())
37    }
38
39    fn apply_change(&mut self, change: &StateChange) -> Result<(), RuntimeError> {
40        match change.change_type {
41            StateChangeType::BalanceChange => {
42                let new_balance =
43                    u64::from_le_bytes(change.new_value.as_slice().try_into().map_err(|_| {
44                        RuntimeError::StateError {
45                            message: "Invalid balance format".to_string(),
46                        }
47                    })?);
48                self.set_balance(&change.account, new_balance)
49            }
50            StateChangeType::NonceChange => {
51                let new_nonce =
52                    u64::from_le_bytes(change.new_value.as_slice().try_into().map_err(|_| {
53                        RuntimeError::StateError {
54                            message: "Invalid nonce format".to_string(),
55                        }
56                    })?);
57                self.set_nonce(&change.account, new_nonce)
58            }
59            StateChangeType::CodeChange => self.set_code(&change.account, &change.new_value),
60            StateChangeType::AccountCreation => {
61                let initial_balance =
62                    u64::from_le_bytes(change.new_value.as_slice().try_into().map_err(|_| {
63                        RuntimeError::StateError {
64                            message: "Invalid balance format".to_string(),
65                        }
66                    })?);
67                self.create_account(&change.account, initial_balance)
68            }
69            StateChangeType::AccountDeletion => self.delete_account(&change.account),
70            StateChangeType::StorageChange => {
71                // Storage changes are handled by storage manager
72                Ok(())
73            }
74        }
75    }
76}