Skip to main content

miden_protocol/block/
block_account_update.rs

1use alloc::string::ToString;
2
3use crate::Word;
4use crate::account::{AccountId, AccountUpdateDetails, validate_new_public_account};
5use crate::errors::BlockAccountUpdateError;
6use crate::utils::serde::{
7    ByteReader,
8    ByteWriter,
9    Deserializable,
10    DeserializationError,
11    Serializable,
12};
13
14// BLOCK ACCOUNT UPDATE
15// ================================================================================================
16
17/// Describes the changes made to an account state resulting from executing transactions contained
18/// in a block.
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct BlockAccountUpdate {
21    /// ID of the updated account.
22    account_id: AccountId,
23
24    /// Final commitment to the new state of the account after this update.
25    final_state_commitment: Word,
26
27    /// A set of changes which can be applied to the previous account state (i.e., the state as of
28    /// the last block) to get the new account state. For private accounts, this is set to
29    /// [AccountUpdateDetails::Private].
30    details: AccountUpdateDetails,
31}
32
33impl BlockAccountUpdate {
34    /// Returns a new validated [`BlockAccountUpdate`].
35    pub fn new(
36        account_id: AccountId,
37        final_state_commitment: Word,
38        details: AccountUpdateDetails,
39    ) -> Result<Self, BlockAccountUpdateError> {
40        let update = Self::new_unchecked(account_id, final_state_commitment, details);
41        update.validate()?;
42        Ok(update)
43    }
44
45    /// Returns a new [`BlockAccountUpdate`] without validating its invariants.
46    ///
47    /// Callers must ensure that the update details are compatible with the account ID.
48    pub(crate) const fn new_unchecked(
49        account_id: AccountId,
50        final_state_commitment: Word,
51        details: AccountUpdateDetails,
52    ) -> Self {
53        Self {
54            account_id,
55            final_state_commitment,
56            details,
57        }
58    }
59
60    /// Validates that this account update's details are compatible with its account ID.
61    pub(crate) fn validate(&self) -> Result<(), BlockAccountUpdateError> {
62        self.details.validate_for_account(self.account_id)?;
63        Ok(())
64    }
65
66    /// Validates that the update of a new public account reconstructs to its final state
67    /// commitment.
68    ///
69    /// The update itself cannot tell a creation from an upgrade, so the caller must know that the
70    /// account is new.
71    pub(super) fn validate_new_account_patch(&self) -> Result<(), BlockAccountUpdateError> {
72        if let AccountUpdateDetails::Public(patch) = &self.details {
73            validate_new_public_account(patch, self.final_state_commitment)?;
74        }
75
76        Ok(())
77    }
78
79    /// Returns the ID of the updated account.
80    pub fn account_id(&self) -> AccountId {
81        self.account_id
82    }
83
84    /// Returns the state commitment of the account after this update.
85    pub fn final_state_commitment(&self) -> Word {
86        self.final_state_commitment
87    }
88
89    /// Returns the account update details for this account update.
90    ///
91    /// These details 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 private account.
97    pub fn is_private(&self) -> bool {
98        self.details.is_private()
99    }
100}
101
102impl Serializable for BlockAccountUpdate {
103    fn write_into<W: ByteWriter>(&self, target: &mut W) {
104        self.account_id.write_into(target);
105        self.final_state_commitment.write_into(target);
106        self.details.write_into(target);
107    }
108}
109
110impl Deserializable for BlockAccountUpdate {
111    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
112        Self::new(
113            AccountId::read_from(source)?,
114            Word::read_from(source)?,
115            AccountUpdateDetails::read_from(source)?,
116        )
117        .map_err(|error| DeserializationError::InvalidValue(error.to_string()))
118    }
119}
120
121// TESTS
122// ================================================================================================
123
124#[cfg(test)]
125mod tests {
126    use super::BlockAccountUpdate;
127    use crate::Word;
128    use crate::account::{AccountId, AccountPatch, AccountUpdateDetails};
129    use crate::testing::account_id::ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE;
130
131    #[test]
132    fn accepts_partial_public_account_patch() -> anyhow::Result<()> {
133        let account_id = AccountId::try_from(ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_UPDATABLE_CODE)?;
134
135        BlockAccountUpdate::new(
136            account_id,
137            Word::empty(),
138            AccountUpdateDetails::Public(AccountPatch::empty(account_id)),
139        )?;
140
141        Ok(())
142    }
143}