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}