diff --git a/crates/iota-indexer/src/apis/read_api.rs b/crates/iota-indexer/src/apis/read_api.rs index 8b14399714b..4af88e81518 100644 --- a/crates/iota-indexer/src/apis/read_api.rs +++ b/crates/iota-indexer/src/apis/read_api.rs @@ -56,8 +56,7 @@ impl ReadApi { } async fn get_chain_identifier(&self) -> RpcResult { - let genesis_checkpoint = self.get_checkpoint(CheckpointId::SequenceNumber(0)).await?; - Ok(ChainIdentifier::from(genesis_checkpoint.digest)) + Ok(self.inner.get_chain_identifier_in_blocking_task().await?) } async fn object_read_to_object_response( diff --git a/crates/iota-indexer/tests/common/mod.rs b/crates/iota-indexer/tests/common/mod.rs index 5bb7d609c97..76b88c935fc 100644 --- a/crates/iota-indexer/tests/common/mod.rs +++ b/crates/iota-indexer/tests/common/mod.rs @@ -56,9 +56,12 @@ impl ApiTestSetup { GLOBAL_API_TEST_SETUP.get_or_init(|| { let runtime = tokio::runtime::Runtime::new().unwrap(); - let (cluster, store, client) = runtime.block_on( - start_test_cluster_with_read_write_indexer(Some("shared_test_indexer_db"), None), - ); + let (cluster, store, client) = + runtime.block_on(start_test_cluster_with_read_write_indexer( + Some("shared_test_indexer_db"), + None, + None, + )); Self { runtime, @@ -110,10 +113,11 @@ impl SimulacrumTestSetup { } /// Start a [`TestCluster`][`test_cluster::TestCluster`] with a `Read` & -/// `Write` indexer +/// `Write` indexer. Set `epochs_to_keep` (> 0) to enable indexer pruning. pub async fn start_test_cluster_with_read_write_indexer( database_name: Option<&str>, builder_modifier: Option TestClusterBuilder>>, + epochs_to_keep: Option, ) -> (TestCluster, PgIndexerStore, HttpClient) { let temp = tempdir().unwrap().into_path(); let mut builder = TestClusterBuilder::new(); @@ -131,7 +135,7 @@ pub async fn start_test_cluster_with_read_write_indexer( true, None, cluster.rpc_url().to_string(), - IndexerTypeConfig::writer_mode(None, None), + IndexerTypeConfig::writer_mode(None, epochs_to_keep), None, ) .await; @@ -216,6 +220,29 @@ pub async fn indexer_wait_for_object( .expect("Timeout waiting for indexer to catchup to given object's sequence number"); } +/// Wait for the indexer to prune the given checkpoint number +pub async fn indexer_wait_for_checkpoint_pruned( + pg_store: &PgIndexerStore, + checkpoint_sequence_number: u64, +) { + tokio::time::timeout(Duration::from_secs(30), async { + loop { + let (min, _max) = pg_store + .get_available_checkpoint_range() + .await + .expect("Failed to get available checkpoint range"); + + if min > checkpoint_sequence_number { + break; + } + + tokio::time::sleep(Duration::from_millis(100)).await; + } + }) + .await + .expect("Timeout waiting for indexer to prune checkpoint"); +} + pub async fn indexer_wait_for_transaction( tx_digest: TransactionDigest, pg_store: &PgIndexerStore, diff --git a/crates/iota-indexer/tests/rpc-tests/governance_api.rs b/crates/iota-indexer/tests/rpc-tests/governance_api.rs index 1dbb26f2c52..053cebd339f 100644 --- a/crates/iota-indexer/tests/rpc-tests/governance_api.rs +++ b/crates/iota-indexer/tests/rpc-tests/governance_api.rs @@ -32,7 +32,7 @@ fn test_staking() { runtime.block_on(async move { let (cluster, store, client) = - &start_test_cluster_with_read_write_indexer(Some("test_staking"), None).await; + &start_test_cluster_with_read_write_indexer(Some("test_staking"), None, None).await; indexer_wait_for_checkpoint(store, 1).await; @@ -109,7 +109,7 @@ fn test_unstaking() { runtime.block_on(async move { let (cluster, store, client) = - &start_test_cluster_with_read_write_indexer(Some("test_unstaking"), None).await; + &start_test_cluster_with_read_write_indexer(Some("test_unstaking"), None, None).await; indexer_wait_for_checkpoint(store, 1).await; @@ -212,9 +212,12 @@ fn test_timelocked_staking() { let ApiTestSetup { runtime, .. } = ApiTestSetup::get_or_init(); runtime.block_on(async move { - let (cluster, store, client) = - &start_test_cluster_with_read_write_indexer(Some("test_timelocked_staking"), None) - .await; + let (cluster, store, client) = &start_test_cluster_with_read_write_indexer( + Some("test_timelocked_staking"), + None, + None, + ) + .await; indexer_wait_for_checkpoint(store, 1).await; @@ -321,9 +324,12 @@ fn test_timelocked_unstaking() { let ApiTestSetup { runtime, .. } = ApiTestSetup::get_or_init(); runtime.block_on(async move { - let (cluster, store, client) = - &start_test_cluster_with_read_write_indexer(Some("test_timelocked_unstaking"), None) - .await; + let (cluster, store, client) = &start_test_cluster_with_read_write_indexer( + Some("test_timelocked_unstaking"), + None, + None, + ) + .await; indexer_wait_for_checkpoint(store, 1).await; diff --git a/crates/iota-indexer/tests/rpc-tests/indexer_api.rs b/crates/iota-indexer/tests/rpc-tests/indexer_api.rs index 649450fed01..cdc1706bd79 100644 --- a/crates/iota-indexer/tests/rpc-tests/indexer_api.rs +++ b/crates/iota-indexer/tests/rpc-tests/indexer_api.rs @@ -189,9 +189,12 @@ fn query_events_supported_events() { #[tokio::test] async fn query_validator_epoch_info_event() { - let (cluster, store, client) = - &start_test_cluster_with_read_write_indexer(Some("query_validator_epoch_info_event"), None) - .await; + let (cluster, store, client) = &start_test_cluster_with_read_write_indexer( + Some("query_validator_epoch_info_event"), + None, + None, + ) + .await; indexer_wait_for_checkpoint(store, 1).await; cluster.force_new_epoch().await; diff --git a/crates/iota-indexer/tests/rpc-tests/read_api.rs b/crates/iota-indexer/tests/rpc-tests/read_api.rs index 0fcf98c5853..6a3eb62e37c 100644 --- a/crates/iota-indexer/tests/rpc-tests/read_api.rs +++ b/crates/iota-indexer/tests/rpc-tests/read_api.rs @@ -14,13 +14,14 @@ use iota_test_transaction_builder::{create_nft, delete_nft, publish_nfts_package use iota_types::{ base_types::{ObjectID, SequenceNumber}, crypto::{AccountKeyPair, get_key_pair}, - digests::{ObjectDigest, TransactionDigest}, + digests::{ChainIdentifier, ObjectDigest, TransactionDigest}, error::IotaObjectResponseError, }; use crate::common::{ ApiTestSetup, execute_tx_and_wait_for_indexer, indexer_wait_for_checkpoint, - indexer_wait_for_object, indexer_wait_for_transaction, rpc_call_error_msg_matches, + indexer_wait_for_checkpoint_pruned, indexer_wait_for_object, indexer_wait_for_transaction, + rpc_call_error_msg_matches, start_test_cluster_with_read_write_indexer, }; fn is_ascending(vec: &[u64]) -> bool { @@ -1683,3 +1684,53 @@ fn try_get_object_before_version() { } }); } + +#[test] +fn get_chain_identifier_with_pruning_enabled() { + let ApiTestSetup { runtime, .. } = ApiTestSetup::get_or_init(); + + runtime.block_on(async move { + let (cluster, store, client) = &start_test_cluster_with_read_write_indexer( + Some("test_get_chain_identifier_with_pruning_enabled"), + None, + Some(1), + ) + .await; + + indexer_wait_for_checkpoint(store, 1).await; + + let chain_identifier = ChainIdentifier::from( + client + .get_checkpoint(CheckpointId::SequenceNumber(0)) + .await + .unwrap() + .digest, + ); + + let indexer_chain_identifier = client.get_chain_identifier().await.unwrap(); + + assert_eq!( + chain_identifier.to_string(), + indexer_chain_identifier.to_string() + ); + + cluster.force_new_epoch().await; + + // Prune the genesis checkpoint + indexer_wait_for_checkpoint_pruned(store, 0).await; + + let indexer_chain_identifier = client.get_chain_identifier().await.unwrap(); + + assert_eq!( + chain_identifier.to_string(), + indexer_chain_identifier.to_string() + ); + + assert!( + client + .get_checkpoint(CheckpointId::SequenceNumber(0)) + .await + .is_err() + ) + }); +} diff --git a/crates/iota-indexer/tests/rpc-tests/transaction_builder.rs b/crates/iota-indexer/tests/rpc-tests/transaction_builder.rs index a6c0fda495f..36f099e6133 100644 --- a/crates/iota-indexer/tests/rpc-tests/transaction_builder.rs +++ b/crates/iota-indexer/tests/rpc-tests/transaction_builder.rs @@ -453,6 +453,7 @@ fn request_add_stake() { let (cluster, store, client) = &start_test_cluster_with_read_write_indexer( Some("transaction_builder_request_add_stake"), None, + None, ) .await; let (address, keypair): (_, AccountKeyPair) = get_key_pair(); @@ -564,6 +565,7 @@ fn request_withdraw_stake_from_active() { let (cluster, store, client) = &start_test_cluster_with_read_write_indexer( Some("transaction_builder_request_withdraw_stake_from_active"), None, + None, ) .await; let (address, keypair): (_, AccountKeyPair) = get_key_pair(); @@ -844,6 +846,7 @@ async fn create_cluster_with_timelocked_iota( ) .with_objects([timelock_iota.into()]) })), + None, ) .await;