From 34a404ceae79d6afec1c619a8e0869367cfba328 Mon Sep 17 00:00:00 2001 From: George Mitenkov Date: Sun, 14 Jul 2024 13:24:52 +0200 Subject: [PATCH] [move] Minor improvements for code storage (#13892) - `ModulePath` uses a boolean + have a new method to construct a state key from address + module name. - `DeltaStorage` removed. - Move resolvers to VM types. - `ResourceResolver` and `ModuleResolver` are clearly separated. --- Cargo.lock | 2 +- aptos-move/aptos-vm/src/aptos_vm.rs | 9 +- aptos-move/aptos-vm/src/data_cache.rs | 14 ++- aptos-move/aptos-vm/src/keyless_validation.rs | 9 +- .../aptos-vm/src/move_vm_ext/resolver.rs | 8 +- .../src/proptest_types/types.rs | 40 +++----- aptos-move/block-executor/src/view.rs | 4 +- aptos-move/mvhashmap/src/types.rs | 13 ++- aptos-move/mvhashmap/src/unsync_map.rs | 2 +- .../async/move-async-vm/src/async_vm.rs | 10 +- .../async/move-async-vm/tests/testsuite.rs | 24 ++--- third_party/move/move-core/types/src/lib.rs | 1 - .../src/tests/bad_storage_tests.rs | 67 ++++++++----- .../move/move-vm/runtime/src/data_cache.rs | 8 +- .../move/move-vm/runtime/src/move_vm.rs | 19 ++-- .../move/move-vm/runtime/src/session.rs | 11 +-- .../move/move-vm/test-utils/src/lib.rs | 2 +- .../move/move-vm/test-utils/src/storage.rs | 93 ++----------------- third_party/move/move-vm/types/Cargo.toml | 4 +- third_party/move/move-vm/types/src/lib.rs | 1 + .../types/src/resolver.rs | 44 ++------- .../testing-infra/test-generation/Cargo.toml | 1 - .../testing-infra/test-generation/src/lib.rs | 41 ++++---- .../src/vm_test_harness.rs | 9 +- types/src/executable.rs | 68 +++----------- 25 files changed, 184 insertions(+), 320 deletions(-) rename third_party/move/{move-core => move-vm}/types/src/resolver.rs (64%) diff --git a/Cargo.lock b/Cargo.lock index 2de1a23b7976f..b5a1495d60d0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11254,6 +11254,7 @@ name = "move-vm-types" version = "0.1.0" dependencies = [ "bcs 0.1.4", + "bytes", "claims", "derivative", "itertools 0.12.1", @@ -15430,7 +15431,6 @@ dependencies = [ "move-vm-test-utils", "move-vm-types", "num_cpus", - "once_cell", "rand 0.8.5", "tracing", "tracing-subscriber 0.3.18", diff --git a/aptos-move/aptos-vm/src/aptos_vm.rs b/aptos-move/aptos-vm/src/aptos_vm.rs index 7c837a6ab3b46..bcb6f2e5462c7 100644 --- a/aptos-move/aptos-vm/src/aptos_vm.rs +++ b/aptos-move/aptos-vm/src/aptos_vm.rs @@ -2704,8 +2704,13 @@ pub(crate) fn is_account_init_for_sponsored_transaction( && txn_data.fee_payer.is_some() && txn_data.sequence_number == 0 && resolver - .get_resource(&txn_data.sender(), &AccountResource::struct_tag()) - .map(|data| data.is_none()) + .get_resource_bytes_with_metadata_and_layout( + &txn_data.sender(), + &AccountResource::struct_tag(), + &resolver.get_module_metadata(&AccountResource::struct_tag().module_id()), + None, + ) + .map(|(data, _)| data.is_none()) .map_err(|e| e.finish(Location::Undefined))?, ) } diff --git a/aptos-move/aptos-vm/src/data_cache.rs b/aptos-move/aptos-vm/src/data_cache.rs index da4e7a544c1f4..7861880b40479 100644 --- a/aptos-move/aptos-vm/src/data_cache.rs +++ b/aptos-move/aptos-vm/src/data_cache.rs @@ -40,10 +40,12 @@ use move_core_types::{ account_address::AccountAddress, language_storage::{ModuleId, StructTag}, metadata::Metadata, - resolver::{resource_size, ModuleResolver, ResourceResolver}, value::MoveTypeLayout, }; -use move_vm_types::delayed_values::delayed_field_id::DelayedFieldID; +use move_vm_types::{ + delayed_values::delayed_field_id::DelayedFieldID, + resolver::{resource_size, ModuleResolver, ResourceResolver}, +}; use std::{ cell::RefCell, collections::{BTreeMap, HashMap, HashSet}, @@ -172,22 +174,18 @@ impl<'e, E: ExecutorView> ResourceGroupResolver for StorageAdapter<'e, E> { impl<'e, E: ExecutorView> AptosMoveResolver for StorageAdapter<'e, E> {} impl<'e, E: ExecutorView> ResourceResolver for StorageAdapter<'e, E> { - type Error = PartialVMError; - fn get_resource_bytes_with_metadata_and_layout( &self, address: &AccountAddress, struct_tag: &StructTag, metadata: &[Metadata], maybe_layout: Option<&MoveTypeLayout>, - ) -> Result<(Option, usize), Self::Error> { + ) -> PartialVMResult<(Option, usize)> { self.get_any_resource_with_layout(address, struct_tag, metadata, maybe_layout) } } impl<'e, E: ExecutorView> ModuleResolver for StorageAdapter<'e, E> { - type Error = PartialVMError; - fn get_module_metadata(&self, module_id: &ModuleId) -> Vec { let module_bytes = match self.get_module(module_id) { Ok(Some(bytes)) => bytes, @@ -202,7 +200,7 @@ impl<'e, E: ExecutorView> ModuleResolver for StorageAdapter<'e, E> { module.metadata } - fn get_module(&self, module_id: &ModuleId) -> Result, Self::Error> { + fn get_module(&self, module_id: &ModuleId) -> PartialVMResult> { self.executor_view .get_module_bytes(&StateKey::module_id(module_id)) } diff --git a/aptos-move/aptos-vm/src/keyless_validation.rs b/aptos-move/aptos-vm/src/keyless_validation.rs index bcf69050eb661..e5f19bd1a3171 100644 --- a/aptos-move/aptos-vm/src/keyless_validation.rs +++ b/aptos-move/aptos-vm/src/keyless_validation.rs @@ -33,9 +33,16 @@ macro_rules! value_deserialization_error { fn get_resource_on_chain Deserialize<'a>>( resolver: &impl AptosMoveResolver, ) -> anyhow::Result { + let metadata = resolver.get_module_metadata(&T::struct_tag().module_id()); let bytes = resolver - .get_resource(&CORE_CODE_ADDRESS, &T::struct_tag()) + .get_resource_bytes_with_metadata_and_layout( + &CORE_CODE_ADDRESS, + &T::struct_tag(), + &metadata, + None, + ) .map_err(|e| e.finish(Location::Undefined).into_vm_status())? + .0 .ok_or_else(|| { value_deserialization_error!(format!( "get_resource failed on {}::{}::{}", diff --git a/aptos-move/aptos-vm/src/move_vm_ext/resolver.rs b/aptos-move/aptos-vm/src/move_vm_ext/resolver.rs index 0df61e5088f17..1791a38bb08d6 100644 --- a/aptos-move/aptos-vm/src/move_vm_ext/resolver.rs +++ b/aptos-move/aptos-vm/src/move_vm_ext/resolver.rs @@ -8,8 +8,9 @@ use aptos_vm_types::resolver::{ ExecutorView, ResourceGroupSize, ResourceGroupView, StateStorageView, }; use bytes::Bytes; -use move_binary_format::errors::{PartialVMError, PartialVMResult}; -use move_core_types::{language_storage::StructTag, resolver::MoveResolver}; +use move_binary_format::errors::PartialVMResult; +use move_core_types::language_storage::StructTag; +use move_vm_types::resolver::{ModuleResolver, ResourceResolver}; use std::collections::{BTreeMap, HashMap}; /// A general resolver used by AptosVM. Allows to implement custom hooks on @@ -19,7 +20,8 @@ pub trait AptosMoveResolver: AggregatorV1Resolver + ConfigStorage + DelayedFieldResolver - + MoveResolver + + ModuleResolver + + ResourceResolver + ResourceGroupResolver + StateStorageView + TableResolver diff --git a/aptos-move/block-executor/src/proptest_types/types.rs b/aptos-move/block-executor/src/proptest_types/types.rs index 2e2fed2badd4e..f6d5de2dd1b39 100644 --- a/aptos-move/block-executor/src/proptest_types/types.rs +++ b/aptos-move/block-executor/src/proptest_types/types.rs @@ -10,7 +10,6 @@ use aptos_aggregator::{ }; use aptos_mvhashmap::types::TxnIndex; use aptos_types::{ - access_path::AccessPath, account_address::AccountAddress, contract_event::TransactionEvent, delayed_fields::PanicError, @@ -29,14 +28,13 @@ use aptos_types::{ use aptos_vm_types::resolver::{TExecutorView, TResourceGroupView}; use bytes::Bytes; use claims::{assert_ge, assert_le, assert_ok}; -use move_core_types::value::MoveTypeLayout; +use move_core_types::{identifier::IdentStr, value::MoveTypeLayout}; use move_vm_types::delayed_values::delayed_field_id::DelayedFieldID; use once_cell::sync::OnceCell; use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*, proptest, sample::Index}; use proptest_derive::Arbitrary; use std::{ collections::{hash_map::DefaultHasher, BTreeMap, BTreeSet, HashMap, HashSet}, - convert::TryInto, fmt::Debug, hash::{Hash, Hasher}, marker::PhantomData, @@ -154,21 +152,12 @@ pub(crate) struct KeyType( ); impl ModulePath for KeyType { - fn module_path(&self) -> Option { - // Since K is generic, use its hash to assign addresses. - let mut hasher = DefaultHasher::new(); - self.0.hash(&mut hasher); - let mut hashed_address = vec![1u8; AccountAddress::LENGTH - 8]; - hashed_address.extend_from_slice(&hasher.finish().to_ne_bytes()); - - if self.1 { - Some(AccessPath { - address: AccountAddress::new(hashed_address.try_into().unwrap()), - path: b"/foo/b".to_vec(), - }) - } else { - None - } + fn is_module_path(&self) -> bool { + self.1 + } + + fn from_address_and_module_name(_address: &AccountAddress, _module_name: &IdentStr) -> Self { + unimplemented!() } } @@ -894,15 +883,16 @@ where for k in behavior.reads.iter() { // TODO: later test errors as well? (by fixing state_view behavior). // TODO: test aggregator reads. - match k.module_path() { - Some(_) => match view.get_module_bytes(k) { + if k.is_module_path() { + match view.get_module_bytes(k) { Ok(v) => read_results.push(v.map(Into::into)), Err(_) => read_results.push(None), - }, - None => match view.get_resource_bytes(k, None) { + } + } else { + match view.get_resource_bytes(k, None) { Ok(v) => read_results.push(v.map(Into::into)), Err(_) => read_results.push(None), - }, + } } } // Read from groups. @@ -1057,7 +1047,7 @@ where fn resource_write_set(&self) -> Vec<(K, Arc, Option>)> { self.writes .iter() - .filter(|(k, _)| k.module_path().is_none()) + .filter(|(k, _)| !k.is_module_path()) .cloned() .map(|(k, v)| (k, Arc::new(v), None)) .collect() @@ -1066,7 +1056,7 @@ where fn module_write_set(&self) -> BTreeMap { self.writes .iter() - .filter(|(k, _)| k.module_path().is_some()) + .filter(|(k, _)| k.is_module_path()) .cloned() .collect() } diff --git a/aptos-move/block-executor/src/view.rs b/aptos-move/block-executor/src/view.rs index f72a408749488..837a47d419ed9 100644 --- a/aptos-move/block-executor/src/view.rs +++ b/aptos-move/block-executor/src/view.rs @@ -1296,7 +1296,7 @@ impl<'a, T: Transaction, S: TStateView, X: Executable> LatestView< kind: ReadKind, ) -> PartialVMResult { debug_assert!( - state_key.module_path().is_none(), + !state_key.is_module_path(), "Reading a module {:?} using ResourceView", state_key, ); @@ -1529,7 +1529,7 @@ impl<'a, T: Transaction, S: TStateView, X: Executable> TModuleView fn get_module_state_value(&self, state_key: &Self::Key) -> PartialVMResult> { debug_assert!( - state_key.module_path().is_some(), + state_key.is_module_path(), "Reading a resource {:?} using ModuleView", state_key, ); diff --git a/aptos-move/mvhashmap/src/types.rs b/aptos-move/mvhashmap/src/types.rs index c5c8b0938e541..62cc81e30eaa6 100644 --- a/aptos-move/mvhashmap/src/types.rs +++ b/aptos-move/mvhashmap/src/types.rs @@ -246,13 +246,13 @@ pub(crate) mod test { use super::*; use aptos_aggregator::delta_change_set::serialize; use aptos_types::{ - access_path::AccessPath, executable::ModulePath, state_store::state_value::StateValue, write_set::{TransactionWrite, WriteOpKind}, }; use bytes::Bytes; use claims::{assert_err, assert_ok_eq}; + use move_core_types::{account_address::AccountAddress, identifier::IdentStr}; use std::{fmt::Debug, hash::Hash, sync::Arc}; #[derive(Clone, Eq, Hash, PartialEq, Debug)] @@ -262,8 +262,15 @@ pub(crate) mod test { ); impl ModulePath for KeyType { - fn module_path(&self) -> Option { - None + fn is_module_path(&self) -> bool { + false + } + + fn from_address_and_module_name( + _address: &AccountAddress, + _module_name: &IdentStr, + ) -> Self { + unreachable!("Irrelevant for test") } } diff --git a/aptos-move/mvhashmap/src/unsync_map.rs b/aptos-move/mvhashmap/src/unsync_map.rs index 539b95100a862..f3bbcef5f404d 100644 --- a/aptos-move/mvhashmap/src/unsync_map.rs +++ b/aptos-move/mvhashmap/src/unsync_map.rs @@ -242,7 +242,7 @@ impl< pub fn fetch_module(&self, key: &K) -> Option> { use MVModulesOutput::*; - debug_assert!(key.module_path().is_some()); + debug_assert!(key.is_module_path()); self.module_map.borrow_mut().get_mut(key).map(|entry| { let hash = entry.1.get_or_insert(module_hash(entry.0.as_ref())); diff --git a/third_party/move/extensions/async/move-async-vm/src/async_vm.rs b/third_party/move/extensions/async/move-async-vm/src/async_vm.rs index a35eb3933e919..f1225bc1a9d44 100644 --- a/third_party/move/extensions/async/move-async-vm/src/async_vm.rs +++ b/third_party/move/extensions/async/move-async-vm/src/async_vm.rs @@ -14,7 +14,6 @@ use move_core_types::{ effects::{ChangeSet, Op}, identifier::Identifier, language_storage::{ModuleId, StructTag, TypeTag}, - resolver::MoveResolver, vm_status::StatusCode, }; use move_vm_runtime::{ @@ -25,7 +24,10 @@ use move_vm_runtime::{ session::{SerializedReturnValues, Session}, }; use move_vm_test_utils::gas_schedule::{Gas, GasStatus}; -use move_vm_types::values::{Reference, Value}; +use move_vm_types::{ + resolver::MoveResolver, + values::{Reference, Value}, +}; use std::{ collections::HashMap, error::Error, @@ -82,7 +84,7 @@ impl AsyncVM { &'l self, for_actor: AccountAddress, virtual_time: u128, - move_resolver: &'r mut impl MoveResolver, + move_resolver: &'r impl MoveResolver, ) -> AsyncSession<'r, 'l> { self.new_session_with_extensions( for_actor, @@ -97,7 +99,7 @@ impl AsyncVM { &'l self, for_actor: AccountAddress, virtual_time: u128, - move_resolver: &'r mut impl MoveResolver, + move_resolver: &'r impl MoveResolver, ext: NativeContextExtensions<'r>, ) -> AsyncSession<'r, 'l> { let extensions = make_extensions(ext, for_actor, virtual_time, true); diff --git a/third_party/move/extensions/async/move-async-vm/tests/testsuite.rs b/third_party/move/extensions/async/move-async-vm/tests/testsuite.rs index d4010bfc9acb7..d6031021a2f02 100644 --- a/third_party/move/extensions/async/move-async-vm/tests/testsuite.rs +++ b/third_party/move/extensions/async/move-async-vm/tests/testsuite.rs @@ -10,7 +10,7 @@ use move_async_vm::{ async_vm::{AsyncResult, AsyncSession, AsyncVM, Message}, natives::GasParameters as ActorGasParameters, }; -use move_binary_format::{access::ModuleAccess, errors::PartialVMError}; +use move_binary_format::{access::ModuleAccess, errors::PartialVMResult}; use move_command_line_common::testing::get_compiler_exp_extension; use move_compiler::{ attr_derivation, compiled_unit::CompiledUnit, diagnostics::report_diagnostics_to_buffer, @@ -23,11 +23,11 @@ use move_core_types::{ identifier::{IdentStr, Identifier}, language_storage::{ModuleId, StructTag}, metadata::Metadata, - resolver::{resource_size, ModuleResolver, ResourceResolver}, value::MoveTypeLayout, }; use move_prover_test_utils::{baseline_test::verify_or_update_baseline, extract_test_directives}; use move_vm_test_utils::gas_schedule::GasStatus; +use move_vm_types::resolver::{resource_size, ModuleResolver, ResourceResolver}; use std::{ cell::RefCell, collections::{BTreeMap, BTreeSet, VecDeque}, @@ -87,8 +87,8 @@ impl Harness { let mut gas = GasStatus::new_unmetered(); let mut tick = 0; // Publish modules. - let mut proxy = HarnessProxy { harness: self }; - let mut session = self.vm.new_session(test_account(), 0, &mut proxy); + let proxy = HarnessProxy { harness: self }; + let mut session = self.vm.new_session(test_account(), 0, &proxy); let mut done = BTreeSet::new(); for id in self.module_cache.keys() { self.publish_module(&mut session, id, &mut gas, &mut done)?; @@ -102,8 +102,8 @@ impl Harness { actor.short_str_lossless() )); { - let mut proxy = HarnessProxy { harness: self }; - let session = self.vm.new_session(addr, 0, &mut proxy); + let proxy = HarnessProxy { harness: self }; + let session = self.vm.new_session(addr, 0, &proxy); let result = session.new_actor(&actor, addr, &mut gas); self.handle_result(&mut mailbox, result); }; @@ -133,8 +133,8 @@ impl Harness { )) } // Handling - let mut proxy = HarnessProxy { harness: self }; - let session = self.vm.new_session(actor, tick, &mut proxy); + let proxy = HarnessProxy { harness: self }; + let session = self.vm.new_session(actor, tick, &proxy); tick += 1000_1000; // micros let result = session.handle_message(actor, message_hash, args, &mut gas); self.handle_result(&mut mailbox, result); @@ -382,13 +382,11 @@ struct HarnessProxy<'a> { } impl<'a> ModuleResolver for HarnessProxy<'a> { - type Error = PartialVMError; - fn get_module_metadata(&self, _module_id: &ModuleId) -> Vec { vec![] } - fn get_module(&self, id: &ModuleId) -> Result, Self::Error> { + fn get_module(&self, id: &ModuleId) -> PartialVMResult> { Ok(self .harness .module_cache @@ -398,15 +396,13 @@ impl<'a> ModuleResolver for HarnessProxy<'a> { } impl<'a> ResourceResolver for HarnessProxy<'a> { - type Error = PartialVMError; - fn get_resource_bytes_with_metadata_and_layout( &self, address: &AccountAddress, typ: &StructTag, _metadata: &[Metadata], _maybe_layout: Option<&MoveTypeLayout>, - ) -> Result<(Option, usize), Self::Error> { + ) -> PartialVMResult<(Option, usize)> { let res = self .harness .resource_store diff --git a/third_party/move/move-core/types/src/lib.rs b/third_party/move/move-core/types/src/lib.rs index 7e6b2637ef9e1..abcda6742d674 100644 --- a/third_party/move/move-core/types/src/lib.rs +++ b/third_party/move/move-core/types/src/lib.rs @@ -16,7 +16,6 @@ pub mod move_resource; pub mod parser; #[cfg(any(test, feature = "fuzzing"))] pub mod proptest_types; -pub mod resolver; mod safe_serialize; pub mod state; pub mod transaction_argument; diff --git a/third_party/move/move-vm/integration-tests/src/tests/bad_storage_tests.rs b/third_party/move/move-vm/integration-tests/src/tests/bad_storage_tests.rs index bc40b581302a5..64dc94029efd3 100644 --- a/third_party/move/move-vm/integration-tests/src/tests/bad_storage_tests.rs +++ b/third_party/move/move-vm/integration-tests/src/tests/bad_storage_tests.rs @@ -4,20 +4,21 @@ use crate::compiler::{as_module, as_script, compile_units}; use bytes::Bytes; -use move_binary_format::errors::PartialVMError; +use move_binary_format::errors::{PartialVMError, PartialVMResult}; use move_core_types::{ account_address::AccountAddress, - effects::{ChangeSet, Op}, identifier::Identifier, language_storage::{ModuleId, StructTag}, metadata::Metadata, - resolver::{ModuleResolver, ResourceResolver}, value::{serialize_values, MoveTypeLayout, MoveValue}, vm_status::{StatusCode, StatusType}, }; use move_vm_runtime::{module_traversal::*, move_vm::MoveVM}; -use move_vm_test_utils::{DeltaStorage, InMemoryStorage}; -use move_vm_types::gas::UnmeteredGasMeter; +use move_vm_test_utils::InMemoryStorage; +use move_vm_types::{ + gas::UnmeteredGasMeter, + resolver::{ModuleResolver, ResourceResolver}, +}; const TEST_ADDR: AccountAddress = AccountAddress::new([42; AccountAddress::LENGTH]); @@ -523,32 +524,56 @@ fn test_unverifiable_module_dependency() { } } -struct BogusStorage { +struct BogusModuleStorage { bad_status_code: StatusCode, } -impl ModuleResolver for BogusStorage { - type Error = PartialVMError; - +impl ModuleResolver for BogusModuleStorage { fn get_module_metadata(&self, _module_id: &ModuleId) -> Vec { vec![] } - fn get_module(&self, _module_id: &ModuleId) -> Result, Self::Error> { + fn get_module(&self, _module_id: &ModuleId) -> PartialVMResult> { Err(PartialVMError::new(self.bad_status_code)) } } -impl ResourceResolver for BogusStorage { - type Error = PartialVMError; +impl ResourceResolver for BogusModuleStorage { + fn get_resource_bytes_with_metadata_and_layout( + &self, + _address: &AccountAddress, + _tag: &StructTag, + _metadata: &[Metadata], + _maybe_layout: Option<&MoveTypeLayout>, + ) -> PartialVMResult<(Option, usize)> { + unreachable!() + } +} + +// Need another bogus storage implementation to allow querying modules but not resources. +struct BogusResourceStorage { + module_storage: InMemoryStorage, + bad_status_code: StatusCode, +} + +impl ModuleResolver for BogusResourceStorage { + fn get_module_metadata(&self, module_id: &ModuleId) -> Vec { + self.module_storage.get_module_metadata(module_id) + } + fn get_module(&self, module_id: &ModuleId) -> PartialVMResult> { + self.module_storage.get_module(module_id) + } +} + +impl ResourceResolver for BogusResourceStorage { fn get_resource_bytes_with_metadata_and_layout( &self, _address: &AccountAddress, _tag: &StructTag, _metadata: &[Metadata], _maybe_layout: Option<&MoveTypeLayout>, - ) -> Result<(Option, usize), Self::Error> { + ) -> PartialVMResult<(Option, usize)> { Err(PartialVMError::new(self.bad_status_code)) } } @@ -570,7 +595,7 @@ fn test_storage_returns_bogus_error_when_loading_module() { let traversal_storage = TraversalStorage::new(); for error_code in LIST_OF_ERROR_CODES { - let storage = BogusStorage { + let storage = BogusModuleStorage { bad_status_code: *error_code, }; let vm = MoveVM::new(vec![]); @@ -625,13 +650,6 @@ fn test_storage_returns_bogus_error_when_loading_resource() { let mut s_blob = vec![]; m.serialize(&mut m_blob).unwrap(); s.serialize(&mut s_blob).unwrap(); - let mut delta = ChangeSet::new(); - delta - .add_module_op(m.self_id(), Op::New(m_blob.into())) - .unwrap(); - delta - .add_module_op(s.self_id(), Op::New(s_blob.into())) - .unwrap(); let m_id = m.self_id(); let foo_name = Identifier::new("foo").unwrap(); @@ -639,10 +657,13 @@ fn test_storage_returns_bogus_error_when_loading_resource() { let traversal_storage = TraversalStorage::new(); for error_code in LIST_OF_ERROR_CODES { - let storage = BogusStorage { + let mut module_storage = InMemoryStorage::new(); + module_storage.publish_or_overwrite_module(m.self_id(), m_blob.clone()); + module_storage.publish_or_overwrite_module(s.self_id(), s_blob.clone()); + let storage = BogusResourceStorage { + module_storage, bad_status_code: *error_code, }; - let storage = DeltaStorage::new(&storage, &delta); let vm = MoveVM::new(move_stdlib::natives::all_natives( AccountAddress::from_hex_literal("0x1").unwrap(), diff --git a/third_party/move/move-vm/runtime/src/data_cache.rs b/third_party/move/move-vm/runtime/src/data_cache.rs index 7fe1aa0f22c6f..c562de0594e80 100644 --- a/third_party/move/move-vm/runtime/src/data_cache.rs +++ b/third_party/move/move-vm/runtime/src/data_cache.rs @@ -19,12 +19,12 @@ use move_core_types::{ identifier::Identifier, language_storage::{ModuleId, TypeTag}, metadata::Metadata, - resolver::MoveResolver, value::MoveTypeLayout, vm_status::StatusCode, }; use move_vm_types::{ loaded_data::runtime_types::Type, + resolver::MoveResolver, value_serde::deserialize_and_allow_delayed_values, values::{GlobalValue, Value}, }; @@ -51,7 +51,7 @@ impl AccountDataCache { } fn load_module_impl( - remote: &dyn MoveResolver, + remote: &dyn MoveResolver, account_map: &BTreeMap, module_id: &ModuleId, ) -> PartialVMResult { @@ -80,7 +80,7 @@ fn load_module_impl( /// for a data store related to a transaction. Clients should create an instance of this type /// and pass it to the Move VM. pub(crate) struct TransactionDataCache<'r> { - remote: &'r dyn MoveResolver, + remote: &'r dyn MoveResolver, account_map: BTreeMap, deserializer_config: DeserializerConfig, @@ -95,7 +95,7 @@ impl<'r> TransactionDataCache<'r> { /// not updated in the transaction. pub(crate) fn new( deserializer_config: DeserializerConfig, - remote: &'r impl MoveResolver, + remote: &'r impl MoveResolver, ) -> Self { TransactionDataCache { remote, diff --git a/third_party/move/move-vm/runtime/src/move_vm.rs b/third_party/move/move-vm/runtime/src/move_vm.rs index ee65b744cf6fb..ab01a38850dd3 100644 --- a/third_party/move/move-vm/runtime/src/move_vm.rs +++ b/third_party/move/move-vm/runtime/src/move_vm.rs @@ -11,14 +11,12 @@ use crate::{ runtime::VMRuntime, session::Session, }; -use move_binary_format::{ - errors::{PartialVMError, VMResult}, - CompiledModule, -}; +use move_binary_format::{errors::VMResult, CompiledModule}; use move_core_types::{ account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId, - metadata::Metadata, resolver::MoveResolver, + metadata::Metadata, }; +use move_vm_types::resolver::MoveResolver; use std::sync::Arc; #[derive(Clone)] @@ -70,17 +68,14 @@ impl MoveVM { /// cases where this may not be necessary, with the most notable one being the common module /// publishing flow: you can keep using the same Move VM if you publish some modules in a Session /// and apply the effects to the storage when the Session ends. - pub fn new_session<'r>( - &self, - remote: &'r impl MoveResolver, - ) -> Session<'r, '_> { + pub fn new_session<'r>(&self, remote: &'r impl MoveResolver) -> Session<'r, '_> { self.new_session_with_extensions(remote, NativeContextExtensions::default()) } /// Create a new session, as in `new_session`, but provide native context extensions. pub fn new_session_with_extensions<'r>( &self, - remote: &'r impl MoveResolver, + remote: &'r impl MoveResolver, native_extensions: NativeContextExtensions<'r>, ) -> Session<'r, '_> { Session { @@ -101,7 +96,7 @@ impl MoveVM { /// Create a new session, as in `new_session`, but provide native context extensions and custome storage for resolved modules. pub fn new_session_with_extensions_and_modules<'r>( &self, - remote: &'r impl MoveResolver, + remote: &'r impl MoveResolver, module_storage: Arc, native_extensions: NativeContextExtensions<'r>, ) -> Session<'r, '_> { @@ -124,7 +119,7 @@ impl MoveVM { pub fn load_module( &self, module_id: &ModuleId, - remote: &impl MoveResolver, + remote: &impl MoveResolver, ) -> VMResult> { self.runtime .loader() diff --git a/third_party/move/move-vm/runtime/src/session.rs b/third_party/move/move-vm/runtime/src/session.rs index ac56890c06432..cab1742518ff6 100644 --- a/third_party/move/move-vm/runtime/src/session.rs +++ b/third_party/move/move-vm/runtime/src/session.rs @@ -11,11 +11,7 @@ use crate::{ native_extensions::NativeContextExtensions, }; use bytes::Bytes; -use move_binary_format::{ - compatibility::Compatibility, - errors::*, - file_format::{AbilitySet, LocalIndex}, -}; +use move_binary_format::{compatibility::Compatibility, errors::*, file_format::LocalIndex}; use move_core_types::{ account_address::AccountAddress, effects::{ChangeSet, Changes}, @@ -414,11 +410,6 @@ impl<'r, 'l> Session<'r, 'l> { .map_err(|e| e.finish(Location::Undefined)) } - /// Gets the abilities for this type, at it's particular instantiation - pub fn get_type_abilities(&self, ty: &Type) -> VMResult { - ty.abilities().map_err(|e| e.finish(Location::Undefined)) - } - /// Gets the underlying native extensions. pub fn get_native_extensions(&mut self) -> &mut NativeContextExtensions<'r> { &mut self.native_extensions diff --git a/third_party/move/move-vm/test-utils/src/lib.rs b/third_party/move/move-vm/test-utils/src/lib.rs index a56aba81a8fd5..60eac026c7bde 100644 --- a/third_party/move/move-vm/test-utils/src/lib.rs +++ b/third_party/move/move-vm/test-utils/src/lib.rs @@ -7,4 +7,4 @@ mod storage; pub mod gas_schedule; -pub use storage::{BlankStorage, DeltaStorage, InMemoryStorage}; +pub use storage::{BlankStorage, InMemoryStorage}; diff --git a/third_party/move/move-vm/test-utils/src/storage.rs b/third_party/move/move-vm/test-utils/src/storage.rs index 501f13ad289c0..fd25015171fbf 100644 --- a/third_party/move/move-vm/test-utils/src/storage.rs +++ b/third_party/move/move-vm/test-utils/src/storage.rs @@ -16,12 +16,12 @@ use move_core_types::{ identifier::Identifier, language_storage::{ModuleId, StructTag}, metadata::Metadata, - resolver::{resource_size, ModuleResolver, MoveResolver, ResourceResolver}, value::MoveTypeLayout, vm_status::StatusCode, }; #[cfg(feature = "table-extension")] use move_table_extension::{TableChangeSet, TableHandle, TableResolver}; +use move_vm_types::resolver::{resource_size, ModuleResolver, ResourceResolver}; use std::{ collections::{btree_map, BTreeMap}, fmt::Debug, @@ -38,27 +38,23 @@ impl BlankStorage { } impl ModuleResolver for BlankStorage { - type Error = PartialVMError; - fn get_module_metadata(&self, _module_id: &ModuleId) -> Vec { vec![] } - fn get_module(&self, _module_id: &ModuleId) -> Result, Self::Error> { + fn get_module(&self, _module_id: &ModuleId) -> PartialVMResult> { Ok(None) } } impl ResourceResolver for BlankStorage { - type Error = PartialVMError; - fn get_resource_bytes_with_metadata_and_layout( &self, _address: &AccountAddress, _tag: &StructTag, _metadata: &[Metadata], _maybe_layout: Option<&MoveTypeLayout>, - ) -> Result<(Option, usize), Self::Error> { + ) -> PartialVMResult<(Option, usize)> { Ok((None, 0)) } } @@ -70,82 +66,11 @@ impl TableResolver for BlankStorage { _handle: &TableHandle, _key: &[u8], _maybe_layout: Option<&MoveTypeLayout>, - ) -> Result, PartialVMError> { + ) -> PartialVMResult> { Ok(None) } } -/// A storage adapter created by stacking a change set on top of an existing storage backend. -/// This can be used for additional computations without modifying the base. -#[derive(Debug, Clone)] -pub struct DeltaStorage<'a, 'b, S> { - base: &'a S, - change_set: &'b ChangeSet, -} - -impl<'a, 'b, S: ModuleResolver> ModuleResolver for DeltaStorage<'a, 'b, S> { - type Error = S::Error; - - fn get_module_metadata(&self, _module_id: &ModuleId) -> Vec { - vec![] - } - - fn get_module(&self, module_id: &ModuleId) -> Result, Self::Error> { - if let Some(account_storage) = self.change_set.accounts().get(module_id.address()) { - if let Some(blob_opt) = account_storage.modules().get(module_id.name()) { - return Ok(blob_opt.clone().ok()); - } - } - - self.base.get_module(module_id) - } -} - -impl<'a, 'b, S: ResourceResolver> ResourceResolver for DeltaStorage<'a, 'b, S> { - type Error = S::Error; - - fn get_resource_bytes_with_metadata_and_layout( - &self, - address: &AccountAddress, - tag: &StructTag, - metadata: &[Metadata], - layout: Option<&MoveTypeLayout>, - ) -> Result<(Option, usize), Self::Error> { - if let Some(account_storage) = self.change_set.accounts().get(address) { - if let Some(blob_opt) = account_storage.resources().get(tag) { - let buf = blob_opt.clone().ok(); - let buf_size = resource_size(&buf); - return Ok((buf, buf_size)); - } - } - self.base - .get_resource_bytes_with_metadata_and_layout(address, tag, metadata, layout) - } -} - -#[cfg(feature = "table-extension")] -impl<'a, 'b, S: TableResolver> TableResolver for DeltaStorage<'a, 'b, S> { - fn resolve_table_entry_bytes_with_layout( - &self, - handle: &TableHandle, - key: &[u8], - maybe_layout: Option<&MoveTypeLayout>, - ) -> Result, PartialVMError> { - // TODO: In addition to `change_set`, cache table outputs. - self.base - .resolve_table_entry_bytes_with_layout(handle, key, maybe_layout) - } -} - -impl<'a, 'b, S: MoveResolver> DeltaStorage<'a, 'b, S> { - pub fn new(base: &'a S, delta: &'b ChangeSet) -> Self { - Self { - base, - change_set: delta, - } - } -} - /// Simple in-memory storage for modules and resources under an account. #[derive(Debug, Clone)] struct InMemoryAccountStorage { @@ -329,13 +254,11 @@ impl InMemoryStorage { } impl ModuleResolver for InMemoryStorage { - type Error = PartialVMError; - fn get_module_metadata(&self, _module_id: &ModuleId) -> Vec { vec![] } - fn get_module(&self, module_id: &ModuleId) -> Result, Self::Error> { + fn get_module(&self, module_id: &ModuleId) -> PartialVMResult> { if let Some(account_storage) = self.accounts.get(module_id.address()) { return Ok(account_storage.modules.get(module_id.name()).cloned()); } @@ -344,15 +267,13 @@ impl ModuleResolver for InMemoryStorage { } impl ResourceResolver for InMemoryStorage { - type Error = PartialVMError; - fn get_resource_bytes_with_metadata_and_layout( &self, address: &AccountAddress, tag: &StructTag, _metadata: &[Metadata], _maybe_layout: Option<&MoveTypeLayout>, - ) -> Result<(Option, usize), Self::Error> { + ) -> PartialVMResult<(Option, usize)> { if let Some(account_storage) = self.accounts.get(address) { let buf = account_storage.resources.get(tag).cloned(); let buf_size = resource_size(&buf); @@ -369,7 +290,7 @@ impl TableResolver for InMemoryStorage { handle: &TableHandle, key: &[u8], _maybe_layout: Option<&MoveTypeLayout>, - ) -> Result, PartialVMError> { + ) -> PartialVMResult> { Ok(self.tables.get(handle).and_then(|t| t.get(key).cloned())) } } diff --git a/third_party/move/move-vm/types/Cargo.toml b/third_party/move/move-vm/types/Cargo.toml index 4952f0cdf0b7b..53cbd1e3b23fd 100644 --- a/third_party/move/move-vm/types/Cargo.toml +++ b/third_party/move/move-vm/types/Cargo.toml @@ -10,6 +10,8 @@ publish = false edition = "2021" [dependencies] +bcs = { workspace = true } +bytes = { workspace = true } derivative = { workspace = true } itertools = { workspace = true } proptest = { workspace = true, optional = true } @@ -18,8 +20,6 @@ smallbitvec = { workspace = true } smallvec = { workspace = true } triomphe = { workspace = true } -bcs = { workspace = true } - move-binary-format = { path = "../../move-binary-format" } move-core-types = { path = "../../move-core/types" } diff --git a/third_party/move/move-vm/types/src/lib.rs b/third_party/move/move-vm/types/src/lib.rs index 7565b35943f91..45269861108c3 100644 --- a/third_party/move/move-vm/types/src/lib.rs +++ b/third_party/move/move-vm/types/src/lib.rs @@ -28,6 +28,7 @@ pub mod delayed_values; pub mod gas; pub mod loaded_data; pub mod natives; +pub mod resolver; pub mod value_serde; pub mod value_traversal; pub mod values; diff --git a/third_party/move/move-core/types/src/resolver.rs b/third_party/move/move-vm/types/src/resolver.rs similarity index 64% rename from third_party/move/move-core/types/src/resolver.rs rename to third_party/move/move-vm/types/src/resolver.rs index a0b3200aff64c..3d3971baf5e4f 100644 --- a/third_party/move/move-core/types/src/resolver.rs +++ b/third_party/move/move-vm/types/src/resolver.rs @@ -2,14 +2,14 @@ // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 -use crate::{ +use bytes::Bytes; +use move_binary_format::errors::PartialVMResult; +use move_core_types::{ account_address::AccountAddress, language_storage::{ModuleId, StructTag}, metadata::Metadata, value::MoveTypeLayout, }; -use bytes::Bytes; -use std::fmt::Debug; /// Traits for resolving Move modules and resources from persistent storage @@ -23,11 +23,9 @@ use std::fmt::Debug; /// are always structurally valid) /// - storage encounters internal error pub trait ModuleResolver { - type Error: Debug; - fn get_module_metadata(&self, module_id: &ModuleId) -> Vec; - fn get_module(&self, id: &ModuleId) -> Result, Self::Error>; + fn get_module(&self, id: &ModuleId) -> PartialVMResult>; } pub fn resource_size(resource: &Option) -> usize { @@ -44,44 +42,16 @@ pub fn resource_size(resource: &Option) -> usize { /// are always structurally valid) /// - storage encounters internal error pub trait ResourceResolver { - type Error: Debug; - fn get_resource_bytes_with_metadata_and_layout( &self, address: &AccountAddress, struct_tag: &StructTag, metadata: &[Metadata], layout: Option<&MoveTypeLayout>, - ) -> Result<(Option, usize), Self::Error>; + ) -> PartialVMResult<(Option, usize)>; } /// A persistent storage implementation that can resolve both resources and modules -pub trait MoveResolver: ModuleResolver + ResourceResolver { - fn get_resource( - &self, - address: &AccountAddress, - struct_tag: &StructTag, - ) -> Result, ::Error> { - Ok(self - .get_resource_with_metadata( - address, - struct_tag, - &self.get_module_metadata(&struct_tag.module_id()), - )? - .0) - } +pub trait MoveResolver: ModuleResolver + ResourceResolver {} - fn get_resource_with_metadata( - &self, - address: &AccountAddress, - struct_tag: &StructTag, - metadata: &[Metadata], - ) -> Result<(Option, usize), ::Error> { - self.get_resource_bytes_with_metadata_and_layout(address, struct_tag, metadata, None) - } -} - -impl + ResourceResolver + ?Sized> MoveResolver - for T -{ -} +impl MoveResolver for T {} diff --git a/third_party/move/testing-infra/test-generation/Cargo.toml b/third_party/move/testing-infra/test-generation/Cargo.toml index 86f443cfc0b81..5989986af868a 100644 --- a/third_party/move/testing-infra/test-generation/Cargo.toml +++ b/third_party/move/testing-infra/test-generation/Cargo.toml @@ -17,7 +17,6 @@ hex = { workspace = true } itertools = { workspace = true } module-generation = { path = "../module-generation" } num_cpus = { workspace = true } -once_cell = { workspace = true } # Cannot use workspace version as aptos-core currently cannot be upgraded # to newer rand. See https://github.com/aptos-labs/aptos-core/issues/13031 rand = { version = "0.8.5" } diff --git a/third_party/move/testing-infra/test-generation/src/lib.rs b/third_party/move/testing-infra/test-generation/src/lib.rs index 428cc31f479e1..b5ef94b651fe9 100644 --- a/third_party/move/testing-infra/test-generation/src/lib.rs +++ b/third_party/move/testing-infra/test-generation/src/lib.rs @@ -20,7 +20,6 @@ use getrandom::getrandom; use module_generation::generate_module; use move_binary_format::{ access::ModuleAccess, - errors::PartialVMError, file_format::{ AbilitySet, CompiledModule, FunctionDefinitionIndex, SignatureToken, StructHandleIndex, }, @@ -33,16 +32,13 @@ use move_compiler::{ }; use move_core_types::{ account_address::AccountAddress, - effects::{ChangeSet, Op}, language_storage::TypeTag, - resolver::MoveResolver, value::MoveValue, vm_status::{StatusCode, VMStatus}, }; use move_vm_runtime::{module_traversal::*, move_vm::MoveVM}; -use move_vm_test_utils::{DeltaStorage, InMemoryStorage}; +use move_vm_test_utils::InMemoryStorage; use move_vm_types::gas::UnmeteredGasMeter; -use once_cell::sync::Lazy; use rand::{rngs::StdRng, Rng, SeedableRng}; use std::{fs, io::Write, panic, thread}; use tracing::{debug, error, info}; @@ -58,8 +54,11 @@ fn run_verifier(module: CompiledModule) -> Result { } } -static STORAGE_WITH_MOVE_STDLIB: Lazy = Lazy::new(|| { +// Creates a storage with Move standard library as well as a few additional modules. +fn storage_with_stdlib_and_modules(additional_modules: Vec<&CompiledModule>) -> InMemoryStorage { let mut storage = InMemoryStorage::new(); + + // First, compile and add standard library. let (_, compiled_units) = Compiler::from_files( move_stdlib::move_stdlib_files(), vec![], @@ -78,8 +77,15 @@ static STORAGE_WITH_MOVE_STDLIB: Lazy = Lazy::new(|| { module.serialize(&mut blob).unwrap(); storage.publish_or_overwrite_module(module.self_id(), blob); } + + // Now add the additional modules. + for module in additional_modules { + let mut blob = vec![]; + module.serialize(&mut blob).unwrap(); + storage.publish_or_overwrite_module(module.self_id(), blob); + } storage -}); +} /// This function runs a verified module in the VM runtime fn run_vm(module: CompiledModule) -> Result<(), VMStatus> { @@ -118,13 +124,7 @@ fn run_vm(module: CompiledModule) -> Result<(), VMStatus> { }) .collect(); - execute_function_in_module( - module, - entry_idx, - vec![], - main_args, - &*STORAGE_WITH_MOVE_STDLIB, - ) + execute_function_in_module(module, entry_idx, vec![], main_args) } /// Execute the first function in a module @@ -133,7 +133,6 @@ fn execute_function_in_module( idx: FunctionDefinitionIndex, ty_args: Vec, args: Vec>, - storage: &impl MoveResolver, ) -> Result<(), VMStatus> { let module_id = module.self_id(); let entry_name = { @@ -147,16 +146,10 @@ fn execute_function_in_module( move_stdlib::natives::GasParameters::zeros(), )); - let mut changeset = ChangeSet::new(); - let mut blob = vec![]; - module.serialize(&mut blob).unwrap(); - changeset - .add_module_op(module_id.clone(), Op::New(blob.into())) - .unwrap(); - let delta_storage = DeltaStorage::new(storage, &changeset); - let mut sess = vm.new_session(&delta_storage); - let traversal_storage = TraversalStorage::new(); + let storage = storage_with_stdlib_and_modules(vec![&module]); + let mut sess = vm.new_session(&storage); + let traversal_storage = TraversalStorage::new(); sess.execute_function_bypass_visibility( &module_id, entry_name, diff --git a/third_party/move/testing-infra/transactional-test-runner/src/vm_test_harness.rs b/third_party/move/testing-infra/transactional-test-runner/src/vm_test_harness.rs index 732d08dfe6a68..bbbedfca4ee96 100644 --- a/third_party/move/testing-infra/transactional-test-runner/src/vm_test_harness.rs +++ b/third_party/move/testing-infra/transactional-test-runner/src/vm_test_harness.rs @@ -31,7 +31,6 @@ use move_core_types::{ account_address::AccountAddress, identifier::{IdentStr, Identifier}, language_storage::{ModuleId, StructTag, TypeTag}, - resolver::MoveResolver, value::MoveValue, }; use move_model::metadata::LanguageVersion; @@ -48,6 +47,7 @@ use move_vm_test_utils::{ gas_schedule::{CostTable, Gas, GasStatus}, InMemoryStorage, }; +use move_vm_types::resolver::ResourceResolver; use once_cell::sync::Lazy; use std::{ collections::{BTreeMap, BTreeSet}, @@ -349,7 +349,12 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { name: resource.to_owned(), type_args, }; - match self.storage.get_resource(&address, &tag).unwrap() { + match self + .storage + .get_resource_bytes_with_metadata_and_layout(&address, &tag, &[], None) + .unwrap() + .0 + { None => Ok("[No Resource Exists]".to_owned()), Some(data) => { let annotated = diff --git a/types/src/executable.rs b/types/src/executable.rs index d1794e4650d10..7a4ec3e2bf704 100644 --- a/types/src/executable.rs +++ b/types/src/executable.rs @@ -1,12 +1,9 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::{ - access_path::AccessPath, - state_store::state_key::{inner::StateKeyInner, StateKey}, -}; +use crate::state_store::state_key::{inner::StateKeyInner, StateKey}; use aptos_crypto::HashValue; -use std::sync::Arc; +use move_core_types::{account_address::AccountAddress, identifier::IdentStr}; #[derive(PartialEq, Eq, Debug)] pub enum ExecutableDescriptor { @@ -18,17 +15,22 @@ pub enum ExecutableDescriptor { } pub trait ModulePath { - fn module_path(&self) -> Option; + // TODO(George): + // Improve this in the future, right now all writes use state keys + // and we need to use this trait to check if a generic state key is + // for code or not. + fn is_module_path(&self) -> bool; + + fn from_address_and_module_name(address: &AccountAddress, module_name: &IdentStr) -> Self; } impl ModulePath for StateKey { - fn module_path(&self) -> Option { - if let StateKeyInner::AccessPath(ap) = self.inner() { - if ap.is_code() { - return Some(ap.clone()); - } - } - None + fn is_module_path(&self) -> bool { + matches!(self.inner(), StateKeyInner::AccessPath(ap) if ap.is_code()) + } + + fn from_address_and_module_name(address: &AccountAddress, module_name: &IdentStr) -> Self { + Self::module(address, module_name) } } @@ -49,43 +51,3 @@ impl Executable for ExecutableTestType { 0 } } - -// TODO: variant for a compiled module when available to avoid deserialization. -pub enum FetchedModule { - Blob(Option>), - // Note: We could use Weak / & for parallel and sequential modes, respectively - // but rely on Arc for a simple and unified treatment for the time being. - // TODO: change Arc to custom reference when we have memory manager / arena. - Executable(Arc), -} - -/// View for the VM for interacting with the multi-versioned executable cache. -pub trait ExecutableView { - type Key; - type Executable: Executable; - - /// This is an optimization to bypass transactional semantics and share the - /// executable (and all the useful work for producing it) as early as possible - /// other txns / VM sessions. It is safe as storage-version module can't change, - /// and o.w. the key is the (cryptographic) hash of the module blob. - /// - /// W.o. this, we would have to include executables in TransactionOutputExt. - /// This may occur much later leading to work duplication (producing the same - /// executable by other sessions) in the common case when the executable isn't - /// based on the module published by the transaction itself. - fn store_executable( - &self, - key: &Self::Key, - descriptor: ExecutableDescriptor, - executable: Self::Executable, - ); - - /// Returns either the blob of the module, that will need to be deserialized into - /// CompiledModule and then made into an executable, or executable directly, if - /// the executable corresponding to the latest published module was already stored. - /// TODO: Return CompiledModule directly to avoid deserialization. - fn fetch_module( - &self, - key: &Self::Key, - ) -> anyhow::Result<(ExecutableDescriptor, FetchedModule)>; -}