From 7fb64ec6df2d367624f468c1ca77c719b07b4327 Mon Sep 17 00:00:00 2001 From: azurwastaken Date: Wed, 2 Oct 2024 17:26:25 +0200 Subject: [PATCH] feat(test): block conversion on sync tests (#283) Co-authored-by: 0xevolve --- CHANGELOG.md | 1 + Cargo.lock | 1 + crates/client/block_import/Cargo.toml | 3 + crates/client/block_import/src/tests.rs | 2 +- .../src/tests/block_import_utils.rs | 28 ++- crates/client/sync/Cargo.toml | 3 + crates/client/sync/src/l2.rs | 174 ++++++++++++++++++ 7 files changed, 210 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54bb151bc..d8293428d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Next release +- test: add block conversion task test - fix(docs): updated readme and fixed launcher - fix(ci): added gateway key to fix rate limit on tests - feat(cli): launcher script and release workflows diff --git a/Cargo.lock b/Cargo.lock index 1e1c1d636..186f14a19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5817,6 +5817,7 @@ dependencies = [ "mp-class", "mp-convert", "mp-receipt", + "mp-state-update", "mp-transactions", "mp-utils", "num-traits 0.2.19", diff --git a/crates/client/block_import/Cargo.toml b/crates/client/block_import/Cargo.toml index f1ca6e95d..1c64eede2 100644 --- a/crates/client/block_import/Cargo.toml +++ b/crates/client/block_import/Cargo.toml @@ -8,6 +8,9 @@ repository.workspace = true version.workspace = true license.workspace = true +[features] +default = [] +testing = [] [dependencies] anyhow.workspace = true bitvec.workspace = true diff --git a/crates/client/block_import/src/tests.rs b/crates/client/block_import/src/tests.rs index bf8a7d7a6..9081a22b4 100644 --- a/crates/client/block_import/src/tests.rs +++ b/crates/client/block_import/src/tests.rs @@ -1,2 +1,2 @@ -#[cfg(test)] +#[cfg(any(test, feature = "testing"))] pub mod block_import_utils; diff --git a/crates/client/block_import/src/tests/block_import_utils.rs b/crates/client/block_import/src/tests/block_import_utils.rs index d92742231..ceb47a31f 100644 --- a/crates/client/block_import/src/tests/block_import_utils.rs +++ b/crates/client/block_import/src/tests/block_import_utils.rs @@ -2,9 +2,11 @@ use mp_block::header::{GasPrices, L1DataAvailabilityMode}; use mp_block::Header; use mp_chain_config::StarknetVersion; use mp_state_update::StateDiff; +use starknet_core::types::Felt; use crate::{ - BlockValidationContext, PreValidatedBlock, PreValidatedPendingBlock, UnverifiedHeader, ValidatedCommitments, + BlockValidationContext, PreValidatedBlock, PreValidatedPendingBlock, UnverifiedCommitments, UnverifiedFullBlock, + UnverifiedHeader, ValidatedCommitments, }; use starknet_api::{core::ChainId, felt}; @@ -105,6 +107,30 @@ pub fn create_dummy_block() -> PreValidatedBlock { } } +/// Creates a dummy PreValidatedBlock for testing purposes. +/// +/// This function generates a PreValidatedBlock with predefined values, +/// useful for testing update_tries scenarios. +pub fn create_dummy_unverified_full_block() -> UnverifiedFullBlock { + UnverifiedFullBlock { + header: UnverifiedHeader { + parent_block_hash: Some(Felt::ZERO), + sequencer_address: Felt::ZERO, + block_timestamp: 0, + protocol_version: StarknetVersion::default(), + l1_gas_price: GasPrices::default(), + l1_da_mode: L1DataAvailabilityMode::Blob, + }, + transactions: vec![], + unverified_block_number: Some(0), + state_diff: StateDiff::default(), + receipts: vec![], + declared_classes: vec![], + commitments: UnverifiedCommitments::default(), + trusted_converted_classes: vec![], + } +} + /// Creates a dummy PreValidatedPendingBlock for testing purposes. pub fn create_dummy_pending_block() -> PreValidatedPendingBlock { PreValidatedPendingBlock { diff --git a/crates/client/sync/Cargo.toml b/crates/client/sync/Cargo.toml index 900828be0..579ca2560 100644 --- a/crates/client/sync/Cargo.toml +++ b/crates/client/sync/Cargo.toml @@ -27,6 +27,7 @@ mp-chain-config = { workspace = true } mp-class = { workspace = true } mp-convert = { workspace = true } mp-receipt = { workspace = true } +mp-state-update = { workspace = true } mp-transactions = { workspace = true } mp-utils = { workspace = true } @@ -61,3 +62,5 @@ url = { workspace = true } httpmock = { workspace = true } tempfile = { workspace = true } rstest = { workspace = true } +mc-db = { workspace = true, features = ["testing"] } +mc-block-import = { workspace = true, features = ["testing"] } diff --git a/crates/client/sync/src/l2.rs b/crates/client/sync/src/l2.rs index faf83a81a..35eb44465 100644 --- a/crates/client/sync/src/l2.rs +++ b/crates/client/sync/src/l2.rs @@ -247,3 +247,177 @@ pub async fn sync( Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::utils::gateway::{test_setup, TestContext}; + use mc_block_import::tests::block_import_utils::create_dummy_unverified_full_block; + use mc_block_import::BlockImporter; + use mc_db::{db_block_id::DbBlockId, MadaraBackend}; + use mc_metrics::MetricsRegistry; + use mc_telemetry::TelemetryService; + use mp_block::header::L1DataAvailabilityMode; + use mp_block::MadaraBlock; + use mp_chain_config::StarknetVersion; + use rstest::rstest; + use starknet_types_core::felt::Felt; + use std::sync::Arc; + use tokio::sync::mpsc; + + /// Test the `l2_verify_and_apply_task` function. + /// + /// + /// This test verifies the behavior of the `l2_verify_and_apply_task` by simulating + /// a block verification and application process. + /// + /// # Test Steps + /// 1. Initialize the backend and necessary components. + /// 2. Create a mock block. + /// 3. Spawn the `l2_verify_and_apply_task` in a new thread. + /// 4. Send the mock block for verification and application. + /// 5. Wait for the task to complete or for a timeout to occur. + /// 6. Verify that the block has been correctly applied to the backend. + /// + /// # Panics + /// - If the task fails or if the waiting timeout is exceeded. + /// - If the block is not correctly applied to the backend. + #[rstest] + #[tokio::test] + async fn test_l2_verify_and_apply_task(test_setup: Arc) { + let backend = test_setup; + let (block_conv_sender, block_conv_receiver) = mpsc::channel(100); + let block_importer = + Arc::new(BlockImporter::new(backend.clone(), &MetricsRegistry::dummy(), None, true).unwrap()); + let validation = BlockValidationContext::new(backend.chain_config().chain_id.clone()); + let telemetry = TelemetryService::new(true, vec![]).unwrap().new_handle(); + + let mock_block = create_dummy_unverified_full_block(); + + let task_handle = tokio::spawn(l2_verify_and_apply_task( + backend.clone(), + block_conv_receiver, + block_importer.clone(), + validation.clone(), + Some(1), + telemetry, + )); + + let mock_pre_validated_block = block_importer.pre_validate(mock_block, validation.clone()).await.unwrap(); + block_conv_sender.send(mock_pre_validated_block).await.unwrap(); + + drop(block_conv_sender); + + match tokio::time::timeout(std::time::Duration::from_secs(120), task_handle).await { + Ok(Ok(_)) => (), + Ok(Err(e)) => panic!("Task failed: {:?}", e), + Err(_) => panic!("Timeout reached while waiting for task completion"), + } + + let applied_block = backend.get_block(&DbBlockId::BlockN(0)).unwrap(); + assert!(applied_block.is_some(), "The block was not applied correctly"); + let applied_block = MadaraBlock::try_from(applied_block.unwrap()).unwrap(); + + assert_eq!(applied_block.info.header.block_number, 0, "Block number does not match"); + assert_eq!(applied_block.info.header.block_timestamp, 0, "Block timestamp does not match"); + assert_eq!(applied_block.info.header.parent_block_hash, Felt::ZERO, "Parent block hash does not match"); + assert!(applied_block.inner.transactions.is_empty(), "Block should not contain any transactions"); + assert_eq!( + applied_block.info.header.protocol_version, + StarknetVersion::default(), + "Protocol version does not match" + ); + assert_eq!(applied_block.info.header.sequencer_address, Felt::ZERO, "Sequencer address does not match"); + assert_eq!(applied_block.info.header.l1_gas_price.eth_l1_gas_price, 0, "L1 gas price (ETH) does not match"); + assert_eq!(applied_block.info.header.l1_gas_price.strk_l1_gas_price, 0, "L1 gas price (STRK) does not match"); + assert_eq!(applied_block.info.header.l1_da_mode, L1DataAvailabilityMode::Blob, "L1 DA mode does not match"); + } + + /// Test the `l2_block_conversion_task` function. + /// + /// Steps: + /// 1. Initialize necessary components. + /// 2. Create a mock block. + /// 3. Send the mock block to updates_sender + /// 4. Call the `l2_block_conversion_task` function with the mock data. + /// 5. Verify the results and ensure the function behaves as expected. + #[rstest] + #[tokio::test] + async fn test_l2_block_conversion_task(test_setup: Arc) { + let backend = test_setup; + let (updates_sender, updates_receiver) = mpsc::channel(100); + let (output_sender, mut output_receiver) = mpsc::channel(100); + let block_import = + Arc::new(BlockImporter::new(backend.clone(), &MetricsRegistry::dummy(), None, true).unwrap()); + let validation = BlockValidationContext::new(backend.chain_config().chain_id.clone()); + + let mock_block = create_dummy_unverified_full_block(); + + updates_sender.send(mock_block).await.unwrap(); + + let task_handle = + tokio::spawn(l2_block_conversion_task(updates_receiver, output_sender, block_import, validation)); + + let result = tokio::time::timeout(std::time::Duration::from_secs(5), output_receiver.recv()).await; + match result { + Ok(Some(b)) => { + assert_eq!(b.unverified_block_number, Some(0), "Block number does not match"); + } + Ok(None) => panic!("Channel closed without receiving a result"), + Err(_) => panic!("Timeout reached while waiting for result"), + } + + // Close the updates_sender channel to allow the task to complete + drop(updates_sender); + + match tokio::time::timeout(std::time::Duration::from_secs(5), task_handle).await { + Ok(Ok(_)) => (), + Ok(Err(e)) => panic!("Task failed: {:?}", e), + Err(_) => panic!("Timeout reached while waiting for task completion"), + } + } + + /// Test the `l2_pending_block_task` function. + /// + /// This test function verifies the behavior of the `l2_pending_block_task`. + /// It simulates the necessary environment and checks that the task executes correctly + /// within a specified timeout. + /// + /// # Test Steps + /// 1. Initialize the backend and test context. + /// 2. Create a `BlockImporter` and a `BlockValidationContext`. + /// 3. Spawn the `l2_pending_block_task` in a new thread. + /// 4. Simulate the "once_caught_up" signal. + /// 5. Wait for the task to complete or for a timeout to occur. + /// + /// # Panics + /// - If the task fails or if the waiting timeout is exceeded. + #[rstest] + #[tokio::test] + async fn test_l2_pending_block_task(test_setup: Arc) { + let backend = test_setup; + let ctx = TestContext::new(backend.clone()); + let block_import = + Arc::new(BlockImporter::new(backend.clone(), &MetricsRegistry::dummy(), None, true).unwrap()); + let validation = BlockValidationContext::new(backend.chain_config().chain_id.clone()); + + let task_handle = tokio::spawn(l2_pending_block_task( + backend.clone(), + block_import.clone(), + validation.clone(), + ctx.once_caught_up_receiver, + ctx.provider.clone(), + std::time::Duration::from_secs(5), + )); + + // Simulate the "once_caught_up" signal + ctx.once_caught_up_sender.send(()).unwrap(); + + // Wait for the task to complete + match tokio::time::timeout(std::time::Duration::from_secs(120), task_handle).await { + Ok(Ok(_)) => (), + Ok(Err(e)) => panic!("Task failed: {:?}", e), + Err(_) => panic!("Timeout reached while waiting for task completion"), + } + } +}