diff --git a/crates/katana/core/src/backend/mod.rs b/crates/katana/core/src/backend/mod.rs index a50cf6e384..f2a6d5f906 100644 --- a/crates/katana/core/src/backend/mod.rs +++ b/crates/katana/core/src/backend/mod.rs @@ -3,7 +3,8 @@ use std::sync::Arc; use gas_oracle::L1GasOracle; use katana_executor::{ExecutionOutput, ExecutionResult, ExecutorFactory}; use katana_primitives::block::{ - FinalityStatus, Header, PartialHeader, SealedBlock, SealedBlockWithStatus, + BlockHash, BlockNumber, FinalityStatus, Header, PartialHeader, SealedBlock, + SealedBlockWithStatus, }; use katana_primitives::chain_spec::ChainSpec; use katana_primitives::da::L1DataAvailabilityMode; @@ -11,7 +12,7 @@ use katana_primitives::env::BlockEnv; use katana_primitives::receipt::{Event, ReceiptWithTxHash}; use katana_primitives::state::{compute_state_diff_hash, StateUpdates}; use katana_primitives::transaction::{TxHash, TxWithHash}; -use katana_primitives::Felt; +use katana_primitives::{address, ContractAddress, Felt}; use katana_provider::traits::block::{BlockHashProvider, BlockWriter}; use katana_provider::traits::trie::TrieWriter; use katana_trie::compute_merkle_root; @@ -49,7 +50,7 @@ impl Backend { pub fn do_mine_block( &self, block_env: &BlockEnv, - execution_output: ExecutionOutput, + mut execution_output: ExecutionOutput, ) -> Result { // we optimistically allocate the maximum amount possible let mut txs = Vec::with_capacity(execution_output.transactions.len()); @@ -68,6 +69,12 @@ impl Backend { let tx_count = txs.len() as u32; let tx_hashes = txs.iter().map(|tx| tx.hash).collect::>(); + // Update special contract address 0x1 + self.update_block_hash_registry_contract( + &mut execution_output.states.state_updates, + block_env.number, + )?; + // create a new block and compute its commitment let block = self.commit_block( block_env.clone(), @@ -93,6 +100,35 @@ impl Backend { Ok(MinedBlockOutcome { block_number, txs: tx_hashes, stats: execution_output.stats }) } + // TODO: create a dedicated struct for this contract. + // https://docs.starknet.io/architecture-and-concepts/network-architecture/starknet-state/#address_0x1 + fn update_block_hash_registry_contract( + &self, + state_updates: &mut StateUpdates, + block_number: BlockNumber, + ) -> Result<(), BlockProductionError> { + const STORED_BLOCK_HASH_BUFFER: u64 = 10; + + if block_number >= STORED_BLOCK_HASH_BUFFER { + let block_number = block_number - STORED_BLOCK_HASH_BUFFER; + let block_hash = self.blockchain.provider().block_hash_by_num(block_number)?; + + // When in forked mode, we might not have the older block hash in the database. This + // could be the case where the `block_number - STORED_BLOCK_HASH_BUFFER` is + // earlier than the forked block, which right now, Katana doesn't + // yet have the ability to fetch older blocks on the database level. So, we default to + // `BlockHash::ZERO` in this case. + // + // TODO: Fix quick! + let block_hash = block_hash.unwrap_or(BlockHash::ZERO); + + let storages = state_updates.storage_updates.entry(address!("0x1")).or_default(); + storages.insert(block_number.into(), block_hash); + } + + Ok(()) + } + pub fn update_block_env(&self, block_env: &mut BlockEnv) { let mut context_gen = self.block_context_generator.write(); let current_timestamp_secs = get_current_timestamp().as_secs() as i64; diff --git a/crates/katana/rpc/rpc/src/starknet/mod.rs b/crates/katana/rpc/rpc/src/starknet/mod.rs index 520b3d507c..3828b2c332 100644 --- a/crates/katana/rpc/rpc/src/starknet/mod.rs +++ b/crates/katana/rpc/rpc/src/starknet/mod.rs @@ -280,6 +280,12 @@ where contract_address: ContractAddress, ) -> StarknetApiResult { self.on_io_blocking_task(move |this| { + // Contract address 0x1 is special system contract and does not + // have a class. See https://docs.starknet.io/architecture-and-concepts/network-architecture/starknet-state/#address_0x1. + if contract_address.0 == Felt::ONE { + return Ok(ClassHash::ZERO); + } + let state = this.state(&block_id)?; let class_hash = state.class_hash_of_contract(contract_address)?; class_hash.ok_or(StarknetApiError::ContractNotFound) @@ -305,10 +311,14 @@ where ) -> StarknetApiResult { let state = self.state(&block_id)?; - // check that contract exist by checking the class hash of the contract - let Some(_) = state.class_hash_of_contract(contract_address)? else { + // Check that contract exist by checking the class hash of the contract, + // unless its address 0x1 which is special system contract and does not + // have a class. See https://docs.starknet.io/architecture-and-concepts/network-architecture/starknet-state/#address_0x1. + if contract_address.0 != Felt::ONE + && state.class_hash_of_contract(contract_address)?.is_none() + { return Err(StarknetApiError::ContractNotFound); - }; + } let value = state.storage(contract_address, storage_key)?; Ok(value.unwrap_or_default()) diff --git a/crates/katana/storage/provider/tests/contract.rs b/crates/katana/storage/provider/tests/contract.rs index 0a7541f5d1..46339dfa5c 100644 --- a/crates/katana/storage/provider/tests/contract.rs +++ b/crates/katana/storage/provider/tests/contract.rs @@ -43,8 +43,8 @@ mod latest { #[rstest::rstest] #[case( vec![ - (ContractAddress::from(felt!("1")), Some(felt!("22")), Some(felt!("3"))), - (ContractAddress::from(felt!("2")), Some(felt!("33")), Some(felt!("2"))), + (ContractAddress::from(felt!("1337")), Some(felt!("22")), Some(felt!("3"))), + (ContractAddress::from(felt!("80085")), Some(felt!("33")), Some(felt!("2"))), ] )] fn test_latest_contract_info_read( @@ -95,31 +95,31 @@ mod historical { #[case::storage_at_block_0( 0, vec![ - (ContractAddress::from(felt!("1")), None, None), - (ContractAddress::from(felt!("2")), None, None) - ]) - ] + (ContractAddress::from(felt!("1337")), None, None), + (ContractAddress::from(felt!("80085")), None, None) + ]) +] #[case::storage_at_block_1( - 1, - vec![ - (ContractAddress::from(felt!("1")), Some(felt!("11")), Some(felt!("1"))), - (ContractAddress::from(felt!("2")), Some(felt!("11")), Some(felt!("1"))), - ]) - ] + 1, + vec![ + (ContractAddress::from(felt!("1337")), Some(felt!("11")), Some(felt!("1"))), + (ContractAddress::from(felt!("80085")), Some(felt!("11")), Some(felt!("1"))), + ]) +] #[case::storage_at_block_4( - 4, - vec![ - (ContractAddress::from(felt!("1")), Some(felt!("11")), Some(felt!("2"))), - (ContractAddress::from(felt!("2")), Some(felt!("22")), Some(felt!("1"))), - ]) - ] + 4, + vec![ + (ContractAddress::from(felt!("1337")), Some(felt!("11")), Some(felt!("2"))), + (ContractAddress::from(felt!("80085")), Some(felt!("22")), Some(felt!("1"))), + ]) +] #[case::storage_at_block_5( - 5, - vec![ - (ContractAddress::from(felt!("1")), Some(felt!("22")), Some(felt!("3"))), - (ContractAddress::from(felt!("2")), Some(felt!("33")), Some(felt!("2"))), - ]) - ] + 5, + vec![ + (ContractAddress::from(felt!("1337")), Some(felt!("22")), Some(felt!("3"))), + (ContractAddress::from(felt!("80085")), Some(felt!("33")), Some(felt!("2"))), + ]) +] fn test_historical_storage_read( #[from(provider_with_states)] provider: BlockchainProvider, #[case] block_num: BlockNumber, diff --git a/crates/katana/storage/provider/tests/fixtures.rs b/crates/katana/storage/provider/tests/fixtures.rs index 918c98975c..44d8e66a89 100644 --- a/crates/katana/storage/provider/tests/fixtures.rs +++ b/crates/katana/storage/provider/tests/fixtures.rs @@ -69,8 +69,8 @@ pub fn db_provider() -> BlockchainProvider { #[rstest::fixture] pub fn mock_state_updates() -> [StateUpdatesWithClasses; 3] { - let address_1 = address!("1"); - let address_2 = address!("2"); + let address_1 = address!("1337"); + let address_2 = address!("80085"); let class_hash_1 = felt!("11"); let compiled_class_hash_1 = felt!("1000"); diff --git a/crates/katana/storage/provider/tests/storage.rs b/crates/katana/storage/provider/tests/storage.rs index f401e189b6..9961d4c6a9 100644 --- a/crates/katana/storage/provider/tests/storage.rs +++ b/crates/katana/storage/provider/tests/storage.rs @@ -39,11 +39,11 @@ mod latest { #[rstest::rstest] #[case( vec![ - (ContractAddress::from(felt!("1")), felt!("1"), Some(felt!("111"))), - (ContractAddress::from(felt!("1")), felt!("2"), Some(felt!("222"))), - (ContractAddress::from(felt!("1")), felt!("3"), Some(felt!("77"))), - (ContractAddress::from(felt!("2")), felt!("1"), Some(felt!("12"))), - (ContractAddress::from(felt!("2")), felt!("2"), Some(felt!("13"))) + (ContractAddress::from(felt!("1337")), felt!("1"), Some(felt!("111"))), + (ContractAddress::from(felt!("1337")), felt!("2"), Some(felt!("222"))), + (ContractAddress::from(felt!("1337")), felt!("3"), Some(felt!("77"))), + (ContractAddress::from(felt!("80085")), felt!("1"), Some(felt!("12"))), + (ContractAddress::from(felt!("80085")), felt!("2"), Some(felt!("13"))) ] )] fn test_latest_storage_read( @@ -95,38 +95,38 @@ mod historical { #[case::storage_at_block_0( 0, vec![ - (ContractAddress::from(felt!("1")), felt!("1"), None), - (ContractAddress::from(felt!("1")), felt!("2"), None), - (ContractAddress::from(felt!("2")), felt!("1"), None), - (ContractAddress::from(felt!("2")), felt!("2"), None) + (ContractAddress::from(felt!("1337")), felt!("1"), None), + (ContractAddress::from(felt!("1337")), felt!("2"), None), + (ContractAddress::from(felt!("80085")), felt!("1"), None), + (ContractAddress::from(felt!("80085")), felt!("2"), None) ]) ] #[case::storage_at_block_1( 1, vec![ - (ContractAddress::from(felt!("1")), felt!("1"), Some(felt!("100"))), - (ContractAddress::from(felt!("1")), felt!("2"), Some(felt!("101"))), - (ContractAddress::from(felt!("2")), felt!("1"), Some(felt!("200"))), - (ContractAddress::from(felt!("2")), felt!("2"), Some(felt!("201"))), + (ContractAddress::from(felt!("1337")), felt!("1"), Some(felt!("100"))), + (ContractAddress::from(felt!("1337")), felt!("2"), Some(felt!("101"))), + (ContractAddress::from(felt!("80085")), felt!("1"), Some(felt!("200"))), + (ContractAddress::from(felt!("80085")), felt!("2"), Some(felt!("201"))), ]) ] #[case::storage_at_block_4( 4, vec![ - (ContractAddress::from(felt!("1")), felt!("1"), Some(felt!("111"))), - (ContractAddress::from(felt!("1")), felt!("2"), Some(felt!("222"))), - (ContractAddress::from(felt!("2")), felt!("1"), Some(felt!("200"))), - (ContractAddress::from(felt!("2")), felt!("2"), Some(felt!("201"))), + (ContractAddress::from(felt!("1337")), felt!("1"), Some(felt!("111"))), + (ContractAddress::from(felt!("1337")), felt!("2"), Some(felt!("222"))), + (ContractAddress::from(felt!("80085")), felt!("1"), Some(felt!("200"))), + (ContractAddress::from(felt!("80085")), felt!("2"), Some(felt!("201"))), ]) ] #[case::storage_at_block_5( 5, vec![ - (ContractAddress::from(felt!("1")), felt!("1"), Some(felt!("111"))), - (ContractAddress::from(felt!("1")), felt!("2"), Some(felt!("222"))), - (ContractAddress::from(felt!("1")), felt!("3"), Some(felt!("77"))), - (ContractAddress::from(felt!("2")), felt!("1"), Some(felt!("12"))), - (ContractAddress::from(felt!("2")), felt!("2"), Some(felt!("13"))), + (ContractAddress::from(felt!("1337")), felt!("1"), Some(felt!("111"))), + (ContractAddress::from(felt!("1337")), felt!("2"), Some(felt!("222"))), + (ContractAddress::from(felt!("1337")), felt!("3"), Some(felt!("77"))), + (ContractAddress::from(felt!("80085")), felt!("1"), Some(felt!("12"))), + (ContractAddress::from(felt!("80085")), felt!("2"), Some(felt!("13"))), ]) ] fn test_historical_storage_read(