diff --git a/crates/iota-core/src/authority.rs b/crates/iota-core/src/authority.rs index ea62b573629..27ded8442d7 100644 --- a/crates/iota-core/src/authority.rs +++ b/crates/iota-core/src/authority.rs @@ -143,8 +143,9 @@ use crate::{ consensus_adapter::ConsensusAdapter, epoch::committee_store::CommitteeStore, execution_cache::{ - ExecutionCacheCommit, ExecutionCacheReconfigAPI, ExecutionCacheTraitPointers, - ExecutionCacheWrite, ObjectCacheRead, StateSyncAPI, TransactionCacheRead, + CheckpointCache, ExecutionCacheCommit, ExecutionCacheReconfigAPI, + ExecutionCacheTraitPointers, ExecutionCacheWrite, ObjectCacheRead, StateSyncAPI, + TransactionCacheRead, }, execution_driver::execution_process, metrics::{LatencyObserver, RateTracker}, @@ -2735,6 +2736,10 @@ impl AuthorityState { &self.execution_cache_trait_pointers.accumulator_store } + pub fn get_checkpoint_cache(&self) -> &Arc { + &self.execution_cache_trait_pointers.checkpoint_cache + } + pub fn get_state_sync_store(&self) -> &Arc { &self.execution_cache_trait_pointers.state_sync_store } @@ -4981,6 +4986,15 @@ impl TransactionKeyValueStoreTrait for AuthorityState { Ok((summaries, contents, summaries_by_digest, contents_by_digest)) } + async fn deprecated_get_transaction_checkpoint( + &self, + digest: TransactionDigest, + ) -> IotaResult> { + self.get_checkpoint_cache() + .deprecated_get_transaction_checkpoint(&digest) + .map(|res| res.map(|(_epoch, checkpoint)| checkpoint)) + } + async fn get_object( &self, object_id: ObjectID, @@ -4995,10 +5009,14 @@ impl TransactionKeyValueStoreTrait for AuthorityState { &self, digests: &[TransactionDigest], ) -> IotaResult>> { - Ok(self - .epoch_store - .load() - .multi_get_transaction_checkpoint(digests)?) + let res = self + .get_checkpoint_cache() + .deprecated_multi_get_transaction_checkpoint(digests)?; + + Ok(res + .into_iter() + .map(|maybe| maybe.map(|(_epoch, checkpoint)| checkpoint)) + .collect()) } } diff --git a/crates/iota-core/src/authority/authority_per_epoch_store.rs b/crates/iota-core/src/authority/authority_per_epoch_store.rs index cc6340a840e..3dbdfaa7333 100644 --- a/crates/iota-core/src/authority/authority_per_epoch_store.rs +++ b/crates/iota-core/src/authority/authority_per_epoch_store.rs @@ -679,13 +679,6 @@ impl AuthorityEpochTables { Ok(()) } - pub fn get_transaction_checkpoint( - &self, - digest: &TransactionDigest, - ) -> IotaResult> { - Ok(self.executed_transactions_to_checkpoint.get(digest)?) - } - /// WARNING: This method is very subtle and can corrupt the database if used /// incorrectly. It should only be used in one-off cases or tests after /// fully understanding the risk. @@ -1464,7 +1457,9 @@ impl AuthorityPerEpochStore { Ok(self .tables()? .executed_transactions_to_checkpoint - .multi_get(digests)?) + .multi_get(digests)? + .into_iter() + .collect()) } // For each id in objects_to_init, return the next version for that id as diff --git a/crates/iota-core/src/authority/authority_store.rs b/crates/iota-core/src/authority/authority_store.rs index 01072d6a1c5..563d820f00b 100644 --- a/crates/iota-core/src/authority/authority_store.rs +++ b/crates/iota-core/src/authority/authority_store.rs @@ -541,6 +541,50 @@ impl AuthorityStore { Ok(result) } + // DEPRECATED -- use function of same name in AuthorityPerEpochStore + pub fn deprecated_insert_finalized_transactions( + &self, + digests: &[TransactionDigest], + epoch: EpochId, + sequence: CheckpointSequenceNumber, + ) -> IotaResult { + let mut batch = self + .perpetual_tables + .executed_transactions_to_checkpoint + .batch(); + batch.insert_batch( + &self.perpetual_tables.executed_transactions_to_checkpoint, + digests.iter().map(|d| (*d, (epoch, sequence))), + )?; + batch.write()?; + trace!("Transactions {digests:?} finalized at checkpoint {sequence} epoch {epoch}"); + Ok(()) + } + + // DEPRECATED -- use function of same name in AuthorityPerEpochStore + pub fn deprecated_get_transaction_checkpoint( + &self, + digest: &TransactionDigest, + ) -> IotaResult> { + Ok(self + .perpetual_tables + .executed_transactions_to_checkpoint + .get(digest)?) + } + + // DEPRECATED -- use function of same name in AuthorityPerEpochStore + pub fn deprecated_multi_get_transaction_checkpoint( + &self, + digests: &[TransactionDigest], + ) -> IotaResult>> { + Ok(self + .perpetual_tables + .executed_transactions_to_checkpoint + .multi_get(digests)? + .into_iter() + .collect()) + } + /// Returns true if there are no objects in the database pub fn database_is_empty(&self) -> IotaResult { self.perpetual_tables.database_is_empty() diff --git a/crates/iota-core/src/authority/authority_store_pruner.rs b/crates/iota-core/src/authority/authority_store_pruner.rs index 335eb679634..c0988067c54 100644 --- a/crates/iota-core/src/authority/authority_store_pruner.rs +++ b/crates/iota-core/src/authority/authority_store_pruner.rs @@ -269,6 +269,10 @@ impl AuthorityStorePruner { perpetual_batch.delete_batch(&perpetual_db.transactions, transactions.iter())?; perpetual_batch.delete_batch(&perpetual_db.executed_effects, transactions.iter())?; + perpetual_batch.delete_batch( + &perpetual_db.executed_transactions_to_checkpoint, + transactions, + )?; let mut effect_digests = vec![]; for effects in effects_to_prune { diff --git a/crates/iota-core/src/authority/authority_store_tables.rs b/crates/iota-core/src/authority/authority_store_tables.rs index e92fed936ae..932b08e9036 100644 --- a/crates/iota-core/src/authority/authority_store_tables.rs +++ b/crates/iota-core/src/authority/authority_store_tables.rs @@ -102,6 +102,13 @@ pub struct AuthorityPerpetualTables { #[default_options_override_fn = "events_table_default_config"] pub(crate) events: DBMap<(TransactionEventsDigest, usize), Event>, + /// DEPRECATED in favor of the table of the same name in + /// authority_per_epoch_store. Please do not add new + /// accessors/callsites. When transaction is executed via checkpoint + /// executor, we store association here + pub(crate) executed_transactions_to_checkpoint: + DBMap, + // Finalized root state accumulator for epoch, to be included in CheckpointSummary // of last checkpoint of epoch. These values should only ever be written once // and never changed @@ -349,6 +356,15 @@ impl AuthorityPerpetualTables { Ok(self.effects.get(&effect_digest)?) } + // DEPRECATED as the backing table has been moved to authority_per_epoch_store. + // Please do not add new accessors/callsites. + pub fn get_checkpoint_sequence_number( + &self, + digest: &TransactionDigest, + ) -> IotaResult> { + Ok(self.executed_transactions_to_checkpoint.get(digest)?) + } + pub fn get_newer_object_keys( &self, object: &(ObjectID, SequenceNumber), @@ -418,6 +434,7 @@ impl AuthorityPerpetualTables { self.live_owned_object_markers.unsafe_clear()?; self.executed_effects.unsafe_clear()?; self.events.unsafe_clear()?; + self.executed_transactions_to_checkpoint.unsafe_clear()?; self.root_state_hash_by_epoch.unsafe_clear()?; self.epoch_start_configuration.unsafe_clear()?; self.pruned_checkpoint.unsafe_clear()?; diff --git a/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs b/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs index f80f388f057..f9a89209377 100644 --- a/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs +++ b/crates/iota-core/src/checkpoints/checkpoint_executor/mod.rs @@ -1304,6 +1304,15 @@ async fn finalize_checkpoint( debug!("finalizing checkpoint"); epoch_store.insert_finalized_transactions(tx_digests, checkpoint.sequence_number)?; + // TODO remove once we no longer need to support this table for read RPC + state + .get_checkpoint_cache() + .deprecated_insert_finalized_transactions( + tx_digests, + epoch_store.epoch(), + checkpoint.sequence_number, + )?; + let checkpoint_acc = accumulator.accumulate_checkpoint(effects, checkpoint.sequence_number, epoch_store)?; diff --git a/crates/iota-core/src/execution_cache.rs b/crates/iota-core/src/execution_cache.rs index 38cab05b8b2..e9d885485ed 100644 --- a/crates/iota-core/src/execution_cache.rs +++ b/crates/iota-core/src/execution_cache.rs @@ -63,6 +63,7 @@ pub struct ExecutionCacheTraitPointers { pub object_store: Arc, pub reconfig_api: Arc, pub accumulator_store: Arc, + pub checkpoint_cache: Arc, pub state_sync_store: Arc, pub cache_commit: Arc, pub testing_api: Arc, @@ -79,6 +80,7 @@ impl ExecutionCacheTraitPointers { + ObjectStore + ExecutionCacheReconfigAPI + AccumulatorStore + + CheckpointCache + StateSyncAPI + ExecutionCacheCommit + TestingAPI @@ -93,6 +95,7 @@ impl ExecutionCacheTraitPointers { object_store: cache.clone(), reconfig_api: cache.clone(), accumulator_store: cache.clone(), + checkpoint_cache: cache.clone(), state_sync_store: cache.clone(), cache_commit: cache.clone(), testing_api: cache.clone(), @@ -655,6 +658,29 @@ pub trait ExecutionCacheWrite: Send + Sync { ) -> BoxFuture<'a, IotaResult>; } +pub trait CheckpointCache: Send + Sync { + // TODO: In addition to the deprecated methods below, this will eventually + // include access to the CheckpointStore + + // DEPRECATED METHODS + fn deprecated_get_transaction_checkpoint( + &self, + digest: &TransactionDigest, + ) -> IotaResult>; + + fn deprecated_multi_get_transaction_checkpoint( + &self, + digests: &[TransactionDigest], + ) -> IotaResult>>; + + fn deprecated_insert_finalized_transactions( + &self, + digests: &[TransactionDigest], + epoch: EpochId, + sequence: CheckpointSequenceNumber, + ) -> IotaResult; +} + pub trait ExecutionCacheReconfigAPI: Send + Sync { fn insert_genesis_object(&self, object: Object) -> IotaResult; fn bulk_insert_genesis_objects(&self, objects: &[Object]) -> IotaResult; @@ -800,6 +826,33 @@ macro_rules! implement_storage_traits { // store. macro_rules! implement_passthrough_traits { ($implementor: ident) => { + impl CheckpointCache for $implementor { + fn deprecated_get_transaction_checkpoint( + &self, + digest: &TransactionDigest, + ) -> IotaResult> { + self.store.deprecated_get_transaction_checkpoint(digest) + } + + fn deprecated_multi_get_transaction_checkpoint( + &self, + digests: &[TransactionDigest], + ) -> IotaResult>> { + self.store + .deprecated_multi_get_transaction_checkpoint(digests) + } + + fn deprecated_insert_finalized_transactions( + &self, + digests: &[TransactionDigest], + epoch: EpochId, + sequence: CheckpointSequenceNumber, + ) -> IotaResult { + self.store + .deprecated_insert_finalized_transactions(digests, epoch, sequence) + } + } + impl ExecutionCacheReconfigAPI for $implementor { fn insert_genesis_object(&self, object: Object) -> IotaResult { self.store.insert_genesis_object(object) @@ -900,6 +953,7 @@ pub trait ExecutionCacheAPI: + ExecutionCacheWrite + ExecutionCacheCommit + ExecutionCacheReconfigAPI + + CheckpointCache + StateSyncAPI { } diff --git a/crates/iota-core/src/execution_cache/passthrough_cache.rs b/crates/iota-core/src/execution_cache/passthrough_cache.rs index 52da684f110..0261942423a 100644 --- a/crates/iota-core/src/execution_cache/passthrough_cache.rs +++ b/crates/iota-core/src/execution_cache/passthrough_cache.rs @@ -27,8 +27,9 @@ use tracing::instrument; use typed_store::Map; use super::{ - ExecutionCacheCommit, ExecutionCacheMetrics, ExecutionCacheReconfigAPI, ExecutionCacheWrite, - ObjectCacheRead, StateSyncAPI, TestingAPI, TransactionCacheRead, implement_passthrough_traits, + CheckpointCache, ExecutionCacheCommit, ExecutionCacheMetrics, ExecutionCacheReconfigAPI, + ExecutionCacheWrite, ObjectCacheRead, StateSyncAPI, TestingAPI, TransactionCacheRead, + implement_passthrough_traits, }; use crate::{ authority::{ diff --git a/crates/iota-core/src/execution_cache/proxy_cache.rs b/crates/iota-core/src/execution_cache/proxy_cache.rs index 9c05892f0e2..0a9fd5f8c2f 100644 --- a/crates/iota-core/src/execution_cache/proxy_cache.rs +++ b/crates/iota-core/src/execution_cache/proxy_cache.rs @@ -21,7 +21,7 @@ use iota_types::{ use parking_lot::RwLock; use super::{ - ExecutionCacheCommit, ExecutionCacheConfigType, ExecutionCacheMetrics, + CheckpointCache, ExecutionCacheCommit, ExecutionCacheConfigType, ExecutionCacheMetrics, ExecutionCacheReconfigAPI, ExecutionCacheWrite, ObjectCacheRead, PassthroughCache, StateSyncAPI, TestingAPI, TransactionCacheRead, WritebackCache, }; @@ -317,6 +317,31 @@ impl ExecutionCacheCommit for ProxyCache { } } +impl CheckpointCache for ProxyCache { + fn deprecated_get_transaction_checkpoint( + &self, + digest: &TransactionDigest, + ) -> IotaResult> { + delegate_method!(self.deprecated_get_transaction_checkpoint(digest)) + } + + fn deprecated_multi_get_transaction_checkpoint( + &self, + digests: &[TransactionDigest], + ) -> IotaResult>> { + delegate_method!(self.deprecated_multi_get_transaction_checkpoint(digests)) + } + + fn deprecated_insert_finalized_transactions( + &self, + digests: &[TransactionDigest], + epoch: EpochId, + sequence: CheckpointSequenceNumber, + ) -> IotaResult { + delegate_method!(self.deprecated_insert_finalized_transactions(digests, epoch, sequence)) + } +} + impl ExecutionCacheReconfigAPI for ProxyCache { fn insert_genesis_object(&self, object: Object) -> IotaResult { delegate_method!(self.insert_genesis_object(object)) diff --git a/crates/iota-core/src/execution_cache/writeback_cache.rs b/crates/iota-core/src/execution_cache/writeback_cache.rs index 01707050887..4307f7485da 100644 --- a/crates/iota-core/src/execution_cache/writeback_cache.rs +++ b/crates/iota-core/src/execution_cache/writeback_cache.rs @@ -78,9 +78,10 @@ use tap::TapOptional; use tracing::{debug, info, instrument, trace, warn}; use super::{ - ExecutionCacheAPI, ExecutionCacheCommit, ExecutionCacheMetrics, ExecutionCacheReconfigAPI, - ExecutionCacheWrite, ObjectCacheRead, StateSyncAPI, TestingAPI, TransactionCacheRead, - cache_types::CachedVersionMap, implement_passthrough_traits, object_locks::ObjectLocks, + CheckpointCache, ExecutionCacheAPI, ExecutionCacheCommit, ExecutionCacheMetrics, + ExecutionCacheReconfigAPI, ExecutionCacheWrite, ObjectCacheRead, StateSyncAPI, TestingAPI, + TransactionCacheRead, cache_types::CachedVersionMap, implement_passthrough_traits, + object_locks::ObjectLocks, }; use crate::{ authority::{ diff --git a/crates/iota-json-rpc/src/authority_state.rs b/crates/iota-json-rpc/src/authority_state.rs index cf1d7bcf05a..38aaeb4c307 100644 --- a/crates/iota-json-rpc/src/authority_state.rs +++ b/crates/iota-json-rpc/src/authority_state.rs @@ -29,7 +29,7 @@ use iota_storage::{ use iota_types::{ base_types::{IotaAddress, MoveObjectType, ObjectID, ObjectInfo, ObjectRef, SequenceNumber}, bridge::Bridge, - committee::Committee, + committee::{Committee, EpochId}, digests::{ChainIdentifier, TransactionDigest, TransactionEventsDigest}, dynamic_field::DynamicFieldInfo, effects::TransactionEffects, @@ -223,6 +223,16 @@ pub trait StateRead: Send + Sync { digest: CheckpointDigest, ) -> StateReadResult; + fn deprecated_multi_get_transaction_checkpoint( + &self, + digests: &[TransactionDigest], + ) -> StateReadResult>>; + + fn deprecated_get_transaction_checkpoint( + &self, + digest: &TransactionDigest, + ) -> StateReadResult>; + fn multi_get_checkpoint_by_sequence_number( &self, sequence_numbers: &[CheckpointSequenceNumber], @@ -534,6 +544,24 @@ impl StateRead for AuthorityState { Ok(self.get_verified_checkpoint_summary_by_digest(digest)?) } + fn deprecated_multi_get_transaction_checkpoint( + &self, + digests: &[TransactionDigest], + ) -> StateReadResult>> { + Ok(self + .get_checkpoint_cache() + .deprecated_multi_get_transaction_checkpoint(digests)?) + } + + fn deprecated_get_transaction_checkpoint( + &self, + digest: &TransactionDigest, + ) -> StateReadResult> { + Ok(self + .get_checkpoint_cache() + .deprecated_get_transaction_checkpoint(digest)?) + } + fn multi_get_checkpoint_by_sequence_number( &self, sequence_numbers: &[CheckpointSequenceNumber], diff --git a/crates/iota-json-rpc/src/coin_api.rs b/crates/iota-json-rpc/src/coin_api.rs index 90d9a114c45..354152049ef 100644 --- a/crates/iota-json-rpc/src/coin_api.rs +++ b/crates/iota-json-rpc/src/coin_api.rs @@ -460,6 +460,11 @@ mod tests { checkpoint_contents_by_digest: &[CheckpointContentsDigest], ) -> IotaResult; + async fn deprecated_get_transaction_checkpoint( + &self, + digest: TransactionDigest, + ) -> IotaResult>; + async fn get_object(&self, object_id: ObjectID, version: SequenceNumber) -> IotaResult>; async fn multi_get_transaction_checkpoint( diff --git a/crates/iota-json-rpc/src/read_api.rs b/crates/iota-json-rpc/src/read_api.rs index efda39276e8..861dd2f7a5b 100644 --- a/crates/iota-json-rpc/src/read_api.rs +++ b/crates/iota-json-rpc/src/read_api.rs @@ -788,14 +788,12 @@ impl ReadApiServer for ReadApi { temp_response.checkpoint_seq = self .transaction_kv_store - .multi_get_transaction_checkpoint(&[digest]) + .deprecated_get_transaction_checkpoint(digest) .await .map_err(|e| { error!("Failed to retrieve checkpoint sequence for transaction {digest:?} with error: {e:?}"); Error::from(e) - })? - .pop() - .flatten(); + })?; if let Some(checkpoint_seq) = &temp_response.checkpoint_seq { let kv_store = self.transaction_kv_store.clone(); diff --git a/crates/iota-storage/src/http_key_value_store.rs b/crates/iota-storage/src/http_key_value_store.rs index 00cd44b8cf5..431362c1f4e 100644 --- a/crates/iota-storage/src/http_key_value_store.rs +++ b/crates/iota-storage/src/http_key_value_store.rs @@ -411,6 +411,17 @@ impl TransactionKeyValueStoreTrait for HttpKVStore { )) } + #[instrument(level = "trace", skip_all)] + async fn deprecated_get_transaction_checkpoint( + &self, + digest: TransactionDigest, + ) -> IotaResult> { + let key = Key::TxToCheckpoint(digest); + self.fetch(key).await.map(|maybe| { + maybe.and_then(|bytes| deser::<_, CheckpointSequenceNumber>(&key, bytes.as_ref())) + }) + } + #[instrument(level = "trace", skip_all)] async fn get_object( &self, diff --git a/crates/iota-storage/src/key_value_store.rs b/crates/iota-storage/src/key_value_store.rs index 69194a98832..6040313f19b 100644 --- a/crates/iota-storage/src/key_value_store.rs +++ b/crates/iota-storage/src/key_value_store.rs @@ -403,6 +403,15 @@ impl TransactionKeyValueStore { }) } + pub async fn deprecated_get_transaction_checkpoint( + &self, + digest: TransactionDigest, + ) -> IotaResult> { + self.inner + .deprecated_get_transaction_checkpoint(digest) + .await + } + pub async fn get_object( &self, object_id: ObjectID, @@ -443,6 +452,11 @@ pub trait TransactionKeyValueStoreTrait { checkpoint_contents_by_digest: &[CheckpointContentsDigest], ) -> IotaResult; + async fn deprecated_get_transaction_checkpoint( + &self, + digest: TransactionDigest, + ) -> IotaResult>; + async fn get_object( &self, object_id: ObjectID, @@ -574,6 +588,24 @@ impl TransactionKeyValueStoreTrait for FallbackTransactionKVStore { Ok((res.0, res.1, res.2, res.3)) } + #[instrument(level = "trace", skip_all)] + async fn deprecated_get_transaction_checkpoint( + &self, + digest: TransactionDigest, + ) -> IotaResult> { + let mut res = self + .primary + .deprecated_get_transaction_checkpoint(digest) + .await?; + if res.is_none() { + res = self + .fallback + .deprecated_get_transaction_checkpoint(digest) + .await?; + } + Ok(res) + } + #[instrument(level = "trace", skip_all)] async fn get_object( &self, diff --git a/crates/iota-storage/tests/key_value_tests.rs b/crates/iota-storage/tests/key_value_tests.rs index d6a78c0a868..07fa3b01421 100644 --- a/crates/iota-storage/tests/key_value_tests.rs +++ b/crates/iota-storage/tests/key_value_tests.rs @@ -215,6 +215,13 @@ impl TransactionKeyValueStoreTrait for MockTxStore { Ok((summaries, contents, summaries_by_digest, contents_by_digest)) } + async fn deprecated_get_transaction_checkpoint( + &self, + digest: TransactionDigest, + ) -> IotaResult> { + Ok(self.tx_to_checkpoint.get(&digest).cloned()) + } + async fn get_object( &self, object_id: ObjectID, diff --git a/crates/iota-tool/src/db_tool/mod.rs b/crates/iota-tool/src/db_tool/mod.rs index 000c25809c2..d5757917440 100644 --- a/crates/iota-tool/src/db_tool/mod.rs +++ b/crates/iota-tool/src/db_tool/mod.rs @@ -102,13 +102,6 @@ pub struct Options { #[derive(Parser)] #[command(rename_all = "kebab-case")] pub struct PrintTransactionOptions { - #[arg( - long = "epoch", - short = 'e', - help = "The epoch to use when loading the AuthorityEpochTables" - )] - epoch: EpochId, - #[arg(long, help = "The transaction digest to print")] digest: TransactionDigest, } @@ -273,21 +266,15 @@ pub fn print_last_consensus_index(path: &Path) -> anyhow::Result<()> { } pub fn print_transaction(path: &Path, opt: PrintTransactionOptions) -> anyhow::Result<()> { - let epoch_db = AuthorityEpochTables::open_tables_read_write( - AuthorityEpochTables::path(opt.epoch, path), - MetricConf::default(), - None, - None, - ); - - if let Some(checkpoint_seq_num) = epoch_db.get_transaction_checkpoint(&opt.digest)? { + let perpetual_db = AuthorityPerpetualTables::open(&path.join("store"), None); + if let Some((epoch, checkpoint_seq_num)) = + perpetual_db.get_checkpoint_sequence_number(&opt.digest)? + { println!( "Transaction {:?} executed in epoch {} checkpoint {}", - opt.digest, opt.epoch, checkpoint_seq_num + opt.digest, epoch, checkpoint_seq_num ); }; - - let perpetual_db = AuthorityPerpetualTables::open(&path.join("store"), None); if let Some(effects) = perpetual_db.get_effects(&opt.digest)? { println!( "Transaction {:?} dependencies: {:#?}",