diff --git a/language/benchmarks/src/move_vm.rs b/language/benchmarks/src/move_vm.rs index 1b07b7580b..98eb6d77f9 100644 --- a/language/benchmarks/src/move_vm.rs +++ b/language/benchmarks/src/move_vm.rs @@ -83,7 +83,13 @@ fn execute( c.bench_function(fun, |b| { b.iter(|| { session - .execute_function(&module_id, fun_name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_or_else(|err| { panic!( "{:?}::{} failed with {:?}", diff --git a/language/extensions/async/move-async-vm/src/async_vm.rs b/language/extensions/async/move-async-vm/src/async_vm.rs index 7743fcfa76..42cd2dc07f 100644 --- a/language/extensions/async/move-async-vm/src/async_vm.rs +++ b/language/extensions/async/move-async-vm/src/async_vm.rs @@ -11,6 +11,7 @@ use move_binary_format::errors::{Location, PartialVMError, PartialVMResult, VMEr use move_core_types::{ account_address::AccountAddress, effects::{ChangeSet, Event}, + gas_schedule::GasAlgebra, identifier::Identifier, language_storage::{ModuleId, StructTag, TypeTag}, resolver::MoveResolver, @@ -19,7 +20,7 @@ use move_core_types::{ use move_vm_runtime::{ move_vm::MoveVM, native_functions::{NativeContextExtensions, NativeFunction}, - session::{ExecutionResult, Session}, + session::{SerializedReturnValues, Session}, }; use move_vm_types::{ gas_schedule::GasStatus, @@ -175,25 +176,29 @@ impl<'r, 'l, S: MoveResolver> AsyncSession<'r, 'l, S> { } // Execute the initializer. - let result = self.vm_session.execute_function_for_effects( - &actor.module_id, - &actor.initializer, - vec![], - Vec::>::new(), - gas_status, - ); + let gas_before = gas_status.remaining_gas().get(); + let result = self + .vm_session + .execute_function_bypass_visibility( + &actor.module_id, + &actor.initializer, + vec![], + Vec::>::new(), + gas_status, + ) + .and_then(|ret| Ok((ret, self.vm_session.finish_with_extensions()?))); + let gas_used = gas_status.remaining_gas().get() - gas_before; // Process the result, moving the return value of the initializer function into the // changeset. match result { - ExecutionResult::Success { - mut change_set, - events, - mut return_values, - gas_used, - mut native_extensions, - .. - } => { + Ok(( + SerializedReturnValues { + mutable_reference_outputs: _, + mut return_values, + }, + (mut change_set, events, mut native_extensions), + )) => { if return_values.len() != 1 { Err(async_extension_error(format!( "inconsistent initializer `{}`", @@ -204,7 +209,7 @@ impl<'r, 'l, S: MoveResolver> AsyncSession<'r, 'l, S> { &mut change_set, actor_addr, actor.state_tag.clone(), - return_values.remove(0), + return_values.remove(0).0, ) .map_err(partial_vm_error_to_async)?; let async_ext = native_extensions.remove::(); @@ -216,7 +221,7 @@ impl<'r, 'l, S: MoveResolver> AsyncSession<'r, 'l, S> { }) } } - ExecutionResult::Fail { error, gas_used } => Err(AsyncError { error, gas_used }), + Err(error) => Err(AsyncError { error, gas_used }), } } @@ -266,37 +271,36 @@ impl<'r, 'l, S: MoveResolver> AsyncSession<'r, 'l, S> { ); // Execute the handler. - let result = self.vm_session.execute_function_for_effects( - module_id, - handler_id, - vec![], - args, - gas_status, - ); + let gas_before = gas_status.remaining_gas().get(); + let result = self + .vm_session + .execute_function_bypass_visibility(module_id, handler_id, vec![], args, gas_status) + .and_then(|ret| Ok((ret, self.vm_session.finish_with_extensions()?))); + + let gas_used = gas_status.remaining_gas().get() - gas_before; // Process the result, moving the mutated value of the handlers first parameter // into the changeset. match result { - ExecutionResult::Success { - mut change_set, - events, - mut mutable_ref_values, - gas_used, - mut native_extensions, - .. - } => { - if mutable_ref_values.len() > 1 { + Ok(( + SerializedReturnValues { + mut mutable_reference_outputs, + return_values: _, + }, + (mut change_set, events, mut native_extensions), + )) => { + if mutable_reference_outputs.len() > 1 { Err(async_extension_error(format!( "inconsistent handler `{}`", handler_id ))) } else { - if !mutable_ref_values.is_empty() { + if !mutable_reference_outputs.is_empty() { publish_actor_state( &mut change_set, actor_addr, actor.state_tag.clone(), - mutable_ref_values.remove(0), + mutable_reference_outputs.remove(0).1, ) .map_err(partial_vm_error_to_async)?; } @@ -309,7 +313,7 @@ impl<'r, 'l, S: MoveResolver> AsyncSession<'r, 'l, S> { }) } } - ExecutionResult::Fail { error, gas_used } => Err(AsyncError { error, gas_used }), + Err(error) => Err(AsyncError { error, gas_used }), } } diff --git a/language/move-binary-format/src/check_bounds.rs b/language/move-binary-format/src/check_bounds.rs index a1d0538c3b..2f2318d11d 100644 --- a/language/move-binary-format/src/check_bounds.rs +++ b/language/move-binary-format/src/check_bounds.rs @@ -37,21 +37,30 @@ impl<'a> BoundsChecker<'a> { }; bounds_check.verify_impl()?; - let signatures = &script.signatures; - let parameters = &script.parameters; - match signatures.get(parameters.into_index()) { - // The bounds checker has already checked each function definition's code, but a script's - // code exists outside of any function definition. It gets checked here. - Some(signature) => { - bounds_check.check_code(&script.code, &script.type_parameters, signature) + let type_param_count = script.type_parameters.len(); + + check_bounds_impl(bounds_check.view.signatures(), script.parameters)?; + if let Some(sig) = bounds_check + .view + .signatures() + .get(script.parameters.into_index()) + { + for ty in &sig.0 { + bounds_check.check_type_parameter(ty, type_param_count)? } - None => Err(bounds_error( - StatusCode::INDEX_OUT_OF_BOUNDS, - IndexKind::Signature, - parameters.into_index() as u16, - signatures.len(), - )), } + + // The bounds checker has already checked each function definition's code, but a + // script's code exists outside of any function definition. It gets checked here. + bounds_check.check_code( + &script.code, + &script.type_parameters, + bounds_check + .view + .signatures() + .get(script.parameters.into_index()) + .unwrap(), + ) } pub fn verify_module(module: &'a CompiledModule) -> PartialVMResult<()> { diff --git a/language/move-binary-format/src/file_format_common.rs b/language/move-binary-format/src/file_format_common.rs index d25c119a69..0ff9522dfe 100644 --- a/language/move-binary-format/src/file_format_common.rs +++ b/language/move-binary-format/src/file_format_common.rs @@ -374,8 +374,12 @@ pub const VERSION_3: u32 = 3; /// + bytecode for vector operations pub const VERSION_4: u32 = 4; +/// Version 5: changes compared with version 4 +/// +/- script and public(script) verification is now adapter specific +pub const VERSION_5: u32 = 5; + // Mark which version is the latest version -pub const VERSION_MAX: u32 = VERSION_4; +pub const VERSION_MAX: u32 = VERSION_5; pub(crate) mod versioned_data { use crate::{errors::*, file_format_common::*}; diff --git a/language/move-bytecode-verifier/bytecode-verifier-tests/src/unit_tests/bounds_tests.rs b/language/move-bytecode-verifier/bytecode-verifier-tests/src/unit_tests/bounds_tests.rs index 5914ec1b14..bf6c70f1c2 100644 --- a/language/move-bytecode-verifier/bytecode-verifier-tests/src/unit_tests/bounds_tests.rs +++ b/language/move-bytecode-verifier/bytecode-verifier-tests/src/unit_tests/bounds_tests.rs @@ -70,6 +70,19 @@ fn invalid_type_param_in_fn_parameters() { ); } +#[test] +fn invalid_type_param_in_script_parameters() { + use SignatureToken::*; + + let mut s = basic_test_script(); + s.parameters = SignatureIndex(1); + s.signatures.push(Signature(vec![TypeParameter(0)])); + assert_eq!( + BoundsChecker::verify_script(&s).unwrap_err().major_status(), + StatusCode::INDEX_OUT_OF_BOUNDS + ); +} + #[test] fn invalid_struct_in_fn_return_() { use SignatureToken::*; diff --git a/language/move-bytecode-verifier/src/lib.rs b/language/move-bytecode-verifier/src/lib.rs index 923b39391c..8cdd5dc2b7 100644 --- a/language/move-bytecode-verifier/src/lib.rs +++ b/language/move-bytecode-verifier/src/lib.rs @@ -25,6 +25,9 @@ pub mod verifier; pub use check_duplication::DuplicationChecker; pub use code_unit_verifier::CodeUnitVerifier; pub use instruction_consistency::InstructionConsistency; +pub use script_signature::{ + legacy_script_signature_checks, no_additional_script_signature_checks, FnCheckScriptSignature, +}; pub use signature::SignatureChecker; pub use struct_defs::RecursiveStructDefChecker; pub use verifier::{verify_module, verify_script}; diff --git a/language/move-bytecode-verifier/src/script_signature.rs b/language/move-bytecode-verifier/src/script_signature.rs index 12ce8d30bf..a527deb8c2 100644 --- a/language/move-bytecode-verifier/src/script_signature.rs +++ b/language/move-bytecode-verifier/src/script_signature.rs @@ -1,90 +1,182 @@ // Copyright (c) The Diem Core Contributors // SPDX-License-Identifier: Apache-2.0 -//! This module implements a checker for verifying that a script (or script function when serving -//! as the entry point for script execution) has a valid signature, which entails -//! - All signer arguments are occur before non-signer arguments -//! - All types non-signer arguments have a type that is valid for constants -//! - Has an empty return type +//! This module implements a checker for verifying that a script, public(script), or other entry +//! point function a valid signature, which entails +//! - (DEPRECATED) All signer arguments are occur before non-signer arguments +//! - (DEPRECATED) All types non-signer arguments have a type that is valid for constants +//! - (DEPRECATED) Has an empty return type +//! - All return types are not references +//! - Satisfies the additional checks provided as an argument via `check_signature` +//! `check_signature` should be used by adapters to quickly and easily verify custom signature +//! rules for entrypoints + use move_binary_format::{ access::ModuleAccess, binary_views::BinaryIndexedView, errors::{Location, PartialVMError, PartialVMResult, VMResult}, file_format::{ - CompiledModule, CompiledScript, SignatureIndex, SignatureToken, TableIndex, Visibility, + CompiledModule, CompiledScript, FunctionDefinitionIndex, SignatureIndex, SignatureToken, + TableIndex, Visibility, }, - file_format_common::VERSION_1, + file_format_common::{VERSION_1, VERSION_5}, IndexKind, }; use move_core_types::{identifier::IdentStr, vm_status::StatusCode}; +pub type FnCheckScriptSignature = fn( + &BinaryIndexedView, + Visibility, + SignatureIndex, + Option, +) -> PartialVMResult<()>; + /// This function checks the extra requirements on the signature of the main function of a script. -pub fn verify_script(script: &CompiledScript) -> VMResult<()> { +pub fn verify_script( + script: &CompiledScript, + check_signature: FnCheckScriptSignature, +) -> VMResult<()> { + if script.version >= VERSION_5 { + return Ok(()); + } + let resolver = &BinaryIndexedView::Script(script); let parameters = script.parameters; - let return_type_opt = None; - verify_main_signature_impl(resolver, parameters, return_type_opt) - .map_err(|e| e.finish(Location::Script)) + let return_ = None; + verify_main_signature_impl( + resolver, + Visibility::Script, + parameters, + return_, + check_signature, + ) + .map_err(|e| e.finish(Location::Script)) +} + +pub fn verify_module( + module: &CompiledModule, + check_signature: FnCheckScriptSignature, +) -> VMResult<()> { + // important for not breaking old modules + if module.version < VERSION_5 { + return Ok(()); + } + + for (idx, _fdef) in module + .function_defs() + .iter() + .enumerate() + .filter(|(_idx, fdef)| matches!(fdef.visibility, Visibility::Script)) + { + verify_module_function_signature( + module, + FunctionDefinitionIndex(idx as TableIndex), + check_signature, + )? + } + Ok(()) } /// This function checks the extra requirements on the signature of the script visible function /// when it serves as an entry point for script execution -pub fn verify_module_script_function(module: &CompiledModule, name: &IdentStr) -> VMResult<()> { +pub fn verify_module_function_signature_by_name( + module: &CompiledModule, + name: &IdentStr, + check_signature: FnCheckScriptSignature, +) -> VMResult<()> { let fdef_opt = module.function_defs().iter().enumerate().find(|(_, fdef)| { module.identifier_at(module.function_handle_at(fdef.function).name) == name }); - let (idx, fdef) = fdef_opt.ok_or_else(|| { + let (idx, _fdef) = fdef_opt.ok_or_else(|| { PartialVMError::new(StatusCode::VERIFICATION_ERROR) .with_message("function not found in verify_module_script_function".to_string()) .finish(Location::Module(module.self_id())) })?; + verify_module_function_signature( + module, + FunctionDefinitionIndex(idx as TableIndex), + check_signature, + ) +} - match fdef.visibility { - Visibility::Script => (), - Visibility::Private | Visibility::Friend | Visibility::Public => { - return Err(PartialVMError::new( - StatusCode::EXECUTE_SCRIPT_FUNCTION_CALLED_ON_NON_SCRIPT_VISIBLE, - ) - .at_index(IndexKind::FunctionDefinition, idx as TableIndex) - .finish(Location::Module(module.self_id()))) - } - } +/// This function checks the extra requirements on the signature of the script visible function +/// when it serves as an entry point for script execution +fn verify_module_function_signature( + module: &CompiledModule, + idx: FunctionDefinitionIndex, + check_signature: FnCheckScriptSignature, +) -> VMResult<()> { + let fdef = module.function_def_at(idx); let resolver = &BinaryIndexedView::Module(module); let fhandle = module.function_handle_at(fdef.function); let parameters = fhandle.parameters; - let return_type_opt = Some(fhandle.return_); - verify_main_signature_impl(resolver, parameters, return_type_opt).map_err(|e| { - e.at_index(IndexKind::FunctionDefinition, idx as TableIndex) + let return_ = fhandle.return_; + verify_main_signature_impl( + resolver, + fdef.visibility, + parameters, + Some(return_), + check_signature, + ) + .map_err(|e| { + e.at_index(IndexKind::FunctionDefinition, idx.0) .finish(Location::Module(module.self_id())) }) } fn verify_main_signature_impl( resolver: &BinaryIndexedView, - parameters: SignatureIndex, - return_type_opt: Option, + visibility: Visibility, + parameters_idx: SignatureIndex, + return_idx: Option, + check_signature: FnCheckScriptSignature, +) -> PartialVMResult<()> { + let deprecated_logic = + resolver.version() < VERSION_5 && matches!(visibility, Visibility::Script); + + if deprecated_logic { + legacy_script_signature_checks(resolver, visibility, parameters_idx, return_idx)?; + } + check_signature(resolver, visibility, parameters_idx, return_idx) +} + +pub fn no_additional_script_signature_checks( + _resolver: &BinaryIndexedView, + _visibility: Visibility, + _parameters: SignatureIndex, + _return_type: Option, +) -> PartialVMResult<()> { + Ok(()) +} + +pub fn legacy_script_signature_checks( + resolver: &BinaryIndexedView, + _visibility: Visibility, + parameters_idx: SignatureIndex, + return_idx: Option, ) -> PartialVMResult<()> { use SignatureToken as S; - let arguments = &resolver.signature_at(parameters).0; + let empty_vec = &vec![]; + let parameters = &resolver.signature_at(parameters_idx).0; + let return_types = return_idx + .map(|idx| &resolver.signature_at(idx).0) + .unwrap_or(empty_vec); // Check that all `signer` arguments occur before non-`signer` arguments // signer is a type that can only be populated by the Move VM. And its value is filled // based on the sender of the transaction let all_args_have_valid_type = if resolver.version() <= VERSION_1 { - arguments + parameters .iter() .skip_while(|typ| matches!(typ, S::Reference(inner) if matches!(&**inner, S::Signer))) .all(|typ| typ.is_valid_for_constant()) } else { - arguments + parameters .iter() .skip_while(|typ| matches!(typ, S::Signer)) .all(|typ| typ.is_valid_for_constant()) }; - let has_valid_return_type = match return_type_opt { - Some(idx) => resolver.signature_at(idx).0.is_empty(), - None => true, - }; + let has_valid_return_type = return_types.is_empty(); if !all_args_have_valid_type || !has_valid_return_type { Err(PartialVMError::new( StatusCode::INVALID_MAIN_FUNCTION_SIGNATURE, diff --git a/language/move-bytecode-verifier/src/signature.rs b/language/move-bytecode-verifier/src/signature.rs index bd44db026b..7beaa5b3e2 100644 --- a/language/move-bytecode-verifier/src/signature.rs +++ b/language/move-bytecode-verifier/src/signature.rs @@ -46,6 +46,7 @@ impl<'a> SignatureChecker<'a> { }; sig_check.verify_signature_pool(script.signatures())?; sig_check.verify_function_signatures(script.function_handles())?; + sig_check.check_instantiation(script.parameters, &script.type_parameters)?; sig_check.verify_code(script.code(), &script.type_parameters) } diff --git a/language/move-bytecode-verifier/src/verifier.rs b/language/move-bytecode-verifier/src/verifier.rs index 872087b6a8..109bc533f6 100644 --- a/language/move-bytecode-verifier/src/verifier.rs +++ b/language/move-bytecode-verifier/src/verifier.rs @@ -6,7 +6,8 @@ use crate::{ ability_field_requirements, check_duplication::DuplicationChecker, code_unit_verifier::CodeUnitVerifier, constants, friends, instantiation_loops::InstantiationLoopChecker, instruction_consistency::InstructionConsistency, - script_signature, signature::SignatureChecker, struct_defs::RecursiveStructDefChecker, + script_signature, script_signature::no_additional_script_signature_checks, + signature::SignatureChecker, struct_defs::RecursiveStructDefChecker, }; use move_binary_format::{ check_bounds::BoundsChecker, @@ -38,7 +39,8 @@ pub fn verify_module(module: &CompiledModule) -> VMResult<()> { ability_field_requirements::verify_module(module)?; RecursiveStructDefChecker::verify_module(module)?; InstantiationLoopChecker::verify_module(module)?; - CodeUnitVerifier::verify_module(module) + CodeUnitVerifier::verify_module(module)?; + script_signature::verify_module(module, no_additional_script_signature_checks) } /// Helper for a "canonical" verification of a script. @@ -58,5 +60,5 @@ pub fn verify_script(script: &CompiledScript) -> VMResult<()> { InstructionConsistency::verify_script(script)?; constants::verify_script(script)?; CodeUnitVerifier::verify_script(script)?; - script_signature::verify_script(script) + script_signature::verify_script(script, no_additional_script_signature_checks) } diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.exp b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.exp index 5add4723fd..b134af1aea 100644 --- a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.exp +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.exp @@ -1,37 +1 @@ processed 5 tasks - -task 0 'run'. lines 1-6: -Error: Script execution failed with VMError: { - major_status: INVALID_MAIN_FUNCTION_SIGNATURE, - sub_status: None, - location: script, - indices: [], - offsets: [], -} - -task 1 'run'. lines 9-13: -Error: Script execution failed with VMError: { - major_status: INVALID_MAIN_FUNCTION_SIGNATURE, - sub_status: None, - location: script, - indices: [], - offsets: [], -} - -task 2 'run'. lines 16-20: -Error: Script execution failed with VMError: { - major_status: INVALID_MAIN_FUNCTION_SIGNATURE, - sub_status: None, - location: script, - indices: [], - offsets: [], -} - -task 4 'run'. lines 29-35: -Error: Script execution failed with VMError: { - major_status: INVALID_MAIN_FUNCTION_SIGNATURE, - sub_status: None, - location: script, - indices: [], - offsets: [], -} diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.mvir b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.mvir index d8df7637d9..55aeca9456 100644 --- a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.mvir +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/script_type_parameters_in_args.mvir @@ -1,4 +1,4 @@ -//# run +//# run --type-args u64 --args 0 // all invalid signatures main(x: T) { label b0: @@ -6,14 +6,14 @@ label b0: } -//# run +//# run --type-args u8 --args 1u8 main(x: &T) { label b0: return; } -//# run +//# run --type-args u8 --args b"hello" main(v: vector) { label b0: return; @@ -26,7 +26,8 @@ module 0x42.M { } -//# run +//# run --type-args u8 --args 0u8 +// bit of a hack but works because structs have no overhead import 0x42.M; main(x: M.Box>) { diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.exp b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.exp index d2cb319454..4b0549d55c 100644 --- a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.exp +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.exp @@ -2,7 +2,7 @@ processed 3 tasks task 0 'run'. lines 1-6: Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, + major_status: NUMBER_OF_ARGUMENTS_MISMATCH, sub_status: None, location: undefined, indices: [], @@ -11,18 +11,9 @@ Error: Script execution failed with VMError: { task 1 'run'. lines 8-13: Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, + major_status: NUMBER_OF_ARGUMENTS_MISMATCH, sub_status: None, location: undefined, indices: [], offsets: [], } - -task 2 'run'. lines 15-20: -Error: Script execution failed with VMError: { - major_status: INVALID_MAIN_FUNCTION_SIGNATURE, - sub_status: None, - location: script, - indices: [], - offsets: [], -} diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.mvir b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.mvir index fa97b4323f..cd5c4d56a2 100644 --- a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.mvir +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_double_signer.mvir @@ -12,8 +12,8 @@ label b0: return; } -//# run --signers 0x1 0x2 --args 0 -// invalid signature +//# run --args 0x1 0 0x2 +// no longer an invalid signature, after V5 main(s: signer, u: u64, s2: signer) { label b0: return; diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.exp b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.exp index fa09ee9db2..5d92c423f3 100644 --- a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.exp +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.exp @@ -1,19 +1 @@ processed 2 tasks - -task 0 'run'. lines 1-6: -Error: Script execution failed with VMError: { - major_status: INVALID_MAIN_FUNCTION_SIGNATURE, - sub_status: None, - location: script, - indices: [], - offsets: [], -} - -task 1 'run'. lines 8-13: -Error: Script execution failed with VMError: { - major_status: INVALID_MAIN_FUNCTION_SIGNATURE, - sub_status: None, - location: script, - indices: [], - offsets: [], -} diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.mvir b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.mvir index b0dbfaa110..12aeb1b1c0 100644 --- a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.mvir +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/signer_misplaced_signer_arg.mvir @@ -1,12 +1,12 @@ -//# run --signers 0x1 --args 0 -// invalid signature +//# run --args 0 0x1 +// no longer an invalid signature, after V5 main(u: u64, s: &signer) { label b0: return; } -//# run --signers 0x1 --args 0 1 -// invalid signature +//# run --args 0 0x1 1 +// no longer an invalid signature, after V5 main(u: u64, s: &signer, u2: u64) { label b0: return; diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/struct_arguments.exp b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/struct_arguments.exp new file mode 100644 index 0000000000..8f8129931b --- /dev/null +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/struct_arguments.exp @@ -0,0 +1,19 @@ +processed 3 tasks + +task 1 'run'. lines 15-20: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 2 'run'. lines 22-22: +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: 0x42::M, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} diff --git a/language/move-bytecode-verifier/transactional-tests/tests/script_signature/struct_arguments.mvir b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/struct_arguments.mvir new file mode 100644 index 0000000000..013651e4d6 --- /dev/null +++ b/language/move-bytecode-verifier/transactional-tests/tests/script_signature/struct_arguments.mvir @@ -0,0 +1,22 @@ +//# publish + +// struct and struct ref arguments are now allowed +// should abort indicating no verification errors occurred + +module 0x42.M { + struct S { f: u64 } + + public(script) foo(s: Self.S, i: &Self.S, m: &mut Self.S) { + label l0: + abort 0; + } +} + +//# run --args 0 0 0 +import 0x42.M; +main(s: M.S, i: &M.S, m: &mut M.S) { + label l0: + abort 0; +} + +//# run 0x42::M::foo --args 0 0 0 diff --git a/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script.exp b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script.exp new file mode 100644 index 0000000000..88cabce756 --- /dev/null +++ b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script.exp @@ -0,0 +1,73 @@ +processed 9 tasks + +task 1 'run'. lines 19-24: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 2 'run'. lines 26-31: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 3 'run'. lines 33-38: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 4 'run'. lines 40-45: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 5 'run'. lines 47-52: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 6 'run'. lines 54-59: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 7 'run'. lines 61-66: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} + +task 8 'run'. lines 68-73: +Error: Script execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: script, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} diff --git a/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script.mvir b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script.mvir new file mode 100644 index 0000000000..e7bcc25a67 --- /dev/null +++ b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script.mvir @@ -0,0 +1,73 @@ +//# publish +module 0x1.M { + struct K has key { dummy: bool } + + struct BoxCopy has copy { f: T } + struct BoxDrop has drop { f: T } + struct BoxStore has store { f: T } + struct BoxKey has key { f: T } + + struct NeedsCopy { dummy: bool } + struct NeedsDrop { dummy: bool } + struct NeedsStore { dummy: bool } + struct NeedsKey { dummy: bool } +} + +// tests various valid constraints in script arguments +// all tests should abort, signaling they pass verification + +//# run --type-args u64 --args false +import 0x1.M; +main(x: M.NeedsCopy) { + label l0: + abort 0; +} + +//# run --type-args u64 --args false +import 0x1.M; +main(x: M.NeedsDrop) { + label l0: + abort 0; +} + +//# run --type-args u64 --args false +import 0x1.M; +main(x: M.NeedsStore) { + label l0: + abort 0; +} + +//# run --type-args 0x1::M::K --args false +import 0x1.M; +main(x: M.NeedsKey) { + label l0: + abort 0; +} + +//# run --type-args u64 --args false +import 0x1.M; +main(x: M.NeedsCopy>) { + label l0: + abort 0; +} + +//# run --type-args u64 --args false +import 0x1.M; +main(x: M.NeedsDrop>) { + label l0: + abort 0; +} + +//# run --type-args u64 --args false +import 0x1.M; +main(x: M.NeedsStore>) { + label l0: + abort 0; +} + +//# run --type-args u64 --args false +import 0x1.M; +main(x: M.NeedsKey>) { + label l0: + abort 0; +} diff --git a/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script_invalid.exp b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script_invalid.exp new file mode 100644 index 0000000000..6958d8d8d9 --- /dev/null +++ b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script_invalid.exp @@ -0,0 +1,73 @@ +processed 9 tasks + +task 1 'run'. lines 17-22: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} + +task 2 'run'. lines 24-29: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} + +task 3 'run'. lines 31-36: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} + +task 4 'run'. lines 38-43: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} + +task 5 'run'. lines 45-50: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} + +task 6 'run'. lines 52-57: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} + +task 7 'run'. lines 59-64: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} + +task 8 'run'. lines 66-71: +Error: Script execution failed with VMError: { + major_status: CONSTRAINT_NOT_SATISFIED, + sub_status: None, + location: script, + indices: [], + offsets: [], +} diff --git a/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script_invalid.mvir b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script_invalid.mvir new file mode 100644 index 0000000000..f417e26c19 --- /dev/null +++ b/language/move-bytecode-verifier/transactional-tests/tests/signature/check_constraints_script_invalid.mvir @@ -0,0 +1,71 @@ +//# publish +module 0x1.M { + struct BoxCopy has copy { f: T } + struct BoxDrop has drop { f: T } + struct BoxStore has store { f: T } + struct BoxKey has key { f: T } + + struct NeedsCopy { dummy: bool } + struct NeedsDrop { dummy: bool } + struct NeedsStore { dummy: bool } + struct NeedsKey { dummy: bool } +} + +// tests various invalid constraints in script arguments +// all tests should give a constraint violation, even without type arguments passed in to 'run' + +//# run +import 0x1.M; +main(x: M.NeedsCopy) { + label l0: + abort 0; +} + +//# run +import 0x1.M; +main(x: M.NeedsDrop) { + label l0: + abort 0; +} + +//# run +import 0x1.M; +main(x: M.NeedsStore) { + label l0: + abort 0; +} + +//# run +import 0x1.M; +main(x: M.NeedsKey) { + label l0: + abort 0; +} + +//# run +import 0x1.M; +main(x: M.NeedsCopy>) { + label l0: + abort 0; +} + +//# run +import 0x1.M; +main(x: M.NeedsDrop>) { + label l0: + abort 0; +} + +//# run +import 0x1.M; +main(x: M.NeedsStore>) { + label l0: + abort 0; +} + +//# run +import 0x1.M; +main(x: M.NeedsKey>) { + label l0: + abort 0; +} diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/function.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/function.exp index a0b818c277..a64e96bbc9 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/function.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/function.exp @@ -1,7 +1,7 @@ processed 2 tasks task 0 'print-bytecode'. lines 1-31: -// Move bytecode v4 +// Move bytecode v5 module 3d10.Example { struct Coin { value: u64 @@ -48,7 +48,7 @@ B0: } task 1 'print-bytecode'. lines 33-46: -// Move bytecode v4 +// Move bytecode v5 module 4d10.M { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/let.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/let.exp index f526612ca3..0d4fb74930 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/let.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/declarations/let.exp @@ -1,7 +1,7 @@ processed 2 tasks task 0 'print-bytecode'. lines 1-7: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/binary_add.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/binary_add.exp index 1434d93113..9d76d85f19 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/binary_add.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/binary_add.exp @@ -1,7 +1,7 @@ processed 1 task task 0 'print-bytecode'. lines 1-13: -// Move bytecode v4 +// Move bytecode v5 module e.Expressions { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow.exp index cbc105f4e3..bea80a2672 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow.exp @@ -1,7 +1,7 @@ processed 9 tasks task 0 'print-bytecode'. lines 1-11: -// Move bytecode v4 +// Move bytecode v5 script { @@ -20,7 +20,7 @@ B0: } task 1 'print-bytecode'. lines 13-24: -// Move bytecode v4 +// Move bytecode v5 script { @@ -40,7 +40,7 @@ B0: } task 2 'print-bytecode'. lines 26-59: -// Move bytecode v4 +// Move bytecode v5 module 1d4.M { struct T { u: u64 @@ -76,7 +76,7 @@ B0: } task 3 'print-bytecode'. lines 61-80: -// Move bytecode v4 +// Move bytecode v5 module 2d4.M { struct T { u: Ty0 diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow_mut.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow_mut.exp index f3d141c27f..0a86af489a 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow_mut.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/borrow_mut.exp @@ -1,7 +1,7 @@ processed 3 tasks task 0 'print-bytecode'. lines 1-10: -// Move bytecode v4 +// Move bytecode v5 script { @@ -21,7 +21,7 @@ B0: } task 1 'print-bytecode'. lines 12-23: -// Move bytecode v4 +// Move bytecode v5 module 3d.Foobar { struct FooCoin { value: u64 @@ -39,7 +39,7 @@ B0: } task 2 'print-bytecode'. lines 25-36: -// Move bytecode v4 +// Move bytecode v5 module 4d.Foobar { struct FooCoin { value: u64 diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global.exp index 5d0b092055..06ae99607a 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global.exp @@ -1,7 +1,7 @@ processed 4 tasks task 0 'print-bytecode'. lines 1-14: -// Move bytecode v4 +// Move bytecode v5 module 1d6.M { struct T has key { b: bool diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global_mut.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global_mut.exp index 07834b3115..87366f6587 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global_mut.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/borrow_global_mut.exp @@ -1,7 +1,7 @@ processed 4 tasks task 0 'print-bytecode'. lines 1-14: -// Move bytecode v4 +// Move bytecode v5 module 1d6.M { struct T has key { b: bool diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/exists.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/exists.exp index f172129168..cc653ac720 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/exists.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/exists.exp @@ -1,7 +1,7 @@ processed 4 tasks task 0 'print-bytecode'. lines 1-11: -// Move bytecode v4 +// Move bytecode v5 module 5d5.M { struct T has key { b: bool diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/move_to.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/move_to.exp index dec2077551..68c9fd61d5 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/move_to.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/move_to.exp @@ -1,7 +1,7 @@ processed 4 tasks task 0 'print-bytecode'. lines 1-16: -// Move bytecode v4 +// Move bytecode v5 module 2d6.M { struct T has key { b: bool diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/vector.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/vector.exp index e4927494aa..6ee59d5353 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/vector.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/builtins/vector.exp @@ -1,7 +1,7 @@ processed 1 task task 0 'print-bytecode'. lines 1-31: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/combined.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/combined.exp index 7bd906f764..de271bd332 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/combined.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/combined.exp @@ -1,7 +1,7 @@ processed 1 task task 0 'print-bytecode'. lines 1-11: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/pack.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/pack.exp index a6808bc01d..c9397e2f13 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/pack.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/pack.exp @@ -1,7 +1,7 @@ processed 3 tasks task 0 'print-bytecode'. lines 1-9: -// Move bytecode v4 +// Move bytecode v5 module 2d20.M { struct T { u: u64 diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/unpack.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/unpack.exp index 0913741606..f00261b08b 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/unpack.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/expressions/unpack.exp @@ -1,7 +1,7 @@ processed 3 tasks task 0 'print-bytecode'. lines 1-18: -// Move bytecode v4 +// Move bytecode v5 module 1d12.M { struct T { b: bool diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/assert.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/assert.exp index 0333f08519..4920decd25 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/assert.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/assert.exp @@ -1,7 +1,7 @@ processed 1 task task 0 'print-bytecode'. lines 1-8: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump.exp index 35a1906279..aee3650753 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump.exp @@ -1,7 +1,7 @@ processed 3 tasks task 0 'print-bytecode'. lines 1-6: -// Move bytecode v4 +// Move bytecode v5 script { @@ -12,7 +12,7 @@ B0: } task 1 'print-bytecode'. lines 8-15: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if.exp index cb579fbf15..b9596ab6ba 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if.exp @@ -1,7 +1,7 @@ processed 4 tasks task 0 'print-bytecode'. lines 1-22: -// Move bytecode v4 +// Move bytecode v5 script { @@ -28,7 +28,7 @@ B4: } task 1 'print-bytecode'. lines 24-41: -// Move bytecode v4 +// Move bytecode v5 script { @@ -52,7 +52,7 @@ B3: } task 2 'print-bytecode'. lines 43-59: -// Move bytecode v4 +// Move bytecode v5 script { @@ -85,7 +85,7 @@ B5: } task 3 'print-bytecode'. lines 61-74: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if_false.exp b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if_false.exp index 9a7e570bb8..92b3873907 100644 --- a/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if_false.exp +++ b/language/move-ir-compiler/transactional-tests/tests/bytecode-generation/statements/jump_if_false.exp @@ -1,7 +1,7 @@ processed 4 tasks task 0 'print-bytecode'. lines 1-18: -// Move bytecode v4 +// Move bytecode v5 script { @@ -27,7 +27,7 @@ B3: } task 1 'print-bytecode'. lines 20-32: -// Move bytecode v4 +// Move bytecode v5 script { @@ -49,7 +49,7 @@ B4: } task 2 'print-bytecode'. lines 34-42: -// Move bytecode v4 +// Move bytecode v5 script { @@ -65,7 +65,7 @@ B2: } task 3 'print-bytecode'. lines 44-55: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-ir-compiler/transactional-tests/tests/parsing/comments.exp b/language/move-ir-compiler/transactional-tests/tests/parsing/comments.exp index 515abcbdad..9adc3af979 100644 --- a/language/move-ir-compiler/transactional-tests/tests/parsing/comments.exp +++ b/language/move-ir-compiler/transactional-tests/tests/parsing/comments.exp @@ -1,7 +1,7 @@ processed 6 tasks task 0 'print-bytecode'. lines 1-6: -// Move bytecode v4 +// Move bytecode v5 script { @@ -12,7 +12,7 @@ B0: } task 1 'print-bytecode'. lines 8-14: -// Move bytecode v4 +// Move bytecode v5 script { @@ -23,7 +23,7 @@ B0: } task 2 'print-bytecode'. lines 16-20: -// Move bytecode v4 +// Move bytecode v5 script { @@ -34,7 +34,7 @@ B0: } task 3 'print-bytecode'. lines 22-27: -// Move bytecode v4 +// Move bytecode v5 script { @@ -48,7 +48,7 @@ task 4 'print-bytecode'. lines 29-36: Error: ParserError: Invalid Token: invalid token kind for statement Slash task 5 'print-bytecode'. lines 38-46: -// Move bytecode v4 +// Move bytecode v5 script { diff --git a/language/move-prover/move-abigen/src/abigen.rs b/language/move-prover/move-abigen/src/abigen.rs index f2b47278bc..025097290a 100644 --- a/language/move-prover/move-abigen/src/abigen.rs +++ b/language/move-prover/move-abigen/src/abigen.rs @@ -129,15 +129,35 @@ impl<'env> Abigen<'env> { let func_name = module_env.symbol_pool().string(func.get_name()); let func_ident = IdentStr::new(&func_name).unwrap(); // only pick up script functions that also have a script-callable signature. + // and check all arguments have a valid type tag func.visibility() == FunctionVisibility::Script - && script_signature::verify_module_script_function(module, func_ident) - .is_ok() + && script_signature::verify_module_function_signature_by_name( + module, + func_ident, + script_signature::no_additional_script_signature_checks, + ) + .is_ok() + && func + .get_parameters() + .iter() + .skip_while(|param| match ¶m.1 { + ty::Type::Primitive(ty::PrimitiveType::Signer) => true, + ty::Type::Reference(_, inner) => matches!( + &**inner, + ty::Type::Primitive(ty::PrimitiveType::Signer) + ), + _ => false, + }) + .all(|param| { + matches!(self.get_type_tag(¶m.1), Err(_) | Ok(Some(_))) + }) + && func.get_return_count() == 0 }) .collect() }; let mut abis = Vec::new(); - for func in script_iter.iter() { + for func in &script_iter { abis.push(self.generate_abi_for_function(func, module_env)?); } @@ -164,7 +184,7 @@ impl<'env> Abigen<'env> { .iter() .filter(|param| !matches!(¶m.1, ty::Type::Primitive(ty::PrimitiveType::Signer))) .map(|param| { - let tag = self.get_type_tag(¶m.1)?; + let tag = self.get_type_tag(¶m.1)?.unwrap(); Ok(ArgumentABI::new( symbol_pool.string(param.0).to_string(), tag, @@ -220,7 +240,7 @@ impl<'env> Abigen<'env> { } } - fn get_type_tag(&self, ty0: &ty::Type) -> anyhow::Result { + fn get_type_tag(&self, ty0: &ty::Type) -> anyhow::Result> { use ty::Type::*; let tag = match ty0 { Primitive(prim) => { @@ -238,7 +258,10 @@ impl<'env> Abigen<'env> { } } Vector(ty) => { - let tag = self.get_type_tag(ty)?; + let tag = match self.get_type_tag(ty)? { + Some(tag) => tag, + None => return Ok(None), + }; TypeTag::Vector(Box::new(tag)) } Tuple(_) @@ -249,8 +272,8 @@ impl<'env> Abigen<'env> { | ResourceDomain(..) | Error | Var(_) - | Reference(_, _) => bail!("Type {:?} is not allowed in scripts.", ty0), + | Reference(_, _) => return Ok(None), }; - Ok(tag) + Ok(Some(tag)) } } diff --git a/language/move-vm/integration-tests/src/tests/bad_entry_point_tests.rs b/language/move-vm/integration-tests/src/tests/bad_entry_point_tests.rs index 001ba714a7..680f3a01db 100644 --- a/language/move-vm/integration-tests/src/tests/bad_entry_point_tests.rs +++ b/language/move-vm/integration-tests/src/tests/bad_entry_point_tests.rs @@ -26,7 +26,7 @@ fn call_non_existent_module() { let mut gas_status = GasStatus::new_unmetered(); let err = sess - .execute_function( + .execute_function_bypass_visibility( &module_id, &fun_name, vec![], @@ -61,7 +61,7 @@ fn call_non_existent_function() { let mut gas_status = GasStatus::new_unmetered(); let err = sess - .execute_function( + .execute_function_bypass_visibility( &module_id, &fun_name, vec![], diff --git a/language/move-vm/integration-tests/src/tests/bad_storage_tests.rs b/language/move-vm/integration-tests/src/tests/bad_storage_tests.rs index 4f0072f5fe..570bc9111a 100644 --- a/language/move-vm/integration-tests/src/tests/bad_storage_tests.rs +++ b/language/move-vm/integration-tests/src/tests/bad_storage_tests.rs @@ -97,10 +97,10 @@ fn test_malformed_resource() { sess.execute_script( script_blob, vec![], - vec![], - vec![TEST_ADDR], + vec![MoveValue::Signer(TEST_ADDR).simple_serialize().unwrap()], &mut gas_status, ) + .map(|_| ()) .unwrap(); let (changeset, _) = sess.finish().unwrap(); storage.apply(changeset).unwrap(); @@ -115,10 +115,10 @@ fn test_malformed_resource() { sess.execute_script( script_blob.clone(), vec![], - vec![], - vec![TEST_ADDR], + vec![MoveValue::Signer(TEST_ADDR).simple_serialize().unwrap()], &mut gas_status, ) + .map(|_| ()) .unwrap(); } @@ -142,10 +142,10 @@ fn test_malformed_resource() { .execute_script( script_blob, vec![], - vec![], - vec![TEST_ADDR], + vec![MoveValue::Signer(TEST_ADDR).simple_serialize().unwrap()], &mut gas_status, ) + .map(|_| ()) .unwrap_err(); assert_eq!(err.status_type(), StatusType::InvariantViolation); } @@ -179,8 +179,14 @@ fn test_malformed_module() { storage.publish_or_overwrite_module(m.self_id(), blob.clone()); let vm = MoveVM::new(vec![]).unwrap(); let mut sess = vm.new_session(&storage); - sess.execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) - .unwrap(); + sess.execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) + .unwrap(); } // Start over with a fresh storage and publish a corrupted version of M. @@ -199,7 +205,13 @@ fn test_malformed_module() { let vm = MoveVM::new(vec![]).unwrap(); let mut sess = vm.new_session(&storage); let err = sess - .execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_err(); assert_eq!(err.status_type(), StatusType::InvariantViolation); } @@ -233,8 +245,14 @@ fn test_unverifiable_module() { let vm = MoveVM::new(vec![]).unwrap(); let mut sess = vm.new_session(&storage); - sess.execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) - .unwrap(); + sess.execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) + .unwrap(); } // Erase the body of M::foo to make it fail verification. @@ -252,7 +270,13 @@ fn test_unverifiable_module() { let mut sess = vm.new_session(&storage); let err = sess - .execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_err(); assert_eq!(err.status_type(), StatusType::InvariantViolation); @@ -298,8 +322,14 @@ fn test_missing_module_dependency() { let vm = MoveVM::new(vec![]).unwrap(); let mut sess = vm.new_session(&storage); - sess.execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) - .unwrap(); + sess.execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) + .unwrap(); } // Publish only N and try to call N::bar. The VM should fail to find M and raise @@ -312,7 +342,13 @@ fn test_missing_module_dependency() { let mut sess = vm.new_session(&storage); let err = sess - .execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_err(); assert_eq!(err.status_type(), StatusType::InvariantViolation); @@ -358,8 +394,14 @@ fn test_malformed_module_denpency() { let vm = MoveVM::new(vec![]).unwrap(); let mut sess = vm.new_session(&storage); - sess.execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) - .unwrap(); + sess.execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) + .unwrap(); } // Publish N and a corrupted version of M and try to call N::bar, the VM should fail to load M. @@ -378,7 +420,13 @@ fn test_malformed_module_denpency() { let mut sess = vm.new_session(&storage); let err = sess - .execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_err(); assert_eq!(err.status_type(), StatusType::InvariantViolation); @@ -425,8 +473,14 @@ fn test_unverifiable_module_dependency() { let vm = MoveVM::new(vec![]).unwrap(); let mut sess = vm.new_session(&storage); - sess.execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) - .unwrap(); + sess.execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) + .unwrap(); } // Publish N and an unverifiable version of M and try to call N::bar, the VM should fail to load M. @@ -445,7 +499,13 @@ fn test_unverifiable_module_dependency() { let mut sess = vm.new_session(&storage); let err = sess - .execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_err(); assert_eq!(err.status_type(), StatusType::InvariantViolation); @@ -500,7 +560,13 @@ fn test_storage_returns_bogus_error_when_loading_module() { let mut sess = vm.new_session(&storage); let err = sess - .execute_function(&module_id, &fun_name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + &fun_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_err(); assert_eq!(err.status_type(), StatusType::InvariantViolation); @@ -563,11 +629,17 @@ fn test_storage_returns_bogus_error_when_loading_resource() { .unwrap(); let mut sess = vm.new_session(&storage); - sess.execute_function(&m_id, &foo_name, vec![], vec![], &mut gas_status) - .unwrap(); + sess.execute_function_bypass_visibility( + &m_id, + &foo_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) + .unwrap(); let err = sess - .execute_function( + .execute_function_bypass_visibility( &m_id, &bar_name, vec![], diff --git a/language/move-vm/integration-tests/src/tests/exec_func_effects_tests.rs b/language/move-vm/integration-tests/src/tests/exec_func_effects_tests.rs index 986404f33b..ee9f477df2 100644 --- a/language/move-vm/integration-tests/src/tests/exec_func_effects_tests.rs +++ b/language/move-vm/integration-tests/src/tests/exec_func_effects_tests.rs @@ -3,19 +3,23 @@ use std::convert::TryInto; +use crate::compiler::{as_module, compile_units}; +use move_binary_format::errors::VMResult; use move_core_types::{ account_address::AccountAddress, + effects::{ChangeSet, Event}, + gas_schedule::GasAlgebra, identifier::Identifier, language_storage::ModuleId, value::{serialize_values, MoveValue}, vm_status::StatusCode, }; -use move_vm_runtime::{move_vm::MoveVM, session::ExecutionResult}; +use move_vm_runtime::{ + move_vm::MoveVM, native_functions::NativeContextExtensions, session::SerializedReturnValues, +}; use move_vm_test_utils::InMemoryStorage; use move_vm_types::gas_schedule::GasStatus; -use crate::compiler::{as_module, compile_units}; - const TEST_ADDR: AccountAddress = AccountAddress::new([42; AccountAddress::LENGTH]); const TEST_MODULE_ID: &str = "M"; const EXPECT_MUTREF_OUT_VALUE: u64 = 90; @@ -28,45 +32,34 @@ const FUN_NAMES: [&str; 2] = [USE_MUTREF_LABEL, USE_REF_LABEL]; fn fail_arg_deserialize() { let mod_code = setup_module(); // all of these should fail to deserialize because the functions expect u64 args - vec![ + let values = vec![ MoveValue::U8(16), MoveValue::U128(512), MoveValue::Bool(true), - ] - .iter() - .for_each(|mv| { - FUN_NAMES - .iter() - .for_each(|name| match run(&mod_code, name, mv.clone()) { - ExecutionResult::Success { .. } => { - panic!("Should have failed to deserialize non-u64 type to u64"); - } - ExecutionResult::Fail { error, .. } => { - assert_eq!( - error.major_status(), - StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT - ); - } - }); - }); + ]; + for value in values { + for name in FUN_NAMES { + let err = run(&mod_code, name, value.clone()) + .1 + .map(|_| ()) + .expect_err("Should have failed to deserialize non-u64 type to u64"); + assert_eq!( + err.major_status(), + StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT + ); + } + } } // check happy path for writing to mut ref args - may be unecessary / covered by other tests #[test] fn mutref_output_success() { let mod_code = setup_module(); - match run(&mod_code, USE_MUTREF_LABEL, MoveValue::U64(1)) { - ExecutionResult::Success { - mutable_ref_values, .. - } => { - assert_eq!(1, mutable_ref_values.len()); - let parsed = parse_u64_arg(mutable_ref_values.first().unwrap()); - assert_eq!(EXPECT_MUTREF_OUT_VALUE, parsed); - } - ExecutionResult::Fail { error, .. } => { - panic!("{:?}", error); - } - } + let (_gas_used, result) = run(&mod_code, USE_MUTREF_LABEL, MoveValue::U64(1)); + let (_, _, _, ret_values) = result.unwrap(); + assert_eq!(1, ret_values.mutable_reference_outputs.len()); + let parsed = parse_u64_arg(&ret_values.mutable_reference_outputs.first().unwrap().1); + assert_eq!(EXPECT_MUTREF_OUT_VALUE, parsed); } // TODO - how can we cause serialization errors in values returned from Move ? @@ -88,22 +81,41 @@ fn setup_module() -> ModuleCode { (module_id, code) } -fn run(module: &ModuleCode, fun_name: &str, arg_val0: MoveValue) -> ExecutionResult { +fn run( + module: &ModuleCode, + fun_name: &str, + arg_val0: MoveValue, +) -> ( + /* gas used */ u64, + VMResult<( + ChangeSet, + Vec, + NativeContextExtensions, + SerializedReturnValues, + )>, +) { let module_id = &module.0; let modules = vec![module.clone()]; let (vm, storage) = setup_vm(&modules); - let sess = vm.new_session(&storage); + let mut session = vm.new_session(&storage); let fun_name = Identifier::new(fun_name).unwrap(); let mut gas_status = GasStatus::new_unmetered(); - - sess.execute_function_for_effects( - module_id, - &fun_name, - vec![], - serialize_values(&vec![arg_val0]), - &mut gas_status, - ) + let gas_start = gas_status.remaining_gas().get(); + let res = session + .execute_function_bypass_visibility( + module_id, + &fun_name, + vec![], + serialize_values(&vec![arg_val0]), + &mut gas_status, + ) + .and_then(|ret_values| { + let (change_set, events, extensions) = session.finish_with_extensions()?; + Ok((change_set, events, extensions, ret_values)) + }); + let gas_used = gas_start - gas_status.remaining_gas().get(); + (gas_used, res) } type ModuleCode = (ModuleId, String); diff --git a/language/move-vm/integration-tests/src/tests/function_arg_tests.rs b/language/move-vm/integration-tests/src/tests/function_arg_tests.rs index c77ac70224..18bcc92586 100644 --- a/language/move-vm/integration-tests/src/tests/function_arg_tests.rs +++ b/language/move-vm/integration-tests/src/tests/function_arg_tests.rs @@ -66,7 +66,7 @@ fn run( .map(|val| val.simple_serialize().unwrap()) .collect(); - sess.execute_function(&module_id, &fun_name, ty_args, args, &mut gas_status)?; + sess.execute_function_bypass_visibility(&module_id, &fun_name, ty_args, args, &mut gas_status)?; Ok(()) } @@ -181,12 +181,8 @@ fn expected_u64_got_bool() { } #[test] -fn invalid_param_type_u64_ref() { - expect_err( - &["&u64"], - vec![MoveValue::U64(0)], - StatusCode::INVALID_PARAM_TYPE_FOR_DESERIALIZATION, - ) +fn param_type_u64_ref() { + expect_ok(&["&u64"], vec![MoveValue::U64(0)]) } #[test] @@ -249,13 +245,7 @@ fn expected_T__T_got_bool__u64() { #[test] #[allow(non_snake_case)] fn expected_T__T_ref_got_u64__u64() { - expect_err_generic( - &["T"], - &["&T"], - vec![TypeTag::U64], - vec![MoveValue::U64(0)], - StatusCode::INVALID_PARAM_TYPE_FOR_DESERIALIZATION, - ) + expect_ok_generic(&["T"], &["&T"], vec![TypeTag::U64], vec![MoveValue::U64(0)]) } #[test] diff --git a/language/move-vm/integration-tests/src/tests/loader_tests.rs b/language/move-vm/integration-tests/src/tests/loader_tests.rs index 912c8b8a53..82a857b167 100644 --- a/language/move-vm/integration-tests/src/tests/loader_tests.rs +++ b/language/move-vm/integration-tests/src/tests/loader_tests.rs @@ -87,7 +87,13 @@ impl Adapter { let mut gas_status = GasStatus::new_unmetered(); let mut session = vm.new_session(&data_store); session - .execute_function(&module_id, &name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + &module_id, + &name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_or_else(|_| { panic!("Failure executing {:?}::{:?}", module_id, name) }); @@ -103,7 +109,13 @@ impl Adapter { let mut gas_status = GasStatus::new_unmetered(); let mut session = self.vm.new_session(&self.store); session - .execute_function(module, name, vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + module, + name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .unwrap_or_else(|_| panic!("Failure executing {:?}::{:?}", module, name)); } } diff --git a/language/move-vm/integration-tests/src/tests/mutated_accounts_tests.rs b/language/move-vm/integration-tests/src/tests/mutated_accounts_tests.rs index fa2366c401..f6d46416af 100644 --- a/language/move-vm/integration-tests/src/tests/mutated_accounts_tests.rs +++ b/language/move-vm/integration-tests/src/tests/mutated_accounts_tests.rs @@ -53,7 +53,7 @@ fn mutated_accounts() { let account1 = AccountAddress::random(); - sess.execute_function( + sess.execute_function_bypass_visibility( &module_id, &publish, vec![], @@ -67,7 +67,7 @@ fn mutated_accounts() { // transaction epilogue). assert_eq!(sess.num_mutated_accounts(&TEST_ADDR), 2); - sess.execute_function( + sess.execute_function_bypass_visibility( &module_id, &get, vec![], @@ -78,7 +78,7 @@ fn mutated_accounts() { assert_eq!(sess.num_mutated_accounts(&TEST_ADDR), 2); - sess.execute_function( + sess.execute_function_bypass_visibility( &module_id, &flip, vec![], @@ -92,7 +92,7 @@ fn mutated_accounts() { storage.apply(changes).unwrap(); let mut sess = vm.new_session(&storage); - sess.execute_function( + sess.execute_function_bypass_visibility( &module_id, &get, vec![], diff --git a/language/move-vm/integration-tests/src/tests/return_value_tests.rs b/language/move-vm/integration-tests/src/tests/return_value_tests.rs index 986b5c190c..61e733c1a9 100644 --- a/language/move-vm/integration-tests/src/tests/return_value_tests.rs +++ b/language/move-vm/integration-tests/src/tests/return_value_tests.rs @@ -8,9 +8,8 @@ use move_core_types::{ identifier::Identifier, language_storage::{ModuleId, TypeTag}, value::{MoveTypeLayout, MoveValue}, - vm_status::StatusCode, }; -use move_vm_runtime::move_vm::MoveVM; +use move_vm_runtime::{move_vm::MoveVM, session::SerializedReturnValues}; use move_vm_test_utils::InMemoryStorage; use move_vm_types::gas_schedule::GasStatus; @@ -58,10 +57,21 @@ fn run( .map(|val| val.simple_serialize().unwrap()) .collect(); - let return_vals = - sess.execute_function(&module_id, &fun_name, ty_args, args, &mut gas_status)?; - - Ok(return_vals) + let SerializedReturnValues { + return_values, + mutable_reference_outputs: _, + } = sess.execute_function_bypass_visibility( + &module_id, + &fun_name, + ty_args, + args, + &mut gas_status, + )?; + + Ok(return_values + .into_iter() + .map(|(bytes, _layout)| bytes) + .collect()) } fn expect_success( @@ -80,22 +90,6 @@ fn expect_success( } } -fn expect_failure( - structs: &[&str], - fun_sig: &str, - fun_body: &str, - ty_args: Vec, - args: Vec, - expected_status: StatusCode, -) { - assert_eq!( - run(structs, fun_sig, fun_body, ty_args, args) - .unwrap_err() - .major_status(), - expected_status - ); -} - #[test] fn return_nothing() { expect_success(&[], "()", "", vec![], vec![], &[]) @@ -120,12 +114,12 @@ fn return_u64_bool() { #[test] fn return_signer_ref() { - expect_failure( + expect_success( &[], "(s: &signer): &signer", "s", vec![], vec![MoveValue::Signer(TEST_ADDR)], - StatusCode::INTERNAL_TYPE_ERROR, + &[MoveTypeLayout::Signer], ) } diff --git a/language/move-vm/runtime/src/loader.rs b/language/move-vm/runtime/src/loader.rs index c230d528c9..5164eaea21 100644 --- a/language/move-vm/runtime/src/loader.rs +++ b/language/move-vm/runtime/src/loader.rs @@ -18,7 +18,9 @@ use move_binary_format::{ }, IndexKind, }; -use move_bytecode_verifier::{self, cyclic_dependencies, dependencies, script_signature}; +use move_bytecode_verifier::{ + self, cyclic_dependencies, dependencies, script_signature, FnCheckScriptSignature, +}; use move_core_types::{ identifier::{IdentStr, Identifier}, language_storage::{ModuleId, StructTag, TypeTag}, @@ -88,18 +90,30 @@ impl ScriptCache { } } - fn get(&self, hash: &ScriptHash) -> Option<(Arc, Vec)> { - self.scripts - .get(hash) - .map(|script| (script.entry_point(), script.parameter_tys.clone())) + fn get(&self, hash: &ScriptHash) -> Option<(Arc, Vec, Vec)> { + self.scripts.get(hash).map(|script| { + ( + script.entry_point(), + script.parameter_tys.clone(), + script.return_tys.clone(), + ) + }) } - fn insert(&mut self, hash: ScriptHash, script: Script) -> (Arc, Vec) { + fn insert( + &mut self, + hash: ScriptHash, + script: Script, + ) -> (Arc, Vec, Vec) { match self.get(&hash) { Some(cached) => cached, None => { let script = self.scripts.insert(hash, script); - (script.entry_point(), script.parameter_tys.clone()) + ( + script.entry_point(), + script.parameter_tys.clone(), + script.return_tys.clone(), + ) } } } @@ -456,14 +470,14 @@ impl Loader { script_blob: &[u8], ty_args: &[TypeTag], data_store: &mut impl DataStore, - ) -> VMResult<(Arc, Vec, Vec)> { + ) -> VMResult<(Arc, Vec, Vec, Vec)> { // retrieve or load the script let mut sha3_256 = Sha3_256::new(); sha3_256.update(script_blob); let hash_value: [u8; 32] = sha3_256.finalize().into(); let mut scripts = self.scripts.write(); - let (main, parameter_tys) = match scripts.get(&hash_value) { + let (main, parameter_tys, return_tys) = match scripts.get(&hash_value) { Some(main) => main, None => { let ver_script = self.deserialize_and_verify_script(script_blob, data_store)?; @@ -480,7 +494,7 @@ impl Loader { self.verify_ty_args(main.type_parameters(), &type_arguments) .map_err(|e| e.finish(Location::Script))?; - Ok((main, type_arguments, parameter_tys)) + Ok((main, type_arguments, parameter_tys, return_tys)) } // The process of deserialization and verification is not and it must not be under lock. @@ -554,8 +568,8 @@ impl Loader { function_name: &IdentStr, module_id: &ModuleId, ty_args: &[TypeTag], - is_script_execution: bool, data_store: &impl DataStore, + additional_signature_checks: FnCheckScriptSignature, ) -> VMResult<(Arc, Vec, Vec, Vec)> { let module = self.load_module(module_id, data_store)?; let idx = self @@ -597,10 +611,11 @@ impl Loader { self.verify_ty_args(func.type_parameters(), &type_params) .map_err(|e| e.finish(Location::Module(module_id.clone())))?; - if is_script_execution { - let compiled_module = module.module(); - script_signature::verify_module_script_function(compiled_module, function_name)?; - } + script_signature::verify_module_function_signature_by_name( + module.module(), + function_name, + additional_signature_checks, + )?; Ok((func, type_params, parameter_tys, return_tys)) } @@ -1623,6 +1638,9 @@ struct Script { // parameters of main parameter_tys: Vec, + // return values + return_tys: Vec, + // a map of single-token signature indices to type single_signature_token_map: BTreeMap, } @@ -1691,7 +1709,6 @@ impl Script { .map(|tok| cache.make_type(BinaryIndexedView::Script(&script), tok)) .collect::>>() .map_err(|err| err.finish(Location::Undefined))?; - let return_ = Signature(vec![]); let locals = Signature( parameters .0 @@ -1700,6 +1717,13 @@ impl Script { .cloned() .collect(), ); + let return_ = Signature(vec![]); + let return_tys = return_ + .0 + .iter() + .map(|tok| cache.make_type(BinaryIndexedView::Script(&script), tok)) + .collect::>>() + .map_err(|err| err.finish(Location::Undefined))?; let type_parameters = script.type_parameters.clone(); // TODO: main does not have a name. Revisit. let name = Identifier::new("main").unwrap(); @@ -1762,6 +1786,7 @@ impl Script { function_instantiations, main, parameter_tys, + return_tys, single_signature_token_map, }) } @@ -1794,6 +1819,7 @@ enum Scope { // #[derive(Debug)] // https://github.com/rust-lang/rust/issues/70263 pub(crate) struct Function { + #[allow(unused)] file_format_version: u32, index: FunctionDefinitionIndex, code: Vec, @@ -1858,6 +1884,7 @@ impl Function { } } + #[allow(unused)] pub(crate) fn file_format_version(&self) -> u32 { self.file_format_version } diff --git a/language/move-vm/runtime/src/move_vm.rs b/language/move-vm/runtime/src/move_vm.rs index ed09d83ac4..b738d01abd 100644 --- a/language/move-vm/runtime/src/move_vm.rs +++ b/language/move-vm/runtime/src/move_vm.rs @@ -18,10 +18,9 @@ pub struct MoveVM { } impl MoveVM { - pub fn new(natives: I) -> VMResult - where - I: IntoIterator, - { + pub fn new( + natives: impl IntoIterator, + ) -> VMResult { Ok(Self { runtime: VMRuntime::new(natives).map_err(|err| err.finish(Location::Undefined))?, }) diff --git a/language/move-vm/runtime/src/runtime.rs b/language/move-vm/runtime/src/runtime.rs index a02f50ed1f..78431044f4 100644 --- a/language/move-vm/runtime/src/runtime.rs +++ b/language/move-vm/runtime/src/runtime.rs @@ -4,15 +4,15 @@ use crate::{ data_cache::TransactionDataCache, interpreter::Interpreter, - loader::Loader, + loader::{Function, Loader}, native_functions::{NativeContextExtensions, NativeFunction, NativeFunctions}, - session::Session, + session::{SerializedReturnValues, Session}, }; use move_binary_format::{ access::ModuleAccess, compatibility::Compatibility, errors::{verification_error, Location, PartialVMError, PartialVMResult, VMResult}, - file_format_common::VERSION_1, + file_format::{LocalIndex, Visibility}, normalized, CompiledModule, IndexKind, }; use move_core_types::{ @@ -20,16 +20,16 @@ use move_core_types::{ identifier::{IdentStr, Identifier}, language_storage::{ModuleId, TypeTag}, resolver::MoveResolver, - value::{MoveTypeLayout, MoveValue}, + value::MoveTypeLayout, vm_status::StatusCode, }; use move_vm_types::{ data_store::DataStore, gas_schedule::GasStatus, loaded_data::runtime_types::Type, - values::{Locals, Value}, + values::{Locals, Reference, VMValueCast, Value}, }; -use std::{borrow::Borrow, collections::BTreeSet}; +use std::{borrow::Borrow, collections::BTreeSet, sync::Arc}; use tracing::warn; /// An instantiation of the MoveVM. @@ -37,19 +37,10 @@ pub(crate) struct VMRuntime { loader: Loader, } -// signer helper closure -fn is_signer_reference(s: &Type) -> bool { - match s { - Type::Reference(ty) => matches!(&**ty, Type::Signer), - _ => false, - } -} - impl VMRuntime { - pub(crate) fn new(natives: I) -> PartialVMResult - where - I: IntoIterator, - { + pub(crate) fn new( + natives: impl IntoIterator, + ) -> PartialVMResult { Ok(VMRuntime { loader: Loader::new(NativeFunctions::new(natives)?), }) @@ -203,10 +194,7 @@ impl VMRuntime { Ok(()) } - fn deserialize_value(&self, ty: &Type, arg: V) -> PartialVMResult - where - V: Borrow<[u8]>, - { + fn deserialize_value(&self, ty: &Type, arg: impl Borrow<[u8]>) -> PartialVMResult { let layout = match self.loader.type_to_type_layout(ty) { Ok(layout) => layout, Err(_err) => { @@ -228,435 +216,232 @@ impl VMRuntime { } } - fn deserialize_arg(&self, ty: &Type, arg: V) -> PartialVMResult - where - V: Borrow<[u8]>, - { - if is_signer_reference(ty) { - // TODO signer_reference should be version gated - match MoveValue::simple_deserialize(arg.borrow(), &MoveTypeLayout::Signer) { - Ok(MoveValue::Signer(addr)) => Ok(Value::signer_reference(addr)), - Ok(_) | Err(_) => { - warn!("[VM] failed to deserialize argument"); - Err(PartialVMError::new( - StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT, - )) - } - } - } else { - self.deserialize_value(ty, arg) - } - } - fn deserialize_args( &self, - _file_format_version: u32, - tys: &[Type], - args: Vec>, - ) -> PartialVMResult> { - if tys.len() != args.len() { + arg_tys: Vec, + serialized_args: Vec>, + ) -> PartialVMResult<(Locals, Vec)> { + if arg_tys.len() != serialized_args.len() { return Err( PartialVMError::new(StatusCode::NUMBER_OF_ARGUMENTS_MISMATCH).with_message( format!( "argument length mismatch: expected {} got {}", - tys.len(), - args.len() + arg_tys.len(), + serialized_args.len() ), ), ); } - // Deserialize arguments. This operation will fail if the parameter type is not deserializable. - // - // Special rule: `&signer` can be created from data with the layout of `signer`. - let mut vals = vec![]; - for (ty, arg) in tys.iter().zip(args.into_iter()) { - vals.push(self.deserialize_arg(ty, arg)?) - } - - Ok(vals) + // Create a list of dummy locals. Each value stored will be used be borrowed and passed + // by reference to the invoked function + let mut dummy_locals = Locals::new(arg_tys.len()); + // Arguments for the invoked function. These can be owned values or references + let deserialized_args = arg_tys + .into_iter() + .zip(serialized_args) + .enumerate() + .map(|(idx, (arg_ty, arg_bytes))| match &arg_ty { + Type::MutableReference(inner_t) | Type::Reference(inner_t) => { + dummy_locals.store_loc(idx, self.deserialize_value(inner_t, arg_bytes)?)?; + dummy_locals.borrow_loc(idx) + } + _ => self.deserialize_value(&arg_ty, arg_bytes), + }) + .collect::>>()?; + Ok((dummy_locals, deserialized_args)) } - fn create_signers_and_arguments( + fn serialize_return_value( &self, - file_format_version: u32, - tys: &[Type], - senders: Vec, - args: Vec>, - ) -> PartialVMResult> { - fn number_of_signer_params(file_format_version: u32, tys: &[Type]) -> usize { - let is_signer = if file_format_version <= VERSION_1 { - |ty: &Type| matches!(ty, Type::Reference(inner) if matches!(&**inner, Type::Signer)) - } else { - |ty: &Type| matches!(ty, Type::Signer) - }; - for (i, ty) in tys.iter().enumerate() { - if !is_signer(ty) { - return i; - } - } - tys.len() - } - - // Build the arguments list and check the arguments are of restricted types. - // Signers are built up from left-to-right. Either all signer arguments are used, or no - // signer arguments can be be used by a script. - let n_signer_params = number_of_signer_params(file_format_version, tys); - - let args = if n_signer_params == 0 { - self.deserialize_args(file_format_version, tys, args)? - } else { - let n_signers = senders.len(); - if n_signer_params != n_signers { - return Err( - PartialVMError::new(StatusCode::NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH) - .with_message(format!( - "Expected {} signer args got {}", - n_signer_params, n_signers - )), - ); + ty: &Type, + value: Value, + ) -> PartialVMResult<(Vec, MoveTypeLayout)> { + let (ty, value) = match ty { + Type::Reference(inner) | Type::MutableReference(inner) => { + let ref_value: Reference = value.cast().map_err(|_err| { + PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR).with_message( + "non reference value given for a reference typed return value".to_string(), + ) + })?; + let inner_value = ref_value.read_ref()?; + (&**inner, inner_value) } - let make_signer = if file_format_version <= VERSION_1 { - Value::signer_reference - } else { - Value::signer - }; - let mut vals: Vec = senders.into_iter().map(make_signer).collect(); - vals.extend(self.deserialize_args(file_format_version, &tys[n_signers..], args)?); - vals + _ => (ty, value), }; - Ok(args) + let layout = self.loader.type_to_type_layout(ty).map_err(|_err| { + PartialVMError::new(StatusCode::VERIFICATION_ERROR).with_message( + "entry point functions cannot have non-serializable return types".to_string(), + ) + })?; + let bytes = value.simple_serialize(&layout).ok_or_else(|| { + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message("failed to serialize return values".to_string()) + })?; + Ok((bytes, layout)) } - // See Session::execute_script for what contracts to follow. - pub(crate) fn execute_script( + fn serialize_return_values( &self, - script: Vec, - ty_args: Vec, - args: Vec>, - senders: Vec, - data_store: &mut impl DataStore, - gas_status: &mut GasStatus, - extensions: &mut NativeContextExtensions, - ) -> VMResult<()> { - // load the script, perform verification - let (main, ty_args, params) = self.loader.load_script(&script, &ty_args, data_store)?; - - let signers_and_args = self - .create_signers_and_arguments(main.file_format_version(), ¶ms, senders, args) - .map_err(|err| err.finish(Location::Undefined))?; - // run the script - let return_vals = Interpreter::entrypoint( - main, - ty_args, - signers_and_args, - data_store, - gas_status, - extensions, - &self.loader, - )?; - - if !return_vals.is_empty() { + return_types: &[Type], + return_values: Vec, + ) -> PartialVMResult, MoveTypeLayout)>> { + if return_types.len() != return_values.len() { return Err( - PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message( - "scripts cannot have return values -- this should not happen".to_string(), - ) - .finish(Location::Undefined), + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message( + format!( + "declared {} return types, but got {} return values", + return_types.len(), + return_values.len() + ), + ), ); } - Ok(()) + return_types + .iter() + .zip(return_values) + .map(|(ty, value)| self.serialize_return_value(ty, value)) + .collect() } - pub(crate) fn execute_function_for_effects( + fn execute_function_impl( &self, - module: &ModuleId, - function_name: &IdentStr, - ty_args: Vec, - args: Vec, + func: Arc, + ty_args: Vec, + param_types: Vec, + return_types: Vec, + serialized_args: Vec>, data_store: &mut impl DataStore, gas_status: &mut GasStatus, extensions: &mut NativeContextExtensions, - ) -> VMResult<(Vec>, Vec>)> - where - V: Borrow<[u8]>, - { - let is_script_execution = false; - let (func, ty_args, params, return_tys) = self.loader.load_function( - function_name, - module, - &ty_args, - is_script_execution, - data_store, - )?; - - // actuals to be passed into the function. this can contain pure values, or references to dummy locals - let mut actuals = Vec::new(); - // create a list of dummy locals. each element of this list is a value passed by reference to `actuals` - let mut dummy_locals = Locals::new(params.len()); - // index and (inner) type of mutable ref inputs. we will use them to return the effects of `func` on these inputs - let mut mut_ref_inputs = Vec::new(); - for (idx, (arg, mut arg_type)) in args.into_iter().zip(params).enumerate() { - if let Type::TyParam(_) = arg_type { - arg_type = arg_type - .subst(&ty_args) - .map_err(|err| err.finish(Location::Undefined))?; - } - match arg_type { - Type::MutableReference(inner_t) => { - match self.borrow_arg(idx, arg, &inner_t, &mut dummy_locals) { - Err(err) => return Err(err.finish(Location::Undefined)), - Ok(val) => actuals.push(val), - } - mut_ref_inputs.push((idx, *inner_t)); - } - Type::Reference(inner_t) => { - match self.borrow_arg(idx, arg, &inner_t, &mut dummy_locals) { - Err(err) => return Err(err.finish(Location::Undefined)), - Ok(val) => actuals.push(val), - } - } - arg_type => { - match self.deserialize_value(&arg_type, arg) { - Ok(val) => actuals.push(val), - Err(err) => return Err(err.finish(Location::Undefined)), - }; - } - } - } + ) -> VMResult { + let arg_types = param_types + .into_iter() + .map(|ty| ty.subst(&ty_args)) + .collect::>>() + .map_err(|err| err.finish(Location::Undefined))?; + let mut_ref_args = arg_types + .iter() + .enumerate() + .filter_map(|(idx, ty)| match ty { + Type::MutableReference(inner) => Some((idx, inner.clone())), + _ => None, + }) + .collect::>(); + let (mut dummy_locals, deserialized_args) = self + .deserialize_args(arg_types, serialized_args) + .map_err(|e| e.finish(Location::Undefined))?; - let return_vals = Interpreter::entrypoint( + let return_values = Interpreter::entrypoint( func, ty_args, - actuals, + deserialized_args, data_store, gas_status, extensions, &self.loader, )?; - let return_layouts = return_tys - .iter() - .map(|ty| { - self.loader.type_to_type_layout(ty).map_err(|_err| { - PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) - .with_message( - "cannot be called with non-serializable return type".to_string(), - ) - .finish(Location::Undefined) - }) + let serialized_return_values = self + .serialize_return_values(&return_types, return_values) + .map_err(|e| e.finish(Location::Undefined))?; + let serialized_mut_ref_outputs = mut_ref_args + .into_iter() + .map(|(idx, ty)| { + // serialize return values first in the case that a value points into this local + let local_val = dummy_locals.move_loc(idx)?; + let (bytes, layout) = self.serialize_return_value(&ty, local_val)?; + Ok((idx as LocalIndex, bytes, layout)) }) - .collect::>>()?; - - if return_layouts.len() != return_vals.len() { - return Err( - PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message(format!( - "declared {} return types, but got {} return values", - return_layouts.len(), - return_vals.len() - )) - .finish(Location::Undefined), - ); - } + .collect::>() + .map_err(|e| e.finish(Location::Undefined))?; - let mut serialized_return_vals = vec![]; - for (val, layout) in return_vals.into_iter().zip(return_layouts.iter()) { - serialized_return_vals.push(val.simple_serialize(layout).ok_or_else(|| { - PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) - .with_message("failed to serialize return values".to_string()) - .finish(Location::Undefined) - })?) - } - - let mut serialized_mut_ref_outputs = Vec::new(); - for (idx, ty) in mut_ref_inputs { - let val = match dummy_locals.move_loc(idx) { - Ok(v) => v, - Err(e) => return Err(e.finish(Location::Undefined)), - }; - let layout = self.loader.type_to_type_layout(&ty).map_err(|_err| { - PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) - .with_message("cannot be called with non-serializable return type".to_string()) - .finish(Location::Undefined) - })?; - match val.simple_serialize(&layout) { - Some(bytes) => serialized_mut_ref_outputs.push(bytes), - None => { - return Err(PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) - .with_message("failed to serialize mutable ref values".to_string()) - .finish(Location::Undefined)) - } - }; - } - - Ok((serialized_return_vals, serialized_mut_ref_outputs)) - } + // locals should not be dropped until all return values are serialized + std::mem::drop(dummy_locals); - fn borrow_arg( - &self, - idx: usize, - arg: V, - arg_t: &Type, - locals: &mut Locals, - ) -> Result - where - V: Borrow<[u8]>, - { - let arg_value = match self.deserialize_value(arg_t, arg) { - Ok(val) => val, - Err(err) => return Err(err), - }; - if let Err(err) = locals.store_loc(idx, arg_value) { - return Err(err); - }; - let val = match locals.borrow_loc(idx) { - Ok(v) => v, - Err(err) => return Err(err), - }; - Ok(val) + Ok(SerializedReturnValues { + mutable_reference_outputs: serialized_mut_ref_outputs, + return_values: serialized_return_values, + }) } - fn execute_function_impl( + pub(crate) fn execute_function( &self, module: &ModuleId, function_name: &IdentStr, ty_args: Vec, - make_args: F, - is_script_execution: bool, + serialized_args: Vec>, data_store: &mut impl DataStore, gas_status: &mut GasStatus, extensions: &mut NativeContextExtensions, - ) -> VMResult>> - where - F: FnOnce(&VMRuntime, u32, &[Type]) -> PartialVMResult>, - { - let (func, ty_args, params, return_tys) = self.loader.load_function( + bypass_visibility: bool, + ) -> VMResult { + use move_binary_format::{binary_views::BinaryIndexedView, file_format::SignatureIndex}; + fn check_visibility( + _resolver: &BinaryIndexedView, + visibility: Visibility, + _parameters_idx: SignatureIndex, + _return_idx: Option, + ) -> PartialVMResult<()> { + match visibility { + Visibility::Script => Ok(()), + Visibility::Private | Visibility::Public | Visibility::Friend => { + Err(PartialVMError::new( + StatusCode::EXECUTE_SCRIPT_FUNCTION_CALLED_ON_NON_SCRIPT_VISIBLE, + )) + } + } + } + + let additional_signature_checks = if bypass_visibility { + move_bytecode_verifier::no_additional_script_signature_checks + } else { + check_visibility + }; + // load the function + let (func, ty_args, param_types, return_types) = self.loader.load_function( function_name, module, &ty_args, - is_script_execution, data_store, + additional_signature_checks, )?; - - let return_layouts = return_tys - .iter() - .map(|ty| { - self.loader.type_to_type_layout(ty).map_err(|_err| { - PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) - .with_message( - "cannot be called with non-serializable return type".to_string(), - ) - .finish(Location::Undefined) - }) - }) - .collect::>>()?; - - let params = params - .into_iter() - .map(|ty| ty.subst(&ty_args)) - .collect::>>() - .map_err(|err| err.finish(Location::Undefined))?; - - let args = make_args(self, func.file_format_version(), ¶ms) - .map_err(|err| err.finish(Location::Undefined))?; - - let return_vals = Interpreter::entrypoint( + // execute the function + self.execute_function_impl( func, ty_args, - args, + param_types, + return_types, + serialized_args, data_store, gas_status, extensions, - &self.loader, - )?; - - if return_layouts.len() != return_vals.len() { - return Err( - PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message(format!( - "declared {} return types, but got {} return values", - return_layouts.len(), - return_vals.len() - )) - .finish(Location::Undefined), - ); - } - - let mut serialized_vals = vec![]; - for (val, layout) in return_vals.into_iter().zip(return_layouts.iter()) { - serialized_vals.push(val.simple_serialize(layout).ok_or_else(|| { - PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR) - .with_message("failed to serialize return values".to_string()) - .finish(Location::Undefined) - })?) - } - - Ok(serialized_vals) - } - - // See Session::execute_script_function for what contracts to follow. - pub(crate) fn execute_script_function( - &self, - module: &ModuleId, - function_name: &IdentStr, - ty_args: Vec, - args: Vec>, - senders: Vec, - data_store: &mut impl DataStore, - gas_status: &mut GasStatus, - extensions: &mut NativeContextExtensions, - ) -> VMResult<()> { - let return_vals = self.execute_function_impl( - module, - function_name, - ty_args, - move |runtime, version, params| { - runtime.create_signers_and_arguments(version, params, senders, args) - }, - true, - data_store, - gas_status, - extensions, - )?; - - // A script function that serves as the entry point of execution cannot have return values, - // this is checked dynamically when the function is loaded. Hence, if the execution ever - // reaches here, it is an invariant violation - if !return_vals.is_empty() { - return Err( - PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) - .with_message( - "script functions that serve as execution entry points cannot have \ - return values -- this should not happen" - .to_string(), - ) - .finish(Location::Undefined), - ); - } - - Ok(()) + ) } - // See Session::execute_function for what contracts to follow. - pub(crate) fn execute_function( + // See Session::execute_script for what contracts to follow. + pub(crate) fn execute_script( &self, - module: &ModuleId, - function_name: &IdentStr, + script: impl Borrow<[u8]>, ty_args: Vec, - args: Vec>, + serialized_args: Vec>, data_store: &mut impl DataStore, gas_status: &mut GasStatus, extensions: &mut NativeContextExtensions, - ) -> VMResult>> { + ) -> VMResult { + // load the script, perform verification + let (func, ty_args, param_types, return_types) = + self.loader + .load_script(script.borrow(), &ty_args, data_store)?; + // execute the function self.execute_function_impl( - module, - function_name, + func, ty_args, - move |runtime, version, params| runtime.deserialize_args(version, params, args), - false, + param_types, + return_types, + serialized_args, data_store, gas_status, extensions, diff --git a/language/move-vm/runtime/src/session.rs b/language/move-vm/runtime/src/session.rs index bb4bcd4d99..ed6102e19f 100644 --- a/language/move-vm/runtime/src/session.rs +++ b/language/move-vm/runtime/src/session.rs @@ -1,16 +1,13 @@ // Copyright (c) The Diem Core Contributors // SPDX-License-Identifier: Apache-2.0 -use std::borrow::Borrow; - use crate::{ data_cache::TransactionDataCache, native_functions::NativeContextExtensions, runtime::VMRuntime, }; -use move_binary_format::errors::*; +use move_binary_format::{errors::*, file_format::LocalIndex}; use move_core_types::{ account_address::AccountAddress, effects::{ChangeSet, Event}, - gas_schedule::GasAlgebra, identifier::IdentStr, language_storage::{ModuleId, TypeTag}, resolver::MoveResolver, @@ -19,6 +16,7 @@ use move_core_types::{ use move_vm_types::{ data_store::DataStore, gas_schedule::GasStatus, loaded_data::runtime_types::Type, }; +use std::borrow::Borrow; pub struct Session<'r, 'l, S> { pub(crate) runtime: &'l VMRuntime, @@ -26,56 +24,52 @@ pub struct Session<'r, 'l, S> { pub(crate) native_extensions: NativeContextExtensions, } -/// Result of executing a function in the VM -pub enum ExecutionResult { - /// Execution completed successfully and changed global state - Success { - /// Changes to global state that occurred during execution - change_set: ChangeSet, - /// Events emitted during execution - events: Vec, - /// Values returned by the function - return_values: Vec>, - /// Final value of inputs passed in to the entrypoint via a mutable reference - mutable_ref_values: Vec>, - /// Gas used during execution - gas_used: u64, - /// Native extensions end state - native_extensions: NativeContextExtensions, - }, - /// Execution failed and had no side effects - Fail { - /// The reason execution failed - error: VMError, - /// Gas used during execution - gas_used: u64, - }, +/// Serialized return values from function/script execution +/// Simple struct is designed just to convey meaning behind serialized values +#[derive(Debug)] +pub struct SerializedReturnValues { + /// The value of any arguments that were mutably borrowed. + /// Non-mut borrowed values are not included + pub mutable_reference_outputs: Vec<(LocalIndex, Vec, MoveTypeLayout)>, + /// The return values from the function + pub return_values: Vec<(Vec, MoveTypeLayout)>, } impl<'r, 'l, S: MoveResolver> Session<'r, 'l, S> { /// Execute a Move function with the given arguments. This is mainly designed for an external /// environment to invoke system logic written in Move. /// + /// NOTE: There are NO checks on the `args` except that they can deserialize into the provided + /// types. + /// The ability to deserialize `args` into arbitrary types is *very* powerful, e.g. it can + /// used to manufacture `signer`'s or `Coin`'s from raw bytes. It is the responsibility of the + /// caller (e.g. adapter) to ensure that this power is used responsibly/securely for its + /// use-case. + /// /// The caller MUST ensure /// - All types and modules referred to by the type arguments exist. + /// - The signature is valid for the rules of the adapter /// /// The Move VM MUST return an invariant violation if the caller fails to follow any of the /// rules above. /// + /// The VM will check that the function has public(script) visibility. + /// /// Currently if any other error occurs during execution, the Move VM will simply propagate that /// error back to the outer environment without handling/translating it. This behavior may be /// revised in the future. /// /// In case an invariant violation occurs, the whole Session should be considered corrupted and /// one shall not proceed with effect generation. - pub fn execute_function( + pub fn execute_entry_function( &mut self, module: &ModuleId, function_name: &IdentStr, ty_args: Vec, - args: Vec>, + args: Vec>, gas_status: &mut GasStatus, - ) -> VMResult>> { + ) -> VMResult { + let bypass_visibility = false; self.runtime.execute_function( module, function_name, @@ -84,116 +78,29 @@ impl<'r, 'l, S: MoveResolver> Session<'r, 'l, S> { &mut self.data_cache, gas_status, &mut self.native_extensions, + bypass_visibility, ) } - /// Execute `module`::`fuction_name`<`ty_args`>(`args`) and return the effects in - /// an `ExecutionResult`, including - /// * the write set and events - /// * return values of the function - /// * changes to values passes by mutable reference to the function - /// Arguments to the function in `args` can be any type--ground types, user-defined struct - /// types, and references (including mutable references). - /// A reference argument in `args[i]` with type `&T` or `&mut T` will be deserialized as a `T`. - /// Pure arguments are deserialized in the obvious way. - /// - /// NOTE: The ability to deserialize `args` into arbitrary types is very powerful--e.g., it can - /// used to manufacture `signer`'s or `Coin`'s from raw bytes. It is the respmsibility of the - /// caller (e.g. adapter) to ensure that this power is useed responsibility/securely for its use-case. - pub fn execute_function_for_effects( - self, - module: &ModuleId, - function_name: &IdentStr, - ty_args: Vec, - args: Vec, - gas_status: &mut GasStatus, - ) -> ExecutionResult - where - V: Borrow<[u8]>, - { - // Deconstruct the session. - let Session { - runtime, - mut data_cache, - mut native_extensions, - } = self; - let gas_budget = gas_status.remaining_gas().get(); - - let execution_res = runtime.execute_function_for_effects( - module, - function_name, - ty_args, - args, - &mut data_cache, - gas_status, - &mut native_extensions, - ); - let gas_used = gas_budget - gas_status.remaining_gas().get(); - // Reconstruct the session for call to finish, but do not provide extensions as we - // need to put them into the result. - let session = Session { - runtime, - data_cache, - native_extensions: NativeContextExtensions::default(), - }; - match execution_res { - Ok((return_values, mutable_ref_values)) => match session.finish() { - Ok((change_set, events)) => ExecutionResult::Success { - change_set, - events, - return_values, - mutable_ref_values, - gas_used, - native_extensions, - }, - Err(error) => ExecutionResult::Fail { error, gas_used }, - }, - Err(error) => ExecutionResult::Fail { error, gas_used }, - } - } - - /// Execute a Move script function with the given arguments. - /// - /// Unlike `execute_function` which is designed for system logic, `execute_script_function` is - /// mainly designed to call a script function in an existing module. It similar to - /// `execute_script` except that execution of the "script" begins with the specified function - /// - /// The Move VM MUST return a user error (in other words, an error that's not an invariant - /// violation) if - /// - The function does not exist. - /// - The function does not have script visibility. - /// - The signature is not valid for a script. Not all script-visible module functions can - /// be invoked from this entry point. See `move_bytecode_verifier::script_signature` for the - /// rules. - /// - Type arguments refer to a non-existent type. - /// - Arguments (senders included) fail to deserialize or fail to match the signature of the - /// script function. - - /// - /// If any other error occurs during execution, the Move VM MUST propagate that error back to - /// the caller. - /// Besides, no user input should cause the Move VM to return an invariant violation. - /// - /// In case an invariant violation occurs, the whole Session should be considered corrupted and - /// one shall not proceed with effect generation. - pub fn execute_script_function( + /// Similar to execute_entry_function, but it bypasses visibility checks + pub fn execute_function_bypass_visibility( &mut self, module: &ModuleId, function_name: &IdentStr, ty_args: Vec, - args: Vec>, - senders: Vec, + args: Vec>, gas_status: &mut GasStatus, - ) -> VMResult<()> { - self.runtime.execute_script_function( + ) -> VMResult { + let bypass_visibility = true; + self.runtime.execute_function( module, function_name, ty_args, args, - senders, &mut self.data_cache, gas_status, &mut self.native_extensions, + bypass_visibility, ) } @@ -215,17 +122,15 @@ impl<'r, 'l, S: MoveResolver> Session<'r, 'l, S> { /// one shall not proceed with effect generation. pub fn execute_script( &mut self, - script: Vec, + script: impl Borrow<[u8]>, ty_args: Vec, - args: Vec>, - senders: Vec, + args: Vec>, gas_status: &mut GasStatus, - ) -> VMResult<()> { + ) -> VMResult { self.runtime.execute_script( script, ty_args, args, - senders, &mut self.data_cache, gas_status, &mut self.native_extensions, diff --git a/language/move-vm/runtime/src/unit_tests/vm_arguments_tests.rs b/language/move-vm/runtime/src/unit_tests/vm_arguments_tests.rs index 794c731914..5aae9bc934 100644 --- a/language/move-vm/runtime/src/unit_tests/vm_arguments_tests.rs +++ b/language/move-vm/runtime/src/unit_tests/vm_arguments_tests.rs @@ -254,9 +254,20 @@ impl ResourceResolver for RemoteStore { } } +fn combine_signers_and_args( + signers: Vec, + non_signer_args: Vec>, +) -> Vec> { + signers + .into_iter() + .map(|s| MoveValue::Signer(s).simple_serialize().unwrap()) + .chain(non_signer_args) + .collect() +} + fn call_script_with_args_ty_args_signers( script: Vec, - args: Vec>, + non_signer_args: Vec>, ty_args: Vec, signers: Vec, ) -> VMResult<()> { @@ -264,7 +275,14 @@ fn call_script_with_args_ty_args_signers( let remote_view = RemoteStore::new(); let mut session = move_vm.new_session(&remote_view); let mut gas_status = GasStatus::new_unmetered(); - session.execute_script(script, ty_args, args, signers, &mut gas_status) + session + .execute_script( + script, + ty_args, + combine_signers_and_args(signers, non_signer_args), + &mut gas_status, + ) + .map(|_| ()) } fn call_script(script: Vec, args: Vec>) -> VMResult<()> { @@ -274,7 +292,7 @@ fn call_script(script: Vec, args: Vec>) -> VMResult<()> { fn call_script_function_with_args_ty_args_signers( module: CompiledModule, function_name: Identifier, - args: Vec>, + non_signer_args: Vec>, ty_args: Vec, signers: Vec, ) -> VMResult<()> { @@ -284,12 +302,11 @@ fn call_script_function_with_args_ty_args_signers( remote_view.add_module(module); let mut session = move_vm.new_session(&remote_view); let mut gas_status = GasStatus::new_unmetered(); - session.execute_script_function( + session.execute_function_bypass_visibility( &id, function_name.as_ident_str(), ty_args, - args, - signers, + combine_signers_and_args(signers, non_signer_args), &mut gas_status, )?; Ok(()) @@ -303,7 +320,8 @@ fn call_script_function( call_script_function_with_args_ty_args_signers(module, function_name, args, vec![], vec![]) } -fn bad_signatures() -> Vec { +// these signatures used to be bad, but there are no bad signatures for scripts at the VM +fn deprecated_bad_signatures() -> Vec { vec![ // struct in signature Signature(vec![SignatureToken::Struct(StructHandleIndex(0))]), @@ -521,14 +539,14 @@ fn general_cases() -> Vec<( Signature(vec![SignatureToken::Signer, SignatureToken::Signer]), vec![], vec![], - Some(StatusCode::NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH), + Some(StatusCode::NUMBER_OF_ARGUMENTS_MISMATCH), ), // too few signers (1) ( Signature(vec![SignatureToken::Signer, SignatureToken::Signer]), vec![], vec![AccountAddress::random()], - Some(StatusCode::NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH), + Some(StatusCode::NUMBER_OF_ARGUMENTS_MISMATCH), ), // too few signers (3) ( @@ -539,7 +557,7 @@ fn general_cases() -> Vec<( AccountAddress::random(), AccountAddress::random(), ], - Some(StatusCode::NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH), + Some(StatusCode::NUMBER_OF_ARGUMENTS_MISMATCH), ), // correct number of signers (2) ( @@ -548,12 +566,12 @@ fn general_cases() -> Vec<( vec![AccountAddress::random(), AccountAddress::random()], None, ), - // too many signers (1) in a script that expects 0 is ok + // too many signers (1) in a script that expects 0 is no longer ok ( Signature(vec![SignatureToken::U8]), vec![MoveValue::U8(0)], vec![AccountAddress::random()], - None, + Some(StatusCode::NUMBER_OF_ARGUMENTS_MISMATCH), ), // signer ( @@ -577,15 +595,15 @@ fn check_script() { // // Bad signatures // - for signature in bad_signatures() { + for signature in deprecated_bad_signatures() { + let num_args = signature.0.len(); + let dummy_args = vec![MoveValue::Bool(false); num_args]; let script = make_script_with_non_linking_structs(signature); - assert_eq!( - call_script(script, serialize_values(&vec![MoveValue::U128(0)])) - .err() - .unwrap() - .major_status(), - StatusCode::INVALID_MAIN_FUNCTION_SIGNATURE, - ); + let status = call_script(script, serialize_values(&dummy_args)) + .err() + .unwrap() + .major_status(); + assert_eq!(status, StatusCode::LINKER_ERROR); } // @@ -637,19 +655,19 @@ fn check_script_function() { // // Bad signatures // - for signature in bad_signatures() { + for signature in deprecated_bad_signatures() { + let num_args = signature.0.len(); + let dummy_args = vec![MoveValue::Bool(false); num_args]; let (module, function_name) = make_script_function(signature); - let res = call_script_function( - module, - function_name, - serialize_values(&vec![MoveValue::U128(0)]), + let res = call_script_function(module, function_name, serialize_values(&dummy_args)) + .err() + .unwrap(); + // either the dummy arg matches so abort, or it fails to match + // but the important thing is that the signature was accepted + assert!( + res.major_status() == StatusCode::ABORTED + || res.major_status() == StatusCode::FAILED_TO_DESERIALIZE_ARGUMENT ) - .err() - .unwrap(); - assert_eq!( - res.major_status(), - StatusCode::INVALID_MAIN_FUNCTION_SIGNATURE, - ); } // @@ -703,6 +721,7 @@ fn check_script_function() { // // Non script visible + // DEPRECATED this check must now be done by the adapter // // public let (module, function_name) = make_module_with_function( @@ -722,7 +741,7 @@ fn check_script_function() { .err() .unwrap() .major_status(), - StatusCode::EXECUTE_SCRIPT_FUNCTION_CALLED_ON_NON_SCRIPT_VISIBLE, + StatusCode::ABORTED, ); // private let (module, function_name) = make_module_with_function( @@ -742,7 +761,7 @@ fn check_script_function() { .err() .unwrap() .major_status(), - StatusCode::EXECUTE_SCRIPT_FUNCTION_CALLED_ON_NON_SCRIPT_VISIBLE, + StatusCode::ABORTED, ); } @@ -757,7 +776,13 @@ fn call_missing_item() { let mut session = move_vm.new_session(&remote_view); let mut gas_status = GasStatus::new_unmetered(); let error = session - .execute_script_function(id, function_name, vec![], vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + id, + function_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .err() .unwrap(); assert_eq!(error.major_status(), StatusCode::LINKER_ERROR); @@ -767,7 +792,13 @@ fn call_missing_item() { remote_view.add_module(module); let mut session = move_vm.new_session(&remote_view); let error = session - .execute_script_function(id, function_name, vec![], vec![], vec![], &mut gas_status) + .execute_function_bypass_visibility( + id, + function_name, + vec![], + Vec::>::new(), + &mut gas_status, + ) .err() .unwrap(); assert_eq!( diff --git a/language/move-vm/transactional-tests/tests/builtins/gen_move_to.exp b/language/move-vm/transactional-tests/tests/builtins/gen_move_to.exp index fc4092d36a..8c37110a41 100644 --- a/language/move-vm/transactional-tests/tests/builtins/gen_move_to.exp +++ b/language/move-vm/transactional-tests/tests/builtins/gen_move_to.exp @@ -1,73 +1 @@ processed 9 tasks - -task 1 'run'. lines 158-170: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 2 'run'. lines 172-187: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 3 'run'. lines 189-220: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 4 'run'. lines 222-244: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 5 'run'. lines 246-264: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 6 'run'. lines 266-278: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 7 'run'. lines 280-303: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 8 'run'. lines 306-327: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} diff --git a/language/move-vm/transactional-tests/tests/builtins/gen_move_to.mvir b/language/move-vm/transactional-tests/tests/builtins/gen_move_to.mvir index 720284b7f3..178c88b63f 100644 --- a/language/move-vm/transactional-tests/tests/builtins/gen_move_to.mvir +++ b/language/move-vm/transactional-tests/tests/builtins/gen_move_to.mvir @@ -155,7 +155,7 @@ module 0x1.M { // // create, publish and exists -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { @@ -169,7 +169,7 @@ label b0: return; } -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { @@ -186,7 +186,7 @@ label b0: return; } -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { @@ -219,7 +219,7 @@ label b0: // // borrows and mutations -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { @@ -243,7 +243,7 @@ label b0: return; } -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { @@ -263,7 +263,7 @@ label b0: // // unpublish, destroy and exists -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { @@ -277,7 +277,7 @@ label b0: return; } -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { @@ -303,7 +303,7 @@ label b0: } -//# run +//# run --args 0x1 import 0x1.M; main(account: signer) { diff --git a/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.exp b/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.exp index 6e9578aacf..5d92c423f3 100644 --- a/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.exp +++ b/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.exp @@ -1,19 +1 @@ processed 2 tasks - -task 0 'run'. lines 1-6: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 1 'run'. lines 8-13: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} diff --git a/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.mvir b/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.mvir index 4c7a862399..c693ba81a9 100644 --- a/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.mvir +++ b/language/move-vm/transactional-tests/tests/entry_points/address_arg_is_not_signer.mvir @@ -3,11 +3,11 @@ main(s: signer) { label b0: return; } -// should give NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH +// DEPRECATED signers can now be passed in as addrs //# run --args 0x0 0x2 main(s: signer, s2: signer) { label b0: return; } -// should give NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH +// DEPRECATED signers can now be passed in as addrs diff --git a/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.exp b/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.exp index 6cd67db3f6..301fd72bf8 100644 --- a/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.exp +++ b/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.exp @@ -1 +1,10 @@ processed 1 task + +task 0 'run'. lines 1-5: +Error: Script execution failed with VMError: { + major_status: NUMBER_OF_ARGUMENTS_MISMATCH, + sub_status: None, + location: undefined, + indices: [], + offsets: [], +} diff --git a/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.move b/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.move index 4f0472a76f..687f5c3299 100644 --- a/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.move +++ b/language/move-vm/transactional-tests/tests/entry_points/expected_0_signer_args_got_1_ok.move @@ -1,5 +1,5 @@ //# run --signers 0x1 -// will succeed, currently okay to drop a signer? +// DEPRECATED now will fail. signer args same as other args script { fun main() {} } diff --git a/language/move-vm/transactional-tests/tests/entry_points/expected_2_signer_args_got_1.exp b/language/move-vm/transactional-tests/tests/entry_points/expected_2_signer_args_got_1.exp index dd30137124..9ac9e5ab02 100644 --- a/language/move-vm/transactional-tests/tests/entry_points/expected_2_signer_args_got_1.exp +++ b/language/move-vm/transactional-tests/tests/entry_points/expected_2_signer_args_got_1.exp @@ -2,7 +2,7 @@ processed 1 task task 0 'run'. lines 1-6: Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, + major_status: NUMBER_OF_ARGUMENTS_MISMATCH, sub_status: None, location: undefined, indices: [], diff --git a/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_function.exp b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_function.exp new file mode 100644 index 0000000000..457ace9c4a --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_function.exp @@ -0,0 +1 @@ +processed 4 tasks diff --git a/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_function.mvir b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_function.mvir new file mode 100644 index 0000000000..4c2d4b20e0 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_function.mvir @@ -0,0 +1,28 @@ +//# publish + +// tests various mixed usage of signer in function arguments + +module 0x42.M { + +public main1(s1: signer, s2: &signer, s3: signer) { + label l0: + return; +} + +public main2(s1: &signer, u: u64, s2: signer, f: bool, s3: &signer) { + label l0: + return; +} + +public main3(u: u64, f: bool, a: address, s1: signer, s2: &signer) { + label l0: + return; +} + +} + +//# run 0x42::M::main1 --args 0x1 0x1 0x1 + +//# run 0x42::M::main2 --args 0x1 0 0x1 false 0x1 + +//# run 0x42::M::main3 --args 0 false 0x1 0x2 0x3 diff --git a/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_scripts.exp b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_scripts.exp new file mode 100644 index 0000000000..fc5a4436b2 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_scripts.exp @@ -0,0 +1 @@ +processed 3 tasks diff --git a/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_scripts.mvir b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_scripts.mvir new file mode 100644 index 0000000000..68b03c1509 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/mixed_signer_inputs_scripts.mvir @@ -0,0 +1,21 @@ +//# run --args 0x1 0x1 0x1 + +// tests various mixed usage of signer in script arguments + +main(s1: signer, s2: &signer, s3: signer) { + label l0: + return; +} + + +//# run --args 0x1 0 0x1 false 0x1 +main(s1: &signer, u: u64, s2: signer, f: bool, s3: &signer) { + label l0: + return; +} + +//# run --args 0 false 0x1 0x2 0x3 +main(u: u64, f: bool, a: address, s1: signer, s2: &signer) { + label l0: + return; +} diff --git a/language/move-vm/transactional-tests/tests/entry_points/modify_mutable_ref_inputs.exp b/language/move-vm/transactional-tests/tests/entry_points/modify_mutable_ref_inputs.exp new file mode 100644 index 0000000000..edbdf5490e --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/modify_mutable_ref_inputs.exp @@ -0,0 +1,7 @@ +processed 3 tasks + +task 1 'run'. lines 28-40: +mutable inputs after call: local#0: 112, local#1: { 224 }, local#2: [120, 0, 122] + +task 2 'run'. lines 42-42: +mutable inputs after call: local#0: 112, local#1: { 224 }, local#2: [120, 0, 122] diff --git a/language/move-vm/transactional-tests/tests/entry_points/modify_mutable_ref_inputs.mvir b/language/move-vm/transactional-tests/tests/entry_points/modify_mutable_ref_inputs.mvir new file mode 100644 index 0000000000..09cbef0878 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/modify_mutable_ref_inputs.mvir @@ -0,0 +1,42 @@ +//# publish + +// the VM will provide the values for mutable inputs after the function returns +// modifications should be observable + +module 0x42.M { + struct S { f: u64 } + + public set_f(s: &mut Self.S, f: u64) { + label l0: + *(&mut move(s).S::f) = move(f); + return; + } + + public t( + u: &mut u64, + s: &mut Self.S, + v: &mut vector, + ) { + label l0: + *move(u) = 112; + *(&mut move(s).S::f) = 224; + *(vec_mut_borrow(move(v), 1)) = 0u8; + return; + } +} + +//# run --args 0 1 b"xyz" +import 0x42.M; +main( + u: &mut u64, + s: &mut M.S, + v: &mut vector, +) { + label l0: + *move(u) = 112; + M.set_f(move(s), 224); + *(vec_mut_borrow(move(v), 1)) = 0u8; + return; +} + +//# run 0x42::M::t --args 0 1 b"xyz" diff --git a/language/move-vm/transactional-tests/tests/entry_points/ref_inputs.exp b/language/move-vm/transactional-tests/tests/entry_points/ref_inputs.exp new file mode 100644 index 0000000000..1eb93334fc --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/ref_inputs.exp @@ -0,0 +1,19 @@ +processed 9 tasks + +task 2 'run'. lines 35-39: +mutable inputs after call: local#0: false + +task 3 'run'. lines 41-46: +mutable inputs after call: local#1: false, local#3: { 0 } + +task 4 'run'. lines 48-52: +mutable inputs after call: local#1: false, local#3: { 0 } + +task 6 'run'. lines 56-56: +mutable inputs after call: local#0: false + +task 7 'run'. lines 58-58: +mutable inputs after call: local#1: false, local#3: { 0 } + +task 8 'run'. lines 60-60: +mutable inputs after call: local#1: false, local#3: { 0 } diff --git a/language/move-vm/transactional-tests/tests/entry_points/ref_inputs.mvir b/language/move-vm/transactional-tests/tests/entry_points/ref_inputs.mvir new file mode 100644 index 0000000000..3d20a96e4d --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/ref_inputs.mvir @@ -0,0 +1,60 @@ +//# publish + +// ref arguments are now allowed + +module 0x42.M { + struct S has drop { f: u64 } + + public t1(r1: &u64) { + label l0: + return; + } + + public t2(r1: &mut bool) { + label l0: + return; + } + + public t3(r1: &u64, r2: &mut bool, r3: &Self.S, r4: &mut Self.S) { + label l0: + return; + } + + public tgen(r1: &T1, r2: &mut T2, r3: &T3, r4: &mut T4) { + label l0: + return; + } +} + +//# run --args 0 +main(r1: &u64) { + label l0: + return; +} + +//# run --args false +main(r1: &mut bool) { + label l0: + return; +} + +//# run --args 0 false 0 0 +import 0x42.M; +main(r1: &u64, r2: &mut bool, r3: &M.S, r4: &mut M.S) { + label l0: + return; +} + +//# run --type-args u64 bool 0x42::M::S 0x42::M::S --args 0 false 0 0 +main(r1: &T1, r2: &mut T2, r3: &T3, r4: &mut T4) { + label l0: + return; +} + +//# run 0x42::M::t1 --args 0 + +//# run 0x42::M::t2 --args false + +//# run 0x42::M::t3 --args 0 false 0 0 + +//# run 0x42::M::tgen --type-args u64 bool 0x42::M::S 0x42::M::S --args 0 false 0 0 diff --git a/language/move-vm/transactional-tests/tests/entry_points/return_values.exp b/language/move-vm/transactional-tests/tests/entry_points/return_values.exp new file mode 100644 index 0000000000..72c606ef6c --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/return_values.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'run'. lines 20-20: +mutable inputs after call: local#4: { 4 } +return values: 0, { 00000000000000000000000000000001 }, { 2 }, { 3 }, 3, 4 diff --git a/language/move-vm/transactional-tests/tests/entry_points/return_values.mvir b/language/move-vm/transactional-tests/tests/entry_points/return_values.mvir new file mode 100644 index 0000000000..39abd7b8dc --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/return_values.mvir @@ -0,0 +1,20 @@ +//# publish + +// entry points can now return values + +module 0x42.M { + struct S { f: u64 } + + public t( + x: u64, + a: signer, + s: Self.S, + r1: &Self.S, + r2: &mut Self.S + ): u64 * signer * Self.S * &Self.S * &u64 * &mut u64 { + label l0: + return move(x), move(a), move(s), copy(r1), &move(r1).S::f, &mut move(r2).S::f; + } +} + +//# run 0x42::M::t --args 0 0x1 2 3 4 diff --git a/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.exp b/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.exp index 1c8326cfa1..457ace9c4a 100644 --- a/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.exp +++ b/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.exp @@ -1,28 +1 @@ processed 4 tasks - -task 1 'run'. lines 23-31: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 2 'run'. lines 35-43: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - -task 3 'run'. lines 46-54: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} diff --git a/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.mvir b/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.mvir index 2bd9844ebb..4ac0584e36 100644 --- a/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.mvir +++ b/language/move-vm/transactional-tests/tests/entry_points/script_type_args_type_eq.mvir @@ -20,7 +20,7 @@ module 0x42.M { } // check: "Keep(EXECUTED)" -//# run --type-args u64 +//# run --type-args u64 --args 0x1 import 0x42.M; main(account: signer) { @@ -32,7 +32,7 @@ label b0: -//# run --type-args 0x42::M::Item +//# run --type-args 0x42::M::Item --args 0x2 import 0x42.M; main(account: signer) { @@ -43,7 +43,7 @@ label b0: } -//# run --type-args 0x42::M::Cup<0x42::M::Cup>> +//# run --type-args 0x42::M::Cup<0x42::M::Cup>> --args 0x3 import 0x42.M; main(account: signer) { diff --git a/language/move-vm/transactional-tests/tests/entry_points/struct_arguments.exp b/language/move-vm/transactional-tests/tests/entry_points/struct_arguments.exp new file mode 100644 index 0000000000..303d656474 --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/struct_arguments.exp @@ -0,0 +1,13 @@ +processed 5 tasks + +task 1 'run'. lines 19-24: +mutable inputs after call: local#2: { 0 } + +task 2 'run'. lines 26-30: +mutable inputs after call: local#2: { 0 } + +task 3 'run'. lines 32-32: +mutable inputs after call: local#2: { 0 } + +task 4 'run'. lines 34-34: +mutable inputs after call: local#2: { 0 } diff --git a/language/move-vm/transactional-tests/tests/entry_points/struct_arguments.mvir b/language/move-vm/transactional-tests/tests/entry_points/struct_arguments.mvir new file mode 100644 index 0000000000..d21c83ec7a --- /dev/null +++ b/language/move-vm/transactional-tests/tests/entry_points/struct_arguments.mvir @@ -0,0 +1,34 @@ +//# publish + +// struct and struct ref arguments are now allowed + +module 0x42.M { + struct S has drop { f: u64 } + + public(script) foo(s: Self.S, i: &Self.S, m: &mut Self.S) { + label l0: + return; + } + + public(script) foo_gen(s:T, i: &T, m: &mut T) { + label l0: + return; + } +} + +//# run --args 0 0 0 +import 0x42.M; +main(s: M.S, i: &M.S, m: &mut M.S) { + label l0: + return; +} + +//# run --type-args 0x42::M::S --args 0 0 0 +main(s: T, i: &T, m: &mut T) { + label l0: + return; +} + +//# run 0x42::M::foo --args 0 0 0 + +//# run 0x42::M::foo_gen --type-args 0x42::M::S --args 0 0 0 diff --git a/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.exp b/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.exp index 2c22766bc3..a7ad267396 100644 --- a/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.exp +++ b/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.exp @@ -1,28 +1,19 @@ processed 4 tasks -task 1 'run'. lines 69-76: -Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, - sub_status: None, - location: undefined, - indices: [], - offsets: [], -} - task 2 'run'. lines 79-86: Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, + major_status: VM_MAX_VALUE_DEPTH_REACHED, sub_status: None, - location: undefined, + location: 0x42::M, indices: [], - offsets: [], + offsets: [(FunctionDefinitionIndex(8), 3)], } task 3 'run'. lines 89-97: Error: Script execution failed with VMError: { - major_status: NUMBER_OF_SIGNER_ARGUMENTS_MISMATCH, + major_status: VM_MAX_VALUE_DEPTH_REACHED, sub_status: None, - location: undefined, + location: 0x42::M, indices: [], - offsets: [], + offsets: [(FunctionDefinitionIndex(9), 4)], } diff --git a/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.mvir b/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.mvir index 1c2b61ca1e..56be8b0e2d 100644 --- a/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.mvir +++ b/language/move-vm/transactional-tests/tests/recursion/runtime_layout_deeply_nested.mvir @@ -66,7 +66,7 @@ module 0x42.M { } -//# run +//# run --args 0x1 import 0x42.M; main(account: signer) { @@ -76,7 +76,7 @@ label b0: } -//# run +//# run --args 0x2 import 0x42.M; main(account: signer) { @@ -86,7 +86,7 @@ label b0: } -//# run +//# run --args 0x3 import 0x42.M; main(account: signer) { diff --git a/language/testing-infra/test-generation/src/lib.rs b/language/testing-infra/test-generation/src/lib.rs index af81de6567..017ce9e23d 100644 --- a/language/testing-infra/test-generation/src/lib.rs +++ b/language/testing-infra/test-generation/src/lib.rs @@ -141,7 +141,13 @@ fn execute_function_in_module( let mut sess = vm.new_session(&delta_storage); let mut gas_status = GasStatus::new_unmetered(); - sess.execute_function(&module_id, entry_name, ty_args, args, &mut gas_status)?; + sess.execute_function_bypass_visibility( + &module_id, + entry_name, + ty_args, + args, + &mut gas_status, + )?; Ok(()) } diff --git a/language/testing-infra/transactional-test-runner/src/framework.rs b/language/testing-infra/transactional-test-runner/src/framework.rs index 642ff4490a..6892f14e37 100644 --- a/language/testing-infra/transactional-test-runner/src/framework.rs +++ b/language/testing-infra/transactional-test-runner/src/framework.rs @@ -34,6 +34,7 @@ use move_core_types::{ use move_disassembler::disassembler::{Disassembler, DisassemblerOptions}; use move_ir_types::location::Spanned; use move_symbol_pool::Symbol; +use move_vm_runtime::session::SerializedReturnValues; use rayon::iter::Either; use std::{ collections::{BTreeMap, VecDeque}, @@ -121,7 +122,7 @@ pub trait MoveTestAdapter<'a> { args: Vec, gas_budget: Option, extra: Self::ExtraRunArgs, - ) -> Result>; + ) -> Result<(Option, SerializedReturnValues)>; fn call_function( &mut self, module: &ModuleId, @@ -131,7 +132,7 @@ pub trait MoveTestAdapter<'a> { args: Vec, gas_budget: Option, extra: Self::ExtraRunArgs, - ) -> Result>; + ) -> Result<(Option, SerializedReturnValues)>; fn view_data( &mut self, address: AccountAddress, @@ -282,9 +283,13 @@ pub trait MoveTestAdapter<'a> { SyntaxChoice::IR => (compile_ir_script(state.dep_modules(), data_path)?, None), }; let args = self.compiled_state().resolve_args(args); - let output = + let (output, return_values) = self.execute_script(script, type_args, signers, args, gas_budget, extra_args)?; - Ok(merge_output(warning_opt, output)) + let rendered_return_value = display_return_values(return_values); + Ok(merge_output( + warning_opt, + merge_output(output, rendered_return_value), + )) } TaskCommand::Run( RunCommand { @@ -302,7 +307,7 @@ pub trait MoveTestAdapter<'a> { "syntax flag meaningless with function execution" ); let args = self.compiled_state().resolve_args(args); - let output = self.call_function( + let (output, return_values) = self.call_function( &module, name.as_ident_str(), type_args, @@ -311,7 +316,8 @@ pub trait MoveTestAdapter<'a> { gas_budget, extra_args, )?; - Ok(output) + let rendered_return_value = display_return_values(return_values); + Ok(merge_output(output, rendered_return_value)) } TaskCommand::View(ViewCommand { address, @@ -338,6 +344,57 @@ pub trait MoveTestAdapter<'a> { } } +fn display_return_values(return_values: SerializedReturnValues) -> Option { + let SerializedReturnValues { + mutable_reference_outputs, + return_values, + } = return_values; + let mut output = vec![]; + if !mutable_reference_outputs.is_empty() { + let values = mutable_reference_outputs + .iter() + .map(|(idx, bytes, layout)| { + let value = + move_vm_types::values::Value::simple_deserialize(bytes, layout).unwrap(); + (idx, value) + }) + .collect::>(); + let printed = values + .iter() + .map(|(idx, v)| { + let mut buf = String::new(); + move_vm_types::values::debug::print_value(&mut buf, v).unwrap(); + format!("local#{}: {}", idx, buf) + }) + .collect::>() + .join(", "); + output.push(format!("mutable inputs after call: {}", printed)) + }; + if !return_values.is_empty() { + let values = return_values + .iter() + .map(|(bytes, layout)| { + move_vm_types::values::Value::simple_deserialize(bytes, layout).unwrap() + }) + .collect::>(); + let printed = values + .iter() + .map(|v| { + let mut buf = String::new(); + move_vm_types::values::debug::print_value(&mut buf, v).unwrap(); + buf + }) + .collect::>() + .join(", "); + output.push(format!("return values: {}", printed)) + }; + if output.is_empty() { + None + } else { + Some(output.join("\n")) + } +} + impl<'a> CompiledState<'a> { pub fn new( named_address_mapping: BTreeMap, diff --git a/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs b/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs index e139aaffc3..2a36395288 100644 --- a/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs +++ b/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs @@ -24,11 +24,15 @@ use move_core_types::{ language_storage::{ModuleId, StructTag, TypeTag}, resolver::MoveResolver, transaction_argument::{convert_txn_args, TransactionArgument}, + value::MoveValue, }; use move_resource_viewer::MoveValueAnnotator; use move_stdlib::move_stdlib_named_addresses; use move_symbol_pool::Symbol; -use move_vm_runtime::{move_vm::MoveVM, session::Session}; +use move_vm_runtime::{ + move_vm::MoveVM, + session::{SerializedReturnValues, Session}, +}; use move_vm_test_utils::InMemoryStorage; use move_vm_types::gas_schedule::GasStatus; use once_cell::sync::Lazy; @@ -168,7 +172,7 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { txn_args: Vec, gas_budget: Option, _extra_args: Self::ExtraRunArgs, - ) -> Result> { + ) -> Result<(Option, SerializedReturnValues)> { let signers: Vec<_> = signers .into_iter() .map(|addr| self.compiled_state().resolve_address(&addr)) @@ -176,17 +180,25 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { let mut script_bytes = vec![]; script.serialize(&mut script_bytes)?; + let args = convert_txn_args(&txn_args); - self.perform_session_action(gas_budget, |session, gas_status| { - session.execute_script(script_bytes, type_args, args, signers, gas_status) - }) - .map_err(|e| { - anyhow!( - "Script execution failed with VMError: {}", - format_vm_error(&e) - ) - })?; - Ok(None) + // TODO rethink testing signer args + let args = signers + .iter() + .map(|a| MoveValue::Signer(*a).simple_serialize().unwrap()) + .chain(args) + .collect(); + let serialized_return_values = self + .perform_session_action(gas_budget, |session, gas_status| { + session.execute_script(script_bytes, type_args, args, gas_status) + }) + .map_err(|e| { + anyhow!( + "Script execution failed with VMError: {}", + format_vm_error(&e) + ) + })?; + Ok((None, serialized_return_values)) } fn call_function( @@ -198,23 +210,32 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { txn_args: Vec, gas_budget: Option, _extra_args: Self::ExtraRunArgs, - ) -> Result> { + ) -> Result<(Option, SerializedReturnValues)> { let signers: Vec<_> = signers .into_iter() .map(|addr| self.compiled_state().resolve_address(&addr)) .collect(); let args = convert_txn_args(&txn_args); - self.perform_session_action(gas_budget, |session, gas_status| { - session.execute_script_function(module, function, type_args, args, signers, gas_status) - }) - .map_err(|e| { - anyhow!( - "Function execution failed with VMError: {}", - format_vm_error(&e) - ) - })?; - Ok(None) + // TODO rethink testing signer args + let args = signers + .iter() + .map(|a| MoveValue::Signer(*a).simple_serialize().unwrap()) + .chain(args) + .collect(); + let serialized_return_values = self + .perform_session_action(gas_budget, |session, gas_status| { + session.execute_function_bypass_visibility( + module, function, type_args, args, gas_status, + ) + }) + .map_err(|e| { + anyhow!( + "Function execution failed with VMError: {}", + format_vm_error(&e) + ) + })?; + Ok((None, serialized_return_values)) } fn view_data( @@ -256,11 +277,11 @@ pub fn format_vm_error(e: &VMError) -> String { } impl<'a> SimpleVMTestAdapter<'a> { - fn perform_session_action( + fn perform_session_action( &mut self, gas_budget: Option, - f: impl FnOnce(&mut Session, &mut GasStatus) -> VMResult<()>, - ) -> VMResult<()> { + f: impl FnOnce(&mut Session, &mut GasStatus) -> VMResult, + ) -> VMResult { // start session let vm = MoveVM::new(move_stdlib::natives::all_natives(STD_ADDR)).unwrap(); let (mut session, mut gas_status) = { @@ -274,13 +295,13 @@ impl<'a> SimpleVMTestAdapter<'a> { }; // perform op - f(&mut session, &mut gas_status)?; + let res = f(&mut session, &mut gas_status)?; // save changeset // TODO support events let (changeset, _events) = session.finish()?; self.storage.apply(changeset).unwrap(); - Ok(()) + Ok(res) } } diff --git a/language/testing-infra/transactional-test-runner/tests/vm_test_harness/print_bytecode.exp b/language/testing-infra/transactional-test-runner/tests/vm_test_harness/print_bytecode.exp index 282a63fe03..064d865d2e 100644 --- a/language/testing-infra/transactional-test-runner/tests/vm_test_harness/print_bytecode.exp +++ b/language/testing-infra/transactional-test-runner/tests/vm_test_harness/print_bytecode.exp @@ -1,7 +1,7 @@ processed 2 tasks task 0 'print-bytecode'. lines 1-5: -// Move bytecode v4 +// Move bytecode v5 script { @@ -12,7 +12,7 @@ B0: } task 1 'print-bytecode'. lines 7-13: -// Move bytecode v4 +// Move bytecode v5 module 42.M { diff --git a/language/tools/move-cli/src/sandbox/commands/run.rs b/language/tools/move-cli/src/sandbox/commands/run.rs index 72e9bf508d..d748edf9bc 100644 --- a/language/tools/move-cli/src/sandbox/commands/run.rs +++ b/language/tools/move-cli/src/sandbox/commands/run.rs @@ -17,6 +17,7 @@ use move_core_types::{ identifier::IdentStr, language_storage::TypeTag, transaction_argument::{convert_txn_args, TransactionArgument}, + value::MoveValue, }; use move_package::compilation::compiled_package::CompiledPackage; use move_vm_runtime::move_vm::MoveVM; @@ -76,27 +77,33 @@ move run` must be applied to a module inside `storage/`", let script_type_parameters = vec![]; let script_parameters = vec![]; + // TODO rethink move-cli arguments for executing functions + let vm_args = signer_addresses + .iter() + .map(|a| { + MoveValue::Signer(*a) + .simple_serialize() + .expect("transaction arguments must serialize") + }) + .chain(vm_args) + .collect(); let res = match script_name_opt { Some(script_name) => { // script fun. parse module, extract script ID to pass to VM let module = CompiledModule::deserialize(&bytecode) .map_err(|e| anyhow!("Error deserializing module: {:?}", e))?; - session - .execute_script_function( - &module.self_id(), - IdentStr::new(script_name)?, - vm_type_args.clone(), - vm_args, - signer_addresses.clone(), - &mut gas_status, - ) - .map(|_| ()) + session.execute_entry_function( + &module.self_id(), + IdentStr::new(script_name)?, + vm_type_args.clone(), + vm_args, + &mut gas_status, + ) } None => session.execute_script( bytecode.to_vec(), vm_type_args.clone(), vm_args, - signer_addresses.clone(), &mut gas_status, ), }; diff --git a/language/tools/move-cli/tests/testsuite/module_publish_view/args.exp b/language/tools/move-cli/tests/testsuite/module_publish_view/args.exp index b4e8e6ebcc..1a5e89a73c 100644 --- a/language/tools/move-cli/tests/testsuite/module_publish_view/args.exp +++ b/language/tools/move-cli/tests/testsuite/module_publish_view/args.exp @@ -3,7 +3,7 @@ Found 1 modules Publishing a new module 00000000000000000000000000000042::Module (wrote 120 bytes) Wrote 120 bytes of module ID's and code Command `sandbox view storage/0x00000000000000000000000000000042/modules/Module.mv`: -// Move bytecode v4 +// Move bytecode v5 module 42.Module { struct S { i: u64 diff --git a/language/tools/move-cli/tests/testsuite/multi_module_publish/args.exp b/language/tools/move-cli/tests/testsuite/multi_module_publish/args.exp index 773eeeb63a..be3c39e376 100644 --- a/language/tools/move-cli/tests/testsuite/multi_module_publish/args.exp +++ b/language/tools/move-cli/tests/testsuite/multi_module_publish/args.exp @@ -13,7 +13,7 @@ Publishing a new module 00000000000000000000000000000002::A (wrote 89 bytes) Publishing a new module 00000000000000000000000000000002::B (wrote 97 bytes) Wrote 186 bytes of module ID's and code Command `sandbox view storage/0x00000000000000000000000000000002/modules/A.mv`: -// Move bytecode v4 +// Move bytecode v5 module 2.A { @@ -23,7 +23,7 @@ B0: } } Command `sandbox view storage/0x00000000000000000000000000000002/modules/B.mv`: -// Move bytecode v4 +// Move bytecode v5 module 2.B { diff --git a/language/tools/move-cli/tests/testsuite/package_basics/args.exp b/language/tools/move-cli/tests/testsuite/package_basics/args.exp index bd76614d1d..aa1364621f 100644 --- a/language/tools/move-cli/tests/testsuite/package_basics/args.exp +++ b/language/tools/move-cli/tests/testsuite/package_basics/args.exp @@ -49,7 +49,7 @@ module Std::AModule { } } Command `package coverage bytecode --module AModule`: -// Move bytecode v4 +// Move bytecode v5 module 1.AModule { @@ -71,7 +71,7 @@ B2: } } Command `package disassemble --package MoveStdlib --name Errors`: -// Move bytecode v4 +// Move bytecode v5 module 1.Errors { diff --git a/language/tools/move-cli/tests/testsuite/republish/args.exp b/language/tools/move-cli/tests/testsuite/republish/args.exp index 6b3e3d7983..b9bb5c4863 100644 --- a/language/tools/move-cli/tests/testsuite/republish/args.exp +++ b/language/tools/move-cli/tests/testsuite/republish/args.exp @@ -4,14 +4,14 @@ Publishing a new module 00000000000000000000000000000042::M (wrote 56 bytes) Publishing a new module 00000000000000000000000000000043::N (wrote 56 bytes) Wrote 112 bytes of module ID's and code Command `sandbox view storage/0x00000000000000000000000000000042/modules/M.mv`: -// Move bytecode v4 +// Move bytecode v5 module 42.M { } Command `sandbox view storage/0x00000000000000000000000000000043/modules/N.mv`: -// Move bytecode v4 +// Move bytecode v5 module 43.N { @@ -23,14 +23,14 @@ Updating an existing module 00000000000000000000000000000042::M (wrote 56 bytes) Updating an existing module 00000000000000000000000000000043::N (wrote 56 bytes) Wrote 112 bytes of module ID's and code Command `sandbox view storage/0x00000000000000000000000000000042/modules/M.mv`: -// Move bytecode v4 +// Move bytecode v5 module 42.M { } Command `sandbox view storage/0x00000000000000000000000000000043/modules/N.mv`: -// Move bytecode v4 +// Move bytecode v5 module 43.N { diff --git a/language/tools/move-unit-test/src/extensions.rs b/language/tools/move-unit-test/src/extensions.rs index 0b6039188c..e1917796ab 100644 --- a/language/tools/move-unit-test/src/extensions.rs +++ b/language/tools/move-unit-test/src/extensions.rs @@ -18,7 +18,7 @@ use move_vm_test_utils::BlankStorage; use once_cell::sync::Lazy; /// Create all available native context extensions. -#[allow(unused_mut)] +#[allow(unused_mut, clippy::let_and_return)] pub(crate) fn new_extensions() -> NativeContextExtensions { let mut e = NativeContextExtensions::default(); #[cfg(feature = "table-extension")] diff --git a/language/tools/move-unit-test/src/test_runner.rs b/language/tools/move-unit-test/src/test_runner.rs index 3bd6a2c5bc..11f393bf77 100644 --- a/language/tools/move-unit-test/src/test_runner.rs +++ b/language/tools/move-unit-test/src/test_runner.rs @@ -269,13 +269,19 @@ impl SharedTestingConfig { // TODO: collect VM logs if the verbose flag (i.e, `self.verbose`) is set let now = Instant::now(); - let mut return_result = session.execute_function( + let serialized_return_values_result = session.execute_function_bypass_visibility( &test_plan.module_id, IdentStr::new(function_name).unwrap(), vec![], // no ty args, at least for now serialize_values(test_info.arguments.iter()), &mut gas_meter, ); + let mut return_result = serialized_return_values_result.map(|res| { + res.return_values + .into_iter() + .map(|(bytes, _layout)| bytes) + .collect() + }); if !self.report_stacktrace_on_abort { if let Err(err) = &mut return_result { err.remove_stacktrace();