Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into fix-issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Subsegment committed Apr 28, 2024
2 parents 5851ce5 + 814ed90 commit a1edb17
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 126 deletions.
28 changes: 12 additions & 16 deletions crates/blockifier/src/concurrency/versioned_state_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use starknet_api::state::StorageKey;
use crate::concurrency::versioned_storage::VersionedStorage;
use crate::concurrency::TxIndex;
use crate::execution::contract_class::ContractClass;
use crate::state::cached_state::{ContractClassMapping, StateCache};
use crate::state::cached_state::{ContractClassMapping, StateMaps};
use crate::state::state_api::{State, StateReader, StateResult};

#[cfg(test)]
Expand Down Expand Up @@ -42,21 +42,17 @@ impl<S: StateReader> VersionedState<S> {
}
}

// Note: Invoke this function after `update_initial_values_of_write_only_access`.
// Transactions that overwrite previously written values are not charged. Hence, altering a
// write-only cell can impact the fee calculation, leading to a re-execution.
// TODO(Mohammad, 01/04/2024): Store the read set (and write set) within a shared
// object (probabily `VersionedState`). As RefCell operations are not thread-safe. Therefore,
// accessing this function should be protected by a mutex to ensure thread safety.
pub fn validate_read_set(&mut self, tx_index: TxIndex, state_cache: &mut StateCache) -> bool {
pub fn validate_read_set(&mut self, tx_index: TxIndex, reads: &StateMaps) -> bool {
// If is the first transaction in the chunk, then the read set is valid. Since it has no
// predecessors, there's nothing to compare it to.
if tx_index == 0 {
return true;
}
for (&(contract_address, storage_key), expected_value) in
&state_cache.storage_initial_values
{

for (&(contract_address, storage_key), expected_value) in &reads.storage {
let value =
self.storage.read(tx_index, (contract_address, storage_key)).expect(READ_ERR);

Expand All @@ -65,15 +61,15 @@ impl<S: StateReader> VersionedState<S> {
}
}

for (&contract_address, expected_value) in &state_cache.nonce_initial_values {
for (&contract_address, expected_value) in &reads.nonces {
let value = self.nonces.read(tx_index, contract_address).expect(READ_ERR);

if &value != expected_value {
return false;
}
}

for (&contract_address, expected_value) in &state_cache.class_hash_initial_values {
for (&contract_address, expected_value) in &reads.class_hashes {
let value = self.class_hashes.read(tx_index, contract_address).expect(READ_ERR);

if &value != expected_value {
Expand All @@ -82,7 +78,7 @@ impl<S: StateReader> VersionedState<S> {
}

// Added for symmetry. We currently do not update this initial mapping.
for (&class_hash, expected_value) in &state_cache.compiled_class_hash_initial_values {
for (&class_hash, expected_value) in &reads.compiled_class_hashes {
let value = self.compiled_class_hashes.read(tx_index, class_hash).expect(READ_ERR);

if &value != expected_value {
Expand All @@ -100,19 +96,19 @@ impl<S: StateReader> VersionedState<S> {
pub fn apply_writes(
&mut self,
tx_index: TxIndex,
state_cache: &mut StateCache,
writes: &StateMaps,
class_hash_to_class: ContractClassMapping,
) {
for (&key, &value) in &state_cache.storage_writes {
for (&key, &value) in &writes.storage {
self.storage.write(tx_index, key, value);
}
for (&key, &value) in &state_cache.nonce_writes {
for (&key, &value) in &writes.nonces {
self.nonces.write(tx_index, key, value);
}
for (&key, &value) in &state_cache.class_hash_writes {
for (&key, &value) in &writes.class_hashes {
self.class_hashes.write(tx_index, key, value);
}
for (&key, &value) in &state_cache.compiled_class_hash_writes {
for (&key, &value) in &writes.compiled_class_hashes {
self.compiled_class_hashes.write(tx_index, key, value);
}
for (key, value) in class_hash_to_class {
Expand Down
151 changes: 60 additions & 91 deletions crates/blockifier/src/state/cached_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,7 @@ impl<S: StateReader> CachedState<S> {

pub fn update_cache(&mut self, cache_updates: StateCache) {
let mut cache = self.cache.borrow_mut();

cache.nonce_writes.extend(cache_updates.nonce_writes);
cache.class_hash_writes.extend(cache_updates.class_hash_writes);
cache.storage_writes.extend(cache_updates.storage_writes);
cache.compiled_class_hash_writes.extend(cache_updates.compiled_class_hash_writes);
cache.writes.extend(&cache_updates.writes);
}

pub fn update_contract_class_cache(
Expand All @@ -100,30 +96,32 @@ impl<S: StateReader> CachedState<S> {

// Eliminate storage writes that are identical to the initial value (no change). Assumes
// that `set_storage_at` does not affect the state field.
for contract_storage_key in cache.storage_writes.keys() {
if !cache.storage_initial_values.contains_key(contract_storage_key) {
for contract_storage_key in cache.writes.storage.keys() {
if !cache.initial_reads.storage.contains_key(contract_storage_key) {
// First access to this cell was write; cache initial value.
cache.storage_initial_values.insert(
cache.initial_reads.storage.insert(
*contract_storage_key,
self.state.get_storage_at(contract_storage_key.0, contract_storage_key.1)?,
);
}
}

for contract_address in cache.class_hash_writes.keys() {
if !cache.class_hash_initial_values.contains_key(contract_address) {
for contract_address in cache.writes.class_hashes.keys() {
if !cache.initial_reads.class_hashes.contains_key(contract_address) {
// First access to this cell was write; cache initial value.
cache
.class_hash_initial_values
.initial_reads
.class_hashes
.insert(*contract_address, self.state.get_class_hash_at(*contract_address)?);
}
}

for contract_address in cache.nonce_writes.keys() {
if !cache.nonce_initial_values.contains_key(contract_address) {
for contract_address in cache.writes.nonces.keys() {
if !cache.initial_reads.nonces.contains_key(contract_address) {
// First access to this cell was write; cache initial value.
cache
.nonce_initial_values
.initial_reads
.nonces
.insert(*contract_address, self.state.get_nonce_at(*contract_address)?);
}
}
Expand All @@ -142,7 +140,7 @@ impl<S: StateReader> CachedState<S> {
let class_hash_updates = state_cache.get_class_hash_updates();
let storage_diffs = state_cache.get_storage_updates();
let nonces = state_cache.get_nonce_updates();
let declared_classes = state_cache.compiled_class_hash_writes.clone();
let declared_classes = state_cache.writes.compiled_class_hashes.clone();

CommitmentStateDiff {
address_to_class_hash: IndexMap::from_iter(class_hash_updates),
Expand Down Expand Up @@ -348,34 +346,44 @@ impl From<StorageView> for IndexMap<ContractAddress, IndexMap<StorageKey, StarkF
}
}

#[derive(Debug, Default, PartialEq, Eq)]
pub struct StateMaps {
pub(crate) nonces: HashMap<ContractAddress, Nonce>,
pub(crate) class_hashes: HashMap<ContractAddress, ClassHash>,
pub(crate) storage: HashMap<StorageEntry, StarkFelt>,
pub(crate) compiled_class_hashes: HashMap<ClassHash, CompiledClassHash>,
pub(crate) declared_contracts: HashMap<ClassHash, bool>,
}

impl StateMaps {
pub fn extend(&mut self, other: &Self) {
self.nonces.extend(&other.nonces);
self.class_hashes.extend(&other.class_hashes);
self.storage.extend(&other.storage);
self.compiled_class_hashes.extend(&other.compiled_class_hashes);
self.declared_contracts.extend(&other.declared_contracts)
}
}
/// Caches read and write requests.
/// The tracked changes are needed for block state commitment.
// Invariant: keys cannot be deleted from fields (only used internally by the cached state).
#[derive(Debug, Default, PartialEq, Eq)]
pub struct StateCache {
// Reader's cached information; initial values, read before any write operation (per cell).
pub(crate) nonce_initial_values: HashMap<ContractAddress, Nonce>,
pub(crate) class_hash_initial_values: HashMap<ContractAddress, ClassHash>,
pub(crate) storage_initial_values: HashMap<StorageEntry, StarkFelt>,
pub(crate) compiled_class_hash_initial_values: HashMap<ClassHash, CompiledClassHash>,
pub(crate) declared_contract_initial_values: HashMap<ClassHash, bool>,
pub(crate) initial_reads: StateMaps,

// Writer's cached information.
pub(crate) nonce_writes: HashMap<ContractAddress, Nonce>,
pub(crate) class_hash_writes: HashMap<ContractAddress, ClassHash>,
pub(crate) storage_writes: HashMap<StorageEntry, StarkFelt>,
pub(crate) compiled_class_hash_writes: HashMap<ClassHash, CompiledClassHash>,
pub(crate) declared_contract_writes: HashMap<ClassHash, bool>,
pub(crate) writes: StateMaps,
}

impl StateCache {
fn declare_contract(&mut self, class_hash: ClassHash) {
self.declared_contract_writes.insert(class_hash, true);
self.writes.declared_contracts.insert(class_hash, true);
}

fn set_declared_contract_initial_values(&mut self, class_hash: ClassHash, is_declared: bool) {
self.declared_contract_initial_values.insert(class_hash, is_declared);
self.initial_reads.declared_contracts.insert(class_hash, is_declared);
}

fn get_storage_at(
Expand All @@ -384,15 +392,17 @@ impl StateCache {
key: StorageKey,
) -> Option<&StarkFelt> {
let contract_storage_key = (contract_address, key);
self.storage_writes
self.writes
.storage
.get(&contract_storage_key)
.or_else(|| self.storage_initial_values.get(&contract_storage_key))
.or_else(|| self.initial_reads.storage.get(&contract_storage_key))
}

fn get_nonce_at(&self, contract_address: ContractAddress) -> Option<&Nonce> {
self.nonce_writes
self.writes
.nonces
.get(&contract_address)
.or_else(|| self.nonce_initial_values.get(&contract_address))
.or_else(|| self.initial_reads.nonces.get(&contract_address))
}

pub fn set_storage_initial_value(
Expand All @@ -402,7 +412,7 @@ impl StateCache {
value: StarkFelt,
) {
let contract_storage_key = (contract_address, key);
self.storage_initial_values.insert(contract_storage_key, value);
self.initial_reads.storage.insert(contract_storage_key, value);
}

fn set_storage_value(
Expand All @@ -412,67 +422,69 @@ impl StateCache {
value: StarkFelt,
) {
let contract_storage_key = (contract_address, key);
self.storage_writes.insert(contract_storage_key, value);
self.writes.storage.insert(contract_storage_key, value);
}

fn set_nonce_initial_value(&mut self, contract_address: ContractAddress, nonce: Nonce) {
self.nonce_initial_values.insert(contract_address, nonce);
self.initial_reads.nonces.insert(contract_address, nonce);
}

fn set_nonce_value(&mut self, contract_address: ContractAddress, nonce: Nonce) {
self.nonce_writes.insert(contract_address, nonce);
self.writes.nonces.insert(contract_address, nonce);
}

fn get_class_hash_at(&self, contract_address: ContractAddress) -> Option<&ClassHash> {
self.class_hash_writes
self.writes
.class_hashes
.get(&contract_address)
.or_else(|| self.class_hash_initial_values.get(&contract_address))
.or_else(|| self.initial_reads.class_hashes.get(&contract_address))
}

fn set_class_hash_initial_value(
&mut self,
contract_address: ContractAddress,
class_hash: ClassHash,
) {
self.class_hash_initial_values.insert(contract_address, class_hash);
self.initial_reads.class_hashes.insert(contract_address, class_hash);
}

fn set_class_hash_write(&mut self, contract_address: ContractAddress, class_hash: ClassHash) {
self.class_hash_writes.insert(contract_address, class_hash);
self.writes.class_hashes.insert(contract_address, class_hash);
}

fn get_compiled_class_hash(&self, class_hash: ClassHash) -> Option<&CompiledClassHash> {
self.compiled_class_hash_writes
self.writes
.compiled_class_hashes
.get(&class_hash)
.or_else(|| self.compiled_class_hash_initial_values.get(&class_hash))
.or_else(|| self.initial_reads.compiled_class_hashes.get(&class_hash))
}

fn set_compiled_class_hash_initial_value(
&mut self,
class_hash: ClassHash,
compiled_class_hash: CompiledClassHash,
) {
self.compiled_class_hash_initial_values.insert(class_hash, compiled_class_hash);
self.initial_reads.compiled_class_hashes.insert(class_hash, compiled_class_hash);
}

fn set_compiled_class_hash_write(
&mut self,
class_hash: ClassHash,
compiled_class_hash: CompiledClassHash,
) {
self.compiled_class_hash_writes.insert(class_hash, compiled_class_hash);
self.writes.compiled_class_hashes.insert(class_hash, compiled_class_hash);
}

fn get_storage_updates(&self) -> HashMap<StorageEntry, StarkFelt> {
strict_subtract_mappings(&self.storage_writes, &self.storage_initial_values)
strict_subtract_mappings(&self.writes.storage, &self.initial_reads.storage)
}

fn get_class_hash_updates(&self) -> HashMap<ContractAddress, ClassHash> {
strict_subtract_mappings(&self.class_hash_writes, &self.class_hash_initial_values)
strict_subtract_mappings(&self.writes.class_hashes, &self.initial_reads.class_hashes)
}

fn get_nonce_updates(&self) -> HashMap<ContractAddress, Nonce> {
strict_subtract_mappings(&self.nonce_writes, &self.nonce_initial_values)
strict_subtract_mappings(&self.writes.nonces, &self.initial_reads.nonces)
}

fn get_compiled_class_hash_updates(&self) -> HashMap<ClassHash, CompiledClassHash> {
Expand All @@ -482,8 +494,8 @@ impl StateCache {
// class hash writes keys are not a subset of compiled class hash initial values keys.

subtract_mappings(
&self.compiled_class_hash_writes,
&self.compiled_class_hash_initial_values,
&self.writes.compiled_class_hashes,
&self.initial_reads.compiled_class_hashes,
)
}
}
Expand Down Expand Up @@ -525,49 +537,6 @@ impl<'a, S: State + ?Sized> StateReader for MutRefState<'a, S> {
}
}

impl<'a, S: State + ?Sized> State for MutRefState<'a, S> {
fn set_storage_at(
&mut self,
contract_address: ContractAddress,
key: StorageKey,
value: StarkFelt,
) -> StateResult<()> {
self.0.set_storage_at(contract_address, key, value)
}

fn increment_nonce(&mut self, contract_address: ContractAddress) -> StateResult<()> {
self.0.increment_nonce(contract_address)
}

fn set_class_hash_at(
&mut self,
contract_address: ContractAddress,
class_hash: ClassHash,
) -> StateResult<()> {
self.0.set_class_hash_at(contract_address, class_hash)
}

fn set_contract_class(
&mut self,
class_hash: ClassHash,
contract_class: ContractClass,
) -> StateResult<()> {
self.0.set_contract_class(class_hash, contract_class)
}

fn set_compiled_class_hash(
&mut self,
class_hash: ClassHash,
compiled_class_hash: CompiledClassHash,
) -> StateResult<()> {
self.0.set_compiled_class_hash(class_hash, compiled_class_hash)
}

fn add_visited_pcs(&mut self, class_hash: ClassHash, pcs: &HashSet<usize>) {
self.0.add_visited_pcs(class_hash, pcs)
}
}

pub type TransactionalState<'a, S> = CachedState<MutRefState<'a, CachedState<S>>>;

/// Adds the ability to perform a transactional execution.
Expand Down
Loading

0 comments on commit a1edb17

Please sign in to comment.