diff --git a/sui/src/sui_commands.rs b/sui/src/sui_commands.rs index ac0ec3b2751fc..0f02ea26308da 100644 --- a/sui/src/sui_commands.rs +++ b/sui/src/sui_commands.rs @@ -162,7 +162,7 @@ async fn genesis( ); let sui_lib = sui_framework::get_sui_framework_modules(&genesis_conf.sui_framework_lib_path)?; let lib_object = - Object::new_package(sui_lib, SuiAddress::default(), TransactionDigest::genesis()); + Object::new_package(sui_lib, SuiAddress::default(), TransactionDigest::genesis())?; preload_modules.push(lib_object); info!( @@ -174,7 +174,7 @@ async fn genesis( move_lib, SuiAddress::default(), TransactionDigest::genesis(), - ); + )?; preload_modules.push(lib_object); // Build custom move packages @@ -194,7 +194,7 @@ async fn genesis( )?; let object = - Object::new_package(modules, SuiAddress::default(), TransactionDigest::genesis()); + Object::new_package(modules, SuiAddress::default(), TransactionDigest::genesis())?; info!("Loaded package [{}] from {:?}.", object.id(), path); // Writing package id to network.conf for user to retrieve later. config.loaded_move_packages.push((path, object.id())); diff --git a/sui_core/src/authority/authority_store.rs b/sui_core/src/authority/authority_store.rs index 82750ae51a5e4..17860fab3bff6 100644 --- a/sui_core/src/authority/authority_store.rs +++ b/sui_core/src/authority/authority_store.rs @@ -465,6 +465,7 @@ impl ModuleResolver for AuthorityStore { match self.get_object(&ObjectID::from(*module_id.address()))? { Some(o) => match &o.data { Data::Package(c) => Ok(c + .serialized_module_map() .get(module_id.name().as_str()) .cloned() .map(|m| m.into_vec())), diff --git a/sui_core/src/authority/temporary_store.rs b/sui_core/src/authority/temporary_store.rs index e77edd7f3ed86..2914478d7efa9 100644 --- a/sui_core/src/authority/temporary_store.rs +++ b/sui_core/src/authority/temporary_store.rs @@ -268,6 +268,7 @@ impl ModuleResolver for AuthorityTemporaryStore { match self.read_object(&ObjectID::from(*module_id.address())) { Some(o) => match &o.data { Data::Package(c) => Ok(c + .serialized_module_map() .get(module_id.name().as_str()) .cloned() .map(|m| m.into_vec())), diff --git a/sui_core/src/unit_tests/authority_tests.rs b/sui_core/src/unit_tests/authority_tests.rs index 79da0dab628b9..cf15fe515536f 100644 --- a/sui_core/src/unit_tests/authority_tests.rs +++ b/sui_core/src/unit_tests/authority_tests.rs @@ -371,7 +371,9 @@ async fn test_publish_dependent_module_ok() { // create a genesis state that contains the gas object and genesis modules let genesis_module_objects = genesis::clone_genesis_modules(); let genesis_module = match &genesis_module_objects[0].data { - Data::Package(m) => CompiledModule::deserialize(m.values().next().unwrap()).unwrap(), + Data::Package(m) => { + CompiledModule::deserialize(m.serialized_module_map().values().next().unwrap()).unwrap() + } _ => unreachable!(), }; // create a module that depends on a genesis module @@ -462,7 +464,9 @@ async fn test_publish_non_existing_dependent_module() { // create a genesis state that contains the gas object and genesis modules let genesis_module_objects = genesis::clone_genesis_modules(); let genesis_module = match &genesis_module_objects[0].data { - Data::Package(m) => CompiledModule::deserialize(m.values().next().unwrap()).unwrap(), + Data::Package(m) => { + CompiledModule::deserialize(m.serialized_module_map().values().next().unwrap()).unwrap() + } _ => unreachable!(), }; // create a module that depends on a genesis module @@ -1489,6 +1493,7 @@ async fn test_hero() { ) .await .unwrap(); + println!("ERRRR {:?}", effects.status); assert!(matches!(effects.status, ExecutionStatus::Success { .. })); // 7. Give them a boar! @@ -1785,7 +1790,7 @@ fn get_genesis_package_by_module(genesis_objects: &[Object], module: &str) -> Ob .iter() .find_map(|o| match o.data.try_as_package() { Some(p) => { - if p.keys().any(|name| name == module) { + if p.serialized_module_map().keys().any(|name| name == module) { Some(o.to_object_reference()) } else { None diff --git a/sui_programmability/adapter/src/adapter.rs b/sui_programmability/adapter/src/adapter.rs index 09afd4aba625f..ff30466746d65 100644 --- a/sui_programmability/adapter/src/adapter.rs +++ b/sui_programmability/adapter/src/adapter.rs @@ -4,11 +4,7 @@ use anyhow::Result; use crate::bytecode_rewriter::ModuleHandleRewriter; -use move_binary_format::{ - errors::PartialVMResult, - file_format::{CompiledModule, Visibility}, - normalized::{Function, Type}, -}; +use move_binary_format::{errors::PartialVMResult, file_format::CompiledModule}; use sui_framework::EventType; use sui_types::{ base_types::*, @@ -16,18 +12,17 @@ use sui_types::{ event::Event, gas, messages::ExecutionStatus, - object::{Data, MoveObject, Object}, + move_call::*, + object::{MoveObject, Object}, storage::Storage, - SUI_FRAMEWORK_ADDRESS, }; use sui_verifier::verifier; use move_cli::sandbox::utils::get_gas_status; use move_core_types::{ account_address::AccountAddress, - ident_str, - identifier::{IdentStr, Identifier}, - language_storage::{ModuleId, StructTag, TypeTag}, + identifier::Identifier, + language_storage::{ModuleId, TypeTag}, resolver::{ModuleResolver, ResourceResolver}, }; use move_vm_runtime::{native_functions::NativeFunctionTable, session::ExecutionResult}; @@ -96,7 +91,6 @@ pub fn execute + ModuleResolver ok, Err(err) => { @@ -104,6 +98,8 @@ pub fn execute + ModuleResolver + ModuleResolver + ModuleResolver bool { - let function = match Function::new_from_name(module, INIT_FN_NAME) { - Some(v) => v, - None => return false, - }; - if function.visibility != Visibility::Private { - return false; - } - if !function.type_parameters.is_empty() { - return false; - } - if !function.return_.is_empty() { - return false; - } - if function.parameters.len() != 1 { - return false; - } - is_param_tx_context(&function.parameters[0]) -} - /// Given a list of `modules`, links each module against its /// dependencies and runs each module with both the Move VM verifier /// and the FastX verifier. @@ -618,240 +592,6 @@ fn handle_transfer< Ok(()) } -struct TypeCheckSuccess { - module_id: ModuleId, - args: Vec>, - by_value_objects: BTreeMap, - mutable_ref_objects: Vec, -} - -/// - Check that `package_object`, `module` and `function` are valid -/// - Check that the the signature of `function` is well-typed w.r.t `type_args`, `object_args`, and `pure_args` -/// - Return the ID of the resolved module, a vector of BCS encoded arguments to pass to the VM, and a partitioning -/// of the input objects into objects passed by value vs by mutable reference -fn resolve_and_type_check( - package_object: Object, - module: &Identifier, - function: &Identifier, - type_args: &[TypeTag], - object_args: Vec, - mut pure_args: Vec>, - ctx: &TxContext, -) -> Result { - // resolve the function we are calling - let (function_signature, module_id) = match package_object.data { - Data::Package(modules) => { - let bytes = modules - .get(module.as_str()) - .ok_or(SuiError::ModuleNotFound { - module_name: module.to_string(), - })?; - let m = CompiledModule::deserialize(bytes).expect( - "Unwrap safe because FastX serializes/verifies modules before publishing them", - ); - ( - Function::new_from_name(&m, function).ok_or(SuiError::FunctionNotFound { - error: format!( - "Could not resolve function '{}' in module {}", - function, - m.self_id() - ), - })?, - m.self_id(), - ) - } - Data::Move(_) => { - return Err(SuiError::ModuleLoadFailure { - error: "Expected a module object, but found a Move object".to_string(), - }) - } - }; - // check validity conditions on the invoked function - if !function_signature.return_.is_empty() { - return Err(SuiError::InvalidFunctionSignature { - error: "Invoked function must not return a value".to_string(), - }); - } - // check arity of type and value arguments - if function_signature.type_parameters.len() != type_args.len() { - return Err(SuiError::InvalidFunctionSignature { - error: format!( - "Expected {:?} type arguments, but found {:?}", - function_signature.type_parameters.len(), - type_args.len() - ), - }); - } - // total number of args is |objects| + |pure_args| + 1 for the the `TxContext` object - let num_args = object_args.len() + pure_args.len() + 1; - if function_signature.parameters.len() != num_args { - return Err(SuiError::InvalidFunctionSignature { - error: format!( - "Expected {:?} arguments calling function '{}', but found {:?}", - function_signature.parameters.len(), - function, - num_args - ), - }); - } - // check that the last arg is `&mut TxContext` - let last_param = &function_signature.parameters[function_signature.parameters.len() - 1]; - if !is_param_tx_context(last_param) { - return Err(SuiError::InvalidFunctionSignature { - error: format!( - "Expected last parameter of function signature to be &mut {}::{}::{}, but found {}", - SUI_FRAMEWORK_ADDRESS, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME, last_param - ), - }); - } - - // type check object arguments passed in by value and by reference - let mut args = Vec::new(); - let mut mutable_ref_objects = Vec::new(); - let mut by_value_objects = BTreeMap::new(); - #[cfg(debug_assertions)] - let mut num_immutable_objects = 0; - #[cfg(debug_assertions)] - let num_objects = object_args.len(); - - let ty_args: Vec = type_args.iter().map(|t| Type::from(t.clone())).collect(); - for (idx, object) in object_args.into_iter().enumerate() { - let mut param_type = function_signature.parameters[idx].clone(); - if !param_type.is_closed() { - param_type = param_type.subst(&ty_args); - } - match &object.data { - Data::Move(m) => { - args.push(m.contents().to_vec()); - // check that m.type_ matches the parameter types of the function - match ¶m_type { - Type::MutableReference(inner_t) => { - if m.is_read_only() { - return Err(SuiError::TypeError { - error: format!( - "Argument {} is expected to be mutable, immutable object found", - idx - ), - }); - } - type_check_struct(&m.type_, inner_t)?; - mutable_ref_objects.push(object); - } - Type::Reference(inner_t) => { - type_check_struct(&m.type_, inner_t)?; - #[cfg(debug_assertions)] - { - num_immutable_objects += 1 - } - } - Type::Struct { .. } => { - if m.is_read_only() { - return Err(SuiError::TypeError { - error: format!( - "Argument {} is expected to be mutable, immutable object found", - idx - ), - }); - } - type_check_struct(&m.type_, ¶m_type)?; - let res = by_value_objects.insert(object.id(), object); - // should always pass due to earlier "no duplicate ID's" check - debug_assert!(res.is_none()) - } - t => { - return Err(SuiError::TypeError { - error: format!( - "Found object argument {}, but function expects {}", - m.type_, t - ), - }) - } - } - } - Data::Package(_) => { - return Err(SuiError::TypeError { - error: format!("Found module argument, but function expects {}", param_type), - }) - } - } - } - #[cfg(debug_assertions)] - debug_assert!( - by_value_objects.len() + mutable_ref_objects.len() + num_immutable_objects == num_objects - ); - // check that the non-object parameters are primitive types - for param_type in - &function_signature.parameters[args.len()..function_signature.parameters.len() - 1] - { - if !is_primitive(param_type) { - return Err(SuiError::TypeError { - error: format!("Expected primitive type, but found {}", param_type), - }); - } - } - args.append(&mut pure_args); - args.push(ctx.to_vec()); - - Ok(TypeCheckSuccess { - module_id, - args, - by_value_objects, - mutable_ref_objects, - }) -} - -fn is_param_tx_context(param: &Type) -> bool { - if let Type::MutableReference(typ) = param { - match &**typ { - Type::Struct { - address, - module, - name, - .. - } if address == &SUI_FRAMEWORK_ADDRESS - && module.as_ident_str() == TX_CONTEXT_MODULE_NAME - && name.as_ident_str() == TX_CONTEXT_STRUCT_NAME => - { - return true - } - _ => return false, - } - } - false -} - -fn type_check_struct(arg_type: &StructTag, param_type: &Type) -> Result<(), SuiError> { - if let Some(param_struct_type) = param_type.clone().into_struct_tag() { - if arg_type != ¶m_struct_type { - Err(SuiError::TypeError { - error: format!( - "Expected argument of type {}, but found type {}", - param_struct_type, arg_type - ), - }) - } else { - Ok(()) - } - } else { - Err(SuiError::TypeError { - error: format!( - "Expected argument of type {}, but found struct type {}", - param_type, arg_type - ), - }) - } -} - -// TODO: upstream Type::is_primitive in diem -fn is_primitive(t: &Type) -> bool { - use Type::*; - match t { - Bool | U8 | U64 | U128 | Address => true, - Vector(inner_t) => is_primitive(inner_t), - Signer | Struct { .. } | TypeParameter(_) | Reference(_) | MutableReference(_) => false, - } -} - #[cfg(debug_assertions)] fn check_transferred_object_invariants(new_object: &MoveObject, old_object: &Option) { if let Some(o) = old_object { diff --git a/sui_programmability/adapter/src/genesis.rs b/sui_programmability/adapter/src/genesis.rs index 28336fbb837ff..b8f07cc6c8a47 100644 --- a/sui_programmability/adapter/src/genesis.rs +++ b/sui_programmability/adapter/src/genesis.rs @@ -31,8 +31,8 @@ fn create_genesis_module_objects(lib_dir: &Path) -> SuiResult { sui_framework::get_move_stdlib_modules(&lib_dir.join("deps").join("move-stdlib"))?; let owner = SuiAddress::default(); let objects = vec![ - Object::new_package(sui_modules, owner, TransactionDigest::genesis()), - Object::new_package(std_modules, owner, TransactionDigest::genesis()), + Object::new_package(sui_modules, owner, TransactionDigest::genesis())?, + Object::new_package(std_modules, owner, TransactionDigest::genesis())?, ]; Ok(Genesis { objects }) } diff --git a/sui_programmability/adapter/src/unit_tests/adapter_tests.rs b/sui_programmability/adapter/src/unit_tests/adapter_tests.rs index 5d59d21973350..640ac9e1bc11e 100644 --- a/sui_programmability/adapter/src/unit_tests/adapter_tests.rs +++ b/sui_programmability/adapter/src/unit_tests/adapter_tests.rs @@ -6,7 +6,7 @@ use move_binary_format::file_format::{ self, AbilitySet, AddressIdentifierIndex, IdentifierIndex, ModuleHandle, ModuleHandleIndex, StructHandle, }; -use move_core_types::{account_address::AccountAddress, ident_str}; +use move_core_types::{account_address::AccountAddress, ident_str, language_storage::StructTag}; use move_package::BuildConfig; use std::{collections::BTreeSet, mem, path::PathBuf}; use sui_types::{ @@ -56,7 +56,7 @@ impl InMemoryStorage { .values() .find(|o| { if let Some(package) = o.data.try_as_package() { - if package.get(name).is_some() { + if package.serialized_module_map().get(name).is_some() { return true; } } @@ -148,7 +148,9 @@ impl ModuleResolver for InMemoryStorage { Ok(self .read_object(&ObjectID::from(*module_id.address())) .map(|o| match &o.data { - Data::Package(m) => m[module_id.name().as_str()].clone().into_vec(), + Data::Package(m) => m.serialized_module_map()[module_id.name().as_str()] + .clone() + .into_vec(), Data::Move(_) => panic!("Type error"), })) } @@ -780,6 +782,7 @@ fn test_publish_module_linker_error() { .data .try_as_package() .unwrap() + .serialized_module_map() .get("ID") .unwrap(), ) diff --git a/sui_programmability/examples/sources/Hero.move b/sui_programmability/examples/sources/Hero.move index 836580596cde5..8dd90ac00c52c 100644 --- a/sui_programmability/examples/sources/Hero.move +++ b/sui_programmability/examples/sources/Hero.move @@ -273,7 +273,7 @@ module Examples::Hero { } // --- Testing functions --- - fun assert_hero_strength(hero: &Hero, strength: u64, _: &mut TxContext) { + public fun assert_hero_strength(hero: &Hero, strength: u64, _: &mut TxContext) { assert!(hero_strength(hero) == strength, ASSERT_ERR); } diff --git a/sui_programmability/examples/sources/TrustedCoin.move b/sui_programmability/examples/sources/TrustedCoin.move index 4a46847b4b9b5..d4ddb5198df1a 100644 --- a/sui_programmability/examples/sources/TrustedCoin.move +++ b/sui_programmability/examples/sources/TrustedCoin.move @@ -18,12 +18,12 @@ module Examples::TrustedCoin { Transfer::transfer(treasury_cap, TxContext::get_signer_address(ctx)) } - fun mint(treasury_cap: &mut TreasuryCap, amount: u64, ctx: &mut TxContext) { + public fun mint(treasury_cap: &mut TreasuryCap, amount: u64, ctx: &mut TxContext) { let coin = Coin::mint(amount, treasury_cap, ctx); Coin::transfer(coin, TxContext::get_signer_address(ctx)); } - fun transfer(treasury_cap: TreasuryCap, recipient: vector, _ctx: &mut TxContext) { + public fun transfer(treasury_cap: TreasuryCap, recipient: vector, _ctx: &mut TxContext) { Coin::transfer_cap(treasury_cap, Address::new(recipient)); } diff --git a/sui_types/src/lib.rs b/sui_types/src/lib.rs index 864fa68af95fa..e52a690e92f43 100644 --- a/sui_types/src/lib.rs +++ b/sui_types/src/lib.rs @@ -22,6 +22,7 @@ pub mod gas; pub mod gas_coin; pub mod id; pub mod messages; +pub mod move_call; pub mod object; pub mod serialize; pub mod storage; diff --git a/sui_types/src/move_call.rs b/sui_types/src/move_call.rs new file mode 100644 index 0000000000000..e06c47272ee29 --- /dev/null +++ b/sui_types/src/move_call.rs @@ -0,0 +1,265 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + base_types::{ObjectID, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME}, + error::SuiError, + object::{Data, Object}, + SUI_FRAMEWORK_ADDRESS, +}; +use move_binary_format::{ + file_format::Visibility, + normalized::{Function, Type}, + CompiledModule, +}; +use move_core_types::{ + ident_str, + identifier::{IdentStr, Identifier}, + language_storage::{ModuleId, StructTag, TypeTag}, +}; +use std::collections::BTreeMap; + +pub const INIT_FN_NAME: &IdentStr = ident_str!("init"); + +pub struct TypeCheckSuccess { + pub module_id: ModuleId, + pub args: Vec>, + pub by_value_objects: BTreeMap, + pub mutable_ref_objects: Vec, +} + +/// - Check that `package_object`, `module` and `function` are valid +/// - Check that the the signature of `function` is well-typed w.r.t `type_args`, `object_args`, and `pure_args` +/// - Return the ID of the resolved module, a vector of BCS encoded arguments to pass to the VM, and a partitioning +/// of the input objects into objects passed by value vs by mutable reference +pub fn resolve_and_type_check( + package_object: Object, + module: &Identifier, + function: &Identifier, + type_args: &[TypeTag], + object_args: Vec, + mut pure_args: Vec>, +) -> Result { + // resolve the function we are calling + let (function_signature, module_id) = match package_object.data { + Data::Package(package) => ( + package.get_function_signature(module, function)?, + package.module_id(module)?, + ), + Data::Move(_) => { + return Err(SuiError::ModuleLoadFailure { + error: "Expected a module object, but found a Move object".to_string(), + }) + } + }; + + if function_signature.visibility != Visibility::Public { + return Err(SuiError::InvalidFunctionSignature { + error: "Invoked function must have public visibility".to_string(), + }); + } + + // check validity conditions on the invoked function + if !function_signature.return_.is_empty() { + return Err(SuiError::InvalidFunctionSignature { + error: "Invoked function must not return a value".to_string(), + }); + } + // check arity of type and value arguments + if function_signature.type_parameters.len() != type_args.len() { + return Err(SuiError::InvalidFunctionSignature { + error: format!( + "Expected {:?} type arguments, but found {:?}", + function_signature.type_parameters.len(), + type_args.len() + ), + }); + } + // total number of args is |objects| + |pure_args| + 1 for the the `TxContext` object + let num_args = object_args.len() + pure_args.len() + 1; + if function_signature.parameters.len() != num_args { + return Err(SuiError::InvalidFunctionSignature { + error: format!( + "Expected {:?} arguments calling function '{}', but found {:?}", + function_signature.parameters.len(), + function, + num_args + ), + }); + } + // check that the last arg is `&mut TxContext` + let last_param = &function_signature.parameters[function_signature.parameters.len() - 1]; + if !is_param_tx_context(last_param) { + return Err(SuiError::InvalidFunctionSignature { + error: format!( + "Expected last parameter of function signature to be &mut {}::{}::{}, but found {}", + SUI_FRAMEWORK_ADDRESS, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME, last_param + ), + }); + } + + // type check object arguments passed in by value and by reference + let mut args = Vec::new(); + let mut mutable_ref_objects = Vec::new(); + let mut by_value_objects = BTreeMap::new(); + #[cfg(debug_assertions)] + let mut num_immutable_objects = 0; + #[cfg(debug_assertions)] + let num_objects = object_args.len(); + + let ty_args: Vec = type_args.iter().map(|t| Type::from(t.clone())).collect(); + for (idx, object) in object_args.into_iter().enumerate() { + let mut param_type = function_signature.parameters[idx].clone(); + if !param_type.is_closed() { + param_type = param_type.subst(&ty_args); + } + match &object.data { + Data::Move(m) => { + args.push(m.contents().to_vec()); + // check that m.type_ matches the parameter types of the function + match ¶m_type { + Type::MutableReference(inner_t) => { + if m.is_read_only() { + return Err(SuiError::TypeError { + error: format!( + "Argument {} is expected to be mutable, immutable object found", + idx + ), + }); + } + type_check_struct(&m.type_, inner_t)?; + mutable_ref_objects.push(object); + } + Type::Reference(inner_t) => { + type_check_struct(&m.type_, inner_t)?; + #[cfg(debug_assertions)] + { + num_immutable_objects += 1 + } + } + Type::Struct { .. } => { + if m.is_read_only() { + return Err(SuiError::TypeError { + error: format!( + "Argument {} is expected to be mutable, immutable object found", + idx + ), + }); + } + type_check_struct(&m.type_, ¶m_type)?; + let res = by_value_objects.insert(object.id(), object); + // should always pass due to earlier "no duplicate ID's" check + debug_assert!(res.is_none()) + } + t => { + return Err(SuiError::TypeError { + error: format!( + "Found object argument {}, but function expects {}", + m.type_, t + ), + }) + } + } + } + Data::Package(_) => { + return Err(SuiError::TypeError { + error: format!("Found module argument, but function expects {}", param_type), + }) + } + } + } + #[cfg(debug_assertions)] + debug_assert!( + by_value_objects.len() + mutable_ref_objects.len() + num_immutable_objects == num_objects + ); + // check that the non-object parameters are primitive types + for param_type in + &function_signature.parameters[args.len()..function_signature.parameters.len() - 1] + { + if !is_primitive(param_type) { + return Err(SuiError::TypeError { + error: format!("Expected primitive type, but found {}", param_type), + }); + } + } + args.append(&mut pure_args); + + Ok(TypeCheckSuccess { + module_id, + args, + by_value_objects, + mutable_ref_objects, + }) +} + +pub fn is_param_tx_context(param: &Type) -> bool { + if let Type::MutableReference(typ) = param { + match &**typ { + Type::Struct { + address, + module, + name, + .. + } if address == &SUI_FRAMEWORK_ADDRESS + && module.as_ident_str() == TX_CONTEXT_MODULE_NAME + && name.as_ident_str() == TX_CONTEXT_STRUCT_NAME => + { + return true + } + _ => return false, + } + } + false +} + +// TODO: upstream Type::is_primitive in diem +fn is_primitive(t: &Type) -> bool { + use Type::*; + match t { + Bool | U8 | U64 | U128 | Address => true, + Vector(inner_t) => is_primitive(inner_t), + Signer | Struct { .. } | TypeParameter(_) | Reference(_) | MutableReference(_) => false, + } +} + +fn type_check_struct(arg_type: &StructTag, param_type: &Type) -> Result<(), SuiError> { + if let Some(param_struct_type) = param_type.clone().into_struct_tag() { + if arg_type != ¶m_struct_type { + Err(SuiError::TypeError { + error: format!( + "Expected argument of type {}, but found type {}", + param_struct_type, arg_type + ), + }) + } else { + Ok(()) + } + } else { + Err(SuiError::TypeError { + error: format!( + "Expected argument of type {}, but found struct type {}", + param_type, arg_type + ), + }) + } +} + +pub fn module_has_init(module: &CompiledModule) -> bool { + let function = match Function::new_from_name(module, INIT_FN_NAME) { + Some(v) => v, + None => return false, + }; + if function.visibility != Visibility::Private { + return false; + } + if !function.type_parameters.is_empty() { + return false; + } + if !function.return_.is_empty() { + return false; + } + if function.parameters.len() != 1 { + return false; + } + is_param_tx_context(&function.parameters[0]) +} diff --git a/sui_types/src/object.rs b/sui_types/src/object.rs index 02b81c0110e18..00850a18ac210 100644 --- a/sui_types/src/object.rs +++ b/sui_types/src/object.rs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use move_binary_format::binary_views::BinaryIndexedView; +use move_binary_format::normalized::Function; use move_bytecode_utils::layout::TypeLayoutBuilder; use move_bytecode_utils::module_cache::GetModule; -use move_core_types::language_storage::TypeTag; +use move_core_types::identifier::Identifier; +use move_core_types::language_storage::{ModuleId, TypeTag}; use move_core_types::value::{MoveStruct, MoveStructLayout, MoveTypeLayout}; use move_disassembler::disassembler::Disassembler; use move_ir_types::location::Spanned; @@ -169,9 +171,87 @@ impl MoveObject { } } -// TODO: Make MovePackage a NewType so that we can implement functions on it. // serde_bytes::ByteBuf is an analog of Vec with built-in fast serialization. -pub type MovePackage = BTreeMap; +#[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)] +pub struct MovePackage { + id: ObjectID, + module_map: BTreeMap, +} + +impl MovePackage { + pub fn serialized_module_map(&self) -> &BTreeMap { + &self.module_map + } + + pub fn from_map(module_map: &BTreeMap) -> Self { + // All modules in the same package must have the same address. Pick any + let id = ObjectID::from( + *CompiledModule::deserialize(module_map.values().next().unwrap()) + .unwrap() + .self_id() + .address(), + ); + + Self { + id, + module_map: module_map.clone(), + } + } + + pub fn id(&self) -> ObjectID { + self.id + } + + pub fn module_id(&self, module: &Identifier) -> Result { + let ser = + self.serialized_module_map() + .get(module.as_str()) + .ok_or(SuiError::ModuleNotFound { + module_name: module.to_string(), + })?; + Ok(CompiledModule::deserialize(ser)?.self_id()) + } + + pub fn get_function_signature( + &self, + module: &Identifier, + function: &Identifier, + ) -> Result { + let bytes = + self.serialized_module_map() + .get(module.as_str()) + .ok_or(SuiError::ModuleNotFound { + module_name: module.to_string(), + })?; + let m = CompiledModule::deserialize(bytes) + .expect("Unwrap safe because FastX serializes/verifies modules before publishing them"); + + Function::new_from_name(&m, function).ok_or(SuiError::FunctionNotFound { + error: format!( + "Could not resolve function '{}' in module {}::{}", + function, + self.id(), + module + ), + }) + } +} +impl TryFrom<&Vec> for MovePackage { + type Error = SuiError; + fn try_from(compiled_modules: &Vec) -> Result { + let package = MovePackage::from_map( + &compiled_modules + .iter() + .map(|module| { + let mut bytes = Vec::new(); + module.serialize(&mut bytes).unwrap(); + (module.self_id().name().to_string(), ByteBuf::from(bytes)) + }) + .collect(), + ); + Ok(package) + } +} #[derive(Eq, PartialEq, Debug, Clone, Deserialize, Serialize, Hash)] #[allow(clippy::large_enum_variant)] @@ -243,7 +323,7 @@ impl Data { }, Package(p) => { let mut disassembled = serde_json::Map::new(); - for (name, bytecode) in p { + for (name, bytecode) in p.serialized_module_map() { let module = CompiledModule::deserialize(bytecode) .expect("Adapter publish flow ensures that this bytecode deserializes"); let view = BinaryIndexedView::Module(&module); @@ -290,20 +370,12 @@ impl Object { modules: Vec, owner: SuiAddress, previous_transaction: TransactionDigest, - ) -> Self { - let serialized: MovePackage = modules - .into_iter() - .map(|module| { - let mut bytes = Vec::new(); - module.serialize(&mut bytes).unwrap(); - (module.self_id().name().to_string(), ByteBuf::from(bytes)) - }) - .collect(); - Object { - data: Data::Package(serialized), + ) -> Result { + Ok(Object { + data: Data::Package(MovePackage::try_from(&modules)?), owner, previous_transaction, - } + }) } pub fn is_read_only(&self) -> bool { @@ -327,16 +399,7 @@ impl Object { match &self.data { Move(v) => v.id(), - Package(m) => { - // All modules in the same package must have the same address. - // TODO: Use byte trick to get ID directly without deserialization. - ObjectID::from( - *CompiledModule::deserialize(m.values().next().unwrap()) - .unwrap() - .self_id() - .address(), - ) - } + Package(m) => m.id(), } }