From 5e4a018f89f8aba0c9a1085fc5cdb00bac145270 Mon Sep 17 00:00:00 2001 From: Kariy Date: Tue, 19 Dec 2023 23:10:40 +0800 Subject: [PATCH 1/2] test(katana-provider): more elaborate tests --- Cargo.lock | 2 + crates/katana/primitives/src/receipt.rs | 16 +- crates/katana/storage/provider/Cargo.toml | 8 +- .../provider/tests/{common.rs => block.rs} | 59 ++----- crates/katana/storage/provider/tests/class.rs | 146 +++++++++++++++++ .../katana/storage/provider/tests/contract.rs | 145 +++++++++++++++++ .../katana/storage/provider/tests/fixtures.rs | 135 +++++++++++++++- .../katana/storage/provider/tests/storage.rs | 153 ++++++++++++++++++ crates/katana/storage/provider/tests/utils.rs | 46 ++++++ 9 files changed, 653 insertions(+), 57 deletions(-) rename crates/katana/storage/provider/tests/{common.rs => block.rs} (51%) create mode 100644 crates/katana/storage/provider/tests/class.rs create mode 100644 crates/katana/storage/provider/tests/contract.rs create mode 100644 crates/katana/storage/provider/tests/storage.rs create mode 100644 crates/katana/storage/provider/tests/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 05748eb8b5..7db647d70c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5528,6 +5528,8 @@ dependencies = [ "futures", "katana-db", "katana-primitives", + "katana-runner", + "lazy_static", "parking_lot 0.12.1", "rand", "rstest", diff --git a/crates/katana/primitives/src/receipt.rs b/crates/katana/primitives/src/receipt.rs index e053f4e845..34bfa888ed 100644 --- a/crates/katana/primitives/src/receipt.rs +++ b/crates/katana/primitives/src/receipt.rs @@ -3,7 +3,7 @@ use ethers::types::H256; use crate::contract::ContractAddress; use crate::FieldElement; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Event { /// The contract address that emitted the event. @@ -15,7 +15,7 @@ pub struct Event { } /// Represents a message sent to L1. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct MessageToL1 { /// The L2 contract address that sent the message. @@ -27,7 +27,7 @@ pub struct MessageToL1 { } /// Receipt for a `Invoke` transaction. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InvokeTxReceipt { /// Actual fee paid for the transaction. @@ -43,7 +43,7 @@ pub struct InvokeTxReceipt { } /// Receipt for a `Declare` transaction. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DeclareTxReceipt { /// Actual fee paid for the transaction. @@ -59,7 +59,7 @@ pub struct DeclareTxReceipt { } /// Receipt for a `L1Handler` transaction. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct L1HandlerTxReceipt { /// Actual fee paid for the transaction. @@ -77,7 +77,7 @@ pub struct L1HandlerTxReceipt { } /// Receipt for a `DeployAccount` transaction. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DeployAccountTxReceipt { /// Actual fee paid for the transaction. @@ -95,7 +95,7 @@ pub struct DeployAccountTxReceipt { } /// The receipt of a transaction containing the outputs of its execution. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Receipt { Invoke(InvokeTxReceipt), @@ -139,7 +139,7 @@ impl Receipt { /// Transaction execution resources. /// /// The resources consumed by a transaction during its execution. -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TxExecutionResources { /// The number of cairo steps used diff --git a/crates/katana/storage/provider/Cargo.toml b/crates/katana/storage/provider/Cargo.toml index dc59a8d1b0..af9d492a08 100644 --- a/crates/katana/storage/provider/Cargo.toml +++ b/crates/katana/storage/provider/Cargo.toml @@ -22,11 +22,13 @@ starknet.workspace = true tokio.workspace = true [features] -default = ["fork", "in-memory"] -fork = ["in-memory"] -in-memory = [] +default = [ "fork", "in-memory" ] +fork = [ "in-memory" ] +in-memory = [ ] [dev-dependencies] +katana-runner = { path = "../../runner" } +lazy_static.workspace = true rand = "0.8.5" rstest = "0.18.2" rstest_reuse = "0.6.0" diff --git a/crates/katana/storage/provider/tests/common.rs b/crates/katana/storage/provider/tests/block.rs similarity index 51% rename from crates/katana/storage/provider/tests/common.rs rename to crates/katana/storage/provider/tests/block.rs index 9fa4086298..03b8a4cecc 100644 --- a/crates/katana/storage/provider/tests/common.rs +++ b/crates/katana/storage/provider/tests/block.rs @@ -1,46 +1,16 @@ -use katana_primitives::block::{ - Block, BlockHash, BlockHashOrNumber, FinalityStatus, Header, SealedBlockWithStatus, -}; -use katana_primitives::transaction::{Tx, TxHash, TxWithHash}; -use katana_primitives::FieldElement; +use katana_primitives::block::BlockHashOrNumber; use katana_provider::providers::fork::ForkedProvider; use katana_provider::providers::in_memory::InMemoryProvider; use katana_provider::traits::block::{BlockProvider, BlockWriter}; -use katana_provider::traits::transaction::TransactionProvider; +use katana_provider::traits::transaction::{ReceiptProvider, TransactionProvider}; use katana_provider::BlockchainProvider; use rstest_reuse::{self, *}; mod fixtures; +mod utils; use fixtures::{fork_provider, in_memory_provider}; - -fn generate_dummy_txs(count: u64) -> Vec { - let mut txs = Vec::with_capacity(count as usize); - for _ in 0..count { - txs.push(TxWithHash { - hash: TxHash::from(rand::random::()), - transaction: Tx::Invoke(Default::default()), - }); - } - txs -} - -fn generate_dummy_blocks(count: u64) -> Vec { - let mut blocks = Vec::with_capacity(count as usize); - let mut parent_hash: BlockHash = 0u8.into(); - - for i in 0..count { - let body = generate_dummy_txs(rand::random::() % 10); - let header = Header { parent_hash, number: i, ..Default::default() }; - let block = - Block { header, body }.seal_with_hash(FieldElement::from(rand::random::())); - parent_hash = block.header.hash; - - blocks.push(SealedBlockWithStatus { block, status: FinalityStatus::AcceptedOnL2 }); - } - - blocks -} +use utils::generate_dummy_blocks_and_receipts; #[template] #[rstest::rstest] @@ -55,7 +25,7 @@ fn insert_block_with_in_memory_provider( #[from(in_memory_provider)] provider: BlockchainProvider, #[case] block_count: u64, ) -> anyhow::Result<()> { - insert_block_impl(provider, block_count) + insert_block_test_impl(provider, block_count) } #[apply(insert_block_cases)] @@ -63,24 +33,24 @@ fn insert_block_with_fork_provider( #[from(fork_provider)] provider: BlockchainProvider, #[case] block_count: u64, ) -> anyhow::Result<()> { - insert_block_impl(provider, block_count) + insert_block_test_impl(provider, block_count) } -fn insert_block_impl(provider: BlockchainProvider, count: u64) -> anyhow::Result<()> +fn insert_block_test_impl(provider: BlockchainProvider, count: u64) -> anyhow::Result<()> where - Db: BlockProvider + BlockWriter, + Db: BlockProvider + BlockWriter + ReceiptProvider, { - let blocks = generate_dummy_blocks(count); + let blocks = generate_dummy_blocks_and_receipts(count); - for block in &blocks { + for (block, receipts) in &blocks { provider.insert_block_with_states_and_receipts( block.clone(), Default::default(), - Default::default(), + receipts.clone(), )?; } - for block in blocks { + for (block, receipts) in blocks { let block_id = BlockHashOrNumber::Hash(block.block.header.hash); let expected_block = block.block.unseal(); @@ -88,6 +58,11 @@ where let actual_block_txs = provider.transactions_by_block(block_id)?; let actual_block_tx_count = provider.transaction_count_by_block(block_id)?; + let actual_receipts = provider.receipts_by_block(block_id)?; + + assert_eq!(actual_receipts.as_ref().map(|r| r.len()), Some(expected_block.body.len())); + assert_eq!(actual_receipts, Some(receipts)); + assert_eq!(actual_block_tx_count, Some(expected_block.body.len() as u64)); assert_eq!(actual_block_txs, Some(expected_block.body.clone())); assert_eq!(actual_block, Some(expected_block)); diff --git a/crates/katana/storage/provider/tests/class.rs b/crates/katana/storage/provider/tests/class.rs new file mode 100644 index 0000000000..68a2959843 --- /dev/null +++ b/crates/katana/storage/provider/tests/class.rs @@ -0,0 +1,146 @@ +mod fixtures; + +use anyhow::Result; +use fixtures::{fork_provider_with_spawned_fork_network, in_memory_provider, provider_with_states}; +use katana_primitives::block::{BlockHashOrNumber, BlockNumber}; +use katana_primitives::contract::{ClassHash, CompiledClassHash}; +use katana_provider::providers::fork::ForkedProvider; +use katana_provider::providers::in_memory::InMemoryProvider; +use katana_provider::traits::state::{StateFactoryProvider, StateProvider}; +use katana_provider::BlockchainProvider; +use rstest_reuse::{self, *}; +use starknet::macros::felt; + +fn assert_state_provider_class( + state_provider: Box, + expected_class: Vec<(ClassHash, Option)>, +) -> Result<()> { + for (class_hash, expected_compiled_hash) in expected_class { + let actual_compiled_hash = state_provider.compiled_class_hash_of_class_hash(class_hash)?; + assert_eq!(actual_compiled_hash, expected_compiled_hash); + } + Ok(()) +} + +mod latest { + use super::*; + + fn assert_latest_class( + provider: BlockchainProvider, + expected_class: Vec<(ClassHash, Option)>, + ) -> Result<()> { + let state_provider = provider.latest()?; + assert_state_provider_class(state_provider, expected_class) + } + + #[template] + #[rstest::rstest] + #[case( + vec![ + (felt!("11"), Some(felt!("1000"))), + (felt!("22"), Some(felt!("2000"))), + (felt!("33"), Some(felt!("3000"))), + ] + )] + fn test_latest_class_read( + #[from(provider_with_states)] provider: BlockchainProvider, + #[case] expected_class: Vec<(ClassHash, Option)>, + ) { + } + + #[apply(test_latest_class_read)] + fn read_class_from_in_memory_provider( + #[with(in_memory_provider())] provider: BlockchainProvider, + #[case] expected_class: Vec<(ClassHash, Option)>, + ) -> Result<()> { + assert_latest_class(provider, expected_class) + } + + #[apply(test_latest_class_read)] + fn read_class_from_fork_provider( + #[with(fork_provider_with_spawned_fork_network::default())] provider: BlockchainProvider< + ForkedProvider, + >, + #[case] expected_class: Vec<(ClassHash, Option)>, + ) -> Result<()> { + assert_latest_class(provider, expected_class) + } +} + +mod historical { + use super::*; + + fn assert_historical_class( + provider: BlockchainProvider, + block_num: BlockNumber, + expected_class: Vec<(ClassHash, Option)>, + ) -> Result<()> { + let state_provider = provider + .historical(BlockHashOrNumber::Num(block_num))? + .expect(ERROR_CREATE_HISTORICAL_PROVIDER); + assert_state_provider_class(state_provider, expected_class) + } + + const ERROR_CREATE_HISTORICAL_PROVIDER: &str = "Failed to create historical state provider."; + + #[template] + #[rstest::rstest] + #[case::class_hash_at_block_0( + 0, + vec![ + (felt!("11"), None), + (felt!("22"), None), + (felt!("33"), None) + ]) + ] + #[case::class_hash_at_block_1( + 1, + vec![ + (felt!("11"), Some(felt!("1000"))), + (felt!("22"), None), + (felt!("33"), None), + ]) + ] + #[case::class_hash_at_block_4( + 4, + vec![ + (felt!("11"), Some(felt!("1000"))), + (felt!("22"), Some(felt!("2000"))), + (felt!("33"), None), + ]) + ] + #[case::class_hash_at_block_5( + 5, + vec![ + (felt!("11"), Some(felt!("1000"))), + (felt!("22"), Some(felt!("2000"))), + (felt!("33"), Some(felt!("3000"))), + ]) + ] + fn test_historical_class_read( + #[from(provider_with_states)] provider: BlockchainProvider, + #[case] block_num: BlockNumber, + #[case] expected_class: Vec<(ClassHash, Option)>, + ) { + } + + #[apply(test_historical_class_read)] + fn read_class_from_in_memory_provider( + #[with(in_memory_provider())] provider: BlockchainProvider, + #[case] block_num: BlockNumber, + #[case] expected_class: Vec<(ClassHash, Option)>, + ) -> Result<()> { + assert_historical_class(provider, block_num, expected_class) + } + + #[apply(test_historical_class_read)] + fn read_class_from_fork_provider( + #[with(fork_provider_with_spawned_fork_network::default())] provider: BlockchainProvider< + ForkedProvider, + >, + #[case] block_num: BlockNumber, + #[case] expected_class: Vec<(ClassHash, Option)>, + ) -> Result<()> { + assert_historical_class(provider, block_num, expected_class) + } +} diff --git a/crates/katana/storage/provider/tests/contract.rs b/crates/katana/storage/provider/tests/contract.rs new file mode 100644 index 0000000000..3b54fdff78 --- /dev/null +++ b/crates/katana/storage/provider/tests/contract.rs @@ -0,0 +1,145 @@ +mod fixtures; + +use anyhow::Result; +use fixtures::{fork_provider_with_spawned_fork_network, in_memory_provider, provider_with_states}; +use katana_primitives::block::{BlockHashOrNumber, BlockNumber}; +use katana_primitives::contract::{ClassHash, ContractAddress, Nonce}; +use katana_provider::providers::fork::ForkedProvider; +use katana_provider::providers::in_memory::InMemoryProvider; +use katana_provider::traits::state::{StateFactoryProvider, StateProvider}; +use katana_provider::BlockchainProvider; +use rstest_reuse::{self, *}; +use starknet::macros::felt; + +fn assert_state_provider_contract_info( + state_provider: Box, + expected_contract_info: Vec<(ContractAddress, Option, Option)>, +) -> Result<()> { + for (address, expected_class_hash, expected_nonce) in expected_contract_info { + let actual_class_hash = state_provider.class_hash_of_contract(address)?; + let actual_nonce = state_provider.nonce(address)?; + + assert_eq!(actual_class_hash, expected_class_hash); + assert_eq!(actual_nonce, expected_nonce); + } + + Ok(()) +} + +mod latest { + use super::*; + + fn assert_latest_contract_info( + provider: BlockchainProvider, + expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) -> Result<()> { + let state_provider = provider.latest()?; + assert_state_provider_contract_info(state_provider, expected_contract_info) + } + + #[template] + #[rstest::rstest] + #[case( + vec![ + (ContractAddress::from(felt!("1")), Some(felt!("22")), Some(felt!("3"))), + (ContractAddress::from(felt!("2")), Some(felt!("33")), Some(felt!("2"))), + ] + )] + fn test_latest_contract_info_read( + #[from(provider_with_states)] provider: BlockchainProvider, + #[case] expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) { + } + + #[apply(test_latest_contract_info_read)] + fn read_storage_from_in_memory_provider( + #[with(in_memory_provider())] provider: BlockchainProvider, + #[case] expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) -> Result<()> { + assert_latest_contract_info(provider, expected_contract_info) + } + + #[apply(test_latest_contract_info_read)] + fn read_storage_from_fork_provider( + #[with(fork_provider_with_spawned_fork_network::default())] provider: BlockchainProvider< + ForkedProvider, + >, + #[case] expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) -> Result<()> { + assert_latest_contract_info(provider, expected_contract_info) + } +} + +mod historical { + use super::*; + + fn assert_historical_contract_info( + provider: BlockchainProvider, + block_num: BlockNumber, + expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) -> Result<()> { + let state_provider = provider + .historical(BlockHashOrNumber::Num(block_num))? + .expect(ERROR_CREATE_HISTORICAL_PROVIDER); + assert_state_provider_contract_info(state_provider, expected_contract_info) + } + + const ERROR_CREATE_HISTORICAL_PROVIDER: &str = "Failed to create historical state provider."; + + #[template] + #[rstest::rstest] + #[case::storage_at_block_0( + 0, + vec![ + (ContractAddress::from(felt!("1")), None, None), + (ContractAddress::from(felt!("2")), 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"))), + ]) + ] + #[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"))), + ]) + ] + #[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"))), + ]) + ] + fn test_historical_storage_read( + #[from(provider_with_states)] provider: BlockchainProvider, + #[case] block_num: BlockNumber, + #[case] expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) { + } + + #[apply(test_historical_storage_read)] + fn read_storage_from_in_memory_provider( + #[with(in_memory_provider())] provider: BlockchainProvider, + #[case] block_num: BlockNumber, + #[case] expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) -> Result<()> { + assert_historical_contract_info(provider, block_num, expected_contract_info) + } + + #[apply(test_historical_storage_read)] + fn read_storage_from_fork_provider( + #[with(fork_provider_with_spawned_fork_network::default())] provider: BlockchainProvider< + ForkedProvider, + >, + #[case] block_num: BlockNumber, + #[case] expected_contract_info: Vec<(ContractAddress, Option, Option)>, + ) -> Result<()> { + assert_historical_contract_info(provider, block_num, expected_contract_info) + } +} diff --git a/crates/katana/storage/provider/tests/fixtures.rs b/crates/katana/storage/provider/tests/fixtures.rs index d6c0ff6f85..4021ab3672 100644 --- a/crates/katana/storage/provider/tests/fixtures.rs +++ b/crates/katana/storage/provider/tests/fixtures.rs @@ -1,13 +1,30 @@ +use std::collections::HashMap; use std::sync::Arc; -use katana_primitives::block::BlockHashOrNumber; +use katana_primitives::block::{ + BlockHashOrNumber, FinalityStatus, Header, SealedBlock, SealedBlockWithStatus, SealedHeader, +}; +use katana_primitives::contract::ContractAddress; +use katana_primitives::state::{StateUpdates, StateUpdatesWithDeclaredClasses}; use katana_provider::providers::fork::ForkedProvider; use katana_provider::providers::in_memory::InMemoryProvider; +use katana_provider::traits::block::BlockWriter; +use katana_provider::traits::state::StateFactoryProvider; use katana_provider::BlockchainProvider; +use katana_runner::KatanaRunner; +use lazy_static::lazy_static; +use starknet::macros::felt; use starknet::providers::jsonrpc::HttpTransport; use starknet::providers::JsonRpcClient; use url::Url; +lazy_static! { + pub static ref FORKED_PROVIDER: (KatanaRunner, Arc>) = { + let (runner, provider) = katana_runner::KatanaRunner::new().unwrap(); + (runner, Arc::new(provider)) + }; +} + #[rstest::fixture] pub fn in_memory_provider() -> BlockchainProvider { BlockchainProvider::new(InMemoryProvider::new()) @@ -15,10 +32,120 @@ pub fn in_memory_provider() -> BlockchainProvider { #[rstest::fixture] pub fn fork_provider( - #[default("http://localhost:5050")] rpc: &str, + #[default("http://127.0.0.1:5050")] rpc: &str, #[default(0)] block_num: u64, ) -> BlockchainProvider { - let rpc_provider = JsonRpcClient::new(HttpTransport::new(Url::parse(rpc).unwrap())); - let provider = ForkedProvider::new(Arc::new(rpc_provider), BlockHashOrNumber::Num(block_num)); + let provider = JsonRpcClient::new(HttpTransport::new(Url::parse(rpc).unwrap())); + let provider = ForkedProvider::new(Arc::new(provider), BlockHashOrNumber::Num(block_num)); BlockchainProvider::new(provider) } + +#[rstest::fixture] +pub fn fork_provider_with_spawned_fork_network( + #[default(0)] block_num: u64, +) -> BlockchainProvider { + let provider = + ForkedProvider::new(FORKED_PROVIDER.1.clone(), BlockHashOrNumber::Num(block_num)); + BlockchainProvider::new(provider) +} + +#[rstest::fixture] +#[default(BlockchainProvider)] +pub fn provider_with_states( + #[default(in_memory_provider())] provider: BlockchainProvider, +) -> BlockchainProvider +where + Db: BlockWriter + StateFactoryProvider, +{ + let address_1 = ContractAddress::from(felt!("1")); + let address_2 = ContractAddress::from(felt!("2")); + + let class_hash_1 = felt!("11"); + let compiled_class_hash_1 = felt!("1000"); + + let class_hash_2 = felt!("22"); + let compiled_class_hash_2 = felt!("2000"); + + let class_hash_3 = felt!("33"); + let compiled_class_hash_3 = felt!("3000"); + + let state_update_at_block_1 = StateUpdatesWithDeclaredClasses { + state_updates: StateUpdates { + nonce_updates: HashMap::from([(address_1, 1u8.into()), (address_2, 1u8.into())]), + storage_updates: HashMap::from([ + ( + address_1, + HashMap::from([(1u8.into(), 100u32.into()), (2u8.into(), 101u32.into())]), + ), + ( + address_2, + HashMap::from([(1u8.into(), 200u32.into()), (2u8.into(), 201u32.into())]), + ), + ]), + declared_classes: HashMap::from([(class_hash_1, compiled_class_hash_1)]), + contract_updates: HashMap::from([(address_1, class_hash_1), (address_2, class_hash_1)]), + }, + ..Default::default() + }; + + let state_update_at_block_2 = StateUpdatesWithDeclaredClasses { + state_updates: StateUpdates { + nonce_updates: HashMap::from([(address_1, 2u8.into())]), + storage_updates: HashMap::from([( + address_1, + HashMap::from([(felt!("1"), felt!("111")), (felt!("2"), felt!("222"))]), + )]), + declared_classes: HashMap::from([(class_hash_2, compiled_class_hash_2)]), + contract_updates: HashMap::from([(address_2, class_hash_2)]), + }, + ..Default::default() + }; + + let state_update_at_block_5 = StateUpdatesWithDeclaredClasses { + state_updates: StateUpdates { + nonce_updates: HashMap::from([(address_1, 3u8.into()), (address_2, 2u8.into())]), + storage_updates: HashMap::from([ + (address_1, HashMap::from([(3u8.into(), 77u32.into())])), + ( + address_2, + HashMap::from([(1u8.into(), 12u32.into()), (2u8.into(), 13u32.into())]), + ), + ]), + contract_updates: HashMap::from([(address_1, class_hash_2), (address_2, class_hash_3)]), + declared_classes: HashMap::from([(class_hash_3, compiled_class_hash_3)]), + }, + ..Default::default() + }; + + // Fill provider with states. + + for i in 0..=5 { + let block_id = BlockHashOrNumber::from(i); + + let state_update = match block_id { + BlockHashOrNumber::Num(1) => state_update_at_block_1.clone(), + BlockHashOrNumber::Num(2) => state_update_at_block_2.clone(), + BlockHashOrNumber::Num(5) => state_update_at_block_5.clone(), + _ => StateUpdatesWithDeclaredClasses::default(), + }; + + provider + .insert_block_with_states_and_receipts( + SealedBlockWithStatus { + status: FinalityStatus::AcceptedOnL2, + block: SealedBlock { + header: SealedHeader { + hash: i.into(), + header: Header { number: i, ..Default::default() }, + }, + body: Default::default(), + }, + }, + state_update, + Default::default(), + ) + .unwrap(); + } + + provider +} diff --git a/crates/katana/storage/provider/tests/storage.rs b/crates/katana/storage/provider/tests/storage.rs new file mode 100644 index 0000000000..4f12e8f5d8 --- /dev/null +++ b/crates/katana/storage/provider/tests/storage.rs @@ -0,0 +1,153 @@ +mod fixtures; + +use anyhow::Result; +use fixtures::{fork_provider_with_spawned_fork_network, in_memory_provider, provider_with_states}; +use katana_primitives::block::{BlockHashOrNumber, BlockNumber}; +use katana_primitives::contract::{ContractAddress, StorageKey, StorageValue}; +use katana_provider::providers::fork::ForkedProvider; +use katana_provider::providers::in_memory::InMemoryProvider; +use katana_provider::traits::state::{StateFactoryProvider, StateProvider}; +use katana_provider::BlockchainProvider; +use rstest_reuse::{self, *}; +use starknet::macros::felt; + +fn assert_state_provider_storage( + state_provider: Box, + expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, +) -> Result<()> { + for (address, key, expected_value) in expected_storage_entry { + let actual_value = state_provider.storage(address, key)?; + assert_eq!(actual_value, expected_value); + } + Ok(()) +} + +mod latest { + use super::*; + + fn assert_latest_storage_value( + provider: BlockchainProvider, + expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) -> Result<()> { + let state_provider = provider.latest()?; + assert_state_provider_storage(state_provider, expected_storage_entry) + } + + #[template] + #[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"))) + ] + )] + fn test_latest_storage_read( + #[from(provider_with_states)] provider: BlockchainProvider, + #[case] storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) { + } + + #[apply(test_latest_storage_read)] + fn read_storage_from_in_memory_provider( + #[with(in_memory_provider())] provider: BlockchainProvider, + #[case] expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) -> Result<()> { + assert_latest_storage_value(provider, expected_storage_entry) + } + + #[apply(test_latest_storage_read)] + fn read_storage_from_fork_provider_with_spawned_fork_network( + #[with(fork_provider_with_spawned_fork_network::default())] provider: BlockchainProvider< + ForkedProvider, + >, + #[case] expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) -> Result<()> { + assert_latest_storage_value(provider, expected_storage_entry) + } +} + +mod historical { + use super::*; + + fn assert_historical_storage_value( + provider: BlockchainProvider, + block_num: BlockNumber, + expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) -> Result<()> { + let state_provider = provider + .historical(BlockHashOrNumber::Num(block_num))? + .expect(ERROR_CREATE_HISTORICAL_PROVIDER); + assert_state_provider_storage(state_provider, expected_storage_entry) + } + + const ERROR_CREATE_HISTORICAL_PROVIDER: &str = "Failed to create historical state provider."; + + #[template] + #[rstest::rstest] + #[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) + ]) + ] + #[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"))), + ]) + ] + #[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"))), + ]) + ] + #[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"))), + ]) + ] + fn test_historical_storage_read( + #[from(provider_with_states)] provider: BlockchainProvider, + #[case] block_num: BlockNumber, + #[case] expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) { + } + + #[apply(test_historical_storage_read)] + fn read_storage_from_in_memory_provider( + #[with(in_memory_provider())] provider: BlockchainProvider, + #[case] block_num: BlockNumber, + #[case] expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) -> Result<()> { + assert_historical_storage_value(provider, block_num, expected_storage_entry) + } + + #[apply(test_historical_storage_read)] + fn read_storage_from_fork_provider_with_spawned_fork_network( + #[with(fork_provider_with_spawned_fork_network::default())] provider: BlockchainProvider< + ForkedProvider, + >, + #[case] block_num: BlockNumber, + #[case] expected_storage_entry: Vec<(ContractAddress, StorageKey, Option)>, + ) -> Result<()> { + assert_historical_storage_value(provider, block_num, expected_storage_entry) + } +} diff --git a/crates/katana/storage/provider/tests/utils.rs b/crates/katana/storage/provider/tests/utils.rs new file mode 100644 index 0000000000..510b80e70a --- /dev/null +++ b/crates/katana/storage/provider/tests/utils.rs @@ -0,0 +1,46 @@ +use katana_primitives::block::{Block, BlockHash, FinalityStatus, Header, SealedBlockWithStatus}; +use katana_primitives::receipt::{InvokeTxReceipt, Receipt}; +use katana_primitives::transaction::{Tx, TxHash, TxWithHash}; +use katana_primitives::FieldElement; + +pub fn generate_dummy_txs_and_receipts(count: usize) -> (Vec, Vec) { + let mut txs = Vec::with_capacity(count); + let mut receipts = Vec::with_capacity(count); + + // TODO: generate random txs and receipts variants + for _ in 0..count { + txs.push(TxWithHash { + hash: TxHash::from(rand::random::()), + transaction: Tx::Invoke(Default::default()), + }); + + receipts.push(Receipt::Invoke(InvokeTxReceipt::default())); + } + + (txs, receipts) +} + +pub fn generate_dummy_blocks_and_receipts( + count: u64, +) -> Vec<(SealedBlockWithStatus, Vec)> { + let mut blocks = Vec::with_capacity(count as usize); + let mut parent_hash: BlockHash = 0u8.into(); + + for i in 0..count { + let tx_count = (rand::random::() % 10) as usize; + let (body, receipts) = generate_dummy_txs_and_receipts(tx_count); + + let header = Header { parent_hash, number: i, ..Default::default() }; + let block = + Block { header, body }.seal_with_hash(FieldElement::from(rand::random::())); + + parent_hash = block.header.hash; + + blocks.push(( + SealedBlockWithStatus { block, status: FinalityStatus::AcceptedOnL2 }, + receipts, + )); + } + + blocks +} From 376c1c983ba142ea9f003682ef0d3df80b1a76d7 Mon Sep 17 00:00:00 2001 From: Kariy Date: Wed, 20 Dec 2023 05:49:28 +0800 Subject: [PATCH 2/2] ignore log files from runner --- crates/katana/storage/provider/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 crates/katana/storage/provider/.gitignore diff --git a/crates/katana/storage/provider/.gitignore b/crates/katana/storage/provider/.gitignore new file mode 100644 index 0000000000..b6d592eb2a --- /dev/null +++ b/crates/katana/storage/provider/.gitignore @@ -0,0 +1 @@ +/logs