diff --git a/crates/sui-json-rpc-types/src/sui_transaction.rs b/crates/sui-json-rpc-types/src/sui_transaction.rs index d67345aa35db1..1f3ff02c6a4fc 100644 --- a/crates/sui-json-rpc-types/src/sui_transaction.rs +++ b/crates/sui-json-rpc-types/src/sui_transaction.rs @@ -1871,7 +1871,7 @@ fn get_signature_types( .signature_at(func.parameters) .0 .iter() - .map(|s| primitive_type(module, &[], s).1) + .map(|s| primitive_type(module, &[], s)) .collect(), ) } else { diff --git a/crates/sui-json/src/lib.rs b/crates/sui-json/src/lib.rs index f012dcbbbc720..1663dae3e2b3e 100644 --- a/crates/sui-json/src/lib.rs +++ b/crates/sui-json/src/lib.rs @@ -10,14 +10,11 @@ use fastcrypto::encoding::{Encoding, Hex}; use move_binary_format::CompiledModule; use move_binary_format::{binary_config::BinaryConfig, file_format::SignatureToken}; use move_bytecode_utils::resolve_struct; -use move_core_types::account_address::AccountAddress; pub use move_core_types::annotated_value::MoveTypeLayout; use move_core_types::annotated_value::{MoveFieldLayout, MoveVariant}; -use move_core_types::identifier::IdentStr; use move_core_types::u256::U256; use move_core_types::{ - annotated_value::{MoveStruct, MoveStructLayout, MoveValue}, - ident_str, + annotated_value::{MoveStruct, MoveValue}, identifier::Identifier, language_storage::{StructTag, TypeTag}, runtime_value as R, @@ -27,11 +24,12 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Number, Value as JsonValue}; use sui_types::base_types::{ - is_primitive_type_tag, ObjectID, SuiAddress, TxContext, TxContextKind, RESOLVED_ASCII_STR, - RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, STD_ASCII_MODULE_NAME, STD_ASCII_STRUCT_NAME, - STD_OPTION_MODULE_NAME, STD_OPTION_STRUCT_NAME, STD_UTF8_MODULE_NAME, STD_UTF8_STRUCT_NAME, + is_primitive_type_tag, move_ascii_str_layout, move_utf8_str_layout, ObjectID, SuiAddress, + TxContext, TxContextKind, RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, + STD_ASCII_MODULE_NAME, STD_ASCII_STRUCT_NAME, STD_OPTION_MODULE_NAME, STD_OPTION_STRUCT_NAME, + STD_UTF8_MODULE_NAME, STD_UTF8_STRUCT_NAME, }; -use sui_types::id::{ID, RESOLVED_SUI_ID}; +use sui_types::id::{self, ID, RESOLVED_SUI_ID}; use sui_types::move_package::MovePackage; use sui_types::object::bounded_visitor::BoundedVisitor; use sui_types::transfer::RESOLVED_RECEIVING_STRUCT; @@ -566,64 +564,30 @@ pub fn primitive_type( view: &CompiledModule, type_args: &[TypeTag], param: &SignatureToken, -) -> (bool, Option) { - match param { - SignatureToken::Bool => (true, Some(MoveTypeLayout::Bool)), - SignatureToken::U8 => (true, Some(MoveTypeLayout::U8)), - SignatureToken::U16 => (true, Some(MoveTypeLayout::U16)), - SignatureToken::U32 => (true, Some(MoveTypeLayout::U32)), - SignatureToken::U64 => (true, Some(MoveTypeLayout::U64)), - SignatureToken::U128 => (true, Some(MoveTypeLayout::U128)), - SignatureToken::U256 => (true, Some(MoveTypeLayout::U256)), - SignatureToken::Address => (true, Some(MoveTypeLayout::Address)), +) -> Option { + Some(match param { + SignatureToken::Bool => MoveTypeLayout::Bool, + SignatureToken::U8 => MoveTypeLayout::U8, + SignatureToken::U16 => MoveTypeLayout::U16, + SignatureToken::U32 => MoveTypeLayout::U32, + SignatureToken::U64 => MoveTypeLayout::U64, + SignatureToken::U128 => MoveTypeLayout::U128, + SignatureToken::U256 => MoveTypeLayout::U256, + SignatureToken::Address => MoveTypeLayout::Address, SignatureToken::Vector(inner) => { - let (is_primitive, inner_layout_opt) = primitive_type(view, type_args, inner); - match inner_layout_opt { - Some(inner_layout) => ( - is_primitive, - Some(MoveTypeLayout::Vector(Box::new(inner_layout))), - ), - None => (is_primitive, None), - } + MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, inner)?)) } SignatureToken::Datatype(struct_handle_idx) => { let resolved_struct = resolve_struct(view, *struct_handle_idx); if resolved_struct == RESOLVED_ASCII_STR { - ( - true, - Some(MoveTypeLayout::Struct(Box::new(MoveStructLayout { - type_: resolved_to_struct(RESOLVED_ASCII_STR), - fields: Box::new(vec![MoveFieldLayout::new( - ident_str!("bytes").into(), - MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - )]), - }))), - ) + MoveTypeLayout::Struct(Box::new(move_ascii_str_layout())) } else if resolved_struct == RESOLVED_UTF8_STR { // both structs structs representing strings have one field - a vector of type u8 - ( - true, - Some(MoveTypeLayout::Struct(Box::new(MoveStructLayout { - type_: resolved_to_struct(RESOLVED_UTF8_STR), - fields: Box::new(vec![MoveFieldLayout::new( - ident_str!("bytes").into(), - MoveTypeLayout::Vector(Box::new(MoveTypeLayout::U8)), - )]), - }))), - ) + MoveTypeLayout::Struct(Box::new(move_utf8_str_layout())) } else if resolved_struct == RESOLVED_SUI_ID { - ( - true, - Some(MoveTypeLayout::Struct(Box::new(MoveStructLayout { - type_: resolved_to_struct(RESOLVED_SUI_ID), - fields: Box::new(vec![MoveFieldLayout::new( - ident_str!("bytes").into(), - MoveTypeLayout::Address, - )]), - }))), - ) + MoveTypeLayout::Struct(Box::new(id::ID::layout())) } else { - (false, None) + return None; } } SignatureToken::DatatypeInstantiation(struct_inst) => { @@ -631,38 +595,65 @@ pub fn primitive_type( let resolved_struct = resolve_struct(view, *idx); // is option of a primitive if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 { - // there is no MoveLayout for this so while we can still report whether a type - // is primitive or not, we can't return the layout - let (is_primitive, inner_layout) = primitive_type(view, type_args, &targs[0]); - let layout = - inner_layout.map(|inner_layout| MoveTypeLayout::Vector(Box::new(inner_layout))); - (is_primitive, layout) + // there is no MoveLayout for this so the type is not a primitive. + MoveTypeLayout::Vector(Box::new(primitive_type(view, type_args, &targs[0])?)) } else { - (false, None) + return None; } } - - SignatureToken::TypeParameter(idx) => ( - type_args - .get(*idx as usize) - .map(is_primitive_type_tag) - .unwrap_or(false), - None, - ), - + SignatureToken::TypeParameter(idx) => { + layout_of_primitive_typetag(type_args.get(*idx as usize)?)? + } SignatureToken::Signer | SignatureToken::Reference(_) - | SignatureToken::MutableReference(_) => (false, None), - } + | SignatureToken::MutableReference(_) => return None, + }) } -fn resolved_to_struct(resolved_type: (&AccountAddress, &IdentStr, &IdentStr)) -> StructTag { - StructTag { - address: *resolved_type.0, - module: resolved_type.1.into(), - name: resolved_type.2.into(), - type_params: vec![], +fn layout_of_primitive_typetag(tag: &TypeTag) -> Option { + use MoveTypeLayout as MTL; + if !is_primitive_type_tag(tag) { + return None; } + + Some(match tag { + TypeTag::Bool => MTL::Bool, + TypeTag::U8 => MTL::U8, + TypeTag::U16 => MTL::U16, + TypeTag::U32 => MTL::U32, + TypeTag::U64 => MTL::U64, + TypeTag::U128 => MTL::U128, + TypeTag::U256 => MTL::U256, + TypeTag::Address => MTL::Address, + TypeTag::Signer => return None, + TypeTag::Vector(tag) => MTL::Vector(Box::new(layout_of_primitive_typetag(tag)?)), + TypeTag::Struct(stag) => { + let StructTag { + address, + module, + name, + type_params: type_args, + } = &**stag; + let resolved_struct = (address, module.as_ident_str(), name.as_ident_str()); + // is id or.. + if resolved_struct == RESOLVED_SUI_ID { + MTL::Struct(Box::new(id::ID::layout())) + } else if resolved_struct == RESOLVED_ASCII_STR { + MTL::Struct(Box::new(move_ascii_str_layout())) + } else if resolved_struct == RESOLVED_UTF8_STR { + MTL::Struct(Box::new(move_utf8_str_layout())) + } else if resolved_struct == RESOLVED_STD_OPTION // is option of a primitive + && type_args.len() == 1 + && is_primitive_type_tag(&type_args[0]) + { + MTL::Vector(Box::new( + layout_of_primitive_typetag(&type_args[0]).unwrap(), + )) + } else { + return None; + } + } + }) } fn resolve_object_arg(idx: usize, arg: &JsonValue) -> Result { @@ -725,35 +716,18 @@ fn resolve_call_arg( arg: &SuiJsonValue, param: &SignatureToken, ) -> Result { - let (is_primitive, layout_opt) = primitive_type(view, type_args, param); - if is_primitive { - match layout_opt { - Some(layout) => { - return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err( - |e| { - anyhow!( - "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}", - param, - idx, - layout, - e - ) - }, - )?)); - } - None => { - debug_assert!( - false, - "Should be unreachable. All primitive type function args \ - should have a corresponding MoveLayout" - ); - bail!( - "Could not serialize argument of type {:?} at {}", + if let Some(layout) = primitive_type(view, type_args, param) { + return Ok(ResolvedCallArg::Pure(arg.to_bcs_bytes(&layout).map_err( + |e| { + anyhow!( + "Could not serialize argument of type {:?} at {} into {}. Got error: {:?}", param, - idx - ); - } - } + idx, + layout, + e + ) + }, + )?)); } // in terms of non-primitives we only currently support objects and "flat" (depth == 1) vectors diff --git a/crates/sui-types/src/base_types.rs b/crates/sui-types/src/base_types.rs index e31255a7167d8..d0e0b83f773d0 100644 --- a/crates/sui-types/src/base_types.rs +++ b/crates/sui-types/src/base_types.rs @@ -50,6 +50,7 @@ use move_binary_format::file_format::SignatureToken; use move_binary_format::CompiledModule; use move_bytecode_utils::resolve_struct; use move_core_types::account_address::AccountAddress; +use move_core_types::annotated_value as A; use move_core_types::ident_str; use move_core_types::identifier::IdentStr; use move_core_types::language_storage::ModuleId; @@ -440,6 +441,14 @@ pub fn is_primitive_type_tag(t: &TypeTag) -> bool { if resolved_struct == RESOLVED_SUI_ID { return true; } + // is utf8 string + if resolved_struct == RESOLVED_UTF8_STR { + return true; + } + // is ascii string + if resolved_struct == RESOLVED_ASCII_STR { + return true; + } // is option of a primitive resolved_struct == RESOLVED_STD_OPTION && type_args.len() == 1 @@ -893,6 +902,36 @@ pub const RESOLVED_UTF8_STR: (&AccountAddress, &IdentStr, &IdentStr) = ( pub const TX_CONTEXT_MODULE_NAME: &IdentStr = ident_str!("tx_context"); pub const TX_CONTEXT_STRUCT_NAME: &IdentStr = ident_str!("TxContext"); +pub fn move_ascii_str_layout() -> A::MoveStructLayout { + A::MoveStructLayout { + type_: StructTag { + address: MOVE_STDLIB_ADDRESS, + module: STD_ASCII_MODULE_NAME.to_owned(), + name: STD_ASCII_STRUCT_NAME.to_owned(), + type_params: vec![], + }, + fields: Box::new(vec![A::MoveFieldLayout::new( + ident_str!("bytes").into(), + A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)), + )]), + } +} + +pub fn move_utf8_str_layout() -> A::MoveStructLayout { + A::MoveStructLayout { + type_: StructTag { + address: MOVE_STDLIB_ADDRESS, + module: STD_UTF8_MODULE_NAME.to_owned(), + name: STD_UTF8_STRUCT_NAME.to_owned(), + type_params: vec![], + }, + fields: Box::new(vec![A::MoveFieldLayout::new( + ident_str!("bytes").into(), + A::MoveTypeLayout::Vector(Box::new(A::MoveTypeLayout::U8)), + )]), + } +} + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] pub struct TxContext { /// Signer/sender of the transaction diff --git a/crates/sui/src/client_ptb/builder.rs b/crates/sui/src/client_ptb/builder.rs index 8df2dbf90dd7e..9ac73b77c35f8 100644 --- a/crates/sui/src/client_ptb/builder.rs +++ b/crates/sui/src/client_ptb/builder.rs @@ -443,13 +443,13 @@ impl<'a> PTBBuilder<'a> { sp!(loc, arg): Spanned, param: &SignatureToken, ) -> PTBResult { - let (is_primitive, layout) = primitive_type(view, ty_args, param); + let layout = primitive_type(view, ty_args, param); // If it's a primitive value, see if we've already resolved this argument. Otherwise, we // need to resolve it. - if is_primitive { + if let Some(layout) = layout { return self - .resolve(loc.wrap(arg), ToPure::new_from_layout(layout.unwrap())) + .resolve(loc.wrap(arg), ToPure::new_from_layout(layout)) .await; } diff --git a/crates/sui/tests/ptb_files/resolution/type_param_resolution.ptb b/crates/sui/tests/ptb_files/resolution/type_param_resolution.ptb new file mode 100644 index 0000000000000..a7409c7eb614f --- /dev/null +++ b/crates/sui/tests/ptb_files/resolution/type_param_resolution.ptb @@ -0,0 +1,4 @@ +--move-call 0x2::table::new +--assign table +--move-call 0x2::table::add table @0x2 0 +--transfer-objects [table] @0x0 diff --git a/crates/sui/tests/snapshots/ptb_files_tests__type_param_resolution.ptb.snap b/crates/sui/tests/snapshots/ptb_files_tests__type_param_resolution.ptb.snap new file mode 100644 index 0000000000000..8afcce60d97d0 --- /dev/null +++ b/crates/sui/tests/snapshots/ptb_files_tests__type_param_resolution.ptb.snap @@ -0,0 +1,22 @@ +--- +source: crates/sui/tests/ptb_files_tests.rs +expression: "results.join(\"\\n\")" +--- + === PREVIEW === +╭───────────────────────────────────────────────────────────────╮ +│ PTB Preview │ +├──────────────────┬────────────────────────────────────────────┤ +│ command │ values │ +├──────────────────┼────────────────────────────────────────────┤ +│ move-call │ 0x2::table::new │ +│ assign │ table │ +│ move-call │ 0x2::table::add table @0x2 0 │ +│ transfer-objects │ [table] @0x0 │ +╰──────────────────┴────────────────────────────────────────────╯ + === BUILT PTB === +Input 0: Pure([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]) +Input 1: Pure([0, 0, 0, 0, 0, 0, 0, 0]) +Input 2: Pure([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +Command 0: MoveCall(0x0000000000000000000000000000000000000000000000000000000000000002::table::new()) +Command 1: MoveCall(0x0000000000000000000000000000000000000000000000000000000000000002::table::add(Result(0),Input(0),Input(1))) +Command 2: TransferObjects([Result(0)],Input(2))