diff --git a/aptos-move/e2e-move-tests/src/lib.rs b/aptos-move/e2e-move-tests/src/lib.rs index 1b2bc1bc6a5fe3..97b0e0b7c14330 100644 --- a/aptos-move/e2e-move-tests/src/lib.rs +++ b/aptos-move/e2e-move-tests/src/lib.rs @@ -60,5 +60,10 @@ pub(crate) fn build_package_with_compiler_version( ) -> anyhow::Result { let mut options = options; options.compiler_version = Some(compiler_version); + if options.compiler_version.unwrap() != CompilerVersion::V1 { + options.language_version = Some(move_model::metadata::LanguageVersion::latest_stable()); + } else { + options.language_version = Some(move_model::metadata::LanguageVersion::V1); + } BuiltPackage::build(package_path.to_owned(), options) } diff --git a/aptos-move/e2e-move-tests/src/tests/gas.rs b/aptos-move/e2e-move-tests/src/tests/gas.rs index 19ec6e939aec9b..95486f8e357776 100644 --- a/aptos-move/e2e-move-tests/src/tests/gas.rs +++ b/aptos-move/e2e-move-tests/src/tests/gas.rs @@ -30,7 +30,7 @@ use aptos_types::{ transaction::{EntryFunction, TransactionPayload}, }; use aptos_vm_environment::prod_configs::set_paranoid_type_checks; -use move_core_types::{identifier::Identifier, language_storage::ModuleId}; +use move_core_types::{identifier::Identifier, language_storage::ModuleId, value::MoveValue}; use rand::{rngs::StdRng, SeedableRng}; use sha3::{Digest, Sha3_512}; use std::path::Path; @@ -57,7 +57,9 @@ fn test_modify_gas_schedule_check_hash() { "set_for_next_epoch_check_hash", vec![], vec![ - bcs::to_bytes(&CORE_CODE_ADDRESS).unwrap(), + MoveValue::Signer(CORE_CODE_ADDRESS) + .simple_serialize() + .unwrap(), bcs::to_bytes(&old_hash).unwrap(), bcs::to_bytes(&bcs::to_bytes(&gas_schedule).unwrap()).unwrap(), ], @@ -66,7 +68,9 @@ fn test_modify_gas_schedule_check_hash() { harness .executor .exec("reconfiguration_with_dkg", "finish", vec![], vec![ - bcs::to_bytes(&CORE_CODE_ADDRESS).unwrap(), + MoveValue::Signer(CORE_CODE_ADDRESS) + .simple_serialize() + .unwrap(), ]); let (_, gas_params) = harness.get_gas_params(); diff --git a/aptos-move/e2e-tests/src/executor.rs b/aptos-move/e2e-tests/src/executor.rs index ab1d4b78395130..ab404b63a78279 100644 --- a/aptos-move/e2e-tests/src/executor.rs +++ b/aptos-move/e2e-tests/src/executor.rs @@ -74,6 +74,7 @@ use move_core_types::{ identifier::Identifier, language_storage::{ModuleId, StructTag, TypeTag}, move_resource::{MoveResource, MoveStructType}, + value::MoveValue, }; use move_vm_runtime::{ module_traversal::{TraversalContext, TraversalStorage}, @@ -1011,13 +1012,23 @@ impl FakeExecutor { let mut arg = args.clone(); match &dynamic_args { ExecFuncTimerDynamicArgs::DistinctSigners => { - arg.insert(0, bcs::to_bytes(&extra_accounts.pop().unwrap()).unwrap()); + arg.insert( + 0, + MoveValue::Signer(extra_accounts.pop().unwrap()) + .simple_serialize() + .unwrap(), + ); }, ExecFuncTimerDynamicArgs::DistinctSignersAndFixed(signers) => { for signer in signers.iter().rev() { - arg.insert(0, bcs::to_bytes(&signer).unwrap()); + arg.insert(0, MoveValue::Signer(*signer).simple_serialize().unwrap()); } - arg.insert(0, bcs::to_bytes(&extra_accounts.pop().unwrap()).unwrap()); + arg.insert( + 0, + MoveValue::Signer(extra_accounts.pop().unwrap()) + .simple_serialize() + .unwrap(), + ); }, _ => {}, } diff --git a/aptos-move/framework/aptos-framework/doc/transaction_context.md b/aptos-move/framework/aptos-framework/doc/transaction_context.md index cc7d9010ffc74d..d4dbf2d6358111 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_context.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_context.md @@ -1029,6 +1029,7 @@ Returns the inner entry function payload of the multisig payload.
pragma opaque;
+aborts_if [abstract] false;
 ensures [abstract] result == spec_generate_unique_address();
 
@@ -1055,6 +1056,7 @@ Returns the inner entry function payload of the multisig payload.
pragma opaque;
+aborts_if [abstract] false;
 // This enforces high-level requirement 3:
 ensures [abstract] result == spec_generate_unique_address();
 
diff --git a/aptos-move/framework/aptos-framework/sources/permissioned_signer.spec.move b/aptos-move/framework/aptos-framework/sources/permissioned_signer.spec.move new file mode 100644 index 00000000000000..f5818a2e9a0c95 --- /dev/null +++ b/aptos-move/framework/aptos-framework/sources/permissioned_signer.spec.move @@ -0,0 +1,177 @@ +spec aptos_framework::permissioned_signer { + + spec module { + axiom forall a: GrantedPermissionHandles: + (forall i in 0..len(a.active_handles): + forall j in 0..len(a.active_handles): + i != j ==> a.active_handles[i] != a.active_handles[j] + ); + } + + spec fun spec_is_permissioned_signer(s: signer): bool; + + spec is_permissioned_signer(s: &signer): bool { + pragma opaque; + aborts_if [abstract] false; + ensures [abstract] result == spec_is_permissioned_signer(s); + } + + spec fun spec_permission_signer(s: signer): signer; + + spec permission_signer(permissioned: &signer): signer { + pragma opaque; + aborts_if [abstract] !spec_is_permissioned_signer(permissioned); + ensures [abstract] result == spec_permission_signer(permissioned); + } + + spec fun spec_signer_from_permissioned_impl(master_addr: address, permission_addr: address): signer; + + spec signer_from_permissioned_impl(master_addr: address, permission_addr: address): signer { + pragma opaque; + ensures [abstract] result == spec_signer_from_permissioned_impl(master_addr, permission_addr); + } + + spec create_permissioned_handle(master: &signer): PermissionedHandle { + use aptos_framework::transaction_context; + pragma opaque; + aborts_if [abstract] spec_is_permissioned_signer(master); + let permission_addr = transaction_context::spec_generate_unique_address(); + modifies global(permission_addr); + let master_addr = signer::address_of(master); + ensures result.master_addr == master_addr; + ensures result.permission_addr == permission_addr; + } + + spec create_storable_permissioned_handle(master: &signer, expiration_time: u64): StorablePermissionedHandle { + use aptos_framework::transaction_context; + pragma opaque; + aborts_if [abstract] spec_is_permissioned_signer(master); + let permission_addr = transaction_context::spec_generate_unique_address(); + modifies global(permission_addr); + let master_addr = signer::address_of(master); + modifies global(master_addr); + ensures result.master_addr == master_addr; + ensures result.permission_addr == permission_addr; + ensures result.expiration_time == expiration_time; + ensures vector::spec_contains(global(master_addr).active_handles, permission_addr); + ensures exists(master_addr); + } + + spec destroy_permissioned_handle(p: PermissionedHandle) { + ensures !exists(p.permission_addr); + } + + spec destroy_storable_permissioned_handle(p: StorablePermissionedHandle) { + ensures !exists(p.permission_addr); + let post granted_permissions = global(p.master_addr); + // ensures [abstract] !vector::spec_contains(granted_permissions.active_handles, p.permission_addr); + } + + spec revoke_permission_handle(s: &signer, permission_addr: address) { + aborts_if spec_is_permissioned_signer(s); + } + + spec authorize( + master: &signer, + permissioned: &signer, + capacity: u256, + perm: PermKey + ) { + + use aptos_std::type_info; + use std::bcs; + pragma aborts_if_is_partial; + aborts_if !spec_is_permissioned_signer(permissioned); + aborts_if spec_is_permissioned_signer(master); + aborts_if signer::address_of(permissioned) != signer::address_of(master); + ensures exists(signer::address_of(spec_permission_signer(permissioned))); + // let perms = global(permission_signer_addr).perms; + // let post post_perms = global(permission_signer_addr).perms; + // let key = Any { + // type_name: type_info::type_name>(), + // data: bcs::serialize(perm) + // }; + // ensures smart_table::spec_contains(perms, key) ==> + // smart_table::spec_get(post_perms, key) == old(smart_table::spec_get(perms, key)) + capacity; + // ensures !smart_table::spec_contains(perms, key) ==> + // smart_table::spec_get(post_perms, key) == capacity; + } + + spec check_permission_exists( + s: &signer, + perm: PermKey + ): bool { + pragma opaque; + aborts_if false; + ensures result == spec_check_permission_exists(s, perm); + } + + spec fun spec_check_permission_exists( + s: signer, + perm: PermKey + ): bool { + use aptos_std::type_info; + use std::bcs; + let addr = signer::address_of(spec_permission_signer(s)); + let key = Any { + type_name: type_info::type_name(), + data: bcs::serialize(perm) + }; + if (!spec_is_permissioned_signer(s)) { + true + } else if(!exists(addr)) { + false + } else { + simple_map::spec_contains_key(global(addr).perms, key) + } + } + + spec check_permission_capacity_above( + s: &signer, + threshold: u256, + perm: PermKey + ): bool { + use aptos_std::type_info; + use std::bcs; + let permissioned_signer_addr = signer::address_of(spec_permission_signer(s)); + ensures !spec_is_permissioned_signer(s) ==> result == true; + ensures (spec_is_permissioned_signer(s) && !exists(permissioned_signer_addr)) ==> result == false; + let key = Any { + type_name: type_info::type_name>(), + data: bcs::serialize(perm) + }; + // ensures (spec_is_permissioned_signer(s) && exists(permissioned_signer_addr) && !smart_table::spec_contains(global(permissioned_signer_addr).perms, key)) ==> + // result == false; + // ensures (spec_is_permissioned_signer(s) && exists(permissioned_signer_addr) && smart_table::spec_contains(global(permissioned_signer_addr).perms, key)) ==> + // result == (smart_table::spec_get(global(permissioned_signer_addr).perms, key) > threshold); + } + + spec check_permission_consume( + s: &signer, + threshold: u256, + perm: PermKey + ): bool { + let permissioned_signer_addr = signer::address_of(spec_permission_signer(s)); + ensures !spec_is_permissioned_signer(s) ==> result == true; + ensures (spec_is_permissioned_signer(s) && !exists(permissioned_signer_addr)) ==> result == false; + + } + + spec capacity(s: &signer, perm: PermKey): Option { + aborts_if !spec_is_permissioned_signer(s); + let permissioned_signer_addr = signer::address_of(spec_permission_signer(s)); + ensures !exists(permissioned_signer_addr) ==> option::is_none(result); + } + + spec consume_permission( + perm: &mut Permission, + weight: u256, + perm_key: PermKey + ): bool { + ensures perm.key != perm_key ==> result == false; + ensures perm.key == perm_key && old(perm.capacity) < weight ==> result == false; + ensures perm.key == perm_key && perm.capacity >= weight ==> + (perm.capacity == old(perm.capacity) - weight && result == true); + } + +} diff --git a/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move b/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move index f9837e26e6a757..07487cb0919aed 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_context.spec.move @@ -58,11 +58,13 @@ spec aptos_framework::transaction_context { } spec generate_unique_address(): address { pragma opaque; + aborts_if [abstract] false; ensures [abstract] result == spec_generate_unique_address(); } spec fun spec_generate_unique_address(): address; spec generate_auid_address(): address { pragma opaque; + aborts_if [abstract] false; // property 3: Generating the unique address should return a vector with 32 bytes, if the auid feature flag is enabled. /// [high-level-req-3] ensures [abstract] result == spec_generate_unique_address(); diff --git a/aptos-move/framework/aptos-stdlib/doc/smart_table.md b/aptos-move/framework/aptos-stdlib/doc/smart_table.md index ac5388661f2caf..83eb27ba47fff6 100644 --- a/aptos-move/framework/aptos-stdlib/doc/smart_table.md +++ b/aptos-move/framework/aptos-stdlib/doc/smart_table.md @@ -1479,6 +1479,7 @@ map_spec_has_key = spec_contains;
pragma verify = false;
+pragma opaque;
 
@@ -1495,6 +1496,7 @@ map_spec_has_key = spec_contains;
pragma verify = false;
+pragma opaque;
 
diff --git a/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move b/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move index d905a0a40bb3ac..4344eb2329efbd 100644 --- a/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move +++ b/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move @@ -24,10 +24,12 @@ spec aptos_std::smart_table { spec destroy(self: SmartTable) { pragma verify = false; + pragma opaque; } spec clear(self: &mut SmartTable) { pragma verify = false; + pragma opaque; } spec split_one_bucket(self: &mut SmartTable) { diff --git a/aptos-move/framework/src/aptos.rs b/aptos-move/framework/src/aptos.rs index 6717a00135be4e..9e030e69b30bf1 100644 --- a/aptos-move/framework/src/aptos.rs +++ b/aptos-move/framework/src/aptos.rs @@ -113,7 +113,7 @@ impl ReleaseTarget { output_format: None, }), skip_fetch_latest_git_deps: true, - ..BuildOptions::default() + ..BuildOptions::move_2() }, packages: packages.iter().map(|(path, _)| path.to_owned()).collect(), rust_bindings: packages diff --git a/aptos-move/framework/src/natives/mod.rs b/aptos-move/framework/src/natives/mod.rs index dcfc80407f9329..956c590b4afc8d 100644 --- a/aptos-move/framework/src/natives/mod.rs +++ b/aptos-move/framework/src/natives/mod.rs @@ -15,6 +15,7 @@ pub mod function_info; pub mod hash; pub mod object; pub mod object_code_deployment; +pub mod permissioned_signer; pub mod randomness; pub mod state_storage; pub mod string_utils; @@ -91,6 +92,10 @@ pub fn all_natives( "dispatchable_fungible_asset", dispatchable_fungible_asset::make_all(builder) ); + add_natives_from_module!( + "permissioned_signer", + permissioned_signer::make_all(builder) + ); if inject_create_signer_for_gov_sim { add_natives_from_module!( diff --git a/aptos-move/framework/src/natives/permissioned_signer.rs b/aptos-move/framework/src/natives/permissioned_signer.rs new file mode 100644 index 00000000000000..3d34e89a4ec38c --- /dev/null +++ b/aptos-move/framework/src/natives/permissioned_signer.rs @@ -0,0 +1,106 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 +use aptos_native_interface::{ + safely_pop_arg, RawSafeNative, SafeNativeBuilder, SafeNativeContext, SafeNativeError, + SafeNativeResult, +}; +use move_core_types::account_address::AccountAddress; +use move_vm_runtime::native_functions::NativeFunction; +use move_vm_types::{ + loaded_data::runtime_types::Type, + values::{SignerRef, Struct, Value}, +}; +use smallvec::{smallvec, SmallVec}; +use std::collections::VecDeque; + +/*************************************************************************************************** + * native fun is_permissioned_signer + * + * Returns true if the signer passed in is a permissioned signer + * gas cost: base_cost + * + **************************************************************************************************/ +fn native_is_permissioned_signer( + _context: &mut SafeNativeContext, + _ty_args: Vec, + mut arguments: VecDeque, +) -> SafeNativeResult> { + debug_assert!(arguments.len() == 1); + + let s_arg = safely_pop_arg!(arguments, SignerRef); + + // context.charge()?; + let result = s_arg.is_permissioned()?; + + Ok(smallvec![Value::bool(result)]) +} + +/*************************************************************************************************** + * native fun permission_signer + * + * Returns the permission signer if the signer passed in is a permissioned signer + * gas cost: base_cost + * + **************************************************************************************************/ +fn native_permission_signer( + _context: &mut SafeNativeContext, + _ty_args: Vec, + mut arguments: VecDeque, +) -> SafeNativeResult> { + debug_assert!(arguments.len() == 1); + + let s_arg = safely_pop_arg!(arguments, SignerRef); + + // context.charge()?; + if !s_arg.is_permissioned()? { + return Err(SafeNativeError::Abort { abort_code: 3 }); + } + + Ok(smallvec![s_arg.permissioned_signer()?]) +} + +/*************************************************************************************************** + * native fun signer_from_permissioned + * + * Returns the permission signer from a master signer. + * gas cost: base_cost + * + **************************************************************************************************/ +fn native_signer_from_permissioned( + _context: &mut SafeNativeContext, + _ty_args: Vec, + mut arguments: VecDeque, +) -> SafeNativeResult> { + debug_assert!(arguments.len() == 2); + + let permission_addr = safely_pop_arg!(arguments, AccountAddress); + let master_addr = safely_pop_arg!(arguments, AccountAddress); + // context.charge()?; + + Ok(smallvec![Value::struct_(Struct::pack_variant(1, vec![ + Value::address(master_addr), + Value::address(permission_addr) + ]))]) +} + +/*************************************************************************************************** + * module + * + **************************************************************************************************/ +pub fn make_all( + builder: &SafeNativeBuilder, +) -> impl Iterator + '_ { + let natives = [ + ( + "is_permissioned_signer", + native_is_permissioned_signer as RawSafeNative, + ), + ("permission_signer", native_permission_signer), + ( + "signer_from_permissioned_impl", + native_signer_from_permissioned, + ), + ]; + + builder.make_named_natives(natives) +} diff --git a/aptos-move/framework/src/natives/string_utils.rs b/aptos-move/framework/src/natives/string_utils.rs index 0a4c7c71583f89..3cb87c669b2394 100644 --- a/aptos-move/framework/src/natives/string_utils.rs +++ b/aptos-move/framework/src/natives/string_utils.rs @@ -188,7 +188,7 @@ fn native_format_impl( let addr = if fix_enabled { val.value_as::()? .unpack()? - .next() + .nth(1) .unwrap() .value_as::()? } else { diff --git a/aptos-move/framework/tests/gate_v2_features.rs b/aptos-move/framework/tests/gate_v2_features.rs index 76b44089795acf..4dad2039b69fcf 100644 --- a/aptos-move/framework/tests/gate_v2_features.rs +++ b/aptos-move/framework/tests/gate_v2_features.rs @@ -27,11 +27,6 @@ fn compile_pkg_with_v1(path_to_pkg: impl Into) { .unwrap(); } -#[test] -fn compile_aptos_framework_with_v1() { - compile_pkg_with_v1("aptos-framework"); -} - #[test] fn compile_aptos_stdlib_with_v1() { compile_pkg_with_v1("aptos-stdlib"); diff --git a/third_party/move/move-compiler-v2/src/plan_builder.rs b/third_party/move/move-compiler-v2/src/plan_builder.rs index 804ba329bd0a43..4906ca1cba5f37 100644 --- a/third_party/move/move-compiler-v2/src/plan_builder.rs +++ b/third_party/move/move-compiler-v2/src/plan_builder.rs @@ -162,9 +162,16 @@ fn build_test_info( let mut arguments = Vec::new(); for param in function.get_parameters_ref() { - let Parameter(var, _ty, var_loc) = ¶m; + let Parameter(var, ty, var_loc) = ¶m; match test_annotation_params.get(var) { + Some(MoveValue::Address(addr)) => arguments.push(match ty { + Type::Primitive(PrimitiveType::Signer) => MoveValue::Signer(*addr), + Type::Reference(_, inner) if **inner == Type::Primitive(PrimitiveType::Signer) => { + MoveValue::Signer(*addr) + }, + _ => MoveValue::Address(*addr), + }), Some(value) => arguments.push(value.clone()), None => { let missing_param_msg = "Missing test parameter assignment in test. Expected a \ diff --git a/third_party/move/move-compiler/src/unit_test/plan_builder.rs b/third_party/move/move-compiler/src/unit_test/plan_builder.rs index c53926366cbd92..56b3f5140fd746 100644 --- a/third_party/move/move-compiler/src/unit_test/plan_builder.rs +++ b/third_party/move/move-compiler/src/unit_test/plan_builder.rs @@ -8,6 +8,7 @@ use crate::{ expansion::ast::{ self as E, Address, Attribute, AttributeValue, ModuleAccess_, ModuleIdent, ModuleIdent_, }, + hlir::ast::{BaseType_, SingleType_}, parser::ast::ConstantName, shared::{ known_attributes::{AttributeKind, KnownAttribute, TestingAttribute}, @@ -159,10 +160,19 @@ fn build_test_info<'func>( let test_annotation_params = parse_test_attribute(context, test_attribute, 0); let mut arguments = Vec::new(); - for (var, _) in &function.signature.parameters { + for (var, ty) in &function.signature.parameters { match test_annotation_params.get(&var.value()) { - Some(value) => arguments.push(value.clone()), - None => { + Some(MoveValue::Address(addr)) => match &ty.value { + SingleType_::Base(ty) => arguments.push( + if ty == &BaseType_::address(ty.loc) { + MoveValue::Address(*addr) + } else { + MoveValue::Signer(*addr) + }, + ), + SingleType_::Ref(_, _) => arguments.push(MoveValue::Signer(*addr)), + }, + _ => { let missing_param_msg = "Missing test parameter assignment in test. Expected a \ parameter to be assigned in this attribute"; context.env.add_diag(diag!( diff --git a/third_party/move/move-core/types/src/unit_tests/value_test.rs b/third_party/move/move-core/types/src/unit_tests/value_test.rs index 5c3bb088415035..1519fc7961c57a 100644 --- a/third_party/move/move-core/types/src/unit_tests/value_test.rs +++ b/third_party/move/move-core/types/src/unit_tests/value_test.rs @@ -137,3 +137,13 @@ fn nested_typed_struct_deserialization() { }) ); } + +#[test] +fn signer_deserialization() { + let v = MoveValue::Signer(AccountAddress::ZERO); + let bytes = v.simple_serialize().unwrap(); + assert_eq!( + MoveValue::simple_deserialize(&bytes, &crate::value::MoveTypeLayout::Signer).unwrap(), + v + ); +} diff --git a/third_party/move/move-core/types/src/value.rs b/third_party/move/move-core/types/src/value.rs index de4ef817e48320..d3f28133cb3749 100644 --- a/third_party/move/move-core/types/src/value.rs +++ b/third_party/move/move-core/types/src/value.rs @@ -465,6 +465,13 @@ impl MoveStructLayout { }, } } + + pub fn signer() -> Self { + MoveStructLayout::RuntimeVariants(vec![vec![MoveTypeLayout::Address], vec![ + MoveTypeLayout::Address, + MoveTypeLayout::Address, + ]]) + } } impl<'d> serde::de::DeserializeSeed<'d> for &MoveTypeLayout { @@ -486,7 +493,13 @@ impl<'d> serde::de::DeserializeSeed<'d> for &MoveTypeLayout { AccountAddress::deserialize(deserializer).map(MoveValue::Address) }, MoveTypeLayout::Signer => { - AccountAddress::deserialize(deserializer).map(MoveValue::Signer) + let (_, fields) = MoveStructLayout::signer() + .deserialize(deserializer)? + .into_optional_variant_and_fields(); + Ok(MoveValue::Signer(match fields[0] { + MoveValue::Address(addr) => addr, + _ => return Err(D::Error::custom("signer deserialization error")), + })) }, MoveTypeLayout::Struct(ty) => Ok(MoveValue::Struct(ty.deserialize(deserializer)?)), MoveTypeLayout::Vector(layout) => Ok(MoveValue::Vector( @@ -691,7 +704,9 @@ impl serde::Serialize for MoveValue { MoveValue::U128(i) => serializer.serialize_u128(*i), MoveValue::U256(i) => i.serialize(serializer), MoveValue::Address(a) => a.serialize(serializer), - MoveValue::Signer(a) => a.serialize(serializer), + MoveValue::Signer(a) => { + MoveStruct::new_variant(0, vec![MoveValue::Address(*a)]).serialize(serializer) + }, MoveValue::Vector(v) => { let mut t = serializer.serialize_seq(Some(v.len()))?; for val in v { diff --git a/third_party/move/move-prover/boogie-backend/src/prelude/prelude.bpl b/third_party/move/move-prover/boogie-backend/src/prelude/prelude.bpl index af262f11c9fbf2..970ae252a2a530 100644 --- a/third_party/move/move-prover/boogie-backend/src/prelude/prelude.bpl +++ b/third_party/move/move-prover/boogie-backend/src/prelude/prelude.bpl @@ -1102,13 +1102,25 @@ procedure {:inline 1} $1_Account_create_signer( // Native Signer datatype $signer { - $signer($addr: int) + $signer($addr: int), + $permissioned_signer($addr: int, $permission_addr: int) } + function {:inline} $IsValid'signer'(s: $signer): bool { - $IsValid'address'(s->$addr) + if s is $signer then + $IsValid'address'(s->$addr) + else + $IsValid'address'(s->$addr) && + $IsValid'address'(s->$permission_addr) } + function {:inline} $IsEqual'signer'(s1: $signer, s2: $signer): bool { - s1 == s2 + if s1 is $signer && s2 is $signer then + s1 == s2 + else if s1 is $permissioned_signer && s2 is $permissioned_signer then + s1 == s2 + else + false } procedure {:inline 1} $1_signer_borrow_address(signer: $signer) returns (res: int) { diff --git a/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp b/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp index d6f72efdfd3a5c..3738bf28760678 100644 --- a/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp +++ b/third_party/move/move-prover/tests/sources/functional/loops_with_memory_ops.v2_exp @@ -98,8 +98,6 @@ error: unknown assertion failed = y = = at tests/sources/functional/loops_with_memory_ops.move:80: nested_loop2 = at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2 - = a = - = at tests/sources/functional/loops_with_memory_ops.move:81: nested_loop2 = b = = at tests/sources/functional/loops_with_memory_ops.move:85: nested_loop2 = = diff --git a/third_party/move/move-prover/tests/sources/functional/trace.exp b/third_party/move/move-prover/tests/sources/functional/trace.exp index e24a41719d8a78..5f1a4a7b947ab0 100644 --- a/third_party/move/move-prover/tests/sources/functional/trace.exp +++ b/third_party/move/move-prover/tests/sources/functional/trace.exp @@ -47,7 +47,7 @@ error: post-condition does not hold │ = Related Global Memory: = Resource name: TestTracing_R - = Values: {Address(18467): , Default: TestTracing.R{x = 4}} + = Values: {Address(6334): , Default: empty} = Related Bindings: = addr = = exists(addr) = @@ -74,7 +74,7 @@ error: global memory invariant does not hold │ = Related Global Memory: = Resource name: TestTracing_R - = Values: {Address(0): , Default: TestTracing.R{x = 5}} + = Values: {Address(0): , Default: empty} = at tests/sources/functional/trace.move:29: publish_invalid = at tests/sources/functional/trace.move:33: publish_invalid (spec) = `let addr = signer::address_of(s);` = diff --git a/third_party/move/move-prover/tests/sources/functional/trace.v2_exp b/third_party/move/move-prover/tests/sources/functional/trace.v2_exp index 38e6e6733c25b2..dff90ea83d206e 100644 --- a/third_party/move/move-prover/tests/sources/functional/trace.v2_exp +++ b/third_party/move/move-prover/tests/sources/functional/trace.v2_exp @@ -47,7 +47,7 @@ error: post-condition does not hold │ = Related Global Memory: = Resource name: TestTracing_R - = Values: {Address(18467): , Default: empty} + = Values: {Address(6334): , Default: empty} = Related Bindings: = addr = = exists(addr) = diff --git a/third_party/move/move-stdlib/src/natives/signer.rs b/third_party/move/move-stdlib/src/natives/signer.rs index 3a15343186a0ca..5d4c0089f9b236 100644 --- a/third_party/move/move-stdlib/src/natives/signer.rs +++ b/third_party/move/move-stdlib/src/natives/signer.rs @@ -37,10 +37,10 @@ fn native_borrow_address( debug_assert!(arguments.len() == 1); let signer_reference = pop_arg!(arguments, SignerRef); + let out = signer_reference.borrow_signer()?; + println!("borrow_address: {:?}", out); - Ok(NativeResult::ok(gas_params.base, smallvec![ - signer_reference.borrow_signer()? - ])) + Ok(NativeResult::ok(gas_params.base, smallvec![out])) } pub fn make_native_borrow_address(gas_params: BorrowAddressGasParameters) -> NativeFunction { diff --git a/third_party/move/move-vm/runtime/src/interpreter.rs b/third_party/move/move-vm/runtime/src/interpreter.rs index f45aa01f5fd2b3..69169cb6b1177d 100644 --- a/third_party/move/move-vm/runtime/src/interpreter.rs +++ b/third_party/move/move-vm/runtime/src/interpreter.rs @@ -2953,7 +2953,7 @@ impl Frame { let resource = interpreter.operand_stack.pop()?; let signer_reference = interpreter.operand_stack.pop_as::()?; let addr = signer_reference - .borrow_field(0)? + .borrow_field(1)? .value_as::()? .read_ref()? .value_as::()?; @@ -2965,7 +2965,7 @@ impl Frame { let resource = interpreter.operand_stack.pop()?; let signer_reference = interpreter.operand_stack.pop_as::()?; let addr = signer_reference - .borrow_field(0)? + .borrow_field(1)? .value_as::()? .read_ref()? .value_as::()?; diff --git a/third_party/move/move-vm/types/src/value_serde.rs b/third_party/move/move-vm/types/src/value_serde.rs index 5f17bfe9b38489..c7f2c74b0e4d99 100644 --- a/third_party/move/move-vm/types/src/value_serde.rs +++ b/third_party/move/move-vm/types/src/value_serde.rs @@ -104,6 +104,7 @@ impl CustomSerializer for RelaxedCustomSerDe { custom_serializer: None::<&RelaxedCustomSerDe>, layout, value: &value.0, + legacy_signer: false, } .serialize(serializer) } @@ -134,6 +135,7 @@ pub fn serialize_and_allow_delayed_values( custom_serializer: Some(&native_serializer), layout, value: &value.0, + legacy_signer: false, }; bcs::to_bytes(&value) .ok() @@ -163,6 +165,7 @@ pub fn serialized_size_allowing_delayed_values( custom_serializer: Some(&native_serializer), layout, value: &value.0, + legacy_signer: true, }; bcs::serialized_size(&value).map_err(|e| { PartialVMError::new(StatusCode::VALUE_SERIALIZATION_ERROR).with_message(format!( @@ -232,6 +235,7 @@ impl<'a, I: From + ExtractWidth + ExtractUniqueIndex> CustomSerializer custom_serializer: None::<&RelaxedCustomSerDe>, layout, value: &value.0, + legacy_signer: false, } .serialize(serializer) } @@ -291,6 +295,7 @@ pub fn serialize_and_replace_ids_with_values + ExtractWidth + Extra custom_serializer: Some(&custom_serializer), layout, value: &value.0, + legacy_signer: false, }; bcs::to_bytes(&value).ok().filter(|_| { // Should never happen, it should always fail first in serialize_and_allow_delayed_values diff --git a/third_party/move/move-vm/types/src/values/serialization_tests.rs b/third_party/move/move-vm/types/src/values/serialization_tests.rs index 6085a50183e2cb..7b4aef0f147080 100644 --- a/third_party/move/move-vm/types/src/values/serialization_tests.rs +++ b/third_party/move/move-vm/types/src/values/serialization_tests.rs @@ -244,3 +244,17 @@ fn test_serialized_size() { assert_err!(serialized_size_allowing_delayed_values(&value, &layout)); } } + +#[test] +fn signer_round_trip_vm_value() { + let v = MoveValue::Signer(AccountAddress::ZERO); + let bytes = v.simple_serialize().unwrap(); + let vm_value = Value::simple_deserialize(&bytes, &MoveTypeLayout::Signer).unwrap(); + let vm_bytes = serialize_and_allow_delayed_values(&vm_value, &MoveTypeLayout::Signer) + .unwrap() + .unwrap(); + assert_eq!( + v, + MoveValue::simple_deserialize(&vm_bytes, &MoveTypeLayout::Signer).unwrap() + ) +} diff --git a/third_party/move/move-vm/types/src/values/values_impl.rs b/third_party/move/move-vm/types/src/values/values_impl.rs index 42fbe5d9278c39..e64a2322c71a56 100644 --- a/third_party/move/move-vm/types/src/values/values_impl.rs +++ b/third_party/move/move-vm/types/src/values/values_impl.rs @@ -284,7 +284,10 @@ impl Container { } fn signer(x: AccountAddress) -> Self { - Container::Struct(Rc::new(RefCell::new(vec![ValueImpl::Address(x)]))) + Container::Struct(Rc::new(RefCell::new(vec![ + ValueImpl::U16(0), + ValueImpl::Address(x), + ]))) } } @@ -1506,7 +1509,31 @@ impl Locals { impl SignerRef { pub fn borrow_signer(&self) -> PartialVMResult { - Ok(Value(self.0.borrow_elem(0)?)) + Ok(Value(self.0.borrow_elem(1)?)) + } + + pub fn is_permissioned(&self) -> PartialVMResult { + match &self.0 { + ContainerRef::Local(Container::Struct(s)) => { + Ok(*s.borrow()[0].as_value_ref::()? == 1) + }, + _ => Err( + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message(format!("unexpected signer value: {:?}", self)), + ), + } + } + + pub fn permissioned_signer(&self) -> PartialVMResult { + match &self.0 { + ContainerRef::Local(Container::Struct(s)) => Ok(Value::signer( + *s.borrow()[2].as_value_ref::()?, + )), + _ => Err( + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message(format!("unexpected signer value: {:?}", self)), + ), + } } } @@ -3647,6 +3674,7 @@ impl Value { custom_serializer: None::<&RelaxedCustomSerDe>, layout, value: &self.0, + legacy_signer: false, }) .ok() } @@ -3666,6 +3694,7 @@ impl Struct { custom_serializer: None::<&RelaxedCustomSerDe>, layout, value: &self.fields, + legacy_signer: false, }) .ok() } @@ -3680,6 +3709,7 @@ pub(crate) struct SerializationReadyValue<'c, 'l, 'v, L, V, C> { pub(crate) layout: &'l L, // Value to serialize. pub(crate) value: &'v V, + pub(crate) legacy_signer: bool, } fn invariant_violation(message: String) -> S::Error { @@ -3711,6 +3741,7 @@ impl<'c, 'l, 'v, C: CustomSerializer> serde::Serialize custom_serializer: self.custom_serializer, layout: struct_layout, value: &*r.borrow(), + legacy_signer: self.legacy_signer, }) .serialize(serializer) }, @@ -3735,6 +3766,7 @@ impl<'c, 'l, 'v, C: CustomSerializer> serde::Serialize custom_serializer: self.custom_serializer, layout, value, + legacy_signer: self.legacy_signer, })?; } t.end() @@ -3748,19 +3780,25 @@ impl<'c, 'l, 'v, C: CustomSerializer> serde::Serialize // Signer. (L::Signer, ValueImpl::Container(Container::Struct(r))) => { - let v = r.borrow(); - if v.len() != 1 { - return Err(invariant_violation::(format!( - "cannot serialize container as a signer -- expected 1 field got {}", - v.len() - ))); + if self.legacy_signer { + r.borrow()[1] + .as_value_ref::() + .map_err(|_| { + invariant_violation::(format!( + "cannot serialize container {:?} as {:?}", + self.value, self.layout + )) + })? + .serialize(serializer) + } else { + (SerializationReadyValue { + custom_serializer: self.custom_serializer, + layout: &MoveStructLayout::signer(), + value: &*r.borrow(), + legacy_signer: self.legacy_signer, + }) + .serialize(serializer) } - (SerializationReadyValue { - custom_serializer: self.custom_serializer, - layout: &L::Address, - value: &v[0], - }) - .serialize(serializer) }, // Delayed values. For their serialization, we must have custom @@ -3821,6 +3859,7 @@ impl<'c, 'l, 'v, C: CustomSerializer> serde::Serialize custom_serializer: self.custom_serializer, layout: &variant_layouts[0], value: &values[0], + legacy_signer: self.legacy_signer, }, ), _ => { @@ -3835,6 +3874,7 @@ impl<'c, 'l, 'v, C: CustomSerializer> serde::Serialize custom_serializer: self.custom_serializer, layout, value, + legacy_signer: self.legacy_signer, })? } t.end() @@ -3854,6 +3894,7 @@ impl<'c, 'l, 'v, C: CustomSerializer> serde::Serialize custom_serializer: self.custom_serializer, layout: field_layout, value, + legacy_signer: self.legacy_signer, })?; } t.end() @@ -3892,7 +3933,13 @@ impl<'d, 'c, C: CustomDeserializer> serde::de::DeserializeSeed<'d> L::U128 => u128::deserialize(deserializer).map(Value::u128), L::U256 => u256::U256::deserialize(deserializer).map(Value::u256), L::Address => AccountAddress::deserialize(deserializer).map(Value::address), - L::Signer => AccountAddress::deserialize(deserializer).map(Value::signer), + L::Signer => { + let seed = DeserializationSeed { + custom_deserializer: self.custom_deserializer, + layout: &MoveStructLayout::signer(), + }; + Ok(Value::struct_(seed.deserialize(deserializer)?)) + }, // Structs. L::Struct(struct_layout) => { @@ -4658,10 +4705,7 @@ impl ValueImpl { (L::Signer, ValueImpl::Container(Container::Struct(r))) => { let v = r.borrow(); - if v.len() != 1 { - panic!("Unexpected signer layout: {:?}", v); - } - match &v[0] { + match &v[1] { ValueImpl::Address(a) => MoveValue::Signer(*a), v => panic!("Unexpected non-address while converting signer: {:?}", v), } diff --git a/third_party/move/scripts/move_pr.sh b/third_party/move/scripts/move_pr.sh index ef6588b4e6d9ba..e931e993d02be4 100755 --- a/third_party/move/scripts/move_pr.sh +++ b/third_party/move/scripts/move_pr.sh @@ -186,9 +186,9 @@ if [ ! -z "$INTEGRATION_TEST" ]; then echo "*************** [move-pr] Running integration tests" ( cd $BASE - MOVE_COMPILER_V2=false cargo build $CARGO_OP_PARAMS \ + MOVE_COMPILER_V2=false MOVE_LANGUAGE_V2=true cargo build $CARGO_OP_PARAMS \ $MOVE_CRATES $MOVE_CRATES_V2_ENV_DEPENDENT - MOVE_COMPILER_V2=false cargo nextest run $CARGO_NEXTEST_PARAMS \ + MOVE_COMPILER_V2=false MOVE_LANGUAGE_V2=true cargo nextest run $CARGO_NEXTEST_PARAMS \ $MOVE_CRATES $MOVE_CRATES_V2_ENV_DEPENDENT ) fi @@ -197,10 +197,11 @@ if [ ! -z "$COMPILER_V2_TEST" ]; then echo "*************** [move-pr] Running integration tests with compiler v2" ( cd $BASE - MVC_DOCGEN_OUTPUT_DIR=tests/compiler-v2-doc MOVE_COMPILER_V2=true cargo build $CARGO_OP_PARAMS \ + MVC_DOCGEN_OUTPUT_DIR=tests/compiler-v2-doc \ + MOVE_COMPILER_V2=true MOVE_LANGUAGE_V2=true cargo build $CARGO_OP_PARAMS \ $MOVE_CRATES_V2_ENV_DEPENDENT MVC_DOCGEN_OUTPUT_DIR=tests/compiler-v2-doc \ - MOVE_COMPILER_V2=true cargo nextest run $CARGO_NEXTEST_PARAMS \ + MOVE_COMPILER_V2=true MOVE_LANGUAGE_V2=true cargo nextest run $CARGO_NEXTEST_PARAMS \ $MOVE_CRATES_V2_ENV_DEPENDENT ) fi