diff --git a/crates/e2e-move-tests/Cargo.toml b/crates/e2e-move-tests/Cargo.toml index f66ce477..a3f02280 100644 --- a/crates/e2e-move-tests/Cargo.toml +++ b/crates/e2e-move-tests/Cargo.toml @@ -46,6 +46,5 @@ default = [] testing = [ "initia-move-gas/testing", "initia-move-natives/testing", - "initia-move-storage/testing", "move-vm-runtime/testing", ] diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index 9408372f..f251015e 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -23,8 +23,5 @@ parking_lot = { workspace = true } sha3 = { workspace = true } claims = { workspace = true } -[features] -default = [] -testing = [ - "move-vm-runtime/testing", -] +[dev-dependencies] +rand = { workspace = true } diff --git a/crates/storage/src/allocator.rs b/crates/storage/src/allocator.rs new file mode 100644 index 00000000..385d67a7 --- /dev/null +++ b/crates/storage/src/allocator.rs @@ -0,0 +1,84 @@ +use std::{ + alloc::{GlobalAlloc, Layout, System}, + cell::Cell, +}; + +use move_binary_format::errors::VMResult; + +thread_local! { + static METERING: Cell = const { Cell::new(false) }; + static SIZE: Cell = const { Cell::new(0) }; +} + +struct SizeCounterAllocator; + +unsafe impl GlobalAlloc for SizeCounterAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if METERING.with(|metering| metering.get()) { + SIZE.with(|size| size.set(size.get() + layout.size())); + } + + System.alloc(layout) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + System.dealloc(ptr, layout) + } +} + +#[global_allocator] +static GLOBAL: SizeCounterAllocator = SizeCounterAllocator; + +#[inline] +fn start_metering() { + debug_assert!(!METERING.with(|metering| metering.get())); + SIZE.with(|size| size.set(0)); + METERING.with(|metering| metering.set(true)); +} + +#[inline] +fn finish_metering() -> usize { + debug_assert!(METERING.with(|metering| metering.get())); + METERING.with(|metering| metering.set(false)); + SIZE.with(|size| size.get()) +} + +#[inline] +pub(crate) fn get_size VMResult>(f: O) -> VMResult<(T, usize)> { + start_metering(); + let ret = f()?; + let size = finish_metering(); + + Ok((ret, size + size_of::())) +} + +#[cfg(test)] +mod allocator_test { + use rand::Rng; + use std::thread; + + use super::*; + + #[test] + fn test_get_size() { + let num_thread = 100; + for _ in 0..num_thread { + let handle = thread::spawn(|| { + let num_bytes = rand::thread_rng().gen_range(0..5120); // < 5KB + let (_, size) = get_size(|| { + for _ in 0..num_bytes { + // allocate 1 byte + let _ = vec![0u8; 1]; + } + + Ok(()) + }) + .unwrap(); + + assert_eq!(size, num_bytes); + }); + + handle.join().unwrap(); + } + } +} diff --git a/crates/storage/src/code_scale.rs b/crates/storage/src/code_scale.rs index 382ac6ea..eda0b95b 100644 --- a/crates/storage/src/code_scale.rs +++ b/crates/storage/src/code_scale.rs @@ -11,24 +11,45 @@ use crate::module_cache::BytesWithHash; use crate::module_cache::NoVersion; use crate::state_view::Checksum; -pub struct CodeScale; +pub struct ScriptScale; -impl WeightScale> for CodeScale { - fn weight(&self, _key: &Checksum, _value: &Code) -> usize { - 1 +impl WeightScale for ScriptScale { + fn weight(&self, _key: &Checksum, value: &ScriptWrapper) -> usize { + value.size } } -pub struct ModuleCodeScale; - -impl WeightScale>> - for ModuleCodeScale -{ - fn weight( - &self, - _key: &Checksum, - _value: &Arc>, - ) -> usize { - 1 +pub struct ModuleScale; + +impl WeightScale for ModuleScale { + fn weight(&self, _key: &Checksum, value: &ModuleWrapper) -> usize { + value.size + } +} + +#[derive(Clone)] +pub struct ScriptWrapper { + pub code: Code, + pub size: usize, +} + +impl ScriptWrapper { + pub fn new(code: Code, size: usize) -> Self { + Self { code, size } + } +} + +#[derive(Clone)] +pub struct ModuleWrapper { + pub module_code: Arc>, + pub size: usize, +} + +impl ModuleWrapper { + pub fn new( + module_code: Arc>, + size: usize, + ) -> Self { + Self { module_code, size } } } diff --git a/crates/storage/src/code_storage.rs b/crates/storage/src/code_storage.rs index 0dfe76d1..2f512ae8 100644 --- a/crates/storage/src/code_storage.rs +++ b/crates/storage/src/code_storage.rs @@ -10,13 +10,11 @@ use move_vm_runtime::{ logging::expect_no_verification_errors, CodeStorage, Module, ModuleStorage, RuntimeEnvironment, Script, WithRuntimeEnvironment, }; -use move_vm_types::{ - code::{Code, ModuleBytesStorage}, - module_linker_error, sha3_256, -}; +use move_vm_types::{code::ModuleBytesStorage, module_linker_error, sha3_256}; use std::sync::Arc; use crate::{ + allocator::get_size, module_cache::InitiaModuleCache, module_storage::{AsInitiaModuleStorage, InitiaModuleStorage}, script_cache::InitiaScriptCache, @@ -98,29 +96,41 @@ impl CodeStorage for InitiaCodeStorage { ) -> VMResult> { let hash = sha3_256(serialized_script); Ok(match self.script_cache.get_script(&hash) { - Some(script) => script.deserialized().clone(), + Some(script) => script.code.deserialized().clone(), None => { - let deserialized_script = self - .runtime_environment() - .deserialize_into_script(serialized_script)?; - self.script_cache - .insert_deserialized_script(hash, deserialized_script)? + // Deserialize the script and compute its size. + let (deserialized_script, allocated_size) = get_size(move || { + self.runtime_environment() + .deserialize_into_script(serialized_script) + })?; + + // Cache the deserialized script. + self.script_cache.insert_deserialized_script( + hash, + deserialized_script, + allocated_size, + )? } }) } fn verify_and_cache_script(&self, serialized_script: &[u8]) -> VMResult> { - use Code::*; - let hash = sha3_256(serialized_script); - let deserialized_script = match self.script_cache.get_script(&hash) { - Some(Verified(script)) => return Ok(script), - Some(Deserialized(deserialized_script)) => deserialized_script, - None => self - .runtime_environment() - .deserialize_into_script(serialized_script) - .map(Arc::new)?, - }; + let (deserialized_script, compiled_script_allocated_size) = + match self.script_cache.get_script(&hash) { + Some(code_wrapper) => { + if code_wrapper.code.is_verified() { + return Ok(code_wrapper.code.verified().clone()); + } + + (code_wrapper.code.deserialized().clone(), code_wrapper.size) + } + None => get_size(move || { + self.runtime_environment() + .deserialize_into_script(serialized_script) + .map(Arc::new) + })?, + }; // Locally verify the script. let locally_verified_script = self @@ -137,12 +147,19 @@ impl CodeStorage for InitiaCodeStorage { .ok_or_else(|| module_linker_error!(addr, name)) }) .collect::>>()?; - let verified_script = self - .runtime_environment() - .build_verified_script(locally_verified_script, &immediate_dependencies)?; - self.script_cache - .insert_verified_script(hash, verified_script) + // Verify the script and compute its size. + let (verified_script, allocated_size) = get_size(move || { + self.runtime_environment() + .build_verified_script(locally_verified_script, &immediate_dependencies) + })?; + + // Cache the verified script. + self.script_cache.insert_verified_script( + hash, + verified_script, + allocated_size + compiled_script_allocated_size, + ) } } @@ -162,11 +179,11 @@ impl InitiaCodeStorage { ); for hash in deserialized { let script = claims::assert_some!(self.script_cache.get_script(hash)); - assert!(!script.is_verified()) + assert!(!script.code.is_verified()) } for hash in verified { let script = claims::assert_some!(self.script_cache.get_script(hash)); - assert!(script.is_verified()) + assert!(script.code.is_verified()) } } } diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index f02a8b72..cd45fad2 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -9,4 +9,5 @@ pub mod module_cache; pub mod module_storage; pub mod script_cache; +mod allocator; pub mod code_scale; diff --git a/crates/storage/src/module_cache.rs b/crates/storage/src/module_cache.rs index 1a22fce4..62f056a8 100644 --- a/crates/storage/src/module_cache.rs +++ b/crates/storage/src/module_cache.rs @@ -3,7 +3,7 @@ use std::{hash::RandomState, num::NonZeroUsize, sync::Arc}; use bytes::Bytes; use clru::{CLruCache, CLruCacheConfig}; use move_binary_format::{ - errors::{Location, PartialVMError, VMError, VMResult}, + errors::{Location, PartialVMError, VMResult}, CompiledModule, }; use move_core_types::{language_storage::ModuleId, vm_status::StatusCode}; @@ -11,13 +11,11 @@ use move_vm_runtime::Module; use move_vm_types::code::{ModuleCode, ModuleCodeBuilder, WithBytes, WithHash}; use parking_lot::Mutex; -use crate::{code_scale::ModuleCodeScale, state_view::Checksum}; - -fn handle_cache_error(module_id: ModuleId) -> VMError { - PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED) - .with_message("Module storage cache eviction error".to_string()) - .finish(Location::Module(module_id)) -} +use crate::{ + allocator::get_size, + code_scale::{ModuleScale, ModuleWrapper}, + state_view::Checksum, +}; /// Extension for modules stored in [InitialModuleCache] to also capture information about bytes /// and hash. @@ -54,22 +52,15 @@ pub struct NoVersion; pub struct InitiaModuleCache { #[allow(clippy::type_complexity)] - pub(crate) module_cache: Mutex< - CLruCache< - Checksum, - Arc>, - RandomState, - ModuleCodeScale, - >, - >, + pub(crate) module_cache: Mutex>, } impl InitiaModuleCache { pub fn new(cache_capacity: usize) -> Arc { - let capacity = NonZeroUsize::new(cache_capacity).unwrap(); + let capacity = NonZeroUsize::new(cache_capacity * 1024 * 1024).unwrap(); Arc::new(InitiaModuleCache { module_cache: Mutex::new(CLruCache::with_config( - CLruCacheConfig::new(capacity).with_scale(ModuleCodeScale), + CLruCacheConfig::new(capacity).with_scale(ModuleScale), )), }) } @@ -82,6 +73,7 @@ impl InitiaModuleCache { &self, key: Checksum, deserialized_code: CompiledModule, + allocated_size: usize, extension: Arc, version: NoVersion, ) -> VMResult<()> { @@ -98,8 +90,11 @@ impl InitiaModuleCache { version, )); module_cache - .put_with_weight(key, module) - .map_err(|_| handle_cache_error(module_id))?; + .put_with_weight(key, ModuleWrapper::new(module, allocated_size)) + .unwrap_or_else(|_| { + eprintln!("WARNING: failed to insert module {:?} into cache; cache capacity might be too small", module_id.short_str_lossless().to_string()); + None + }); Ok(()) } } @@ -109,31 +104,24 @@ impl InitiaModuleCache { &self, key: Checksum, verified_code: Module, + allocated_size: usize, extension: Arc, version: NoVersion, ) -> VMResult>> { let mut module_cache = self.module_cache.lock(); - match module_cache.get(&key) { - Some(code) => { - if code.code().is_verified() { - Ok(code.clone()) - } else { - let module_id = verified_code.self_id(); - let module = - Arc::new(ModuleCode::from_verified(verified_code, extension, version)); - module_cache - .put_with_weight(key, module.clone()) - .map_err(|_| handle_cache_error(module_id))?; - Ok(module) - } + Some(module_wrapper) if module_wrapper.module_code.code().is_verified() => { + Ok(module_wrapper.module_code.clone()) } - None => { + _ => { let module_id = verified_code.self_id(); let module = Arc::new(ModuleCode::from_verified(verified_code, extension, version)); module_cache - .put_with_weight(key, module.clone()) - .map_err(|_| handle_cache_error(module_id))?; + .put_with_weight(key, ModuleWrapper::new(module.clone(), allocated_size)) + .unwrap_or_else(|_| { + eprintln!("WARNING: failed to insert module {:?} into cache; cache capacity might be too small", module_id.short_str_lossless().to_string()); + None + }); Ok(module) } } @@ -151,32 +139,34 @@ impl InitiaModuleCache { Extension = BytesWithHash, Version = NoVersion, >, - ) -> VMResult>>> { + ) -> VMResult> { let mut module_cache = self.module_cache.lock(); Ok(match module_cache.get(checksum) { - Some(code) => Some(code.clone()), - None => match builder.build(id)? { - Some(code) => { - if code.extension().hash() != checksum { - return Err(PartialVMError::new( - StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, - ) - .with_message("Checksum mismatch".to_string()) - .finish(Location::Module(id.clone()))); + Some(module_wrapper) => Some(module_wrapper.clone()), + None => { + let (build_result, allocated_size) = get_size(move || builder.build(id))?; + match build_result { + Some(code) => { + if code.extension().hash() != checksum { + return Err(PartialVMError::new( + StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR, + ) + .with_message("Checksum mismatch".to_string()) + .finish(Location::Module(id.clone()))); + } + + let code_wrapper = ModuleWrapper::new(Arc::new(code), allocated_size); + module_cache + .put_with_weight(*checksum, code_wrapper.clone()) + .unwrap_or_else(|_| { + eprintln!("WARNING: failed to insert module {:?} into cache; cache capacity might be too small", id.short_str_lossless().to_string()); + None + }); + Some(code_wrapper) } - - let code = Arc::new(code); - module_cache - .put_with_weight(*checksum, code.clone()) - .map_err(|_| { - PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED) - .with_message("Module storage cache eviction error".to_string()) - .finish(Location::Module(id.clone())) - })?; - Some(code) + None => None, } - None => None, - }, + } }) } diff --git a/crates/storage/src/module_storage.rs b/crates/storage/src/module_storage.rs index d8d4981d..10758f54 100644 --- a/crates/storage/src/module_storage.rs +++ b/crates/storage/src/module_storage.rs @@ -2,15 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ + allocator::get_size, + code_scale::ModuleWrapper, module_cache::{BytesWithHash, InitiaModuleCache, NoVersion}, state_view::{Checksum, ChecksumStorage}, }; use bytes::Bytes; -use move_binary_format::{errors::VMResult, CompiledModule}; +use move_binary_format::{ + errors::{Location, PartialVMError, VMResult}, + CompiledModule, +}; use move_core_types::{ account_address::AccountAddress, identifier::IdentStr, language_storage::ModuleId, - metadata::Metadata, + metadata::Metadata, vm_status::StatusCode, }; use move_vm_runtime::{Module, ModuleStorage, RuntimeEnvironment, WithRuntimeEnvironment}; use move_vm_types::{ @@ -121,7 +126,7 @@ impl<'s, S: ModuleBytesStorage + ChecksumStorage> InitiaModuleStorage<'s, S> { .module_cache .get_module_or_build_with(id, checksum, self); let module = claims::assert_some!(claims::assert_ok!(result)); - assert!(!module.code().is_verified()) + assert!(!module.module_code.code().is_verified()) } assert_eq!(verified.len(), verified_checksum.len()); for (id, checksum) in verified.into_iter().zip(verified_checksum) { @@ -129,7 +134,7 @@ impl<'s, S: ModuleBytesStorage + ChecksumStorage> InitiaModuleStorage<'s, S> { .module_cache .get_module_or_build_with(id, checksum, self); let module = claims::assert_some!(claims::assert_ok!(result)); - assert!(module.code().is_verified()) + assert!(module.module_code.code().is_verified()) } } } @@ -168,7 +173,11 @@ impl<'s, S: ModuleBytesStorage + ChecksumStorage> ModuleCodeBuilder for InitiaMo .deserialize_into_compiled_module(&bytes)?; if checksum != hash { - return Err(module_linker_error!(key.address(), key.name())); + return Err( + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message("Checksum mismatch".to_string()) + .finish(Location::Module(compiled_module.self_id())), + ); } let extension = Arc::new(BytesWithHash::new(bytes, hash)); @@ -207,7 +216,7 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule Ok(self .module_cache .get_module_or_build_with(&id, &checksum, self)? - .map(|module| module.extension().bytes().clone())) + .map(|module| module.module_code.extension().bytes().clone())) } fn fetch_module_size_in_bytes( @@ -223,7 +232,7 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule Ok(self .module_cache .get_module_or_build_with(&id, &checksum, self)? - .map(|module| module.extension().bytes().len())) + .map(|module| module.module_code.extension().bytes().len())) } fn fetch_module_metadata( @@ -239,7 +248,7 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule Ok(self .module_cache .get_module_or_build_with(&id, &checksum, self)? - .map(|module| module.code().deserialized().metadata.clone())) + .map(|module| module.module_code.code().deserialized().metadata.clone())) } fn fetch_deserialized_module( @@ -255,7 +264,7 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule Ok(self .module_cache .get_module_or_build_with(&id, &checksum, self)? - .map(|module| module.code().deserialized().clone())) + .map(|module| module.module_code.code().deserialized().clone())) } fn fetch_verified_module( @@ -271,7 +280,7 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule // Look up the verified module in cache, if it is not there, or if the module is not yet // verified, we need to load & verify its transitive dependencies. - let module = match self + let module_code_wrapper = match self .module_cache .get_module_or_build_with(&id, &checksum, self)? { @@ -279,8 +288,10 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule None => return Ok(None), }; - if module.code().is_verified() { - return Ok(Some(module.code().verified().clone())); + if module_code_wrapper.module_code.code().is_verified() { + return Ok(Some( + module_code_wrapper.module_code.code().verified().clone(), + )); } let mut visited = HashSet::new(); @@ -288,7 +299,7 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule Ok(Some(visit_dependencies_and_verify( id, checksum, - module, + module_code_wrapper, &mut visited, self, )?)) @@ -311,11 +322,12 @@ impl<'a, S: ModuleBytesStorage + ChecksumStorage> ModuleStorage for InitiaModule fn visit_dependencies_and_verify( module_id: ModuleId, checksum: Checksum, - module: Arc>, + unverified_module_wrapper: ModuleWrapper, visited: &mut HashSet, module_cache_with_context: &InitiaModuleStorage<'_, S>, ) -> VMResult> { let runtime_environment = module_cache_with_context.runtime_environment; + let module = unverified_module_wrapper.module_code; // Step 1: Local verification. runtime_environment.paranoid_check_module_address_and_name( @@ -334,52 +346,65 @@ fn visit_dependencies_and_verify( let mut verified_dependencies = vec![]; for (addr, name) in locally_verified_code.immediate_dependencies_iter() { let dependency_id = ModuleId::new(*addr, name.to_owned()); - let dependency_checksum = module_cache_with_context + match module_cache_with_context .base_storage .fetch_checksum(addr, name)? - .ok_or_else(|| module_linker_error!(dependency_id.address(), dependency_id.name()))?; - - let dependency = module_cache_with_context - .module_cache - .get_module_or_build_with( - &dependency_id, - &dependency_checksum, - module_cache_with_context, - )? - .ok_or_else(|| module_linker_error!(addr, name))?; - - // Dependency is already verified! - if dependency.code().is_verified() { - verified_dependencies.push(dependency.code().verified().clone()); - continue; - } - - 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.clone(), - dependency_checksum, - dependency, - visited, - module_cache_with_context, - )?; - verified_dependencies.push(verified_dependency); - } else { - // We must have found a cycle otherwise. - return Err(module_cyclic_dependency_error!( - dependency_id.address(), - dependency_id.name() - )); - } + { + Some(dependency_checksum) => { + let dependency = module_cache_with_context + .module_cache + .get_module_or_build_with( + &dependency_id, + &dependency_checksum, + module_cache_with_context, + )? + .ok_or_else(|| module_linker_error!(addr, name))?; + + // Dependency is already verified! + if dependency.module_code.code().is_verified() { + verified_dependencies.push(dependency.module_code.code().verified().clone()); + continue; + } + + 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.clone(), + dependency_checksum, + dependency, + visited, + module_cache_with_context, + )?; + verified_dependencies.push(verified_dependency); + } else { + // We must have found a cycle otherwise. + return Err(module_cyclic_dependency_error!( + dependency_id.address(), + dependency_id.name() + )); + } + } + None => { + return Err(module_linker_error!( + dependency_id.address(), + dependency_id.name() + )); + } + }; } - let verified_code = - runtime_environment.build_verified_module(locally_verified_code, &verified_dependencies)?; + // Build verified module and the compute size of the verified module + let (verified_code, allocated_size_for_verified) = get_size(move || { + runtime_environment.build_verified_module(locally_verified_code, &verified_dependencies) + })?; + + // Cache the verified module let module = module_cache_with_context .module_cache .insert_verified_module( checksum, verified_code, + allocated_size_for_verified + unverified_module_wrapper.size, module.extension().clone(), module.version(), )?; diff --git a/crates/storage/src/script_cache.rs b/crates/storage/src/script_cache.rs index ec4a2587..bcc0f505 100644 --- a/crates/storage/src/script_cache.rs +++ b/crates/storage/src/script_cache.rs @@ -1,28 +1,26 @@ use std::{hash::RandomState, num::NonZeroUsize, sync::Arc}; use clru::{CLruCache, CLruCacheConfig}; -use move_binary_format::{ - errors::{Location, PartialVMError, VMResult}, - file_format::CompiledScript, -}; -use move_core_types::vm_status::StatusCode; +use move_binary_format::{errors::VMResult, file_format::CompiledScript}; use move_vm_runtime::Script; use move_vm_types::code::Code; use parking_lot::Mutex; -use crate::{code_scale::CodeScale, state_view::Checksum}; +use crate::{ + code_scale::{ScriptScale, ScriptWrapper}, + state_view::Checksum, +}; pub struct InitiaScriptCache { - pub(crate) script_cache: - Mutex, RandomState, CodeScale>>, + pub(crate) script_cache: Mutex>, } impl InitiaScriptCache { pub fn new(cache_capacity: usize) -> Arc { Arc::new(InitiaScriptCache { script_cache: Mutex::new(CLruCache::with_config( - CLruCacheConfig::new(NonZeroUsize::new(cache_capacity).unwrap()) - .with_scale(CodeScale), + CLruCacheConfig::new(NonZeroUsize::new(cache_capacity * 1024 * 1024).unwrap()) + .with_scale(ScriptScale), )), }) } @@ -35,20 +33,22 @@ impl InitiaScriptCache { &self, key: Checksum, deserialized_script: CompiledScript, + allocated_size: usize, ) -> VMResult> { let mut script_cache = self.script_cache.lock(); match script_cache.get(&key) { - Some(code) => Ok(code.deserialized().clone()), + Some(code) => Ok(code.code.deserialized().clone()), None => { let new_script = Code::from_deserialized(deserialized_script); let deserialized_script = new_script.deserialized().clone(); // error occurs when the new script has a weight greater than the cache capacity - script_cache.put_with_weight(key, new_script).map_err(|_| { - PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED) - .with_message("Script storage cache eviction error".to_string()) - .finish(Location::Script) - })?; + script_cache + .put_with_weight(key, ScriptWrapper::new(new_script, allocated_size)) + .unwrap_or_else(|_| { + eprintln!("WARNING: failed to insert script into cache; cache capacity might be too small"); + None + }); Ok(deserialized_script) } @@ -59,17 +59,18 @@ impl InitiaScriptCache { &self, key: Checksum, verified_script: Script, + allocated_size: usize, ) -> VMResult> { let mut script_cache = self.script_cache.lock(); let (new_script, verified_script) = match script_cache.get(&key) { - Some(code) => { - if !code.is_verified() { + Some(script_wrapper) => { + if !script_wrapper.code.is_verified() { let new_script = Code::from_verified(verified_script); let verified_script = new_script.verified().clone(); (Some(new_script), verified_script) } else { - (None, code.verified().clone()) + (None, script_wrapper.code.verified().clone()) } } None => { @@ -79,26 +80,23 @@ impl InitiaScriptCache { } }; - if new_script.is_some() { + if let Some(new_script) = new_script { script_cache - .put_with_weight(key, new_script.unwrap()) - .map_err(|_| { - PartialVMError::new(StatusCode::MEMORY_LIMIT_EXCEEDED) - .with_message("Script storage cache eviction error".to_string()) - .finish(Location::Script) - })?; + .put_with_weight(key, ScriptWrapper::new(new_script, allocated_size)) + .unwrap_or_else(|_| { + eprintln!("WARNING: failed to insert script into cache; cache capacity might be too small"); + None + }); } Ok(verified_script) } - pub(crate) fn get_script(&self, key: &Checksum) -> Option> { - let mut script_cache = self.script_cache.lock(); - script_cache.get(key).cloned() + pub(crate) fn get_script(&self, key: &Checksum) -> Option { + self.script_cache.lock().get(key).cloned() } #[allow(unused)] pub(crate) fn num_scripts(&self) -> usize { - let script_cache = self.script_cache.lock(); - script_cache.len() + self.script_cache.lock().len() } }