Skip to main content

miden_protocol/batch/
account_update.rs

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