Skip to content

Commit

Permalink
feat(objects): Implement size limit check on account delta
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippGackstatter committed Sep 24, 2024
1 parent 1001b05 commit f675053
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 6 deletions.
72 changes: 66 additions & 6 deletions objects/src/accounts/delta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
Account, ByteReader, ByteWriter, Deserializable, DeserializationError, Felt, Serializable,
Word, ZERO,
};
use crate::AccountDeltaError;
use crate::{AccountDeltaError, ACCOUNT_DELTA_MAX_SIZE};

mod storage;
pub use storage::{AccountStorageDelta, StorageMapDelta};
Expand Down Expand Up @@ -52,8 +52,11 @@ impl AccountDelta {
/// Returns new [AccountDelta] instantiated from the provided components.
///
/// # Errors
/// Returns an error if storage or vault were updated, but the nonce was either not updated
///
/// - Returns an error if storage or vault were updated, but the nonce was either not updated
/// or set to 0.
/// - Returns an error if the serialized size of the delta exceeds the maximum allowed
/// size.
pub fn new(
storage: AccountStorageDelta,
vault: AccountVaultDelta,
Expand All @@ -62,7 +65,11 @@ impl AccountDelta {
// nonce must be updated if either account storage or vault were updated
validate_nonce(nonce, &storage, &vault)?;

Ok(Self { storage, vault, nonce })
let account_delta = Self { storage, vault, nonce };

account_delta.validate_max_size()?;

Ok(account_delta)
}

/// Merge another [AccountDelta] into this one.
Expand Down Expand Up @@ -107,6 +114,18 @@ impl AccountDelta {
pub fn into_parts(self) -> (AccountStorageDelta, AccountVaultDelta, Option<Felt>) {
(self.storage, self.vault, self.nonce)
}

// VALIDATION
// --------------------------------------------------------------------------------------------

/// Validates that the delta's size does not exceed the maximum allowed size.
fn validate_max_size(&self) -> Result<(), AccountDeltaError> {
if self.get_size_hint() > ACCOUNT_DELTA_MAX_SIZE as usize {
Err(AccountDeltaError::SizeLimitExceeded)
} else {
Ok(())
}
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -269,13 +288,16 @@ pub(super) fn usize_encoded_len(value: usize) -> usize {

#[cfg(test)]
mod tests {
use vm_core::utils::Serializable;
use alloc::collections::BTreeMap;

use vm_core::{utils::Serializable, Felt};
use vm_processor::Digest;

use super::{AccountDelta, AccountStorageDelta, AccountVaultDelta};
use crate::{
accounts::{AccountId, AccountType, StorageMapDelta},
accounts::{delta::WORD_SERIALIZED_SIZE, AccountId, AccountType, StorageMapDelta},
assets::{Asset, FungibleAsset, NonFungibleAsset, NonFungibleAssetDetails},
ONE, ZERO,
AccountDeltaError, ACCOUNT_DELTA_MAX_SIZE, ONE, ZERO,
};

#[test]
Expand Down Expand Up @@ -338,4 +360,42 @@ mod tests {
let account_delta = AccountDelta::new(storage_delta, vault_delta, Some(ONE)).unwrap();
assert_eq!(account_delta.to_bytes().len(), account_delta.get_size_hint());
}

#[test]
fn account_delta_size_limit() {
// A small delta does not exceed the limit.
let storage_delta = AccountStorageDelta::from_iters(
[1, 2, 3, 4],
[(2, [ONE, ONE, ONE, ONE]), (3, [ONE, ONE, ZERO, ONE])],
[],
);
AccountDelta::new(storage_delta, AccountVaultDelta::default(), Some(ONE)).unwrap();

let mut map = BTreeMap::new();
// The number of entries in the map required to exceed the limit.
let required_entries = ACCOUNT_DELTA_MAX_SIZE / (2 * WORD_SERIALIZED_SIZE as u16);
for _ in 0..required_entries {
map.insert(
Digest::new([
Felt::new(rand::random()),
Felt::new(rand::random()),
Felt::new(rand::random()),
Felt::new(rand::random()),
]),
[
Felt::new(rand::random()),
Felt::new(rand::random()),
Felt::new(rand::random()),
Felt::new(rand::random()),
],
);
}
let storage_delta = StorageMapDelta::new(map);

// A delta that exceeds the limit returns an error.
let storage_delta = AccountStorageDelta::from_iters([], [], [(4, storage_delta)]);
let err =
AccountDelta::new(storage_delta, AccountVaultDelta::default(), Some(ONE)).unwrap_err();
assert!(matches!(err, AccountDeltaError::SizeLimitExceeded));
}
}
3 changes: 3 additions & 0 deletions objects/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/// Depth of the account database tree.
pub const ACCOUNT_TREE_DEPTH: u8 = 64;

/// The maximum allowed size of an account delta is 32 KiB.
pub const ACCOUNT_DELTA_MAX_SIZE: u16 = 2u16.pow(15);

/// The maximum number of assets that can be stored in a single note.
pub const MAX_ASSETS_PER_NOTE: usize = 256;

Expand Down
1 change: 1 addition & 0 deletions objects/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pub enum AccountDeltaError {
IncompatibleAccountUpdates(AccountUpdateDetails, AccountUpdateDetails),
InconsistentNonceUpdate(String),
NotAFungibleFaucetId(AccountId),
SizeLimitExceeded,
}

#[cfg(feature = "std")]
Expand Down

0 comments on commit f675053

Please sign in to comment.