diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs index 9a39d03860..6ec89dc4cb 100644 --- a/crates/common/src/lib.rs +++ b/crates/common/src/lib.rs @@ -42,6 +42,17 @@ impl ContractAddress { /// It is used by starknet to store values for smart contracts to access /// using syscalls. For example the block hash. pub const ONE: ContractAddress = contract_address!("0x1"); + /// The contract at 0x2 was introduced in Starknet version 0.13.4. It is + /// used for stateful compression: + /// - storage key 0 points to the global counter, which is the base for + /// index values in the next block, + /// - other storage k-v pairs store the mapping of key to index, + /// - the global counter starts at value 0x80 in the first block from + /// 0.13.4, + /// - keys of value lower than 0x80 are not indexed. + pub const TWO: ContractAddress = contract_address!("0x2"); + /// Useful for iteration over the system contracts + pub const SYSTEM: [ContractAddress; 2] = [ContractAddress::ONE, ContractAddress::TWO]; } // Bytecode and entry point list of a class @@ -595,7 +606,7 @@ impl ContractAddress { } pub fn is_system_contract(&self) -> bool { - *self == ContractAddress::ONE + (*self == ContractAddress::ONE) || (*self == ContractAddress::TWO) } } diff --git a/crates/gateway-types/src/reply.rs b/crates/gateway-types/src/reply.rs index bdba1da3b9..34914148af 100644 --- a/crates/gateway-types/src/reply.rs +++ b/crates/gateway-types/src/reply.rs @@ -2056,17 +2056,19 @@ impl From for pathfinder_common::StateUpdate { // This must occur before we map the contract updates, since we want to first // remove the system contract updates. // - // Currently this is only the contract at address 0x1. + // Currently there are two such contracts, at addresses 0x1 and 0x2. // - // As of starknet v0.12.0 these are embedded in this way, but in the future will + // As of starknet v0.13.4 these are embedded in this way, but in the future will // be a separate property in the state diff. - if let Some((address, storage_updates)) = gateway - .state_diff - .storage_diffs - .remove_entry(&ContractAddress::ONE) - { - for state_update::StorageDiff { key, value } in storage_updates { - state_update = state_update.with_system_storage_update(address, key, value); + for system_contract in ContractAddress::SYSTEM.iter() { + if let Some((address, storage_updates)) = gateway + .state_diff + .storage_diffs + .remove_entry(system_contract) + { + for state_update::StorageDiff { key, value } in storage_updates { + state_update = state_update.with_system_storage_update(address, key, value); + } } } diff --git a/crates/merkle-tree/src/contract_state.rs b/crates/merkle-tree/src/contract_state.rs index 710bd82926..d7eb0a3f4d 100644 --- a/crates/merkle-tree/src/contract_state.rs +++ b/crates/merkle-tree/src/contract_state.rs @@ -86,8 +86,8 @@ pub fn update_contract_state( }; let class_hash = if contract_address.is_system_contract() { - // This is a special system contract at address 0x1, which doesn't have a class - // hash. + // This is a special system contract at address 0x1 or 0x2, which doesn't have a + // class hash. ClassHash::ZERO } else if let Some(class_hash) = new_class_hash { class_hash diff --git a/crates/p2p/src/client/peer_agnostic.rs b/crates/p2p/src/client/peer_agnostic.rs index ba539720da..7fd5f3efb8 100644 --- a/crates/p2p/src/client/peer_agnostic.rs +++ b/crates/p2p/src/client/peer_agnostic.rs @@ -395,7 +395,7 @@ impl BlockClient for Client { } } let address = ContractAddress(address.0); - if address == ContractAddress::ONE { + if address.is_system_contract() { let storage = &mut state_diff .system_contract_updates .entry(address) @@ -1046,7 +1046,7 @@ mod state_diff_stream { progress.checked_sub_assign(values.len())?; - if address == ContractAddress::ONE { + if address.is_system_contract() { let storage = &mut state_diff .system_contract_updates .entry(address) diff --git a/crates/p2p/src/client/peer_agnostic/fixtures.rs b/crates/p2p/src/client/peer_agnostic/fixtures.rs index e338a89e75..bd2a290b28 100644 --- a/crates/p2p/src/client/peer_agnostic/fixtures.rs +++ b/crates/p2p/src/client/peer_agnostic/fixtures.rs @@ -32,6 +32,7 @@ use pathfinder_common::{ TransactionHash, TransactionIndex, }; +use rand::seq::SliceRandom; use tagged::Tagged; use tagged_debug_derive::TaggedDebug; use tokio::sync::Mutex; @@ -249,6 +250,7 @@ pub fn state_diff(tag: i32) -> StateUpdateData { Some(x) => ([].into(), [(SierraHash(Faker.fake()), x)].into()), None => ([ClassHash(Faker.fake())].into(), [].into()), }; + let (contract_updates, system_contract_updates) = if Faker.fake() { ( [( @@ -263,8 +265,18 @@ pub fn state_diff(tag: i32) -> StateUpdateData { [].into(), ) } else { - ([].into(), [(ContractAddress::ONE, Faker.fake())].into()) + ( + [].into(), + [( + *ContractAddress::SYSTEM + .choose(&mut rand::thread_rng()) + .unwrap(), + Faker.fake(), + )] + .into(), + ) }; + Tagged::get(format!("state diff {tag}"), || StateUpdateData { contract_updates, system_contract_updates, diff --git a/crates/pathfinder/src/p2p_network/sync_handlers/tests.rs b/crates/pathfinder/src/p2p_network/sync_handlers/tests.rs index 1556325c78..36f1cf4468 100644 --- a/crates/pathfinder/src/p2p_network/sync_handlers/tests.rs +++ b/crates/pathfinder/src/p2p_network/sync_handlers/tests.rs @@ -235,7 +235,7 @@ mod prop { } proptest! { - #![proptest_config(ProptestConfig::with_cases(25))] + #![proptest_config(ProptestConfig::with_cases(1))] #[test] fn get_state_diffs((num_blocks, seed, start_block, limit, step, direction) in strategy::composite()) { // Fake storage with a given number of blocks @@ -274,10 +274,11 @@ mod prop { // Check the rest responses.into_iter().for_each(|response| match response { StateDiffsResponse::ContractDiff(ContractDiff { address, nonce, class_hash, values, domain: _ }) => { - if address.0 == Felt::from_u64(1) { + let contract_address = ContractAddress(address.0); + if contract_address.is_system_contract() { actual_system_contract_updates.push( ( - ContractAddress(address.0), + contract_address, SystemContractUpdate { storage: values.into_iter().map( |ContractStoredValue { key, value }| (StorageAddress(key), StorageValue(value))).collect()} @@ -285,7 +286,7 @@ mod prop { } else { actual_contract_updates.push( ( - ContractAddress(address.0), + contract_address, ContractUpdate { storage: values.into_iter().map(|ContractStoredValue { key, value }| (StorageAddress(key), StorageValue(value))).collect(), diff --git a/crates/storage/src/connection/state_update.rs b/crates/storage/src/connection/state_update.rs index aa99cf022e..a35eb816d1 100644 --- a/crates/storage/src/connection/state_update.rs +++ b/crates/storage/src/connection/state_update.rs @@ -364,7 +364,7 @@ impl Transaction<'_> { .transpose() .context("Iterating over storage query rows")? { - state_update = if address == ContractAddress::ONE { + state_update = if address.is_system_contract() { state_update.with_system_storage_update(address, key, value) } else { state_update.with_storage_update(address, key, value) @@ -1214,8 +1214,13 @@ mod tests { ) .with_system_storage_update( ContractAddress::ONE, - storage_address_bytes!(b"key"), - storage_value_bytes!(b"value"), + storage_address_bytes!(b"key 1"), + storage_value_bytes!(b"value 1"), + ) + .with_system_storage_update( + ContractAddress::TWO, + storage_address_bytes!(b"key 2"), + storage_value_bytes!(b"value 2"), ) .with_deployed_contract( contract_address_bytes!(b"contract addr 2"), @@ -1308,7 +1313,18 @@ mod tests { ContractAddress::ONE, ReverseContractUpdate::Updated(ContractUpdate { storage: HashMap::from([( - storage_address_bytes!(b"key"), + storage_address_bytes!(b"key 1"), + StorageValue::ZERO + )]), + nonce: None, + class: None + }) + ), + ( + ContractAddress::TWO, + ReverseContractUpdate::Updated(ContractUpdate { + storage: HashMap::from([( + storage_address_bytes!(b"key 2"), StorageValue::ZERO )]), nonce: None, diff --git a/crates/storage/src/fake.rs b/crates/storage/src/fake.rs index a2160ae1c8..c78c7ab2cf 100644 --- a/crates/storage/src/fake.rs +++ b/crates/storage/src/fake.rs @@ -411,12 +411,20 @@ pub mod generate { declared_cairo_classes, declared_sierra_classes, system_contract_updates: if occurrence.system_storage.contains(&1) { - Some(( - ContractAddress::ONE, - SystemContractUpdate { - storage: fake_non_empty_with_rng(rng), - }, - )) + [ + ( + ContractAddress::ONE, + SystemContractUpdate { + storage: fake_non_empty_with_rng(rng), + }, + ), + ( + ContractAddress::TWO, + SystemContractUpdate { + storage: fake_non_empty_with_rng(rng), + }, + ), + ] .into_iter() .collect() } else {