use std::sync::LazyLock;
use indexmap::IndexMap;
use starknet_types_core::felt::Felt;
use crate::core::{
ascii_as_felt,
ClassHash,
CompiledClassHash,
ContractAddress,
Nonce,
StateDiffCommitment,
};
use crate::crypto::utils::HashChain;
use crate::hash::PoseidonHash;
use crate::state::{StorageKey, ThinStateDiff};
#[cfg(test)]
#[path = "state_diff_hash_test.rs"]
mod state_diff_hash_test;
static STARKNET_STATE_DIFF0: LazyLock<Felt> = LazyLock::new(|| {
ascii_as_felt("STARKNET_STATE_DIFF0").expect("ascii_as_felt failed for 'STARKNET_STATE_DIFF0'")
});
pub fn calculate_state_diff_hash(state_diff: &ThinStateDiff) -> StateDiffCommitment {
let mut hash_chain = HashChain::new();
hash_chain = hash_chain.chain(&STARKNET_STATE_DIFF0);
hash_chain = chain_deployed_contracts(&state_diff.deployed_contracts, hash_chain);
hash_chain = chain_declared_classes(&state_diff.class_hash_to_compiled_class_hash, hash_chain);
hash_chain =
chain_deprecated_declared_classes(&state_diff.deprecated_declared_classes, hash_chain);
hash_chain = hash_chain.chain(&Felt::ONE) .chain(&Felt::ZERO); hash_chain = chain_storage_diffs(&state_diff.storage_diffs, hash_chain);
hash_chain = chain_nonces(&state_diff.nonces, hash_chain);
StateDiffCommitment(PoseidonHash(hash_chain.get_poseidon_hash()))
}
fn chain_deployed_contracts(
deployed_contracts: &IndexMap<ContractAddress, ClassHash>,
mut hash_chain: HashChain,
) -> HashChain {
hash_chain = hash_chain.chain(&deployed_contracts.len().into());
for (address, class_hash) in sorted_index_map(&deployed_contracts.iter().collect()) {
hash_chain = hash_chain.chain(&address.0).chain(class_hash);
}
hash_chain
}
fn chain_declared_classes(
declared_classes: &IndexMap<ClassHash, CompiledClassHash>,
mut hash_chain: HashChain,
) -> HashChain {
hash_chain = hash_chain.chain(&declared_classes.len().into());
for (class_hash, compiled_class_hash) in sorted_index_map(declared_classes) {
hash_chain = hash_chain.chain(&class_hash).chain(&compiled_class_hash.0)
}
hash_chain
}
fn chain_deprecated_declared_classes(
deprecated_declared_classes: &[ClassHash],
hash_chain: HashChain,
) -> HashChain {
let mut sorted_deprecated_declared_classes = deprecated_declared_classes.to_vec();
sorted_deprecated_declared_classes.sort_unstable();
hash_chain
.chain(&sorted_deprecated_declared_classes.len().into())
.chain_iter(sorted_deprecated_declared_classes.iter().map(|class_hash| &class_hash.0))
}
fn chain_storage_diffs(
storage_diffs: &IndexMap<ContractAddress, IndexMap<StorageKey, Felt>>,
hash_chain: HashChain,
) -> HashChain {
let mut n_updated_contracts = 0_u64;
let mut storage_diffs_chain = HashChain::new();
for (contract_address, key_value_map) in sorted_index_map(storage_diffs) {
if key_value_map.is_empty() {
continue;
}
n_updated_contracts += 1;
storage_diffs_chain = storage_diffs_chain.chain(&contract_address);
storage_diffs_chain = storage_diffs_chain.chain(&key_value_map.len().into());
for (key, value) in sorted_index_map(&key_value_map) {
storage_diffs_chain = storage_diffs_chain.chain(&key).chain(&value);
}
}
hash_chain.chain(&n_updated_contracts.into()).extend(storage_diffs_chain)
}
fn chain_nonces(nonces: &IndexMap<ContractAddress, Nonce>, mut hash_chain: HashChain) -> HashChain {
hash_chain = hash_chain.chain(&nonces.len().into());
for (contract_address, nonce) in sorted_index_map(nonces) {
hash_chain = hash_chain.chain(&contract_address);
hash_chain = hash_chain.chain(&nonce);
}
hash_chain
}
fn sorted_index_map<K: Clone + std::cmp::Ord, V: Clone>(map: &IndexMap<K, V>) -> IndexMap<K, V> {
let mut sorted_map = map.clone();
sorted_map.sort_unstable_keys();
sorted_map
}