miden_objects/batch/
account_update.rs

1use alloc::boxed::Box;
2
3use crate::Word;
4use crate::account::AccountId;
5use crate::account::delta::AccountUpdateDetails;
6use crate::errors::BatchAccountUpdateError;
7use crate::transaction::ProvenTransaction;
8use crate::utils::serde::{
9    ByteReader,
10    ByteWriter,
11    Deserializable,
12    DeserializationError,
13    Serializable,
14};
15
16// BATCH ACCOUNT UPDATE
17// ================================================================================================
18
19/// Represents the changes made to an account resulting from executing a batch of transactions.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct BatchAccountUpdate {
22    /// ID of the updated account.
23    account_id: AccountId,
24
25    /// Commitment to the state of the account before this update is applied.
26    ///
27    /// Equal to `Word::empty()` for new accounts.
28    initial_state_commitment: Word,
29
30    /// Commitment to the state of the account after this update is applied.
31    final_state_commitment: Word,
32
33    /// A set of changes which can be applied to the previous account state (i.e. `initial_state`)
34    /// to get the new account state. For private accounts, this is set to
35    /// [`AccountUpdateDetails::Private`].
36    details: AccountUpdateDetails,
37}
38
39impl BatchAccountUpdate {
40    // CONSTRUCTORS
41    // --------------------------------------------------------------------------------------------
42
43    /// Creates a [`BatchAccountUpdate`] by cloning the update and other details from the provided
44    /// [`ProvenTransaction`].
45    pub fn from_transaction(transaction: &ProvenTransaction) -> Self {
46        Self {
47            account_id: transaction.account_id(),
48            initial_state_commitment: transaction.account_update().initial_state_commitment(),
49            final_state_commitment: transaction.account_update().final_state_commitment(),
50            details: transaction.account_update().details().clone(),
51        }
52    }
53
54    /// Creates a [`BatchAccountUpdate`] from the provided parts without checking any consistency.
55    #[cfg(any(feature = "testing", test))]
56    pub fn new_unchecked(
57        account_id: AccountId,
58        initial_state_commitment: Word,
59        final_state_commitment: Word,
60        details: AccountUpdateDetails,
61    ) -> Self {
62        Self {
63            account_id,
64            initial_state_commitment,
65            final_state_commitment,
66            details,
67        }
68    }
69
70    // PUBLIC ACCESSORS
71    // --------------------------------------------------------------------------------------------
72
73    /// Returns the ID of the updated account.
74    pub fn account_id(&self) -> AccountId {
75        self.account_id
76    }
77
78    /// Returns a commitment to the state of the account before this update is applied.
79    ///
80    /// This is equal to [`Word::empty()`] for new accounts.
81    pub fn initial_state_commitment(&self) -> Word {
82        self.initial_state_commitment
83    }
84
85    /// Returns a commitment to the state of the account after this update is applied.
86    pub fn final_state_commitment(&self) -> Word {
87        self.final_state_commitment
88    }
89
90    /// Returns the contained [`AccountUpdateDetails`].
91    ///
92    /// This update can be used to build the new account state from the previous account state.
93    pub fn details(&self) -> &AccountUpdateDetails {
94        &self.details
95    }
96
97    /// Returns `true` if the account update details are for a private account.
98    pub fn is_private(&self) -> bool {
99        self.details.is_private()
100    }
101
102    // MUTATORS
103    // --------------------------------------------------------------------------------------------
104
105    /// Merges the transaction's update into this account update.
106    ///
107    /// # Errors
108    ///
109    /// Returns an error if:
110    /// - The account ID of the merging transaction does not match the account ID of the existing
111    ///   update.
112    /// - The merging transaction's initial state commitment does not match the final state
113    ///   commitment of the current update.
114    /// - If the underlying [`AccountUpdateDetails::merge`] fails.
115    pub fn merge_proven_tx(
116        &mut self,
117        tx: &ProvenTransaction,
118    ) -> Result<(), BatchAccountUpdateError> {
119        if self.account_id != tx.account_id() {
120            return Err(BatchAccountUpdateError::AccountUpdateIdMismatch {
121                transaction: tx.id(),
122                expected_account_id: self.account_id,
123                actual_account_id: tx.account_id(),
124            });
125        }
126
127        if self.final_state_commitment != tx.account_update().initial_state_commitment() {
128            return Err(BatchAccountUpdateError::AccountUpdateInitialStateMismatch(tx.id()));
129        }
130
131        self.details = self.details.clone().merge(tx.account_update().details().clone()).map_err(
132            |source_err| {
133                BatchAccountUpdateError::TransactionUpdateMergeError(tx.id(), Box::new(source_err))
134            },
135        )?;
136        self.final_state_commitment = tx.account_update().final_state_commitment();
137
138        Ok(())
139    }
140
141    // CONVERSIONS
142    // --------------------------------------------------------------------------------------------
143
144    /// Consumes the update and returns the underlying [`AccountUpdateDetails`].
145    pub fn into_update(self) -> AccountUpdateDetails {
146        self.details
147    }
148}
149
150// SERIALIZATION
151// ================================================================================================
152
153impl Serializable for BatchAccountUpdate {
154    fn write_into<W: ByteWriter>(&self, target: &mut W) {
155        self.account_id.write_into(target);
156        self.initial_state_commitment.write_into(target);
157        self.final_state_commitment.write_into(target);
158        self.details.write_into(target);
159    }
160}
161
162impl Deserializable for BatchAccountUpdate {
163    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
164        Ok(Self {
165            account_id: AccountId::read_from(source)?,
166            initial_state_commitment: Word::read_from(source)?,
167            final_state_commitment: Word::read_from(source)?,
168            details: AccountUpdateDetails::read_from(source)?,
169        })
170    }
171}