From 33557b122f01c991e933f924a28e565f4119e0e1 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Tue, 21 Apr 2026 17:46:16 +0300 Subject: [PATCH] fix: comments fix 1 --- integration_tests/src/lib.rs | 178 +------------- integration_tests/src/setup.rs | 220 +++++++++++++++++ integration_tests/src/test_context_ffi.rs | 208 +--------------- integration_tests/tests/indexer.rs | 269 +------------------- integration_tests/tests/indexer_ffi.rs | 284 ++++++++++++++++++++++ 5 files changed, 528 insertions(+), 631 deletions(-) create mode 100644 integration_tests/src/setup.rs create mode 100644 integration_tests/tests/indexer_ffi.rs diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 09017db3..fcae2c71 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -1,12 +1,12 @@ //! This library contains common code for integration tests. -use std::{net::SocketAddr, path::PathBuf, sync::LazyLock}; +use std::sync::LazyLock; -use anyhow::{Context as _, Result, bail}; +use anyhow::{Context as _, Result}; use common::{HashType, transaction::NSSATransaction}; use futures::FutureExt as _; use indexer_service::IndexerHandle; -use log::{debug, error, warn}; +use log::{debug, error}; use nssa::{AccountId, PrivacyPreservingTransaction}; use nssa_core::Commitment; use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait as _}; @@ -14,9 +14,12 @@ use sequencer_service::SequencerHandle; use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder}; use tempfile::TempDir; use testcontainers::compose::DockerCompose; -use wallet::{WalletCore, config::WalletConfigOverrides}; +use wallet::WalletCore; + +use crate::setup::{setup_bedrock_node, setup_indexer, setup_sequencer, setup_wallet}; pub mod config; +pub mod setup; pub mod test_context_ffi; // TODO: Remove this and control time from tests @@ -68,13 +71,13 @@ impl TestContext { debug!("Test context setup"); - let (bedrock_compose, bedrock_addr) = Self::setup_bedrock_node().await?; + let (bedrock_compose, bedrock_addr) = setup_bedrock_node().await?; - let (indexer_handle, temp_indexer_dir) = Self::setup_indexer(bedrock_addr, &initial_data) + let (indexer_handle, temp_indexer_dir) = setup_indexer(bedrock_addr, &initial_data) .await .context("Failed to setup Indexer")?; - let (sequencer_handle, temp_sequencer_dir) = Self::setup_sequencer( + let (sequencer_handle, temp_sequencer_dir) = setup_sequencer( sequencer_partial_config, bedrock_addr, indexer_handle.addr(), @@ -84,7 +87,7 @@ impl TestContext { .context("Failed to setup Sequencer")?; let (wallet, temp_wallet_dir, wallet_password) = - Self::setup_wallet(sequencer_handle.addr(), &initial_data) + setup_wallet(sequencer_handle.addr(), &initial_data) .await .context("Failed to setup wallet")?; @@ -113,165 +116,6 @@ impl TestContext { }) } - async fn setup_bedrock_node() -> Result<(DockerCompose, SocketAddr)> { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let bedrock_compose_path = - PathBuf::from(manifest_dir).join("../bedrock/docker-compose.yml"); - - let mut compose = DockerCompose::with_auto_client(&[bedrock_compose_path]) - .await - .context("Failed to setup docker compose for Bedrock")? - // Setting port to 0 to avoid conflicts between parallel tests, actual port will be retrieved after container is up - .with_env("PORT", "0"); - - #[expect( - clippy::items_after_statements, - reason = "This is more readable is this function used just after its definition" - )] - async fn up_and_retrieve_port(compose: &mut DockerCompose) -> Result { - compose - .up() - .await - .context("Failed to bring up Bedrock services")?; - let container = compose - .service(BEDROCK_SERVICE_WITH_OPEN_PORT) - .with_context(|| { - format!( - "Failed to get Bedrock service container `{BEDROCK_SERVICE_WITH_OPEN_PORT}`" - ) - })?; - - let ports = container.ports().await.with_context(|| { - format!( - "Failed to get ports for Bedrock service container `{}`", - container.id() - ) - })?; - ports - .map_to_host_port_ipv4(BEDROCK_SERVICE_PORT) - .with_context(|| { - format!( - "Failed to retrieve host port of {BEDROCK_SERVICE_PORT} container \ - port for container `{}`, existing ports: {ports:?}", - container.id() - ) - }) - } - - let mut port = None; - let mut attempt = 0_u32; - let max_attempts = 5_u32; - while port.is_none() && attempt < max_attempts { - attempt = attempt - .checked_add(1) - .expect("We check that attempt < max_attempts, so this won't overflow"); - match up_and_retrieve_port(&mut compose).await { - Ok(p) => { - port = Some(p); - } - Err(err) => { - warn!( - "Failed to bring up Bedrock services: {err:?}, attempt {attempt}/{max_attempts}" - ); - } - } - } - let Some(port) = port else { - bail!("Failed to bring up Bedrock services after {max_attempts} attempts"); - }; - - let addr = SocketAddr::from(([127, 0, 0, 1], port)); - Ok((compose, addr)) - } - - async fn setup_indexer( - bedrock_addr: SocketAddr, - initial_data: &config::InitialData, - ) -> Result<(IndexerHandle, TempDir)> { - let temp_indexer_dir = - tempfile::tempdir().context("Failed to create temp dir for indexer home")?; - - debug!( - "Using temp indexer home at {}", - temp_indexer_dir.path().display() - ); - - let indexer_config = config::indexer_config( - bedrock_addr, - temp_indexer_dir.path().to_owned(), - initial_data, - ) - .context("Failed to create Indexer config")?; - - indexer_service::run_server(indexer_config, 0) - .await - .context("Failed to run Indexer Service") - .map(|handle| (handle, temp_indexer_dir)) - } - - async fn setup_sequencer( - partial: config::SequencerPartialConfig, - bedrock_addr: SocketAddr, - indexer_addr: SocketAddr, - initial_data: &config::InitialData, - ) -> Result<(SequencerHandle, TempDir)> { - let temp_sequencer_dir = - tempfile::tempdir().context("Failed to create temp dir for sequencer home")?; - - debug!( - "Using temp sequencer home at {}", - temp_sequencer_dir.path().display() - ); - - let config = config::sequencer_config( - partial, - temp_sequencer_dir.path().to_owned(), - bedrock_addr, - indexer_addr, - initial_data, - ) - .context("Failed to create Sequencer config")?; - - let sequencer_handle = sequencer_service::run(config, 0).await?; - - Ok((sequencer_handle, temp_sequencer_dir)) - } - - async fn setup_wallet( - sequencer_addr: SocketAddr, - initial_data: &config::InitialData, - ) -> Result<(WalletCore, TempDir, String)> { - let config = config::wallet_config(sequencer_addr, initial_data) - .context("Failed to create Wallet config")?; - let config_serialized = - serde_json::to_string_pretty(&config).context("Failed to serialize Wallet config")?; - - let temp_wallet_dir = - tempfile::tempdir().context("Failed to create temp dir for wallet home")?; - - let config_path = temp_wallet_dir.path().join("wallet_config.json"); - std::fs::write(&config_path, config_serialized) - .context("Failed to write wallet config in temp dir")?; - - let storage_path = temp_wallet_dir.path().join("storage.json"); - let config_overrides = WalletConfigOverrides::default(); - - let wallet_password = "test_pass".to_owned(); - let (wallet, _mnemonic) = WalletCore::new_init_storage( - config_path, - storage_path, - Some(config_overrides), - &wallet_password, - ) - .context("Failed to init wallet")?; - wallet - .store_persistent_data() - .await - .context("Failed to store wallet persistent data")?; - - Ok((wallet, temp_wallet_dir, wallet_password)) - } - /// Get reference to the wallet. #[must_use] pub const fn wallet(&self) -> &WalletCore { diff --git a/integration_tests/src/setup.rs b/integration_tests/src/setup.rs new file mode 100644 index 00000000..0150cbd7 --- /dev/null +++ b/integration_tests/src/setup.rs @@ -0,0 +1,220 @@ +use std::{ + ffi::{CString, c_char}, + fs::File, + io::Write, + net::SocketAddr, + path::PathBuf, +}; + +use anyhow::{Context as _, Result, bail}; +use indexer_ffi::{IndexerServiceFFI, api::lifecycle::InitializedIndexerServiceFFIResult}; +use indexer_service::IndexerHandle; +use log::{debug, warn}; +use sequencer_service::SequencerHandle; +use tempfile::TempDir; +use testcontainers::compose::DockerCompose; +use wallet::{WalletCore, config::WalletConfigOverrides}; + +use crate::{BEDROCK_SERVICE_PORT, BEDROCK_SERVICE_WITH_OPEN_PORT, config}; + +unsafe extern "C" { + fn start_indexer(config_path: *const c_char, port: u16) -> InitializedIndexerServiceFFIResult; +} + +pub(crate) async fn setup_bedrock_node() -> Result<(DockerCompose, SocketAddr)> { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let bedrock_compose_path = PathBuf::from(manifest_dir).join("../bedrock/docker-compose.yml"); + + let mut compose = DockerCompose::with_auto_client(&[bedrock_compose_path]) + .await + .context("Failed to setup docker compose for Bedrock")? + // Setting port to 0 to avoid conflicts between parallel tests, actual port will be retrieved after container is up + .with_env("PORT", "0"); + + #[expect( + clippy::items_after_statements, + reason = "This is more readable is this function used just after its definition" + )] + async fn up_and_retrieve_port(compose: &mut DockerCompose) -> Result { + compose + .up() + .await + .context("Failed to bring up Bedrock services")?; + let container = compose + .service(BEDROCK_SERVICE_WITH_OPEN_PORT) + .with_context(|| { + format!( + "Failed to get Bedrock service container `{BEDROCK_SERVICE_WITH_OPEN_PORT}`" + ) + })?; + + let ports = container.ports().await.with_context(|| { + format!( + "Failed to get ports for Bedrock service container `{}`", + container.id() + ) + })?; + ports + .map_to_host_port_ipv4(BEDROCK_SERVICE_PORT) + .with_context(|| { + format!( + "Failed to retrieve host port of {BEDROCK_SERVICE_PORT} container \ + port for container `{}`, existing ports: {ports:?}", + container.id() + ) + }) + } + + let mut port = None; + let mut attempt = 0_u32; + let max_attempts = 5_u32; + while port.is_none() && attempt < max_attempts { + attempt = attempt + .checked_add(1) + .expect("We check that attempt < max_attempts, so this won't overflow"); + match up_and_retrieve_port(&mut compose).await { + Ok(p) => { + port = Some(p); + } + Err(err) => { + warn!( + "Failed to bring up Bedrock services: {err:?}, attempt {attempt}/{max_attempts}" + ); + } + } + } + let Some(port) = port else { + bail!("Failed to bring up Bedrock services after {max_attempts} attempts"); + }; + + let addr = SocketAddr::from(([127, 0, 0, 1], port)); + Ok((compose, addr)) +} + +pub(crate) async fn setup_indexer( + bedrock_addr: SocketAddr, + initial_data: &config::InitialData, +) -> Result<(IndexerHandle, TempDir)> { + let temp_indexer_dir = + tempfile::tempdir().context("Failed to create temp dir for indexer home")?; + + debug!( + "Using temp indexer home at {}", + temp_indexer_dir.path().display() + ); + + let indexer_config = config::indexer_config( + bedrock_addr, + temp_indexer_dir.path().to_owned(), + initial_data, + ) + .context("Failed to create Indexer config")?; + + indexer_service::run_server(indexer_config, 0) + .await + .context("Failed to run Indexer Service") + .map(|handle| (handle, temp_indexer_dir)) +} + +pub(crate) async fn setup_sequencer( + partial: config::SequencerPartialConfig, + bedrock_addr: SocketAddr, + indexer_addr: SocketAddr, + initial_data: &config::InitialData, +) -> Result<(SequencerHandle, TempDir)> { + let temp_sequencer_dir = + tempfile::tempdir().context("Failed to create temp dir for sequencer home")?; + + debug!( + "Using temp sequencer home at {}", + temp_sequencer_dir.path().display() + ); + + let config = config::sequencer_config( + partial, + temp_sequencer_dir.path().to_owned(), + bedrock_addr, + indexer_addr, + initial_data, + ) + .context("Failed to create Sequencer config")?; + + let sequencer_handle = sequencer_service::run(config, 0).await?; + + Ok((sequencer_handle, temp_sequencer_dir)) +} + +pub(crate) async fn setup_wallet( + sequencer_addr: SocketAddr, + initial_data: &config::InitialData, +) -> Result<(WalletCore, TempDir, String)> { + let config = config::wallet_config(sequencer_addr, initial_data) + .context("Failed to create Wallet config")?; + let config_serialized = + serde_json::to_string_pretty(&config).context("Failed to serialize Wallet config")?; + + let temp_wallet_dir = + tempfile::tempdir().context("Failed to create temp dir for wallet home")?; + + let config_path = temp_wallet_dir.path().join("wallet_config.json"); + std::fs::write(&config_path, config_serialized) + .context("Failed to write wallet config in temp dir")?; + + let storage_path = temp_wallet_dir.path().join("storage.json"); + let config_overrides = WalletConfigOverrides::default(); + + let wallet_password = "test_pass".to_owned(); + let (wallet, _mnemonic) = WalletCore::new_init_storage( + config_path, + storage_path, + Some(config_overrides), + &wallet_password, + ) + .context("Failed to init wallet")?; + wallet + .store_persistent_data() + .await + .context("Failed to store wallet persistent data")?; + + Ok((wallet, temp_wallet_dir, wallet_password)) +} + +pub(crate) fn setup_indexer_ffi( + bedrock_addr: SocketAddr, + initial_data: &config::InitialData, +) -> Result<(IndexerServiceFFI, TempDir)> { + let temp_indexer_dir = + tempfile::tempdir().context("Failed to create temp dir for indexer home")?; + + debug!( + "Using temp indexer home at {}", + temp_indexer_dir.path().display() + ); + + let indexer_config = config::indexer_config( + bedrock_addr, + temp_indexer_dir.path().to_owned(), + initial_data, + ) + .context("Failed to create Indexer config")?; + + let config_json = serde_json::to_vec(&indexer_config)?; + let config_path = temp_indexer_dir.path().join("indexer_config.json"); + let mut file = File::create(config_path.as_path())?; + file.write_all(&config_json)?; + file.flush()?; + + let res = + // SAFETY: lib function ensures validity of value. + unsafe { start_indexer(CString::new(config_path.to_str().unwrap())?.as_ptr(), 0) }; + + if res.error.is_error() { + anyhow::bail!("Indexer FFI error {:?}", res.error); + } + + Ok(( + // SAFETY: lib function ensures validity of value. + unsafe { std::ptr::read(res.value) }, + temp_indexer_dir, + )) +} diff --git a/integration_tests/src/test_context_ffi.rs b/integration_tests/src/test_context_ffi.rs index 8787c85a..7d21aa28 100644 --- a/integration_tests/src/test_context_ffi.rs +++ b/integration_tests/src/test_context_ffi.rs @@ -1,33 +1,23 @@ -use std::{ - ffi::{CString, c_char}, - fs::File, - io::Write as _, - net::SocketAddr, - path::PathBuf, - sync::Arc, -}; +use std::sync::Arc; -use anyhow::{Context as _, Result, bail}; +use anyhow::{Context as _, Result}; use futures::FutureExt as _; -use indexer_ffi::{IndexerServiceFFI, api::lifecycle::InitializedIndexerServiceFFIResult}; +use indexer_ffi::IndexerServiceFFI; use indexer_service_rpc::RpcClient as _; -use log::{debug, error, warn}; +use log::{debug, error}; use nssa::AccountId; use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait as _}; use sequencer_service::SequencerHandle; use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuilder}; use tempfile::TempDir; use testcontainers::compose::DockerCompose; -use wallet::{WalletCore, config::WalletConfigOverrides}; +use wallet::WalletCore; use crate::{ - BEDROCK_SERVICE_PORT, BEDROCK_SERVICE_WITH_OPEN_PORT, LOGGER, TestContextBuilder, config, + BEDROCK_SERVICE_WITH_OPEN_PORT, LOGGER, TestContextBuilder, config, + setup::{setup_bedrock_node, setup_indexer_ffi, setup_sequencer, setup_wallet}, }; -unsafe extern "C" { - fn start_indexer(config_path: *const c_char, port: u16) -> InitializedIndexerServiceFFIResult; -} - /// Test context which sets up a sequencer, indexer through ffi and a wallet for integration tests. /// /// It's memory and logically safe to create multiple instances of this struct in parallel tests, @@ -86,13 +76,13 @@ impl TestContextFFI { debug!("Test context setup"); - let (bedrock_compose, bedrock_addr) = runtime.block_on(Self::setup_bedrock_node())?; + let (bedrock_compose, bedrock_addr) = runtime.block_on(setup_bedrock_node())?; - let (indexer_ffi, temp_indexer_dir) = Self::setup_indexer_ffi(bedrock_addr, initial_data) - .context("Failed to setup Indexer")?; + let (indexer_ffi, temp_indexer_dir) = + setup_indexer_ffi(bedrock_addr, initial_data).context("Failed to setup Indexer")?; let (sequencer_handle, temp_sequencer_dir) = runtime - .block_on(Self::setup_sequencer( + .block_on(setup_sequencer( sequencer_partial_config, bedrock_addr, // SAFETY: addr is valid if indexer_ffi is valid. @@ -102,7 +92,7 @@ impl TestContextFFI { .context("Failed to setup Sequencer")?; let (wallet, temp_wallet_dir, wallet_password) = runtime - .block_on(Self::setup_wallet(sequencer_handle.addr(), initial_data)) + .block_on(setup_wallet(sequencer_handle.addr(), initial_data)) .context("Failed to setup wallet")?; let sequencer_url = config::addr_to_url(config::UrlProtocol::Http, sequencer_handle.addr()) @@ -136,180 +126,6 @@ impl TestContextFFI { )) } - async fn setup_bedrock_node() -> Result<(DockerCompose, SocketAddr)> { - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let bedrock_compose_path = - PathBuf::from(manifest_dir).join("../bedrock/docker-compose.yml"); - - let mut compose = DockerCompose::with_auto_client(&[bedrock_compose_path]) - .await - .context("Failed to setup docker compose for Bedrock")? - // Setting port to 0 to avoid conflicts between parallel tests, actual port will be retrieved after container is up - .with_env("PORT", "0"); - - #[expect( - clippy::items_after_statements, - reason = "This is more readable is this function used just after its definition" - )] - async fn up_and_retrieve_port(compose: &mut DockerCompose) -> Result { - compose - .up() - .await - .context("Failed to bring up Bedrock services")?; - let container = compose - .service(BEDROCK_SERVICE_WITH_OPEN_PORT) - .with_context(|| { - format!( - "Failed to get Bedrock service container `{BEDROCK_SERVICE_WITH_OPEN_PORT}`" - ) - })?; - - let ports = container.ports().await.with_context(|| { - format!( - "Failed to get ports for Bedrock service container `{}`", - container.id() - ) - })?; - ports - .map_to_host_port_ipv4(BEDROCK_SERVICE_PORT) - .with_context(|| { - format!( - "Failed to retrieve host port of {BEDROCK_SERVICE_PORT} container \ - port for container `{}`, existing ports: {ports:?}", - container.id() - ) - }) - } - - let mut port = None; - let mut attempt = 0_u32; - let max_attempts = 5_u32; - while port.is_none() && attempt < max_attempts { - attempt = attempt - .checked_add(1) - .expect("We check that attempt < max_attempts, so this won't overflow"); - match up_and_retrieve_port(&mut compose).await { - Ok(p) => { - port = Some(p); - } - Err(err) => { - warn!( - "Failed to bring up Bedrock services: {err:?}, attempt {attempt}/{max_attempts}" - ); - } - } - } - let Some(port) = port else { - bail!("Failed to bring up Bedrock services after {max_attempts} attempts"); - }; - - let addr = SocketAddr::from(([127, 0, 0, 1], port)); - Ok((compose, addr)) - } - - fn setup_indexer_ffi( - bedrock_addr: SocketAddr, - initial_data: &config::InitialData, - ) -> Result<(IndexerServiceFFI, TempDir)> { - let temp_indexer_dir = - tempfile::tempdir().context("Failed to create temp dir for indexer home")?; - - debug!( - "Using temp indexer home at {}", - temp_indexer_dir.path().display() - ); - - let indexer_config = config::indexer_config( - bedrock_addr, - temp_indexer_dir.path().to_owned(), - initial_data, - ) - .context("Failed to create Indexer config")?; - - let config_json = serde_json::to_vec(&indexer_config)?; - let config_path = temp_indexer_dir.path().join("indexer_config.json"); - let mut file = File::create(config_path.as_path())?; - file.write_all(&config_json)?; - file.flush()?; - - let res = - // SAFETY: lib function ensures validity of value. - unsafe { start_indexer(CString::new(config_path.to_str().unwrap())?.as_ptr(), 0) }; - - if res.error.is_error() { - anyhow::bail!("Indexer FFI error {:?}", res.error); - } - - Ok(( - // SAFETY: lib function ensures validity of value. - unsafe { std::ptr::read(res.value) }, - temp_indexer_dir, - )) - } - - async fn setup_sequencer( - partial: config::SequencerPartialConfig, - bedrock_addr: SocketAddr, - indexer_addr: SocketAddr, - initial_data: &config::InitialData, - ) -> Result<(SequencerHandle, TempDir)> { - let temp_sequencer_dir = - tempfile::tempdir().context("Failed to create temp dir for sequencer home")?; - - debug!( - "Using temp sequencer home at {}", - temp_sequencer_dir.path().display() - ); - - let config = config::sequencer_config( - partial, - temp_sequencer_dir.path().to_owned(), - bedrock_addr, - indexer_addr, - initial_data, - ) - .context("Failed to create Sequencer config")?; - - let sequencer_handle = sequencer_service::run(config, 0).await?; - - Ok((sequencer_handle, temp_sequencer_dir)) - } - - async fn setup_wallet( - sequencer_addr: SocketAddr, - initial_data: &config::InitialData, - ) -> Result<(WalletCore, TempDir, String)> { - let config = config::wallet_config(sequencer_addr, initial_data) - .context("Failed to create Wallet config")?; - let config_serialized = - serde_json::to_string_pretty(&config).context("Failed to serialize Wallet config")?; - - let temp_wallet_dir = - tempfile::tempdir().context("Failed to create temp dir for wallet home")?; - - let config_path = temp_wallet_dir.path().join("wallet_config.json"); - std::fs::write(&config_path, config_serialized) - .context("Failed to write wallet config in temp dir")?; - - let storage_path = temp_wallet_dir.path().join("storage.json"); - let config_overrides = WalletConfigOverrides::default(); - - let wallet_password = "test_pass".to_owned(); - let (wallet, _mnemonic) = WalletCore::new_init_storage( - config_path, - storage_path, - Some(config_overrides), - &wallet_password, - ) - .context("Failed to init wallet")?; - wallet - .store_persistent_data() - .await - .context("Failed to store wallet persistent data")?; - - Ok((wallet, temp_wallet_dir, wallet_password)) - } - /// Get reference to the wallet. #[must_use] pub const fn wallet(&self) -> &WalletCore { diff --git a/integration_tests/tests/indexer.rs b/integration_tests/tests/indexer.rs index 1e4efea2..f40b3607 100644 --- a/integration_tests/tests/indexer.rs +++ b/integration_tests/tests/indexer.rs @@ -10,8 +10,7 @@ use anyhow::{Context as _, Result}; use indexer_service_rpc::RpcClient as _; use integration_tests::{ TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id, - format_public_account_id, test_context_ffi::BlockingTestContextFFI, - verify_commitment_is_in_state, + format_public_account_id, verify_commitment_is_in_state, }; use log::info; use nssa::AccountId; @@ -276,269 +275,3 @@ async fn indexer_state_consistency_with_labels() -> Result<()> { Ok(()) } - -#[test] -fn indexer_test_run_ffi() -> Result<()> { - let blocking_ctx = BlockingTestContextFFI::new()?; - let runtime_wrapped = blocking_ctx.runtime(); - - // RUN OBSERVATION - runtime_wrapped.block_on(async { - tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; - }); - - let last_block_indexer = blocking_ctx.ctx().get_last_block_indexer(runtime_wrapped)?; - - info!("Last block on ind now is {last_block_indexer}"); - - assert!(last_block_indexer > 1); - - Ok(()) -} - -#[test] -fn indexer_ffi_block_batching() -> Result<()> { - let blocking_ctx = BlockingTestContextFFI::new()?; - let runtime_wrapped = blocking_ctx.runtime(); - let ctx = blocking_ctx.ctx(); - - // WAIT - info!("Waiting for indexer to parse blocks"); - runtime_wrapped.block_on(async { - tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; - }); - - let last_block_indexer = runtime_wrapped - .block_on(ctx.indexer_client().get_last_finalized_block_id()) - .unwrap(); - - info!("Last block on ind now is {last_block_indexer}"); - - assert!(last_block_indexer > 1); - - // Getting wide batch to fit all blocks (from latest backwards) - let mut block_batch = runtime_wrapped - .block_on(ctx.indexer_client().get_blocks(None, 100)) - .unwrap(); - - // Reverse to check chain consistency from oldest to newest - block_batch.reverse(); - - // Checking chain consistency - let mut prev_block_hash = block_batch.first().unwrap().header.hash; - - for block in &block_batch[1..] { - assert_eq!(block.header.prev_block_hash, prev_block_hash); - - info!("Block {} chain-consistent", block.header.block_id); - - prev_block_hash = block.header.hash; - } - - Ok(()) -} - -#[test] -fn indexer_ffi_state_consistency() -> Result<()> { - let mut blocking_ctx = BlockingTestContextFFI::new()?; - let runtime_wrapped = blocking_ctx.runtime_clone(); - let ctx = blocking_ctx.ctx_mut(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: Some(format_public_account_id(ctx.existing_public_accounts()[0])), - from_label: None, - to: Some(format_public_account_id(ctx.existing_public_accounts()[1])), - to_label: None, - to_npk: None, - to_vpk: None, - amount: 100, - }); - - runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?; - - info!("Waiting for next block creation"); - runtime_wrapped.block_on(async { - tokio::time::sleep(std::time::Duration::from_millis( - TIME_TO_WAIT_FOR_BLOCK_SECONDS, - )) - .await; - }); - - info!("Checking correct balance move"); - let acc_1_balance = - runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( - ctx.sequencer_client(), - ctx.existing_public_accounts()[0], - ))?; - let acc_2_balance = - runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( - ctx.sequencer_client(), - ctx.existing_public_accounts()[1], - ))?; - - info!("Balance of sender: {acc_1_balance:#?}"); - info!("Balance of receiver: {acc_2_balance:#?}"); - - assert_eq!(acc_1_balance, 9900); - assert_eq!(acc_2_balance, 20100); - - let from: AccountId = ctx.existing_private_accounts()[0]; - let to: AccountId = ctx.existing_private_accounts()[1]; - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: Some(format_private_account_id(from)), - from_label: None, - to: Some(format_private_account_id(to)), - to_label: None, - to_npk: None, - to_vpk: None, - amount: 100, - }); - - runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?; - - info!("Waiting for next block creation"); - runtime_wrapped.block_on(async { - tokio::time::sleep(std::time::Duration::from_millis( - TIME_TO_WAIT_FOR_BLOCK_SECONDS, - )) - .await; - }); - - let new_commitment1 = ctx - .wallet() - .get_private_account_commitment(from) - .context("Failed to get private account commitment for sender")?; - let commitment_check1 = runtime_wrapped.block_on(verify_commitment_is_in_state( - new_commitment1, - ctx.sequencer_client(), - )); - assert!(commitment_check1); - - let new_commitment2 = ctx - .wallet() - .get_private_account_commitment(to) - .context("Failed to get private account commitment for receiver")?; - let commitment_check2 = runtime_wrapped.block_on(verify_commitment_is_in_state( - new_commitment2, - ctx.sequencer_client(), - )); - assert!(commitment_check2); - - info!("Successfully transferred privately to owned account"); - - // WAIT - info!("Waiting for indexer to parse blocks"); - runtime_wrapped.block_on(async { - tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; - }); - - let acc1_ind_state = runtime_wrapped.block_on( - ctx.indexer_client() - .get_account(ctx.existing_public_accounts()[0].into()), - )?; - let acc2_ind_state = runtime_wrapped.block_on( - ctx.indexer_client() - .get_account(ctx.existing_public_accounts()[1].into()), - )?; - - info!("Checking correct state transition"); - let acc1_seq_state = - runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account( - ctx.sequencer_client(), - ctx.existing_public_accounts()[0], - ))?; - let acc2_seq_state = - runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account( - ctx.sequencer_client(), - ctx.existing_public_accounts()[1], - ))?; - - assert_eq!(acc1_ind_state, acc1_seq_state.into()); - assert_eq!(acc2_ind_state, acc2_seq_state.into()); - - // ToDo: Check private state transition - - Ok(()) -} - -#[test] -fn indexer_ffi_state_consistency_with_labels() -> Result<()> { - let mut blocking_ctx = BlockingTestContextFFI::new()?; - let runtime_wrapped = blocking_ctx.runtime_clone(); - let ctx = blocking_ctx.ctx_mut(); - - // Assign labels to both accounts - let from_label = "idx-sender-label".to_owned(); - let to_label_str = "idx-receiver-label".to_owned(); - - let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label { - account_id: Some(format_public_account_id(ctx.existing_public_accounts()[0])), - account_label: None, - label: from_label.clone(), - }); - runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?; - - let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label { - account_id: Some(format_public_account_id(ctx.existing_public_accounts()[1])), - account_label: None, - label: to_label_str.clone(), - }); - runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?; - - // Send using labels instead of account IDs - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: None, - from_label: Some(from_label), - to: None, - to_label: Some(to_label_str), - to_npk: None, - to_vpk: None, - amount: 100, - }); - - runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?; - - info!("Waiting for next block creation"); - runtime_wrapped.block_on(async { - tokio::time::sleep(std::time::Duration::from_millis( - TIME_TO_WAIT_FOR_BLOCK_SECONDS, - )) - .await; - }); - - let acc_1_balance = - runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( - ctx.sequencer_client(), - ctx.existing_public_accounts()[0], - ))?; - let acc_2_balance = - runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( - ctx.sequencer_client(), - ctx.existing_public_accounts()[1], - ))?; - - assert_eq!(acc_1_balance, 9900); - assert_eq!(acc_2_balance, 20100); - - info!("Waiting for indexer to parse blocks"); - runtime_wrapped.block_on(async { - tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; - }); - - let acc1_ind_state = runtime_wrapped.block_on( - ctx.indexer_client() - .get_account(ctx.existing_public_accounts()[0].into()), - )?; - let acc1_seq_state = - runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account( - ctx.sequencer_client(), - ctx.existing_public_accounts()[0], - ))?; - - assert_eq!(acc1_ind_state, acc1_seq_state.into()); - - info!("Indexer state is consistent after label-based transfer"); - - Ok(()) -} diff --git a/integration_tests/tests/indexer_ffi.rs b/integration_tests/tests/indexer_ffi.rs new file mode 100644 index 00000000..5495e6c6 --- /dev/null +++ b/integration_tests/tests/indexer_ffi.rs @@ -0,0 +1,284 @@ +#![expect( + clippy::shadow_unrelated, + clippy::tests_outside_test_module, + reason = "We don't care about these in tests" +)] + +use anyhow::{Context as _, Result}; +use indexer_service_rpc::RpcClient as _; +use integration_tests::{ + TIME_TO_WAIT_FOR_BLOCK_SECONDS, format_private_account_id, format_public_account_id, + test_context_ffi::BlockingTestContextFFI, verify_commitment_is_in_state, +}; +use log::info; +use nssa::AccountId; +use wallet::cli::{Command, programs::native_token_transfer::AuthTransferSubcommand}; + +/// Maximum time to wait for the indexer to catch up to the sequencer. +const L2_TO_L1_TIMEOUT_MILLIS: u64 = 180_000; + +#[test] +fn indexer_test_run_ffi() -> Result<()> { + let blocking_ctx = BlockingTestContextFFI::new()?; + let runtime_wrapped = blocking_ctx.runtime(); + + // RUN OBSERVATION + runtime_wrapped.block_on(async { + tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; + }); + + let last_block_indexer = blocking_ctx.ctx().get_last_block_indexer(runtime_wrapped)?; + + info!("Last block on ind now is {last_block_indexer}"); + + assert!(last_block_indexer > 1); + + Ok(()) +} + +#[test] +fn indexer_ffi_block_batching() -> Result<()> { + let blocking_ctx = BlockingTestContextFFI::new()?; + let runtime_wrapped = blocking_ctx.runtime(); + let ctx = blocking_ctx.ctx(); + + // WAIT + info!("Waiting for indexer to parse blocks"); + runtime_wrapped.block_on(async { + tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; + }); + + let last_block_indexer = runtime_wrapped + .block_on(ctx.indexer_client().get_last_finalized_block_id()) + .unwrap(); + + info!("Last block on ind now is {last_block_indexer}"); + + assert!(last_block_indexer > 1); + + // Getting wide batch to fit all blocks (from latest backwards) + let mut block_batch = runtime_wrapped + .block_on(ctx.indexer_client().get_blocks(None, 100)) + .unwrap(); + + // Reverse to check chain consistency from oldest to newest + block_batch.reverse(); + + // Checking chain consistency + let mut prev_block_hash = block_batch.first().unwrap().header.hash; + + for block in &block_batch[1..] { + assert_eq!(block.header.prev_block_hash, prev_block_hash); + + info!("Block {} chain-consistent", block.header.block_id); + + prev_block_hash = block.header.hash; + } + + Ok(()) +} + +#[test] +fn indexer_ffi_state_consistency() -> Result<()> { + let mut blocking_ctx = BlockingTestContextFFI::new()?; + let runtime_wrapped = blocking_ctx.runtime_clone(); + let ctx = blocking_ctx.ctx_mut(); + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: Some(format_public_account_id(ctx.existing_public_accounts()[0])), + from_label: None, + to: Some(format_public_account_id(ctx.existing_public_accounts()[1])), + to_label: None, + to_npk: None, + to_vpk: None, + amount: 100, + }); + + runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?; + + info!("Waiting for next block creation"); + runtime_wrapped.block_on(async { + tokio::time::sleep(std::time::Duration::from_millis( + TIME_TO_WAIT_FOR_BLOCK_SECONDS, + )) + .await; + }); + + info!("Checking correct balance move"); + let acc_1_balance = + runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( + ctx.sequencer_client(), + ctx.existing_public_accounts()[0], + ))?; + let acc_2_balance = + runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( + ctx.sequencer_client(), + ctx.existing_public_accounts()[1], + ))?; + + info!("Balance of sender: {acc_1_balance:#?}"); + info!("Balance of receiver: {acc_2_balance:#?}"); + + assert_eq!(acc_1_balance, 9900); + assert_eq!(acc_2_balance, 20100); + + let from: AccountId = ctx.existing_private_accounts()[0]; + let to: AccountId = ctx.existing_private_accounts()[1]; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: Some(format_private_account_id(from)), + from_label: None, + to: Some(format_private_account_id(to)), + to_label: None, + to_npk: None, + to_vpk: None, + amount: 100, + }); + + runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?; + + info!("Waiting for next block creation"); + runtime_wrapped.block_on(async { + tokio::time::sleep(std::time::Duration::from_millis( + TIME_TO_WAIT_FOR_BLOCK_SECONDS, + )) + .await; + }); + + let new_commitment1 = ctx + .wallet() + .get_private_account_commitment(from) + .context("Failed to get private account commitment for sender")?; + let commitment_check1 = runtime_wrapped.block_on(verify_commitment_is_in_state( + new_commitment1, + ctx.sequencer_client(), + )); + assert!(commitment_check1); + + let new_commitment2 = ctx + .wallet() + .get_private_account_commitment(to) + .context("Failed to get private account commitment for receiver")?; + let commitment_check2 = runtime_wrapped.block_on(verify_commitment_is_in_state( + new_commitment2, + ctx.sequencer_client(), + )); + assert!(commitment_check2); + + info!("Successfully transferred privately to owned account"); + + // WAIT + info!("Waiting for indexer to parse blocks"); + runtime_wrapped.block_on(async { + tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; + }); + + let acc1_ind_state = runtime_wrapped.block_on( + ctx.indexer_client() + .get_account(ctx.existing_public_accounts()[0].into()), + )?; + let acc2_ind_state = runtime_wrapped.block_on( + ctx.indexer_client() + .get_account(ctx.existing_public_accounts()[1].into()), + )?; + + info!("Checking correct state transition"); + let acc1_seq_state = + runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account( + ctx.sequencer_client(), + ctx.existing_public_accounts()[0], + ))?; + let acc2_seq_state = + runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account( + ctx.sequencer_client(), + ctx.existing_public_accounts()[1], + ))?; + + assert_eq!(acc1_ind_state, acc1_seq_state.into()); + assert_eq!(acc2_ind_state, acc2_seq_state.into()); + + // ToDo: Check private state transition + + Ok(()) +} + +#[test] +fn indexer_ffi_state_consistency_with_labels() -> Result<()> { + let mut blocking_ctx = BlockingTestContextFFI::new()?; + let runtime_wrapped = blocking_ctx.runtime_clone(); + let ctx = blocking_ctx.ctx_mut(); + + // Assign labels to both accounts + let from_label = "idx-sender-label".to_owned(); + let to_label_str = "idx-receiver-label".to_owned(); + + let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label { + account_id: Some(format_public_account_id(ctx.existing_public_accounts()[0])), + account_label: None, + label: from_label.clone(), + }); + runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?; + + let label_cmd = Command::Account(wallet::cli::account::AccountSubcommand::Label { + account_id: Some(format_public_account_id(ctx.existing_public_accounts()[1])), + account_label: None, + label: to_label_str.clone(), + }); + runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), label_cmd))?; + + // Send using labels instead of account IDs + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: None, + from_label: Some(from_label), + to: None, + to_label: Some(to_label_str), + to_npk: None, + to_vpk: None, + amount: 100, + }); + + runtime_wrapped.block_on(wallet::cli::execute_subcommand(ctx.wallet_mut(), command))?; + + info!("Waiting for next block creation"); + runtime_wrapped.block_on(async { + tokio::time::sleep(std::time::Duration::from_millis( + TIME_TO_WAIT_FOR_BLOCK_SECONDS, + )) + .await; + }); + + let acc_1_balance = + runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( + ctx.sequencer_client(), + ctx.existing_public_accounts()[0], + ))?; + let acc_2_balance = + runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account_balance( + ctx.sequencer_client(), + ctx.existing_public_accounts()[1], + ))?; + + assert_eq!(acc_1_balance, 9900); + assert_eq!(acc_2_balance, 20100); + + info!("Waiting for indexer to parse blocks"); + runtime_wrapped.block_on(async { + tokio::time::sleep(std::time::Duration::from_millis(L2_TO_L1_TIMEOUT_MILLIS)).await; + }); + + let acc1_ind_state = runtime_wrapped.block_on( + ctx.indexer_client() + .get_account(ctx.existing_public_accounts()[0].into()), + )?; + let acc1_seq_state = + runtime_wrapped.block_on(sequencer_service_rpc::RpcClient::get_account( + ctx.sequencer_client(), + ctx.existing_public_accounts()[0], + ))?; + + assert_eq!(acc1_ind_state, acc1_seq_state.into()); + + info!("Indexer state is consistent after label-based transfer"); + + Ok(()) +}