diff --git a/aptos-move/framework/src/natives/mod.rs b/aptos-move/framework/src/natives/mod.rs index dcfc80407f932..c251893032810 100644 --- a/aptos-move/framework/src/natives/mod.rs +++ b/aptos-move/framework/src/natives/mod.rs @@ -16,6 +16,7 @@ pub mod hash; pub mod object; pub mod object_code_deployment; pub mod randomness; +pub mod permissioned_signer; pub mod state_storage; pub mod string_utils; pub mod transaction_context; @@ -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 0000000000000..1d820efb7dbf5 --- /dev/null +++ b/aptos-move/framework/src/natives/permissioned_signer.rs @@ -0,0 +1,107 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 +use aptos_native_interface::{ + safely_pop_arg, RawSafeNative, SafeNativeBuilder, SafeNativeContext, SafeNativeError, + SafeNativeResult, +}; +use move_vm_runtime::native_functions::NativeFunction; +use move_vm_types::{ + loaded_data::runtime_types::Type, + values::{SignerRef, Struct, StructRef, 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() == 1); + + let s_arg = safely_pop_arg!(arguments, StructRef); + let fields = s_arg.read_ref()?.value_as::()?.unpack()?; + + // context.charge()?; + + Ok(smallvec![Value::struct_(Struct::pack_variant(1, fields))]) +} + +/*************************************************************************************************** + * 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/third_party/move/move-compiler/src/unit_test/plan_builder.rs b/third_party/move/move-compiler/src/unit_test/plan_builder.rs index c53926366cbd9..e0932b16847ea 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,21 @@ 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 5c3bb08841503..f7434a552cbcc 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,10 @@ 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 49a4d4eada8df..51d3868f7a794 100644 --- a/third_party/move/move-core/types/src/value.rs +++ b/third_party/move/move-core/types/src/value.rs @@ -452,6 +452,10 @@ impl MoveStructLayout { }, } } + + pub fn signer() -> Self { + MoveStructLayout::RuntimeVariants(vec![vec![MoveTypeLayout::Address], vec![MoveTypeLayout::Address, MoveTypeLayout::Address, MoveTypeLayout::U64]]) + } } impl<'d> serde::de::DeserializeSeed<'d> for &MoveTypeLayout { @@ -473,7 +477,11 @@ 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( @@ -678,7 +686,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-stdlib/src/natives/signer.rs b/third_party/move/move-stdlib/src/natives/signer.rs index 3a15343186a0c..b5a8b906234ec 100644 --- a/third_party/move/move-stdlib/src/natives/signer.rs +++ b/third_party/move/move-stdlib/src/natives/signer.rs @@ -37,9 +37,11 @@ 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()? + out ])) } diff --git a/third_party/move/move-vm/runtime/src/interpreter.rs b/third_party/move/move-vm/runtime/src/interpreter.rs index 2baff79cc43df..08dda5d28ea79 100644 --- a/third_party/move/move-vm/runtime/src/interpreter.rs +++ b/third_party/move/move-vm/runtime/src/interpreter.rs @@ -2988,7 +2988,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::()?; @@ -3008,7 +3008,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/values/serialization_tests.rs b/third_party/move/move-vm/types/src/values/serialization_tests.rs index 6085a50183e2c..261f9cac38a8d 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,12 @@ 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 006c16d6b9dd1..e448d231c9710 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 @@ -283,7 +283,7 @@ 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)]))) } } @@ -1058,7 +1058,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()[1].as_value_ref::()?)) + } + _ => Err( + PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR) + .with_message(format!("unexpected signer value: {:?}", self)), + ) + } } } @@ -3224,17 +3248,10 @@ 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() - ))); - } (SerializationReadyValue { custom_serializer: self.custom_serializer, - layout: &L::Address, - value: &v[0], + layout: &MoveStructLayout::signer(), + value: &*r.borrow(), }) .serialize(serializer) }, @@ -3368,7 +3385,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) => {