miden_objects/batch/
account_update.rs

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