From 6f4715f0f572b9ff4b4ace563cd0d4686c873625 Mon Sep 17 00:00:00 2001 From: Itay Tsabary Date: Sun, 24 Nov 2024 21:03:31 +0200 Subject: [PATCH] wip commit-id:4d9607d8 --- Cargo.lock | 1 + crates/starknet_integration_tests/Cargo.toml | 7 + .../end_to_end_integration_test.rs | 52 ++++- .../src/bin/end_to_end_integration_test.rs | 211 ++++++++++++++++++ .../src/test_utils/compilation.rs | 13 +- 5 files changed, 268 insertions(+), 16 deletions(-) rename crates/starknet_integration_tests/{tests => bin}/end_to_end_integration_test.rs (78%) create mode 100644 crates/starknet_integration_tests/src/bin/end_to_end_integration_test.rs diff --git a/Cargo.lock b/Cargo.lock index c312f12818..6e5f4bafbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10307,6 +10307,7 @@ dependencies = [ "cairo-lang-starknet-classes", "futures", "indexmap 2.6.0", + "infra_utils", "mempool_test_utils", "papyrus_common", "papyrus_consensus", diff --git a/crates/starknet_integration_tests/Cargo.toml b/crates/starknet_integration_tests/Cargo.toml index 2a77b40534..f6e957d73d 100644 --- a/crates/starknet_integration_tests/Cargo.toml +++ b/crates/starknet_integration_tests/Cargo.toml @@ -16,6 +16,7 @@ blockifier.workspace = true cairo-lang-starknet-classes.workspace = true futures.workspace = true indexmap.workspace = true +infra_utils.workspace = true mempool_test_utils.workspace = true papyrus_common.workspace = true papyrus_consensus.workspace = true @@ -44,8 +45,14 @@ tempfile.workspace = true tokio.workspace = true tracing.workspace = true + [dev-dependencies] futures.workspace = true pretty_assertions.workspace = true rstest.workspace = true starknet_sequencer_infra.workspace = true + + +[[bin]] +name = "end_to_end_integration_test" +path = "src/bin/end_to_end_integration_test.rs" diff --git a/crates/starknet_integration_tests/tests/end_to_end_integration_test.rs b/crates/starknet_integration_tests/bin/end_to_end_integration_test.rs similarity index 78% rename from crates/starknet_integration_tests/tests/end_to_end_integration_test.rs rename to crates/starknet_integration_tests/bin/end_to_end_integration_test.rs index 1602e1f87c..e3f3abfb84 100644 --- a/crates/starknet_integration_tests/tests/end_to_end_integration_test.rs +++ b/crates/starknet_integration_tests/bin/end_to_end_integration_test.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use std::process::Stdio; use std::time::Duration; +use infra_utils::path::resolve_project_relative_path; use mempool_test_utils::starknet_api_test_utils::{AccountId, MultiAccountTransactionGenerator}; use papyrus_execution::execution_utils::get_nonce_at; use papyrus_storage::state::StateStorageReader; @@ -14,6 +15,7 @@ use starknet_api::state::StateNumber; use starknet_integration_tests::integration_test_setup::IntegrationTestSetup; use starknet_integration_tests::utils::{create_integration_test_tx_generator, send_account_txs}; use starknet_sequencer_infra::trace_util::configure_tracing; +use starknet_sequencer_node::test_utils::compilation::compile_node_result; use starknet_types_core::felt::Felt; use tokio::process::{Child, Command}; use tokio::task::{self, JoinHandle}; @@ -28,18 +30,28 @@ fn tx_generator() -> MultiAccountTransactionGenerator { // TODO(Tsabary): Move to a suitable util location. async fn spawn_node_child_task(node_config_path: PathBuf) -> Child { // Get the current working directory for the project - let project_path = env::current_dir().expect("Failed to get current directory").join("../.."); - - // TODO(Tsabary): Capture output to a log file, and present it in case of a failure. - // TODO(Tsabary): Change invocation from "cargo run" to separate compilation and invocation - // (build, and then invoke the binary). - Command::new("cargo") - .arg("run") - .arg("--bin") - .arg("starknet_sequencer_node") - .arg("--quiet") + // let project_path = env::current_dir().expect("Failed to get current + // directory").join("../.."); + + let compile_result = compile_node_result(); + info!("Compilation result {:?}.", compile_result); + + assert!(compile_result.is_ok(), "Compilation failed."); + // let compilation_result = Command::new("cargo") + // .arg("build") + // .arg("--bin") + // .arg("starknet_sequencer_node") + // .arg("--quiet") + // .current_dir(&project_path) + // .status().await; + + info!("Compiling the starknet_sequencer_node binary"); + let project_path = resolve_project_relative_path(".").expect("Failed to resolve project path"); + info!("project_path {:?}", project_path); + + // Run `cargo build` to compile the project + Command::new("target/debug/starknet_sequencer_node") .current_dir(&project_path) - .arg("--") .arg("--config_file") .arg(node_config_path.to_str().unwrap()) .stderr(Stdio::inherit()) @@ -47,6 +59,24 @@ async fn spawn_node_child_task(node_config_path: PathBuf) -> Child { .kill_on_drop(true) // Required for stopping the node when the handle is dropped. .spawn() .expect("Failed to spawn the sequencer node.") + + // // TODO(Tsabary): Capture output to a log file, and present it in case of a failure. + // // TODO(Tsabary): Change invocation from "cargo run" to separate compilation and invocation + // // (build, and then invoke the binary). + // Command::new("cargo") + // .arg("run") + // .arg("--bin") + // .arg("starknet_sequencer_node") + // .arg("--quiet") + // .current_dir(&project_path) + // .arg("--") + // .arg("--config_file") + // .arg(node_config_path.to_str().unwrap()) + // .stderr(Stdio::inherit()) + // .stdout(Stdio::null()) + // .kill_on_drop(true) // Required for stopping the node when the handle is dropped. + // .spawn() + // .expect("Failed to spawn the sequencer node.") } async fn spawn_run_node(node_config_path: PathBuf) -> JoinHandle<()> { diff --git a/crates/starknet_integration_tests/src/bin/end_to_end_integration_test.rs b/crates/starknet_integration_tests/src/bin/end_to_end_integration_test.rs new file mode 100644 index 0000000000..cdd72c3e62 --- /dev/null +++ b/crates/starknet_integration_tests/src/bin/end_to_end_integration_test.rs @@ -0,0 +1,211 @@ +use std::env; +use std::path::PathBuf; +use std::process::Stdio; +use std::time::Duration; + +use infra_utils::path::resolve_project_relative_path; +use mempool_test_utils::starknet_api_test_utils::{AccountId, MultiAccountTransactionGenerator}; +use papyrus_execution::execution_utils::get_nonce_at; +use papyrus_storage::state::StateStorageReader; +use papyrus_storage::StorageReader; +use starknet_api::block::BlockNumber; +use starknet_api::core::{ContractAddress, Nonce}; +use starknet_api::state::StateNumber; +use starknet_integration_tests::integration_test_setup::IntegrationTestSetup; +use starknet_integration_tests::utils::{create_integration_test_tx_generator, send_account_txs}; +use starknet_sequencer_infra::trace_util::configure_tracing; +use starknet_sequencer_node::test_utils::compilation::compile_node_result; +use starknet_types_core::felt::Felt; +use tokio::process::{Child, Command}; +use tokio::task::{self, JoinHandle}; +use tokio::time::interval; +use tracing::{error, info}; + + +// TODO(Tsabary): Move to a suitable util location. +async fn spawn_node_child_task(node_config_path: PathBuf) -> Child { + // Get the current working directory for the project + // let project_path = env::current_dir().expect("Failed to get current + // directory").join("../.."); + + let compile_result = compile_node_result(); + info!("Compilation result {:?}.", compile_result); + + assert!(compile_result.is_ok(), "Compilation failed."); + // let compilation_result = Command::new("cargo") + // .arg("build") + // .arg("--bin") + // .arg("starknet_sequencer_node") + // .arg("--quiet") + // .current_dir(&project_path) + // .status().await; + + info!("Compiling the starknet_sequencer_node binary"); + let project_path = resolve_project_relative_path(".").expect("Failed to resolve project path"); + info!("project_path {:?}", project_path); + + // Run `cargo build` to compile the project + Command::new("target/debug/starknet_sequencer_node") + .current_dir(&project_path) + .arg("--config_file") + .arg(node_config_path.to_str().unwrap()) + .stderr(Stdio::inherit()) + .stdout(Stdio::null()) + .kill_on_drop(true) // Required for stopping the node when the handle is dropped. + .spawn() + .expect("Failed to spawn the sequencer node.") + + // // TODO(Tsabary): Capture output to a log file, and present it in case of a failure. + // // TODO(Tsabary): Change invocation from "cargo run" to separate compilation and invocation + // // (build, and then invoke the binary). + // Command::new("cargo") + // .arg("run") + // .arg("--bin") + // .arg("starknet_sequencer_node") + // .arg("--quiet") + // .current_dir(&project_path) + // .arg("--") + // .arg("--config_file") + // .arg(node_config_path.to_str().unwrap()) + // .stderr(Stdio::inherit()) + // .stdout(Stdio::null()) + // .kill_on_drop(true) // Required for stopping the node when the handle is dropped. + // .spawn() + // .expect("Failed to spawn the sequencer node.") +} + +async fn spawn_run_node(node_config_path: PathBuf) -> JoinHandle<()> { + task::spawn(async move { + info!("Running the node from its spawned task."); + let _node_run_result = spawn_node_child_task(node_config_path). + await. // awaits the completion of spawn_node_child_task. + wait(). // runs the node until completion -- should be running indefinitely. + await; // awaits the completion of the node. + panic!("Node stopped unexpectedly."); + }) +} + +/// Reads the latest block number from the storage. +fn get_latest_block_number(storage_reader: &StorageReader) -> BlockNumber { + let txn = storage_reader.begin_ro_txn().unwrap(); + txn.get_state_marker() + .expect("There should always be a state marker") + .prev() + .expect("There should be a previous block in the storage, set by the test setup") +} + +/// Reads an account nonce after a block number from storage. +fn get_account_nonce( + storage_reader: &StorageReader, + block_number: BlockNumber, + contract_address: ContractAddress, +) -> Nonce { + let txn = storage_reader.begin_ro_txn().unwrap(); + let state_number = StateNumber::unchecked_right_after_block(block_number); + get_nonce_at(&txn, state_number, None, contract_address) + .expect("Should always be Ok(Some(Nonce))") + .expect("Should always be Some(Nonce)") +} + +/// Sample a storage until sufficiently many blocks have been stored. Returns an error if after +/// the given number of attempts the target block number has not been reached. +async fn await_block( + interval_duration: Duration, + target_block_number: BlockNumber, + max_attempts: usize, + storage_reader: &StorageReader, +) -> Result<(), ()> { + let mut interval = interval(interval_duration); + let mut count = 0; + loop { + // Read the latest block number. + let latest_block_number = get_latest_block_number(storage_reader); + count += 1; + + // Check if reached the target block number. + if latest_block_number >= target_block_number { + info!("Found block {} after {} queries.", target_block_number, count); + return Ok(()); + } + + // Check if reached the maximum attempts. + if count > max_attempts { + error!( + "Latest block is {}, expected {}, stopping sampling.", + latest_block_number, target_block_number + ); + return Err(()); + } + + // Wait for the next interval. + interval.tick().await; + } +} + +#[tokio::main] +/// Main entry point of the committer CLI. +async fn main() { + + + let mut tx_generator : MultiAccountTransactionGenerator = create_integration_test_tx_generator(); + + + const EXPECTED_BLOCK_NUMBER: BlockNumber = BlockNumber(15); + + configure_tracing(); + info!("Running integration test setup."); + + // Creating the storage for the test. + + let integration_test_setup = IntegrationTestSetup::new_from_tx_generator(&tx_generator).await; + info!("Integration test setup completed."); + + info!("Running sequencer node."); + let node_run_handle = spawn_run_node(integration_test_setup.node_config_path).await; + + // Wait for the node to start. + match integration_test_setup.is_alive_test_client.await_alive(Duration::from_secs(5), 30).await + { + Ok(_) => {} + Err(_) => panic!("Node is not alive."), + } + + info!("Running integration test simulator."); + + let send_rpc_tx_fn = + &mut |rpc_tx| integration_test_setup.add_tx_http_client.assert_add_tx_success(rpc_tx); + + const ACCOUNT_ID_0: AccountId = 0; + let n_txs = 50; + let sender_address = tx_generator.account_with_id(ACCOUNT_ID_0).sender_address(); + info!("Sending {n_txs} txs."); + let tx_hashes = send_account_txs(tx_generator, ACCOUNT_ID_0, n_txs, send_rpc_tx_fn).await; + + info!("Awaiting until {EXPECTED_BLOCK_NUMBER} blocks have been created."); + + let (batcher_storage_reader, _) = + papyrus_storage::open_storage(integration_test_setup.batcher_storage_config) + .expect("Failed to open batcher's storage"); + + match await_block(Duration::from_secs(5), EXPECTED_BLOCK_NUMBER, 15, &batcher_storage_reader) + .await + { + Ok(_) => {} + Err(_) => panic!("Did not reach expected block number."), + } + + info!("Shutting the node down."); + node_run_handle.abort(); + let res = node_run_handle.await; + assert!( + res.expect_err("Node should have been stopped.").is_cancelled(), + "Node should have been stopped." + ); + + info!("Verifying tx sender account nonce."); + let expected_nonce_value = tx_hashes.len() + 1; + let expected_nonce = + Nonce(Felt::from_hex_unchecked(format!("0x{:X}", expected_nonce_value).as_str())); + let nonce = get_account_nonce(&batcher_storage_reader, EXPECTED_BLOCK_NUMBER, sender_address); + assert_eq!(nonce, expected_nonce); +} diff --git a/crates/starknet_sequencer_node/src/test_utils/compilation.rs b/crates/starknet_sequencer_node/src/test_utils/compilation.rs index 645fe536ae..8fc3e8ff70 100644 --- a/crates/starknet_sequencer_node/src/test_utils/compilation.rs +++ b/crates/starknet_sequencer_node/src/test_utils/compilation.rs @@ -1,6 +1,7 @@ +use std::io; use std::process::{Command, ExitStatus, Stdio}; -use std::{env, io}; +use infra_utils::path::resolve_project_relative_path; use tracing::info; #[cfg(test)] @@ -17,15 +18,17 @@ pub enum NodeCompilationError { /// Compiles the node using `cargo build` for testing purposes. fn compile_node() -> io::Result { - info!("Compiling the project"); - // Get the current working directory for the project - let project_path = env::current_dir().expect("Failed to get current directory"); + info!("Compiling the starknet_sequencer_node binary"); + let project_path = resolve_project_relative_path(".").expect("Failed to resolve project path"); + info!("project_path {:?}", project_path); // Run `cargo build` to compile the project let compilation_result = Command::new("cargo") .arg("build") + .arg("--bin") + .arg("starknet_sequencer_node") .current_dir(&project_path) - .arg("--quiet") + // .arg("--quiet") .stderr(Stdio::inherit()) .stdout(Stdio::inherit()) .status();