Skip to content

Commit

Permalink
feat(blockifier): iterate over aliases on the state diff
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavGrs committed Dec 10, 2024
1 parent d9c83da commit 937947e
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 12 deletions.
18 changes: 8 additions & 10 deletions crates/blockifier/src/blockifier/transaction_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::context::BlockContext;
use crate::state::cached_state::{CachedState, CommitmentStateDiff, TransactionalState};
use crate::state::errors::StateError;
use crate::state::state_api::{StateReader, StateResult};
use crate::state::stateful_compression::state_diff_with_allocating_aliases;
use crate::transaction::errors::TransactionExecutionError;
use crate::transaction::objects::TransactionExecutionInfo;
use crate::transaction::transaction_execution::Transaction;
Expand Down Expand Up @@ -166,16 +167,13 @@ impl<S: StateReader> TransactionExecutor<S> {
.collect::<TransactionExecutorResult<_>>()?;

log::debug!("Final block weights: {:?}.", self.bouncer.get_accumulated_weights());
Ok((
self.block_state
.as_mut()
.expect(BLOCK_STATE_ACCESS_ERR)
.to_state_diff()?
.state_maps
.into(),
visited_segments,
*self.bouncer.get_accumulated_weights(),
))
let mut block_state = self.block_state.take().expect(BLOCK_STATE_ACCESS_ERR);
let state_diff = if self.block_context.versioned_constants.enable_stateful_compression {
state_diff_with_allocating_aliases(&mut block_state)?
} else {
block_state.to_state_diff()?.state_maps
};
Ok((state_diff.into(), visited_segments, *self.bouncer.get_accumulated_weights()))
}
}

Expand Down
42 changes: 40 additions & 2 deletions crates/blockifier/src/state/stateful_compression.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::collections::HashMap;
use std::collections::{BTreeSet, HashMap};

use starknet_api::core::{ContractAddress, PatriciaKey};
use starknet_api::state::StorageKey;
use starknet_types_core::felt::Felt;

use super::cached_state::{CachedState, StorageEntry};
use super::cached_state::{CachedState, StateMaps, StorageEntry};
use super::state_api::{StateReader, StateResult};

#[cfg(test)]
Expand All @@ -18,10 +18,48 @@ type AliasKey = StorageKey;
const ALIAS_CONTRACT_ADDRESS: ContractAddress = ContractAddress::new(Felt::TWO);
// The storage key of the alias counter in the alias contract.
const ALIAS_COUNTER_STORAGE_KEY: StorageKey = StorageKey(PatriciaKey::new_unchecked(Felt::ZERO));
// The maximal contract address for which aliases are not used and all keys are serialized as is,
// without compression.
const MAX_NON_COMPRESSED_CONTRACT_ADDRESS: ContractAddress =
ContractAddress::new(Felt::from_hex_unchecked("0xf"));
// The minimal value for a key to be allocated an alias. Smaller keys are serialized as is (their
// alias is identical to the key).
const MIN_VALUE_FOR_ALIAS_ALLOC: Felt = Felt::from_hex_unchecked("0x80");

/// Allocates aliases for the new addresses and storage keys in the alias contract.
/// Iterates over the addresses in ascending order. For each address, sets an alias for the new
/// storage keys (in ascending order) and for the address itself.
pub fn state_diff_with_allocating_aliases<S: StateReader>(
state: &mut CachedState<S>,
) -> StateResult<StateMaps> {
let mut state_diff = state.to_state_diff()?.state_maps;

// Collect the addresses and the storage keys that need aliases.
let addresses: BTreeSet<ContractAddress> =
state_diff.get_modified_contracts().into_iter().collect();
let mut sorted_storage_keys = HashMap::new();
for (address, storage_key) in state_diff.storage.keys() {
if address > &MAX_NON_COMPRESSED_CONTRACT_ADDRESS {
sorted_storage_keys.entry(address).or_insert_with(BTreeSet::new).insert(storage_key);
}
}

// Iterate over the addresses and the storage keys and update the aliases.
let mut alias_updater = AliasUpdater::new(state)?;
for address in addresses {
if let Some(&ref storage_keys) = sorted_storage_keys.get(&address) {
for key in storage_keys {
alias_updater.insert_alias(key)?;
}
}
alias_updater.insert_alias(&StorageKey(address.0))?;
}

let alias_storage_updates = alias_updater.finalize_updates();
state_diff.storage.extend(alias_storage_updates);
Ok(state_diff)
}

/// Generate updates for the alias contract with the new keys.
struct AliasUpdater<'a, S: StateReader> {
state: &'a CachedState<S>,
Expand Down
83 changes: 83 additions & 0 deletions crates/blockifier/src/state/stateful_compression_test.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
use std::collections::HashMap;

use rstest::rstest;
use starknet_api::core::{ClassHash, ContractAddress};
use starknet_api::state::StorageKey;
use starknet_types_core::felt::Felt;

use super::{
state_diff_with_allocating_aliases,
AliasUpdater,
ALIAS_CONTRACT_ADDRESS,
ALIAS_COUNTER_STORAGE_KEY,
MAX_NON_COMPRESSED_CONTRACT_ADDRESS,
MIN_VALUE_FOR_ALIAS_ALLOC,
};
use crate::state::cached_state::{CachedState, StorageEntry};
use crate::state::state_api::{State, StateReader};
use crate::test_utils::dict_state_reader::DictStateReader;

fn insert_to_alias_contract(
Expand Down Expand Up @@ -96,3 +100,82 @@ fn test_alias_updater(

assert_eq!(storage_diff, expected_storage_diff);
}

#[test]
fn test_iterate_aliases() {
let mut state = initial_state(0);
state
.set_storage_at(ContractAddress::from(0x201_u16), StorageKey::from(0x307_u16), Felt::ONE)
.unwrap();
state
.set_storage_at(ContractAddress::from(0x201_u16), StorageKey::from(0x309_u16), Felt::TWO)
.unwrap();
state
.set_storage_at(ContractAddress::from(0x201_u16), StorageKey::from(0x304_u16), Felt::THREE)
.unwrap();
state
.set_storage_at(MAX_NON_COMPRESSED_CONTRACT_ADDRESS, StorageKey::from(0x301_u16), Felt::ONE)
.unwrap();
state.get_class_hash_at(ContractAddress::from(0x202_u16)).unwrap();
state.set_class_hash_at(ContractAddress::from(0x202_u16), ClassHash(Felt::ONE)).unwrap();
state.increment_nonce(ContractAddress::from(0x200_u16)).unwrap();

let storage_diff = state_diff_with_allocating_aliases(&mut state).unwrap().storage;
assert_eq!(
storage_diff,
vec![
(
(ALIAS_CONTRACT_ADDRESS, ALIAS_COUNTER_STORAGE_KEY),
MIN_VALUE_FOR_ALIAS_ALLOC + Felt::from(6_u8)
),
((ALIAS_CONTRACT_ADDRESS, StorageKey::from(0x200_u16)), MIN_VALUE_FOR_ALIAS_ALLOC),
(
(ALIAS_CONTRACT_ADDRESS, StorageKey::from(0x304_u16)),
MIN_VALUE_FOR_ALIAS_ALLOC + Felt::ONE
),
(
(ALIAS_CONTRACT_ADDRESS, StorageKey::from(0x307_u16)),
MIN_VALUE_FOR_ALIAS_ALLOC + Felt::TWO
),
(
(ALIAS_CONTRACT_ADDRESS, StorageKey::from(0x309_u16)),
MIN_VALUE_FOR_ALIAS_ALLOC + Felt::THREE
),
(
(ALIAS_CONTRACT_ADDRESS, StorageKey::from(0x201_u16)),
MIN_VALUE_FOR_ALIAS_ALLOC + Felt::from(4_u8)
),
(
(ALIAS_CONTRACT_ADDRESS, StorageKey::from(0x202_u16)),
MIN_VALUE_FOR_ALIAS_ALLOC + Felt::from(5_u8)
),
((ContractAddress::from(0x201_u16), StorageKey::from(0x304_u16)), Felt::THREE),
((ContractAddress::from(0x201_u16), StorageKey::from(0x307_u16)), Felt::ONE),
((ContractAddress::from(0x201_u16), StorageKey::from(0x309_u16)), Felt::TWO),
((MAX_NON_COMPRESSED_CONTRACT_ADDRESS, StorageKey::from(0x301_u16)), Felt::ONE),
]
.into_iter()
.collect()
);
}

#[rstest]
fn test_read_only_state(#[values(0, 2)] n_existing_aliases: u8) {
let mut state = initial_state(n_existing_aliases);
state
.set_storage_at(ContractAddress::from(0x200_u16), StorageKey::from(0x300_u16), Felt::ZERO)
.unwrap();
state.get_nonce_at(ContractAddress::from(0x201_u16)).unwrap();
state.get_class_hash_at(ContractAddress::from(0x202_u16)).unwrap();
let storage_diff = state_diff_with_allocating_aliases(&mut state).unwrap().storage;

let expected_storage_diff = if n_existing_aliases == 0 {
HashMap::from([(
(ALIAS_CONTRACT_ADDRESS, ALIAS_COUNTER_STORAGE_KEY),
MIN_VALUE_FOR_ALIAS_ALLOC,
)])
} else {
HashMap::new()
};
assert_eq!(storage_diff, expected_storage_diff);
}

0 comments on commit 937947e

Please sign in to comment.