diff --git a/crates/mempool_test_utils/src/starknet_api_test_utils.rs b/crates/mempool_test_utils/src/starknet_api_test_utils.rs index 7b0c75d7d9..05dfbae17c 100644 --- a/crates/mempool_test_utils/src/starknet_api_test_utils.rs +++ b/crates/mempool_test_utils/src/starknet_api_test_utils.rs @@ -263,6 +263,26 @@ impl MultiAccountTransactionGenerator { pub fn accounts(&self) -> &[AccountTransactionGenerator] { self.account_tx_generators.as_slice() } + + pub fn account_tx_generators(&mut self) -> &mut Vec { + &mut self.account_tx_generators + } + + pub fn deployed_accounts(&self) -> Vec { + self.account_tx_generators + .iter() + .filter_map(|tx_gen| if tx_gen.is_deployed() { Some(&tx_gen.account) } else { None }) + .copied() + .collect() + } + + pub fn undeployed_accounts(&self) -> Vec { + self.account_tx_generators + .iter() + .filter_map(|tx_gen| if !tx_gen.is_deployed() { Some(&tx_gen.account) } else { None }) + .copied() + .collect() + } } /// Manages transaction generation for a single account. @@ -417,7 +437,7 @@ impl AccountTransactionGenerator { // Note: feature contracts have their own address generating method, but it a mocked address and is // not related to an actual deploy account transaction, which is the way real account addresses are // calculated. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Contract { pub contract: FeatureContract, pub sender_address: ContractAddress, diff --git a/crates/starknet_integration_tests/Cargo.toml b/crates/starknet_integration_tests/Cargo.toml index 014765eaa3..121a0021ef 100644 --- a/crates/starknet_integration_tests/Cargo.toml +++ b/crates/starknet_integration_tests/Cargo.toml @@ -16,6 +16,7 @@ cairo-lang-starknet-classes.workspace = true futures.workspace = true indexmap.workspace = true infra_utils.workspace = true +itertools.workspace = true mempool_test_utils.workspace = true papyrus_common.workspace = true papyrus_config.workspace = true @@ -48,7 +49,6 @@ url.workspace = true [dev-dependencies] futures.workspace = true -itertools.workspace = true pretty_assertions.workspace = true rstest.workspace = true starknet_sequencer_infra.workspace = true diff --git a/crates/starknet_integration_tests/src/state_reader.rs b/crates/starknet_integration_tests/src/state_reader.rs index 5579c66290..7add790e68 100644 --- a/crates/starknet_integration_tests/src/state_reader.rs +++ b/crates/starknet_integration_tests/src/state_reader.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; @@ -8,6 +9,7 @@ use blockifier::test_utils::{CairoVersion, RunnableCairo1, BALANCE}; use blockifier::versioned_constants::VersionedConstants; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use indexmap::IndexMap; +use itertools::Itertools; use mempool_test_utils::starknet_api_test_utils::{AccountTransactionGenerator, Contract}; use papyrus_common::pending_classes::PendingClasses; use papyrus_rpc::{run_server, RpcConfig}; @@ -189,6 +191,7 @@ fn prepare_sierra_classes( ) -> Vec<(ClassHash, SierraContractClass)> { contract_classes_to_retrieve .filter(|contract| !contract.cairo_version().is_cairo0()) + .dedup_by(|c1, c2| c1.class_hash() == c2.class_hash()) .map(|contract| (contract.class_hash(), contract.sierra())) .collect() } @@ -196,28 +199,28 @@ fn prepare_sierra_classes( fn prepare_compiled_contract_classes( contract_classes_to_retrieve: impl Iterator, ) -> ContractClassesMap { - let mut cairo0_contract_classes = Vec::new(); - let mut cairo1_contract_classes = Vec::new(); + let mut cairo0_contract_classes = HashMap::new(); + let mut cairo1_contract_classes = HashMap::new(); for contract in contract_classes_to_retrieve { match contract.cairo_version() { CairoVersion::Cairo0 => { - cairo0_contract_classes.push(( + cairo0_contract_classes.insert( contract.class_hash(), serde_json::from_str(&contract.raw_class()).unwrap(), - )); + ); } // todo(rdr): including both Cairo1 and Native versions for now. Temporal solution to // avoid compilation errors when using the "cairo_native" feature _ => { - cairo1_contract_classes.push(( + cairo1_contract_classes.insert( contract.class_hash(), serde_json::from_str(&contract.raw_class()).unwrap(), - )); + ); } } } - (cairo0_contract_classes, cairo1_contract_classes) + (cairo0_contract_classes.into_iter().collect(), cairo1_contract_classes.into_iter().collect()) } fn write_state_to_papyrus_storage( diff --git a/crates/starknet_integration_tests/src/utils.rs b/crates/starknet_integration_tests/src/utils.rs index 70f809726a..e3299d11fb 100644 --- a/crates/starknet_integration_tests/src/utils.rs +++ b/crates/starknet_integration_tests/src/utils.rs @@ -8,7 +8,12 @@ use axum::Router; use blockifier::context::ChainInfo; use blockifier::test_utils::contracts::FeatureContract; use blockifier::test_utils::{CairoVersion, RunnableCairo1}; -use mempool_test_utils::starknet_api_test_utils::{AccountId, MultiAccountTransactionGenerator}; +use mempool_test_utils::starknet_api_test_utils::{ + AccountId, + AccountTransactionGenerator, + Contract, + MultiAccountTransactionGenerator, +}; use papyrus_consensus::config::ConsensusConfig; use papyrus_consensus::types::ValidatorId; use papyrus_consensus_orchestrator::cende::RECORDER_WRITE_BLOB_PATH; @@ -22,6 +27,7 @@ use papyrus_storage::StorageConfig; use starknet_api::block::BlockNumber; use starknet_api::core::ChainId; use starknet_api::rpc_transaction::RpcTransaction; +use starknet_api::transaction::fields::ContractAddressSalt; use starknet_api::transaction::TransactionHash; use starknet_batcher::block_builder::BlockBuilderConfig; use starknet_batcher::config::BatcherConfig; @@ -47,6 +53,8 @@ use url::Url; pub const ACCOUNT_ID_0: AccountId = 0; pub const ACCOUNT_ID_1: AccountId = 1; +pub const NEW_ACCOUNT_SALT: ContractAddressSalt = ContractAddressSalt(Felt::THREE); +pub const UNDEPLOYED_ACCOUNT_ID: AccountId = 2; pub fn create_chain_info() -> ChainInfo { let mut chain_info = ChainInfo::create_for_testing(); @@ -181,6 +189,13 @@ pub fn create_integration_test_tx_generator() -> MultiAccountTransactionGenerato ] { tx_generator.register_deployed_account(account); } + // TODO(yair): This is a hack to fund the new account during the setup. Move the registration to + // the test body once funding is supported. + let new_account_id = tx_generator.register_undeployed_account( + FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1(RunnableCairo1::Casm)), + NEW_ACCOUNT_SALT, + ); + assert_eq!(new_account_id, UNDEPLOYED_ACCOUNT_ID); tx_generator } @@ -198,6 +213,24 @@ pub fn create_txs_for_integration_test( vec![account0_invoke_nonce1, account0_invoke_nonce2, account1_invoke_nonce1] } +pub fn create_funding_txs( + tx_generator: &mut MultiAccountTransactionGenerator, +) -> Vec { + // TODO(yair): Register the undeployed account here instead of in the test setup + // once funding is implemented. + let undeployed_account = tx_generator.account_with_id(UNDEPLOYED_ACCOUNT_ID).account; + assert!(tx_generator.undeployed_accounts().contains(&undeployed_account)); + fund_new_account(tx_generator.account_with_id_mut(ACCOUNT_ID_0), &undeployed_account) +} + +fn fund_new_account( + funding_account: &mut AccountTransactionGenerator, + receipient: &Contract, +) -> Vec { + let funding_tx = funding_account.generate_transfer(receipient); + vec![funding_tx] +} + fn create_account_txs( mut tx_generator: MultiAccountTransactionGenerator, account_id: AccountId, diff --git a/crates/starknet_integration_tests/tests/end_to_end_flow_test.rs b/crates/starknet_integration_tests/tests/end_to_end_flow_test.rs index 3925eda1fb..634d060fd4 100644 --- a/crates/starknet_integration_tests/tests/end_to_end_flow_test.rs +++ b/crates/starknet_integration_tests/tests/end_to_end_flow_test.rs @@ -1,7 +1,5 @@ use std::collections::HashSet; -use blockifier::test_utils::contracts::FeatureContract; -use blockifier::test_utils::{CairoVersion, RunnableCairo1}; use futures::StreamExt; use mempool_test_utils::starknet_api_test_utils::MultiAccountTransactionGenerator; use papyrus_consensus::types::ValidatorId; @@ -18,24 +16,23 @@ use pretty_assertions::assert_eq; use rstest::{fixture, rstest}; use starknet_api::block::{BlockHash, BlockNumber}; use starknet_api::rpc_transaction::RpcTransaction; -use starknet_api::transaction::fields::ContractAddressSalt; use starknet_api::transaction::TransactionHash; use starknet_integration_tests::flow_test_setup::{FlowSequencerSetup, FlowTestSetup}; use starknet_integration_tests::test_identifiers::TestIdentifier; use starknet_integration_tests::utils::{ + create_funding_txs, create_integration_test_tx_generator, create_txs_for_integration_test, run_integration_test_scenario, test_tx_hashes_for_integration_test, - ACCOUNT_ID_0, + UNDEPLOYED_ACCOUNT_ID, }; use starknet_sequencer_infra::trace_util::configure_tracing; use starknet_types_core::felt::Felt; use tracing::debug; const INITIAL_HEIGHT: BlockNumber = BlockNumber(0); -const LAST_HEIGHT: BlockNumber = BlockNumber(3); -const NEW_ACCOUNT_SALT: ContractAddressSalt = ContractAddressSalt(Felt::THREE); +const LAST_HEIGHT: BlockNumber = BlockNumber(4); #[fixture] fn tx_generator() -> MultiAccountTransactionGenerator { @@ -61,21 +58,6 @@ async fn end_to_end_flow(mut tx_generator: MultiAccountTransactionGenerator) { wait_for_sequencer_node(&mock_running_system.sequencer_1), ); - let next_height = INITIAL_HEIGHT.unchecked_next(); - let n_heights = next_height.iter_up_to(LAST_HEIGHT.unchecked_next()).count(); - let heights_to_build = next_height.iter_up_to(LAST_HEIGHT.unchecked_next()); - let expected_content_ids = [ - Felt::from_hex_unchecked( - "0x665101f416fd5c4e91083fa9dcac1dba9a282f5211a1a2ad7695e95cb35d6b", - ), - Felt::from_hex_unchecked( - "0x5c17ba5f9681bcc8ca5a9dee3fe3a9fd2da42e660a99c64aa1a95fa826a1b2", - ), - Felt::from_hex_unchecked( - "0x5ec39365c6bbf631f3957f41d7b972f1c57e12544484b1db53458de03714b19", - ), - ]; - let sequencers = [&mock_running_system.sequencer_0, &mock_running_system.sequencer_1]; // We use only the first sequencer's gateway to test that the mempools are syncing. let sequencer_to_add_txs = *sequencers.first().unwrap(); @@ -83,35 +65,9 @@ async fn end_to_end_flow(mut tx_generator: MultiAccountTransactionGenerator) { // We start at height 1, so we need to skip the proposer of the initial height. expected_proposer_iter.next().unwrap(); - let create_rpc_txs_scenarios = - [create_txs_for_integration_test, create_txs_for_integration_test, fund_new_account]; - - let test_tx_hashes_scenarios = - [test_tx_hashes_for_integration_test, test_tx_hashes_for_integration_test, test_funding]; - - assert_eq!( - n_heights, - expected_content_ids.len(), - "Expected the same number of heights and content ids" - ); - assert_eq!( - n_heights, - create_rpc_txs_scenarios.len(), - "Expected the same number of heights and scenarios" - ); - assert_eq!( - n_heights, - test_tx_hashes_scenarios.len(), - "Expected the same number of heights and scenarios" - ); - // Build multiple heights to ensure heights are committed. - for (height, expected_content_id, create_rpc_txs_fn, test_tx_hashes_fn) in itertools::izip!( - heights_to_build, - expected_content_ids, - create_rpc_txs_scenarios.iter(), - test_tx_hashes_scenarios.iter(), - ) { + for (height, create_rpc_txs_fn, test_tx_hashes_fn, expected_content_id) in create_test_blocks() + { debug!("Starting height {}.", height); // Create and send transactions. let expected_batched_tx_hashes = run_integration_test_scenario( @@ -144,6 +100,50 @@ async fn end_to_end_flow(mut tx_generator: MultiAccountTransactionGenerator) { } } +type CreateRpcTxsFn = fn(&mut MultiAccountTransactionGenerator) -> Vec; +type TestTxHashesFn = fn(&[TransactionHash]) -> Vec; +type ExpectedContentId = Felt; + +fn create_test_blocks() -> Vec<(BlockNumber, CreateRpcTxsFn, TestTxHashesFn, ExpectedContentId)> { + let next_height = INITIAL_HEIGHT.unchecked_next(); + let heights_to_build = next_height.iter_up_to(LAST_HEIGHT.unchecked_next()); + let test_scenarios: Vec<(CreateRpcTxsFn, TestTxHashesFn, ExpectedContentId)> = vec![ + ( + create_txs_for_integration_test, + test_tx_hashes_for_integration_test, + Felt::from_hex_unchecked( + "0x665101f416fd5c4e91083fa9dcac1dba9a282f5211a1a2ad7695e95cb35d6b", + ), + ), + ( + create_txs_for_integration_test, + test_tx_hashes_for_integration_test, + Felt::from_hex_unchecked( + "0x5c17ba5f9681bcc8ca5a9dee3fe3a9fd2da42e660a99c64aa1a95fa826a1b2", + ), + ), + ( + create_funding_txs, + test_single_tx, + Felt::from_hex_unchecked( + "0x14155afd2c9f08ff4e5c1b946aafefab3ae8bd190ec47cdece8d5164fe6c795", + ), + ), + ( + deploy_account, + test_single_tx, + Felt::from_hex_unchecked( + "0x773f138ea0d89887a4982df74341ce6e6303e3528027f5a838e1c3f8c2cb701", + ), + ), + ]; + itertools::zip_eq(heights_to_build, test_scenarios) + .map(|(h, (create_txs_fn, test_tx_hashes_fn, expected_content_id))| { + (h, create_txs_fn, test_tx_hashes_fn, expected_content_id) + }) + .collect() +} + async fn wait_for_sequencer_node(sequencer: &FlowSequencerSetup) { sequencer.monitoring_client.await_alive(5000, 50).await.expect("Node should be alive."); } @@ -237,19 +237,14 @@ async fn listen_to_broadcasted_messages( ); } -fn fund_new_account(tx_generator: &mut MultiAccountTransactionGenerator) -> Vec { - let new_account_id = tx_generator.register_undeployed_account( - FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1(RunnableCairo1::Casm)), - NEW_ACCOUNT_SALT, - ); - - let to = tx_generator.account_with_id(new_account_id).account; - - let funding_tx = tx_generator.account_with_id_mut(ACCOUNT_ID_0).generate_transfer(&to); - vec![funding_tx] +fn deploy_account(tx_generator: &mut MultiAccountTransactionGenerator) -> Vec { + let undeployed_account_tx_generator = tx_generator.account_with_id_mut(UNDEPLOYED_ACCOUNT_ID); + assert!(!undeployed_account_tx_generator.is_deployed()); + let deploy_tx = undeployed_account_tx_generator.generate_deploy_account(); + vec![deploy_tx] } -fn test_funding(tx_hashes: &[TransactionHash]) -> Vec { +fn test_single_tx(tx_hashes: &[TransactionHash]) -> Vec { assert_eq!(tx_hashes.len(), 1, "Expected a single transaction"); tx_hashes.to_vec() }