diff --git a/Cargo.lock b/Cargo.lock index 090300f2fefc0..af298689c7e95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -681,7 +681,6 @@ dependencies = [ "aptos-metrics-core", "aptos-mvhashmap", "aptos-types", - "aptos-vm-environment", "aptos-vm-logging", "aptos-vm-types", "arc-swap", diff --git a/aptos-move/aptos-vm/Cargo.toml b/aptos-move/aptos-vm/Cargo.toml index 7a34966207a7d..cc5fd9eb3f1bb 100644 --- a/aptos-move/aptos-vm/Cargo.toml +++ b/aptos-move/aptos-vm/Cargo.toml @@ -60,6 +60,7 @@ serde = { workspace = true } [dev-dependencies] aptos-aggregator = { workspace = true, features = ["testing"] } +aptos-block-executor = { workspace = true, features = ["testing"] } aptos-language-e2e-tests = { workspace = true } aptos-types = { workspace = true, features = ["fuzzing"] } claims = { workspace = true } diff --git a/aptos-move/aptos-vm/src/block_executor/mod.rs b/aptos-move/aptos-vm/src/block_executor/mod.rs index 2d579bc09668c..de16c676676b6 100644 --- a/aptos-move/aptos-vm/src/block_executor/mod.rs +++ b/aptos-move/aptos-vm/src/block_executor/mod.rs @@ -12,8 +12,7 @@ use aptos_aggregator::{ delayed_change::DelayedChange, delta_change_set::DeltaOp, resolver::TAggregatorV1View, }; use aptos_block_executor::{ - cross_block_caches::get_environment_with_delayed_field_optimization_enabled, - errors::BlockExecutionError, executor::BlockExecutor, + code_cache_global::ImmutableModuleCache, errors::BlockExecutionError, executor::BlockExecutor, task::TransactionOutput as BlockExecutorTransactionOutput, txn_commit_hook::TransactionCommitHook, types::InputOutputKey, }; @@ -29,27 +28,32 @@ use aptos_types::{ signature_verified_transaction::SignatureVerifiedTransaction, BlockOutput, TransactionOutput, TransactionStatus, }, + vm::modules::AptosModuleExtension, write_set::WriteOp, }; +use aptos_vm_environment::environment::AptosEnvironment; use aptos_vm_logging::{flush_speculative_logs, init_speculative_logs}; use aptos_vm_types::{ abstract_write_op::AbstractResourceWriteOp, module_write_set::ModuleWrite, output::VMOutput, resolver::ResourceGroupSize, }; +use move_binary_format::{errors::Location, CompiledModule}; use move_core_types::{ - language_storage::StructTag, + language_storage::{ModuleId, StructTag}, value::MoveTypeLayout, vm_status::{StatusCode, VMStatus}, }; +use move_vm_runtime::{Module, WithRuntimeEnvironment}; use move_vm_types::delayed_values::delayed_field_id::DelayedFieldID; use once_cell::sync::{Lazy, OnceCell}; -use rayon::ThreadPool; use std::{ collections::{BTreeMap, HashSet}, + hash::Hash, + ops::Deref, sync::Arc, }; -pub static RAYON_EXEC_POOL: Lazy> = Lazy::new(|| { +static RAYON_EXEC_POOL: Lazy> = Lazy::new(|| { Arc::new( rayon::ThreadPoolBuilder::new() .num_threads(num_cpus::get()) @@ -59,6 +63,60 @@ pub static RAYON_EXEC_POOL: Lazy> = Lazy::new(|| { ) }); +/// Immutable global module cache that can be shared across multiple block executions. The size of +/// the cache is fixed within a single block (modules are not inserted or removed) and it is only +/// mutated at the block boundaries. Do not use if multiple blocks are executed concurrently. +static GLOBAL_MODULE_CACHE: Lazy< + Arc>, +> = Lazy::new(|| Arc::new(ImmutableModuleCache::empty())); + +/// The maximum size of struct name index map in runtime environment. Checked at block boundaries +/// only. +const MAX_STRUCT_NAME_INDEX_MAP_SIZE: usize = 100_000; + +/// A cached environment that can be persisted globally across blocks. +static GLOBAL_ENVIRONMENT: Lazy>> = Lazy::new(|| Mutex::new(None)); + +/// Returns the cached environment if it exists and has the same configuration as if it was +/// created based on the current state, or creates a new one and caches it. Should only be +/// called at the block boundaries. +fn get_environment_with_delayed_field_optimization_enabled( + state_view: &impl StateView, + global_module_cache: &ImmutableModuleCache, +) -> Result +where + K: Hash + Eq + Clone, + VC: Deref>, +{ + // Create a new environment. + let current_env = AptosEnvironment::new_with_delayed_field_optimization_enabled(state_view); + + // Lock the cache, and check if the environment is the same. + let mut global_environment = GLOBAL_ENVIRONMENT.lock(); + if let Some(previous_env) = global_environment.as_ref() { + if ¤t_env == previous_env { + let runtime_env = previous_env.runtime_environment(); + let struct_name_index_map_size = runtime_env + .struct_name_index_map_size() + .map_err(|e| e.finish(Location::Undefined).into_vm_status())?; + if struct_name_index_map_size > MAX_STRUCT_NAME_INDEX_MAP_SIZE { + // Cache is too large, flush it. Also flush the module cache. + runtime_env.flush_struct_name_and_info_caches(); + global_module_cache.flush_unchecked(); + } + return Ok(previous_env.clone()); + } + } + + // It is not cached or has changed, so we have to reset it. As a result, we need to flush + // the cross-block cache because we need to reload all modules with new configs. + *global_environment = Some(current_env.clone()); + drop(global_environment); + global_module_cache.flush_unchecked(); + + Ok(current_env) +} + /// Output type wrapper used by block executor. VM output is stored first, then /// transformed into TransactionOutput type that is returned. #[derive(Debug)] @@ -390,13 +448,16 @@ impl BlockExecutorTransactionOutput for AptosTransactionOutput { pub struct BlockAptosVM; impl BlockAptosVM { - pub fn execute_block_on_thread_pool< + fn execute_block_on_thread_pool< S: StateView + Sync, L: TransactionCommitHook, >( - executor_thread_pool: Arc, + executor_thread_pool: Arc, signature_verified_block: &[SignatureVerifiedTransaction], state_view: &S, + global_module_cache: Arc< + ImmutableModuleCache, + >, config: BlockExecutorConfig, transaction_commit_listener: Option, ) -> Result, VMStatus> { @@ -409,15 +470,25 @@ impl BlockAptosVM { } BLOCK_EXECUTOR_CONCURRENCY.set(config.local.concurrency_level as i64); + + let environment = get_environment_with_delayed_field_optimization_enabled( + state_view, + global_module_cache.as_ref(), + )?; + let executor = BlockExecutor::< SignatureVerifiedTransaction, AptosExecutorTask, S, L, ExecutableTestType, - >::new(config, executor_thread_pool, transaction_commit_listener); + >::new( + config, + executor_thread_pool, + global_module_cache, + transaction_commit_listener, + ); - let environment = get_environment_with_delayed_field_optimization_enabled(state_view)?; let ret = executor.execute_block(environment, signature_verified_block, state_view); match ret { Ok(block_output) => { @@ -449,7 +520,27 @@ impl BlockAptosVM { } } - /// Uses shared thread pool to execute blocks. + pub fn execute_block_on_thread_pool_without_global_module_cache< + S: StateView + Sync, + L: TransactionCommitHook, + >( + executor_thread_pool: Arc, + signature_verified_block: &[SignatureVerifiedTransaction], + state_view: &S, + config: BlockExecutorConfig, + transaction_commit_listener: Option, + ) -> Result, VMStatus> { + Self::execute_block_on_thread_pool::( + executor_thread_pool, + signature_verified_block, + state_view, + Arc::new(ImmutableModuleCache::empty()), + config, + transaction_commit_listener, + ) + } + + /// Uses shared thread pool and shared global module cache to execute blocks. pub fn execute_block< S: StateView + Sync, L: TransactionCommitHook, @@ -463,8 +554,53 @@ impl BlockAptosVM { Arc::clone(&RAYON_EXEC_POOL), signature_verified_block, state_view, + Arc::clone(&GLOBAL_MODULE_CACHE), config, transaction_commit_listener, ) } } + +#[cfg(test)] +mod test { + use super::*; + use aptos_block_executor::code_cache_global::ImmutableModuleCache; + use aptos_language_e2e_tests::data_store::FakeDataStore; + use aptos_types::on_chain_config::{FeatureFlag, Features}; + use aptos_vm_environment::environment::AptosEnvironment; + use claims::assert_ok; + use move_vm_types::code::mock_verified_code; + + #[test] + fn test_cross_block_module_cache_flush() { + let global_module_cache = ImmutableModuleCache::empty(); + + global_module_cache.insert(0, mock_verified_code(0, None)); + assert_eq!(global_module_cache.size(), 1); + + global_module_cache.flush_unchecked(); + assert_eq!(global_module_cache.size(), 0); + + // Now check that cache is flushed when the environment is flushed. + let mut state_view = FakeDataStore::default(); + let env_old = AptosEnvironment::new_with_delayed_field_optimization_enabled(&state_view); + + for i in 0..10 { + global_module_cache.insert(i, mock_verified_code(i, None)); + } + assert_eq!(global_module_cache.size(), 10); + + let mut features = Features::default(); + features.disable(FeatureFlag::KEYLESS_ACCOUNTS); + state_view.set_features(features); + + // New environment means we need to also flush global caches - to invalidate struct name + // indices. + let env_new = assert_ok!(get_environment_with_delayed_field_optimization_enabled( + &state_view, + &global_module_cache, + )); + assert!(env_old != env_new); + assert_eq!(global_module_cache.size(), 0); + } +} diff --git a/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs b/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs index e968f2416d47c..efe860c37103e 100644 --- a/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs +++ b/aptos-move/aptos-vm/src/sharded_block_executor/sharded_executor_service.rs @@ -135,7 +135,7 @@ impl ShardedExecutorService { ); }); s.spawn(move |_| { - let ret = BlockAptosVM::execute_block_on_thread_pool( + let ret = BlockAptosVM::execute_block_on_thread_pool_without_global_module_cache( executor_thread_pool, &signature_verified_transactions, aggr_overridden_state_view.as_ref(), diff --git a/aptos-move/block-executor/Cargo.toml b/aptos-move/block-executor/Cargo.toml index 14000db6c4fbb..aefd0ae6f13cc 100644 --- a/aptos-move/block-executor/Cargo.toml +++ b/aptos-move/block-executor/Cargo.toml @@ -22,7 +22,6 @@ aptos-logger = { workspace = true } aptos-metrics-core = { workspace = true } aptos-mvhashmap = { workspace = true } aptos-types = { workspace = true } -aptos-vm-environment = { workspace = true } aptos-vm-logging = { workspace = true } aptos-vm-types = { workspace = true } arc-swap = { workspace = true } @@ -62,6 +61,7 @@ rand = { workspace = true } test-case = { workspace = true } [features] +testing = [] fuzzing = ["criterion", "proptest", "proptest-derive"] [[bench]] diff --git a/aptos-move/block-executor/src/captured_reads.rs b/aptos-move/block-executor/src/captured_reads.rs index 87b91e88de928..007ae211b9d2c 100644 --- a/aptos-move/block-executor/src/captured_reads.rs +++ b/aptos-move/block-executor/src/captured_reads.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - cross_block_caches::ImmutableModuleCache, types::InputOutputKey, + code_cache_global::ImmutableModuleCache, types::InputOutputKey, value_exchange::filter_value_for_exchange, }; use anyhow::bail; @@ -873,17 +873,17 @@ where #[cfg(test)] mod test { use super::*; - use crate::{ - proptest_types::types::{raw_metadata, KeyType, MockEvent, ValueType}, - types::test_types::{mock_deserialized_code, mock_verified_code}, - }; + use crate::proptest_types::types::{raw_metadata, KeyType, MockEvent, ValueType}; use aptos_mvhashmap::{types::StorageVersion, MVHashMap}; use aptos_types::executable::ExecutableTestType; use claims::{ assert_err, assert_gt, assert_matches, assert_none, assert_ok, assert_ok_eq, assert_some_eq, }; use move_vm_types::{ - code::{MockDeserializedCode, MockVerifiedCode, ModuleCache}, + code::{ + mock_deserialized_code, mock_verified_code, MockDeserializedCode, MockVerifiedCode, + ModuleCache, + }, delayed_values::delayed_field_id::DelayedFieldID, }; use test_case::test_case; diff --git a/aptos-move/block-executor/src/code_cache.rs b/aptos-move/block-executor/src/code_cache.rs index 09baff1ab854b..5f4864621b1f1 100644 --- a/aptos-move/block-executor/src/code_cache.rs +++ b/aptos-move/block-executor/src/code_cache.rs @@ -3,7 +3,6 @@ use crate::{ captured_reads::CacheRead, - cross_block_caches::get_global_module_cache, view::{LatestView, ViewState}, }; use ambassador::delegate_to_methods; @@ -146,7 +145,7 @@ impl<'a, T: Transaction, S: TStateView, X: Executable> ModuleCache > { // First, look up the module in the cross-block global module cache. Record the read for // later validation in case the read module is republished. - if let Some(module) = get_global_module_cache().get(key) { + if let Some(module) = self.global_module_cache.get(key) { match &self.latest_view { ViewState::Sync(state) => state .captured_reads diff --git a/aptos-move/block-executor/src/cross_block_caches.rs b/aptos-move/block-executor/src/code_cache_global.rs similarity index 61% rename from aptos-move/block-executor/src/cross_block_caches.rs rename to aptos-move/block-executor/src/code_cache_global.rs index d095170b56972..5ee5b6cacfa68 100644 --- a/aptos-move/block-executor/src/cross_block_caches.rs +++ b/aptos-move/block-executor/src/code_cache_global.rs @@ -3,16 +3,10 @@ use crate::explicit_sync_wrapper::ExplicitSyncWrapper; use aptos_mvhashmap::types::TxnIndex; -use aptos_types::{error::PanicError, state_store::StateView, vm::modules::AptosModuleExtension}; -use aptos_vm_environment::environment::AptosEnvironment; +use aptos_types::error::PanicError; use crossbeam::utils::CachePadded; use hashbrown::HashMap; -use move_binary_format::{errors::Location, CompiledModule}; -use move_core_types::{language_storage::ModuleId, vm_status::VMStatus}; -use move_vm_runtime::{Module, WithRuntimeEnvironment}; use move_vm_types::code::ModuleCode; -use once_cell::sync::Lazy; -use parking_lot::Mutex; use std::{ hash::Hash, ops::Deref, @@ -22,50 +16,10 @@ use std::{ }, }; -/// The maximum size of struct name index map in runtime environment. Checked at block boundaries -/// only. -const MAX_STRUCT_NAME_INDEX_MAP_SIZE: usize = 100_000; - -/// A cached environment that can be persisted across blocks. Used by block executor only. -static CROSS_BLOCK_ENVIRONMENT: Lazy>> = - Lazy::new(|| Mutex::new(None)); - -/// Returns the cached environment if it exists and has the same configuration as if it was -/// created based on the current state, or creates a new one and caches it. Should only be -/// called at the block boundaries. -pub fn get_environment_with_delayed_field_optimization_enabled( - state_view: &impl StateView, -) -> Result { - // Create a new environment. - let current_env = AptosEnvironment::new_with_delayed_field_optimization_enabled(state_view); - - // Lock the cache, and check if the environment is the same. - let mut cross_block_environment = CROSS_BLOCK_ENVIRONMENT.lock(); - if let Some(previous_env) = cross_block_environment.as_ref() { - if ¤t_env == previous_env { - let runtime_env = previous_env.runtime_environment(); - let struct_name_index_map_size = runtime_env - .struct_name_index_map_size() - .map_err(|e| e.finish(Location::Undefined).into_vm_status())?; - if struct_name_index_map_size > MAX_STRUCT_NAME_INDEX_MAP_SIZE { - // Cache is too large, flush it. Also flush the module cache. - runtime_env.flush_struct_name_and_info_caches(); - get_global_module_cache().flush_unchecked(); - } - return Ok(previous_env.clone()); - } - } - - // It is not cached or has changed, so we have to reset it. As a result, we need to flush - // the cross-block cache because we need to reload all modules with new configs. - *cross_block_environment = Some(current_env.clone()); - drop(cross_block_environment); - get_global_module_cache().flush_unchecked(); - - Ok(current_env) -} - /// Module code stored in cross-block module cache. +// TODO(loader_v2): +// We can move this to move-vm-types, but then we also need to have version generic or expose +// transaction index there, and define PanicError in Move (or convert from VMError). struct ImmutableModuleCode { /// True if this code is "valid" within the block execution context (i.e, there has been no /// republishing of this module so far). If false, executor needs to read the module from the @@ -129,11 +83,10 @@ pub struct ImmutableModuleCache { impl ImmutableModuleCache where K: Hash + Eq + Clone, - VC: Deref>, { /// Returns new empty module cache with default capacity. - pub(crate) fn empty() -> Self { + pub fn empty() -> Self { let default_capacity = 100_000; Self::with_capacity(default_capacity) } @@ -220,56 +173,31 @@ where } /// Insert the module to cache. Used for tests only. - #[cfg(test)] - pub(crate) fn insert(&self, key: K, module: Arc>>) { + #[cfg(any(test, feature = "testing"))] + pub fn insert(&self, key: K, module: Arc>>) { self.module_cache .acquire() .insert(key, ImmutableModuleCode::new(module).unwrap()); } /// Removes the module from cache. Used for tests only. - #[cfg(test)] - pub(crate) fn remove(&self, key: &K) { + #[cfg(any(test, feature = "testing"))] + pub fn remove(&self, key: &K) { self.module_cache.acquire().remove(key); } /// Returns the size of the cache. Used for tests only. - #[cfg(test)] - pub(crate) fn size(&self) -> usize { + #[cfg(any(test, feature = "testing"))] + pub fn size(&self) -> usize { self.module_cache.acquire().len() } } -/// Immutable global cache. The size of the cache is fixed within a single block (modules are not -/// inserted or removed) and it is only mutated at the block boundaries. At the same time, modules -/// in this cache can be marked as "invalid" so that block executor can decide on whether to read -/// the module from this cache or from elsewhere. -#[allow(clippy::redundant_closure)] -static CROSS_BLOCK_MODULE_CACHE: Lazy< - ImmutableModuleCache, -> = Lazy::new(|| ImmutableModuleCache::empty()); - -/// Returns the module from the cross module cache. If the module has not been cached, or is -/// no longer valid due to module publishing, [None] is returned. -pub fn get_global_module_cache( -) -> &'static ImmutableModuleCache { - &CROSS_BLOCK_MODULE_CACHE -} - #[cfg(test)] mod test { use super::*; - use crate::types::test_types::{ - mock_deserialized_code, mock_verified_code, module_id, verified_code, - }; - use aptos_types::{ - on_chain_config::{FeatureFlag, Features}, - state_store::{ - errors::StateviewError, state_key::StateKey, state_storage_usage::StateStorageUsage, - state_value::StateValue, StateViewId, TStateView, - }, - }; use claims::{assert_err, assert_ok, assert_some}; + use move_vm_types::code::{mock_deserialized_code, mock_verified_code}; #[test] fn test_immutable_module_code() { @@ -349,65 +277,4 @@ mod test { assert!(result.is_ok()); assert_eq!(global_cache.size(), 1); } - - #[derive(Default)] - struct HashMapView { - data: HashMap, - } - - impl TStateView for HashMapView { - type Key = StateKey; - - fn get_state_value( - &self, - state_key: &Self::Key, - ) -> Result, StateviewError> { - Ok(self.data.get(state_key).cloned()) - } - - fn id(&self) -> StateViewId { - unreachable!("Not used in tests"); - } - - fn get_usage(&self) -> Result { - unreachable!("Not used in tests"); - } - } - - #[test] - fn test_cross_block_module_cache_flush() { - let c_id = module_id("c"); - get_global_module_cache().insert(c_id.clone(), verified_code("c", None)); - assert_eq!(get_global_module_cache().size(), 1); - - get_global_module_cache().flush_unchecked(); - assert_eq!(get_global_module_cache().size(), 0); - - // Now check that cache is flushed when the environment is flushed. - let mut state_view = HashMapView::default(); - let env_old = AptosEnvironment::new_with_delayed_field_optimization_enabled(&state_view); - - for i in 0..10 { - let name = format!("m_{}", i); - let id = module_id(&name); - get_global_module_cache().insert(id.clone(), verified_code(&name, None)); - } - assert_eq!(get_global_module_cache().size(), 10); - - let state_key = StateKey::on_chain_config::().unwrap(); - let mut features = Features::default(); - features.disable(FeatureFlag::KEYLESS_ACCOUNTS); - state_view.data.insert( - state_key, - StateValue::new_legacy(bcs::to_bytes(&features).unwrap().into()), - ); - - // New environment means we need to also flush global caches - to invalidate struct name - // indices. - let env_new = assert_ok!(get_environment_with_delayed_field_optimization_enabled( - &state_view - )); - assert!(env_old != env_new); - assert_eq!(get_global_module_cache().size(), 0); - } } diff --git a/aptos-move/block-executor/src/executor.rs b/aptos-move/block-executor/src/executor.rs index d20f88e1bc9bd..58fc53d3f774f 100644 --- a/aptos-move/block-executor/src/executor.rs +++ b/aptos-move/block-executor/src/executor.rs @@ -3,12 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ + code_cache_global::ImmutableModuleCache, counters, counters::{ PARALLEL_EXECUTION_SECONDS, RAYON_EXECUTION_SECONDS, TASK_EXECUTE_SECONDS, TASK_VALIDATE_SECONDS, VM_INIT_SECONDS, WORK_WITH_TASK_SECONDS, }, - cross_block_caches::get_global_module_cache, errors::*, executor_utilities::*, explicit_sync_wrapper::ExplicitSyncWrapper, @@ -74,6 +74,8 @@ pub struct BlockExecutor { // threads that may be concurrently participating in parallel execution. config: BlockExecutorConfig, executor_thread_pool: Arc, + global_module_cache: + Arc>, transaction_commit_hook: Option, phantom: PhantomData<(T, E, S, L, X)>, } @@ -91,6 +93,9 @@ where pub fn new( config: BlockExecutorConfig, executor_thread_pool: Arc, + global_module_cache: Arc< + ImmutableModuleCache, + >, transaction_commit_hook: Option, ) -> Self { assert!( @@ -101,6 +106,7 @@ where Self { config, executor_thread_pool, + global_module_cache, transaction_commit_hook, phantom: PhantomData, } @@ -114,6 +120,12 @@ where versioned_cache: &MVHashMap, executor: &E, base_view: &S, + global_module_cache: &ImmutableModuleCache< + ModuleId, + CompiledModule, + Module, + AptosModuleExtension, + >, runtime_environment: &RuntimeEnvironment, parallel_state: ParallelState, ) -> Result> { @@ -123,6 +135,7 @@ where // VM execution. let sync_view = LatestView::new( base_view, + global_module_cache, runtime_environment, ViewState::Sync(parallel_state), idx_to_execute, @@ -388,6 +401,12 @@ where fn validate( idx_to_validate: TxnIndex, last_input_output: &TxnLastInputOutput, + global_module_cache: &ImmutableModuleCache< + ModuleId, + CompiledModule, + Module, + AptosModuleExtension, + >, versioned_cache: &MVHashMap, scheduler: &Scheduler, ) -> bool { @@ -411,10 +430,8 @@ where read_set.validate_data_reads(versioned_cache.data(), idx_to_validate) && read_set.validate_group_reads(versioned_cache.group_data(), idx_to_validate) && (scheduler.skip_module_reads_validation() - || read_set.validate_module_reads( - get_global_module_cache(), - versioned_cache.module_cache(), - )) + || read_set + .validate_module_reads(global_module_cache, versioned_cache.module_cache())) } fn update_transaction_on_abort( @@ -587,6 +604,7 @@ where versioned_cache, executor, base_view, + self.global_module_cache.as_ref(), runtime_environment, ParallelState::new( versioned_cache, @@ -605,6 +623,7 @@ where Self::publish_module_writes( txn_idx, module_write_set, + self.global_module_cache.as_ref(), versioned_cache, scheduler, runtime_environment, @@ -614,8 +633,13 @@ where scheduler.wake_dependencies_and_decrease_validation_idx(txn_idx)?; - let validation_result = - Self::validate(txn_idx, last_input_output, versioned_cache, scheduler); + let validation_result = Self::validate( + txn_idx, + last_input_output, + self.global_module_cache.as_ref(), + versioned_cache, + scheduler, + ); if !validation_result || !Self::validate_and_commit_delayed_fields( txn_idx, @@ -641,6 +665,7 @@ where Self::publish_module_writes( txn_idx, module_write_set, + self.global_module_cache.as_ref(), versioned_cache, scheduler, runtime_environment, @@ -729,6 +754,12 @@ where fn publish_module_writes( txn_idx: TxnIndex, module_write_set: BTreeMap>, + global_module_cache: &ImmutableModuleCache< + ModuleId, + CompiledModule, + Module, + AptosModuleExtension, + >, versioned_cache: &MVHashMap, scheduler: &Scheduler, runtime_environment: &RuntimeEnvironment, @@ -741,6 +772,7 @@ where write, txn_idx, runtime_environment, + global_module_cache, versioned_cache.module_cache(), )?; } @@ -819,6 +851,7 @@ where ); let latest_view = LatestView::new( base_view, + self.global_module_cache.as_ref(), runtime_environment, ViewState::Sync(parallel_state), txn_idx, @@ -981,8 +1014,13 @@ where scheduler_task = match scheduler_task { SchedulerTask::ValidationTask(txn_idx, incarnation, wave) => { - let valid = - Self::validate(txn_idx, last_input_output, versioned_cache, scheduler); + let valid = Self::validate( + txn_idx, + last_input_output, + self.global_module_cache.as_ref(), + versioned_cache, + scheduler, + ); Self::update_on_validation( txn_idx, incarnation, @@ -1007,6 +1045,7 @@ where versioned_cache, &executor, base_view, + self.global_module_cache.as_ref(), runtime_environment, ParallelState::new( versioned_cache, @@ -1128,7 +1167,7 @@ where } counters::update_state_counters(versioned_cache.stats(), true); - get_global_module_cache() + self.global_module_cache .insert_verified_unchecked(versioned_cache.take_modules_iter()) .map_err(|err| { alert!("[BlockSTM] Encountered panic error: {:?}", err); @@ -1158,7 +1197,13 @@ where write: ModuleWrite, txn_idx: TxnIndex, runtime_environment: &RuntimeEnvironment, - module_cache: &impl ModuleCache< + global_module_cache: &ImmutableModuleCache< + ModuleId, + CompiledModule, + Module, + AptosModuleExtension, + >, + per_block_module_cache: &impl ModuleCache< Key = ModuleId, Deserialized = CompiledModule, Verified = Module, @@ -1182,8 +1227,8 @@ where })?; let extension = AptosModuleExtension::new(state_value); - get_global_module_cache().mark_invalid(&id); - module_cache + global_module_cache.mark_invalid(&id); + per_block_module_cache .insert_deserialized_module( id.clone(), compiled_module, @@ -1206,6 +1251,12 @@ where fn apply_output_sequential( txn_idx: TxnIndex, runtime_environment: &RuntimeEnvironment, + global_module_cache: &ImmutableModuleCache< + ModuleId, + CompiledModule, + Module, + AptosModuleExtension, + >, unsync_map: &UnsyncMap, output: &E::Output, resource_write_set: Vec<(T::Key, Arc, Option>)>, @@ -1231,6 +1282,7 @@ where write, txn_idx, runtime_environment, + global_module_cache, unsync_map.module_cache(), )?; } else { @@ -1318,6 +1370,7 @@ where for (idx, txn) in signature_verified_block.iter().enumerate() { let latest_view = LatestView::::new( base_view, + self.global_module_cache.as_ref(), runtime_environment, ViewState::Unsync(SequentialState::new(&unsync_map, start_counter, &counter)), idx as TxnIndex, @@ -1509,6 +1562,7 @@ where Self::apply_output_sequential( idx as TxnIndex, runtime_environment, + self.global_module_cache.as_ref(), &unsync_map, &output, resource_write_set.clone(), @@ -1596,7 +1650,8 @@ where ret.resize_with(num_txns, E::Output::skip_output); counters::update_state_counters(unsync_map.stats(), false); - get_global_module_cache().insert_verified_unchecked(unsync_map.into_modules_iter())?; + self.global_module_cache + .insert_verified_unchecked(unsync_map.into_modules_iter())?; let block_end_info = if self .config @@ -1656,7 +1711,7 @@ where // Flush the cache and the environment to re-run from the "clean" state. env.runtime_environment() .flush_struct_name_and_info_caches(); - get_global_module_cache().flush_unchecked(); + self.global_module_cache.flush_unchecked(); info!("parallel execution requiring fallback"); } diff --git a/aptos-move/block-executor/src/lib.rs b/aptos-move/block-executor/src/lib.rs index 96282ec0ab1c1..d7bfdc4a0c5cf 100644 --- a/aptos-move/block-executor/src/lib.rs +++ b/aptos-move/block-executor/src/lib.rs @@ -141,8 +141,8 @@ extern crate scopeguard; mod captured_reads; mod code_cache; +pub mod code_cache_global; pub mod counters; -pub mod cross_block_caches; pub mod errors; pub mod executor; mod executor_utilities; diff --git a/aptos-move/block-executor/src/proptest_types/bencher.rs b/aptos-move/block-executor/src/proptest_types/bencher.rs index 12c21e1506433..b34768150a126 100644 --- a/aptos-move/block-executor/src/proptest_types/bencher.rs +++ b/aptos-move/block-executor/src/proptest_types/bencher.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ + code_cache_global::ImmutableModuleCache, executor::BlockExecutor, proptest_types::{ baseline::BaselineOutput, @@ -127,6 +128,7 @@ where .build() .unwrap(), ); + let global_module_cache = Arc::new(ImmutableModuleCache::empty()); let config = BlockExecutorConfig::new_no_block_limit(num_cpus::get()); let env = MockEnvironment::new(); @@ -136,7 +138,7 @@ where EmptyDataView>, NoOpTransactionCommitHook, E>, usize>, ExecutableTestType, - >::new(config, executor_thread_pool, None) + >::new(config, executor_thread_pool, global_module_cache, None) .execute_transactions_parallel(&env, &self.transactions, &data_view); self.baseline_output.assert_parallel_output(&output); diff --git a/aptos-move/block-executor/src/proptest_types/tests.rs b/aptos-move/block-executor/src/proptest_types/tests.rs index 90096f24d8bd3..7ed0894bec414 100644 --- a/aptos-move/block-executor/src/proptest_types/tests.rs +++ b/aptos-move/block-executor/src/proptest_types/tests.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ + code_cache_global::ImmutableModuleCache, errors::SequentialBlockExecutionError, executor::BlockExecutor, proptest_types::{ @@ -81,6 +82,7 @@ fn run_transactions( >::new( BlockExecutorConfig::new_maybe_block_limit(num_cpus::get(), maybe_block_gas_limit), executor_thread_pool.clone(), + Arc::new(ImmutableModuleCache::empty()), None, ) .execute_transactions_parallel(&env, &transactions, &data_view); @@ -217,6 +219,7 @@ fn deltas_writes_mixed_with_block_gas_limit(num_txns: usize, maybe_block_gas_lim >::new( BlockExecutorConfig::new_maybe_block_limit(num_cpus::get(), maybe_block_gas_limit), executor_thread_pool.clone(), + Arc::new(ImmutableModuleCache::empty()), None, ) .execute_transactions_parallel(&env, &transactions, &data_view); @@ -269,6 +272,7 @@ fn deltas_resolver_with_block_gas_limit(num_txns: usize, maybe_block_gas_limit: >::new( BlockExecutorConfig::new_maybe_block_limit(num_cpus::get(), maybe_block_gas_limit), executor_thread_pool.clone(), + Arc::new(ImmutableModuleCache::empty()), None, ) .execute_transactions_parallel(&env, &transactions, &data_view); @@ -426,6 +430,7 @@ fn publishing_fixed_params_with_block_gas_limit( >::new( BlockExecutorConfig::new_maybe_block_limit(num_cpus::get(), maybe_block_gas_limit), executor_thread_pool, + Arc::new(ImmutableModuleCache::empty()), None, ) .execute_transactions_parallel(&env, &transactions, &data_view); @@ -472,6 +477,7 @@ fn publishing_fixed_params_with_block_gas_limit( Some(max(w_index, r_index) as u64 * MAX_GAS_PER_TXN + 1), ), executor_thread_pool.clone(), + Arc::new(ImmutableModuleCache::empty()), None, ) // Ensure enough gas limit to commit the module txns (4 is maximum gas per txn) .execute_transactions_parallel(&env, &transactions, &data_view); @@ -552,6 +558,7 @@ fn non_empty_group( >::new( BlockExecutorConfig::new_no_block_limit(num_cpus::get()), executor_thread_pool.clone(), + Arc::new(ImmutableModuleCache::empty()), None, ) .execute_transactions_parallel(&env, &transactions, &data_view); @@ -570,6 +577,7 @@ fn non_empty_group( >::new( BlockExecutorConfig::new_no_block_limit(num_cpus::get()), executor_thread_pool.clone(), + Arc::new(ImmutableModuleCache::empty()), None, ) .execute_transactions_sequential(&env, &transactions, &data_view, false); diff --git a/aptos-move/block-executor/src/types.rs b/aptos-move/block-executor/src/types.rs index 4191c1c2649c5..8e4c987d91fe5 100644 --- a/aptos-move/block-executor/src/types.rs +++ b/aptos-move/block-executor/src/types.rs @@ -55,77 +55,3 @@ impl fmt::Debug for ReadWriteSummary { Ok(()) } } - -#[cfg(test)] -pub(crate) mod test_types { - use aptos_mvhashmap::types::TxnIndex; - use aptos_types::{state_store::state_value::StateValue, vm::modules::AptosModuleExtension}; - use bytes::Bytes; - use move_binary_format::{ - file_format::empty_module_with_dependencies_and_friends, CompiledModule, - }; - use move_core_types::{ - account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId, - }; - use move_vm_runtime::{Module, RuntimeEnvironment}; - use move_vm_types::code::{MockDeserializedCode, MockVerifiedCode, ModuleCode}; - use std::sync::Arc; - - pub(crate) fn mock_deserialized_code( - value: usize, - version: Option, - ) -> Arc>> { - Arc::new(ModuleCode::from_deserialized( - MockDeserializedCode::new(value), - Arc::new(()), - version, - )) - } - - pub(crate) fn mock_verified_code( - value: usize, - version: Option, - ) -> Arc>> { - Arc::new(ModuleCode::from_verified( - MockVerifiedCode::new(value), - Arc::new(()), - version, - )) - } - - /// Returns a dummy [ModuleCode] in verified state. - pub(crate) fn verified_code( - module_name: &str, - version: Option, - ) -> Arc>> { - let compiled_module = Arc::new(empty_module_with_dependencies_and_friends( - module_name, - vec![], - vec![], - )); - let extension = Arc::new(AptosModuleExtension::new(StateValue::new_legacy( - Bytes::new(), - ))); - - // The actual cintents of the module do not matter for tests, but we cannot mock it because - // we have a static global cache for now. - let dummy_runtime_environment = RuntimeEnvironment::new(vec![]); - let locally_verified_module = dummy_runtime_environment - .build_locally_verified_module(compiled_module, 0, &[0; 32]) - .unwrap(); - let verified_module = dummy_runtime_environment - .build_verified_module(locally_verified_module, &[]) - .unwrap(); - - Arc::new(ModuleCode::from_verified( - verified_module, - extension, - version, - )) - } - - /// Returns a [ModuleId] for the given name. - pub(crate) fn module_id(name: &str) -> ModuleId { - ModuleId::new(AccountAddress::ONE, Identifier::new(name).unwrap()) - } -} diff --git a/aptos-move/block-executor/src/unit_tests/mod.rs b/aptos-move/block-executor/src/unit_tests/mod.rs index 94df5cb0ead46..4f7fb54a41155 100644 --- a/aptos-move/block-executor/src/unit_tests/mod.rs +++ b/aptos-move/block-executor/src/unit_tests/mod.rs @@ -5,6 +5,7 @@ mod code_cache_tests; use crate::{ + code_cache_global::ImmutableModuleCache, errors::SequentialBlockExecutionError, executor::BlockExecutor, proptest_types::{ @@ -86,6 +87,7 @@ fn test_resource_group_deletion() { >::new( BlockExecutorConfig::new_no_block_limit(num_cpus::get()), executor_thread_pool, + Arc::new(ImmutableModuleCache::empty()), None, ); @@ -152,6 +154,7 @@ fn resource_group_bcs_fallback() { >::new( BlockExecutorConfig::new_no_block_limit(num_cpus::get()), executor_thread_pool, + Arc::new(ImmutableModuleCache::empty()), None, ); @@ -251,6 +254,7 @@ fn block_output_err_precedence() { >::new( BlockExecutorConfig::new_no_block_limit(num_cpus::get()), executor_thread_pool, + Arc::new(ImmutableModuleCache::empty()), None, ); @@ -290,6 +294,7 @@ fn skip_rest_gas_limit() { >::new( BlockExecutorConfig::new_maybe_block_limit(num_cpus::get(), Some(5)), executor_thread_pool, + Arc::new(ImmutableModuleCache::empty()), None, ); @@ -325,6 +330,7 @@ where >::new( BlockExecutorConfig::new_no_block_limit(num_cpus::get()), executor_thread_pool, + Arc::new(ImmutableModuleCache::empty()), None, ) .execute_transactions_parallel(&env, &transactions, &data_view); diff --git a/aptos-move/block-executor/src/view.rs b/aptos-move/block-executor/src/view.rs index 705903840e996..b70f67a544d5c 100644 --- a/aptos-move/block-executor/src/view.rs +++ b/aptos-move/block-executor/src/view.rs @@ -8,6 +8,7 @@ use crate::{ CapturedReads, DataRead, DelayedFieldRead, DelayedFieldReadKind, GroupRead, ReadKind, UnsyncReadSet, }, + code_cache_global::ImmutableModuleCache, counters, scheduler::{DependencyResult, DependencyStatus, Scheduler, TWaitForDependency}, value_exchange::{ @@ -989,6 +990,8 @@ impl<'a, T: Transaction, X: Executable> ViewState<'a, T, X> { /// must be set according to the latest transaction that the worker was / is executing. pub(crate) struct LatestView<'a, T: Transaction, S: TStateView, X: Executable> { base_view: &'a S, + pub(crate) global_module_cache: + &'a ImmutableModuleCache, pub(crate) runtime_environment: &'a RuntimeEnvironment, pub(crate) latest_view: ViewState<'a, T, X>, pub(crate) txn_idx: TxnIndex, @@ -997,12 +1000,19 @@ pub(crate) struct LatestView<'a, T: Transaction, S: TStateView, X: impl<'a, T: Transaction, S: TStateView, X: Executable> LatestView<'a, T, S, X> { pub(crate) fn new( base_view: &'a S, + global_module_cache: &'a ImmutableModuleCache< + ModuleId, + CompiledModule, + Module, + AptosModuleExtension, + >, runtime_environment: &'a RuntimeEnvironment, latest_view: ViewState<'a, T, X>, txn_idx: TxnIndex, ) -> Self { Self { base_view, + global_module_cache, runtime_environment, latest_view, txn_idx, @@ -2511,9 +2521,11 @@ mod test { let base_view = MockStateView::new(HashMap::new()); let start_counter = 5; let runtime_environment = RuntimeEnvironment::new(vec![]); + let global_module_cache = ImmutableModuleCache::empty(); let latest_view = LatestView::::new( &base_view, + &global_module_cache, &runtime_environment, ViewState::Unsync(SequentialState::new(&unsync_map, start_counter, &counter)), 1, @@ -2777,6 +2789,8 @@ mod test { unsync_map: UnsyncMap, u32, ValueType, DelayedFieldID>, counter: RefCell, base_view: MockStateView, + empty_global_module_cache: + ImmutableModuleCache, runtime_environment: RuntimeEnvironment, } @@ -2790,6 +2804,7 @@ mod test { unsync_map, counter, base_view, + empty_global_module_cache: ImmutableModuleCache::empty(), runtime_environment, } } @@ -2803,6 +2818,7 @@ mod test { LatestView::<'a, TestTransactionType, MockStateView, MockExecutable>::new( &h.base_view, + &h.empty_global_module_cache, &h.runtime_environment, ViewState::Unsync(sequential_state), 1, @@ -2844,6 +2860,7 @@ mod test { let latest_view_par = LatestView::::new( &self.base_view, + &self.holder.empty_global_module_cache, &self.runtime_environment, ViewState::Sync(ParallelState::new( &self.versioned_map, diff --git a/aptos-move/e2e-tests/src/executor.rs b/aptos-move/e2e-tests/src/executor.rs index faa9bb5c75572..9a40f06f4efe8 100644 --- a/aptos-move/e2e-tests/src/executor.rs +++ b/aptos-move/e2e-tests/src/executor.rs @@ -14,9 +14,7 @@ use crate::{ }; use aptos_abstract_gas_usage::CalibrationAlgebra; use aptos_bitvec::BitVec; -use aptos_block_executor::{ - cross_block_caches::get_global_module_cache, txn_commit_hook::NoOpTransactionCommitHook, -}; +use aptos_block_executor::txn_commit_hook::NoOpTransactionCommitHook; use aptos_crypto::HashValue; use aptos_framework::ReleaseBundle; use aptos_gas_algebra::DynamicExpression; @@ -638,7 +636,7 @@ impl FakeExecutor { }, onchain: onchain_config, }; - BlockAptosVM::execute_block_on_thread_pool::< + BlockAptosVM::execute_block_on_thread_pool_without_global_module_cache::< _, NoOpTransactionCommitHook, >( @@ -682,12 +680,6 @@ impl FakeExecutor { // TODO fetch values from state? let onchain_config = BlockExecutorConfigFromOnchain::on_but_large_for_test(); - // Flush cross-block cache if we are comparing sequential and parallel executions. We do it - // twice to make sure that in case modules are published, we start from the empty cache. - if mode == ExecutorMode::BothComparison { - get_global_module_cache().flush_unchecked(); - } - let sequential_output = if mode != ExecutorMode::ParallelOnly { Some(self.execute_transaction_block_impl_with_state_view( &sig_verified_block, @@ -699,12 +691,6 @@ impl FakeExecutor { None }; - // Re-flush the cache again because the previous execution may have put new published code - // into cross-block module cache. - if mode == ExecutorMode::BothComparison { - get_global_module_cache().flush_unchecked(); - } - let parallel_output = if mode != ExecutorMode::SequentialOnly { Some(self.execute_transaction_block_impl_with_state_view( &sig_verified_block, diff --git a/third_party/move/move-vm/runtime/src/storage/module_storage.rs b/third_party/move/move-vm/runtime/src/storage/module_storage.rs index 124b851655417..041c4eb92f775 100644 --- a/third_party/move/move-vm/runtime/src/storage/module_storage.rs +++ b/third_party/move/move-vm/runtime/src/storage/module_storage.rs @@ -273,7 +273,7 @@ where if visited.insert(dependency_id.clone()) { // Dependency is not verified, and we have not visited it yet. let verified_dependency = visit_dependencies_and_verify( - dependency_id, + dependency_id.clone(), dependency, visited, module_cache_with_context, @@ -282,8 +282,8 @@ where } else { // We must have found a cycle otherwise. return Err(module_cyclic_dependency_error!( - module_id.address(), - module_id.name() + dependency_id.address(), + dependency_id.name() )); } } diff --git a/third_party/move/move-vm/types/src/code/cache/mod.rs b/third_party/move/move-vm/types/src/code/cache/mod.rs index 19083eccdeab8..5251a07091b68 100644 --- a/third_party/move/move-vm/types/src/code/cache/mod.rs +++ b/third_party/move/move-vm/types/src/code/cache/mod.rs @@ -3,4 +3,6 @@ pub(crate) mod module_cache; pub(crate) mod script_cache; +#[cfg(any(test, feature = "testing"))] +pub(crate) mod test_types; pub(crate) mod types; diff --git a/third_party/move/move-vm/types/src/code/cache/module_cache.rs b/third_party/move/move-vm/types/src/code/cache/module_cache.rs index ffd775f2c291d..5d9bf1772c633 100644 --- a/third_party/move/move-vm/types/src/code/cache/module_cache.rs +++ b/third_party/move/move-vm/types/src/code/cache/module_cache.rs @@ -455,7 +455,7 @@ where #[cfg(test)] mod test { use super::*; - use crate::code::cache::types::{MockDeserializedCode, MockVerifiedCode}; + use crate::code::{MockDeserializedCode, MockVerifiedCode}; use claims::{assert_ok, assert_some}; use move_binary_format::errors::{Location, PartialVMError}; use move_core_types::vm_status::StatusCode; diff --git a/third_party/move/move-vm/types/src/code/cache/script_cache.rs b/third_party/move/move-vm/types/src/code/cache/script_cache.rs index 64840da96143d..d65948785b7e9 100644 --- a/third_party/move/move-vm/types/src/code/cache/script_cache.rs +++ b/third_party/move/move-vm/types/src/code/cache/script_cache.rs @@ -200,7 +200,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::code::cache::types::{MockDeserializedCode, MockVerifiedCode}; + use crate::code::{MockDeserializedCode, MockVerifiedCode}; use claims::{assert_ok, assert_some}; use std::collections::BTreeSet; diff --git a/third_party/move/move-vm/types/src/code/cache/test_types.rs b/third_party/move/move-vm/types/src/code/cache/test_types.rs new file mode 100644 index 0000000000000..2b79b5d607045 --- /dev/null +++ b/third_party/move/move-vm/types/src/code/cache/test_types.rs @@ -0,0 +1,56 @@ +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::code::ModuleCode; +use std::{ops::Deref, sync::Arc}; + +#[derive(Clone)] +pub struct MockDeserializedCode(usize); + +impl MockDeserializedCode { + pub fn new(value: usize) -> Self { + Self(value) + } + + pub fn value(&self) -> usize { + self.0 + } +} + +pub struct MockVerifiedCode(Arc); + +impl MockVerifiedCode { + pub fn new(value: usize) -> Self { + Self(Arc::new(MockDeserializedCode(value))) + } +} + +impl Deref for MockVerifiedCode { + type Target = Arc; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub fn mock_deserialized_code( + value: usize, + version: V, +) -> Arc> { + Arc::new(ModuleCode::from_deserialized( + MockDeserializedCode::new(value), + Arc::new(()), + version, + )) +} + +pub fn mock_verified_code( + value: usize, + version: V, +) -> Arc> { + Arc::new(ModuleCode::from_verified( + MockVerifiedCode::new(value), + Arc::new(()), + version, + )) +} diff --git a/third_party/move/move-vm/types/src/code/cache/types.rs b/third_party/move/move-vm/types/src/code/cache/types.rs index 4fb6c62973ae7..b9cb0154b65a4 100644 --- a/third_party/move/move-vm/types/src/code/cache/types.rs +++ b/third_party/move/move-vm/types/src/code/cache/types.rs @@ -97,43 +97,10 @@ impl Clone for Code { } } -#[cfg(any(test, feature = "testing"))] -#[derive(Clone)] -pub struct MockDeserializedCode(usize); - -#[cfg(any(test, feature = "testing"))] -impl MockDeserializedCode { - pub fn new(value: usize) -> Self { - Self(value) - } - - pub fn value(&self) -> usize { - self.0 - } -} - -#[cfg(any(test, feature = "testing"))] -pub struct MockVerifiedCode(Arc); - -#[cfg(any(test, feature = "testing"))] -impl MockVerifiedCode { - pub fn new(value: usize) -> Self { - Self(Arc::new(MockDeserializedCode(value))) - } -} - -#[cfg(any(test, feature = "testing"))] -impl Deref for MockVerifiedCode { - type Target = Arc; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - #[cfg(test)] mod tests { use super::*; + use crate::code::{MockDeserializedCode, MockVerifiedCode}; #[test] fn test_deserialized_code() { diff --git a/third_party/move/move-vm/types/src/code/mod.rs b/third_party/move/move-vm/types/src/code/mod.rs index 9e2d0ab57764c..399519dca919b 100644 --- a/third_party/move/move-vm/types/src/code/mod.rs +++ b/third_party/move/move-vm/types/src/code/mod.rs @@ -6,7 +6,9 @@ pub mod errors; mod storage; #[cfg(any(test, feature = "testing"))] -pub use cache::types::{MockDeserializedCode, MockVerifiedCode}; +pub use cache::test_types::{ + mock_deserialized_code, mock_verified_code, MockDeserializedCode, MockVerifiedCode, +}; pub use cache::{ module_cache::{ ambassador_impl_ModuleCache, ModuleCache, ModuleCode, ModuleCodeBuilder, SyncModuleCache,