From aee247b1222b3c9326d9b801ca0057e81ac34255 Mon Sep 17 00:00:00 2001 From: Todd Nowacki Date: Fri, 17 Mar 2023 11:48:06 -0700 Subject: [PATCH] [tests][programmable transactions] Add tests for MakeMoveVec (#9454) ## Description - Added transactional tests for objects - Added "generated" tests for primitive types - BCS bytes are now always checked for `MakeMoveVec` primitive arguments. And checked before args reach the VM. This isn't strictly necessary for safety, but provides a more predicable UX. ## Test Plan - It is tests --- If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process. ### Type of Change (Check all that apply) - [ ] user-visible impact - [ ] breaking change for a client SDKs - [X] breaking change for FNs (FN binary must upgrade) - [X] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [X] necessitate either a data wipe or data migration ### Release notes --- Cargo.lock | 1 + .../tests/programmable/make_vec_objects.exp | 30 ++ .../tests/programmable/make_vec_objects.move | 81 +++ .../sui/move_call_args_type_mismatch.exp | 4 +- crates/sui-adapter/src/adapter.rs | 32 +- .../programmable_transactions/execution.rs | 132 +++-- crates/sui-core/Cargo.toml | 1 + .../Move.lock | 0 .../Move.toml | 4 +- .../sources/entry_point_types.move} | 14 +- .../src/unit_tests/move_integration_tests.rs | 502 +++++++++++++++++- .../src/programmable_transaction_builder.rs | 2 +- .../test/e2e/entry-point-string.test.ts | 4 +- 13 files changed, 691 insertions(+), 116 deletions(-) create mode 100644 crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.exp create mode 100644 crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.move rename crates/sui-core/src/unit_tests/data/{entry_point_string => entry_point_types}/Move.lock (100%) rename crates/sui-core/src/unit_tests/data/{entry_point_string => entry_point_types}/Move.toml (65%) rename crates/sui-core/src/unit_tests/data/{entry_point_string/sources/entry_point_string.move => entry_point_types/sources/entry_point_types.move} (75%) diff --git a/Cargo.lock b/Cargo.lock index 60db7f34544db..b85623fd28b19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8973,6 +8973,7 @@ dependencies = [ "sui-simulator", "sui-storage", "sui-types", + "sui-verifier", "tap", "telemetry-subscribers", "tempfile", diff --git a/crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.exp b/crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.exp new file mode 100644 index 0000000000000..d60363cd05e23 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.exp @@ -0,0 +1,30 @@ +processed 8 tasks + +init: +A: object(100), B: object(101) + +task 1 'publish'. lines 8-47: +created: object(106) +written: object(105) + +task 2 'programmable'. lines 48-55: +written: object(107) + +task 3 'programmable'. lines 56-63: +written: object(108) + +task 4 'programmable'. lines 64-68: +written: object(109) + +task 5 'programmable'. lines 69-71: +created: object(111) +written: object(110) + +task 6 'view-object'. lines 73-73: +Owner: Account Address ( A ) +Version: 2 +Contents: test::m1::Pub {id: sui::object::UID {id: sui::object::ID {bytes: fake(111)}}, value: 112u64} + +task 7 'programmable'. lines 75-81: +written: object(112) +deleted: object(111) diff --git a/crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.move b/crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.move new file mode 100644 index 0000000000000..293ace23fbdf2 --- /dev/null +++ b/crates/sui-adapter-transactional-tests/tests/programmable/make_vec_objects.move @@ -0,0 +1,81 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// tessts various vector instantions with objects + +//# init --addresses test=0x0 --accounts A B + +//# publish +module test::m1 { + use sui::object::{Self, UID}; + use sui::tx_context::TxContext; + use std::vector; + + struct Pub has key, store { + id: UID, + value: u64, + } + + struct Cap {} + + struct Cup has key, store { + id: UID, + value: T, + } + + public fun new(ctx: &mut TxContext): Pub { + Pub { id: object::new(ctx), value: 112 } + } + + public fun cup(value: T, ctx: &mut TxContext): Cup { + Cup { id: object::new(ctx), value } + } + + public fun cap(): Cap { + Cap {} + } + + public fun pubs(v: vector) { + while (!vector::is_empty(&v)) { + let Pub { id, value: _ } = vector::pop_back(&mut v); + object::delete(id); + }; + vector::destroy_empty(v); + } +} + +// objects +//# programmable --sender A +//> 0: test::m1::new(); +//> 1: test::m1::new(); +//> 2: test::m1::new(); +//> 3: MakeMoveVec([Result(0), Result(1), Result(2)]); +//> test::m1::pubs(Result(3)); + +// annotated objects +//# programmable --sender A +//> 0: test::m1::new(); +//> 1: test::m1::new(); +//> 2: test::m1::new(); +//> 3: MakeMoveVec([Result(0), Result(1), Result(2)]); +//> test::m1::pubs(Result(3)); + +// empty objects +//# programmable --sender A +//> 0: MakeMoveVec([]); +//> test::m1::pubs(Result(0)); + +// mixed new and old. Send an object to A and mix it in a vector with the newly created ones. +//# programmable --sender A --inputs @A +//> 0: test::m1::new(); +//> TransferObjects([Result(0)], Input(0)); + +//# view-object 111 + +//# programmable --sender A --inputs object(111) +//> 0: test::m1::new(); +//> 1: test::m1::new(); +//> 2: test::m1::new(); +// use Input and new objects +//> 3: MakeMoveVec([Result(0), Result(1), Input(0), Result(2)]); +//> test::m1::pubs(Result(3)); diff --git a/crates/sui-adapter-transactional-tests/tests/sui/move_call_args_type_mismatch.exp b/crates/sui-adapter-transactional-tests/tests/sui/move_call_args_type_mismatch.exp index 8c25f899c67e7..ab73934940c93 100644 --- a/crates/sui-adapter-transactional-tests/tests/sui/move_call_args_type_mismatch.exp +++ b/crates/sui-adapter-transactional-tests/tests/sui/move_call_args_type_mismatch.exp @@ -9,5 +9,5 @@ Error: Transaction Effects Status: Arity mismatch for Move function. The number Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: ArityMismatch, source: Some("Expected 2 arguments calling function 'create', but found 1"), command: Some(0) } } task 3 'run'. lines 17-17: -Error: Transaction Effects Status: Move Bytecode Verification Error. Please run the Bytecode Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: VMVerificationOrDeserializationError, source: Some(VMError { major_status: FAILED_TO_DESERIALIZE_ARGUMENT, sub_status: None, message: None, exec_state: None, location: Undefined, indices: [], offsets: [] }), command: Some(0) } } +Error: Transaction Effects Status: Invalid command argument at 1. The argument cannot be deserialized into a value of the specified type +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: CommandArgumentError { arg_idx: 1, kind: InvalidBCSBytes }, source: Some("Function expects address but provided argument's value does not match"), command: Some(0) } } diff --git a/crates/sui-adapter/src/adapter.rs b/crates/sui-adapter/src/adapter.rs index 2dbf9f0cae793..2f06bf04bc36d 100644 --- a/crates/sui-adapter/src/adapter.rs +++ b/crates/sui-adapter/src/adapter.rs @@ -30,7 +30,7 @@ use move_vm_runtime::{ use crate::{ execution_mode::ExecutionMode, - programmable_transactions::execution::{special_argument_validate, SpecialArgumentLayout}, + programmable_transactions::execution::{bcs_argument_validate, PrimitiveArgumentLayout}, }; use sui_framework::natives::{object_runtime::ObjectRuntime, NativesCostTable}; use sui_protocol_config::ProtocolConfig; @@ -257,7 +257,7 @@ pub fn resolve_and_type_check( ); } if let Some(layout) = additional_validation_layout(view, param_type) { - special_argument_validate(&arg, idx as u16, layout)?; + bcs_argument_validate(&arg, idx as u16, layout)?; } } CheckCallArg::Object(ObjectArg::ImmOrOwnedObject(ref_)) => { @@ -333,7 +333,7 @@ pub fn resolve_and_type_check( fn additional_validation_layout( view: &BinaryIndexedView, param_type: &SignatureToken, -) -> Option { +) -> Option { match param_type { // should be ruled out above SignatureToken::Reference(_) @@ -343,22 +343,22 @@ fn additional_validation_layout( // actually checking this requires a VM instance to load the type arguments SignatureToken::TypeParameter(_) => None, // primitives - SignatureToken::Bool => Some(SpecialArgumentLayout::Bool), - SignatureToken::U8 => Some(SpecialArgumentLayout::U8), - SignatureToken::U16 => Some(SpecialArgumentLayout::U16), - SignatureToken::U32 => Some(SpecialArgumentLayout::U32), - SignatureToken::U64 => Some(SpecialArgumentLayout::U64), - SignatureToken::U128 => Some(SpecialArgumentLayout::U128), - SignatureToken::U256 => Some(SpecialArgumentLayout::U256), - SignatureToken::Address => Some(SpecialArgumentLayout::Address), + SignatureToken::Bool => Some(PrimitiveArgumentLayout::Bool), + SignatureToken::U8 => Some(PrimitiveArgumentLayout::U8), + SignatureToken::U16 => Some(PrimitiveArgumentLayout::U16), + SignatureToken::U32 => Some(PrimitiveArgumentLayout::U32), + SignatureToken::U64 => Some(PrimitiveArgumentLayout::U64), + SignatureToken::U128 => Some(PrimitiveArgumentLayout::U128), + SignatureToken::U256 => Some(PrimitiveArgumentLayout::U256), + SignatureToken::Address => Some(PrimitiveArgumentLayout::Address), SignatureToken::Vector(inner) => additional_validation_layout(view, inner) - .map(|layout| SpecialArgumentLayout::Vector(Box::new(layout))), + .map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout))), SignatureToken::StructInstantiation(idx, targs) => { let resolved_struct = sui_verifier::resolve_struct(view, *idx); if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 { additional_validation_layout(view, &targs[0]) - .map(|layout| SpecialArgumentLayout::Option(Box::new(layout))) + .map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout))) } else { None } @@ -366,11 +366,11 @@ fn additional_validation_layout( SignatureToken::Struct(idx) => { let resolved_struct = sui_verifier::resolve_struct(view, *idx); if resolved_struct == RESOLVED_SUI_ID { - Some(SpecialArgumentLayout::Address) + Some(PrimitiveArgumentLayout::Address) } else if resolved_struct == RESOLVED_ASCII_STR { - Some(SpecialArgumentLayout::Ascii) + Some(PrimitiveArgumentLayout::Ascii) } else if resolved_struct == RESOLVED_UTF8_STR { - Some(SpecialArgumentLayout::UTF8) + Some(PrimitiveArgumentLayout::UTF8) } else { None } diff --git a/crates/sui-adapter/src/programmable_transactions/execution.rs b/crates/sui-adapter/src/programmable_transactions/execution.rs index e28478b89abce..2c42d2ae17313 100644 --- a/crates/sui-adapter/src/programmable_transactions/execution.rs +++ b/crates/sui-adapter/src/programmable_transactions/execution.rs @@ -958,7 +958,7 @@ fn check_param_type, Mode: ExecutionMode>( msg, )); }; - special_argument_validate(bytes, idx as u16, layout)?; + bcs_argument_validate(bytes, idx as u16, layout)?; return Ok(()); } Value::Raw(RawValueType::Loaded { ty, abilities, .. }, _) => { @@ -1031,24 +1031,24 @@ pub fn is_tx_context>( fn primitive_serialization_layout>( context: &mut ExecutionContext, param_ty: &Type, -) -> Result, ExecutionError> { +) -> Result, ExecutionError> { Ok(match param_ty { Type::Signer => return Ok(None), Type::Reference(_) | Type::MutableReference(_) | Type::TyParam(_) => { invariant_violation!("references and type parameters should be checked elsewhere") } - Type::Bool => Some(SpecialArgumentLayout::Bool), - Type::U8 => Some(SpecialArgumentLayout::U8), - Type::U16 => Some(SpecialArgumentLayout::U16), - Type::U32 => Some(SpecialArgumentLayout::U32), - Type::U64 => Some(SpecialArgumentLayout::U64), - Type::U128 => Some(SpecialArgumentLayout::U128), - Type::U256 => Some(SpecialArgumentLayout::U256), - Type::Address => Some(SpecialArgumentLayout::Address), + Type::Bool => Some(PrimitiveArgumentLayout::Bool), + Type::U8 => Some(PrimitiveArgumentLayout::U8), + Type::U16 => Some(PrimitiveArgumentLayout::U16), + Type::U32 => Some(PrimitiveArgumentLayout::U32), + Type::U64 => Some(PrimitiveArgumentLayout::U64), + Type::U128 => Some(PrimitiveArgumentLayout::U128), + Type::U256 => Some(PrimitiveArgumentLayout::U256), + Type::Address => Some(PrimitiveArgumentLayout::Address), Type::Vector(inner) => { let info_opt = primitive_serialization_layout(context, inner)?; - info_opt.map(|layout| SpecialArgumentLayout::Vector(Box::new(layout))) + info_opt.map(|layout| PrimitiveArgumentLayout::Vector(Box::new(layout))) } Type::StructInstantiation(idx, targs) => { let Some(s) = context.session.get_struct_type(*idx) else { @@ -1058,7 +1058,7 @@ fn primitive_serialization_layout>( // is option of a string if resolved_struct == RESOLVED_STD_OPTION && targs.len() == 1 { let info_opt = primitive_serialization_layout(context, &targs[0])?; - info_opt.map(|layout| SpecialArgumentLayout::Option(Box::new(layout))) + info_opt.map(|layout| PrimitiveArgumentLayout::Option(Box::new(layout))) } else { None } @@ -1069,11 +1069,11 @@ fn primitive_serialization_layout>( }; let resolved_struct = get_struct_ident(&s); if resolved_struct == RESOLVED_SUI_ID { - Some(SpecialArgumentLayout::Address) + Some(PrimitiveArgumentLayout::Address) } else if resolved_struct == RESOLVED_ASCII_STR { - Some(SpecialArgumentLayout::Ascii) + Some(PrimitiveArgumentLayout::Ascii) } else if resolved_struct == RESOLVED_UTF8_STR { - Some(SpecialArgumentLayout::UTF8) + Some(PrimitiveArgumentLayout::UTF8) } else { None } @@ -1089,11 +1089,11 @@ fn primitive_serialization_layout>( /// There is validation to do on top of the BCS layout. Currently only needed for /// strings #[derive(Debug)] -pub enum SpecialArgumentLayout { +pub enum PrimitiveArgumentLayout { /// An option - Option(Box), + Option(Box), /// A vector - Vector(Box), + Vector(Box), /// An ASCII encoded string Ascii, /// A UTF8 encoded string @@ -1108,42 +1108,40 @@ pub enum SpecialArgumentLayout { U256, Address, } -impl SpecialArgumentLayout { + +impl PrimitiveArgumentLayout { /// returns true iff all BCS compatible bytes are actually values for this type. /// For example, this function returns false for Option and Strings since they need additional /// validation. - fn bcs_only(&self) -> bool { + pub fn bcs_only(&self) -> bool { match self { // have additional restrictions past BCS - SpecialArgumentLayout::Option(_) - | SpecialArgumentLayout::Ascii - | SpecialArgumentLayout::UTF8 => false, + PrimitiveArgumentLayout::Option(_) + | PrimitiveArgumentLayout::Ascii + | PrimitiveArgumentLayout::UTF8 => false, // Move primitives are BCS compatible and do not need additional validation - SpecialArgumentLayout::Bool - | SpecialArgumentLayout::U8 - | SpecialArgumentLayout::U16 - | SpecialArgumentLayout::U32 - | SpecialArgumentLayout::U64 - | SpecialArgumentLayout::U128 - | SpecialArgumentLayout::U256 - | SpecialArgumentLayout::Address => true, + PrimitiveArgumentLayout::Bool + | PrimitiveArgumentLayout::U8 + | PrimitiveArgumentLayout::U16 + | PrimitiveArgumentLayout::U32 + | PrimitiveArgumentLayout::U64 + | PrimitiveArgumentLayout::U128 + | PrimitiveArgumentLayout::U256 + | PrimitiveArgumentLayout::Address => true, // vector only needs validation if it's inner type does - SpecialArgumentLayout::Vector(inner) => inner.bcs_only(), + PrimitiveArgumentLayout::Vector(inner) => inner.bcs_only(), } } } /// Checks the bytes against the `SpecialArgumentLayout` using `bcs`. It does not actually generate -/// the deserialized value, only walks the bytes -pub fn special_argument_validate( +/// the deserialized value, only walks the bytes. While not necessary if the layout does not contain +/// special arguments (e.g. Option or String) we check the BCS bytes for predictability +pub fn bcs_argument_validate( bytes: &[u8], idx: u16, - layout: SpecialArgumentLayout, + layout: PrimitiveArgumentLayout, ) -> Result<(), ExecutionError> { - if layout.bcs_only() { - // will be covered in the value is deserialized by the VM - return Ok(()); - } bcs::from_bytes_seed(&layout, bytes).map_err(|_| { ExecutionError::new_with_source( ExecutionErrorKind::command_argument_error(CommandArgumentError::InvalidBCSBytes, idx), @@ -1152,7 +1150,7 @@ pub fn special_argument_validate( }) } -impl<'d> serde::de::DeserializeSeed<'d> for &SpecialArgumentLayout { +impl<'d> serde::de::DeserializeSeed<'d> for &PrimitiveArgumentLayout { type Value = (); fn deserialize>( self, @@ -1160,7 +1158,7 @@ impl<'d> serde::de::DeserializeSeed<'d> for &SpecialArgumentLayout { ) -> Result { use serde::de::Error; match self { - SpecialArgumentLayout::Ascii => { + PrimitiveArgumentLayout::Ascii => { let s: &str = serde::Deserialize::deserialize(deserializer)?; if !s.is_ascii() { Err(D::Error::custom("not an ascii string")) @@ -1168,47 +1166,47 @@ impl<'d> serde::de::DeserializeSeed<'d> for &SpecialArgumentLayout { Ok(()) } } - SpecialArgumentLayout::UTF8 => { + PrimitiveArgumentLayout::UTF8 => { deserializer.deserialize_string(serde::de::IgnoredAny)?; Ok(()) } - SpecialArgumentLayout::Option(layout) => { + PrimitiveArgumentLayout::Option(layout) => { deserializer.deserialize_option(OptionElementVisitor(layout)) } - SpecialArgumentLayout::Vector(layout) => { + PrimitiveArgumentLayout::Vector(layout) => { deserializer.deserialize_seq(VectorElementVisitor(layout)) } // primitive move value cases, which are hit to make sure the correct number of bytes // are removed for elements of an option/vector - SpecialArgumentLayout::Bool => { + PrimitiveArgumentLayout::Bool => { deserializer.deserialize_bool(serde::de::IgnoredAny)?; Ok(()) } - SpecialArgumentLayout::U8 => { + PrimitiveArgumentLayout::U8 => { deserializer.deserialize_u8(serde::de::IgnoredAny)?; Ok(()) } - SpecialArgumentLayout::U16 => { + PrimitiveArgumentLayout::U16 => { deserializer.deserialize_u16(serde::de::IgnoredAny)?; Ok(()) } - SpecialArgumentLayout::U32 => { + PrimitiveArgumentLayout::U32 => { deserializer.deserialize_u32(serde::de::IgnoredAny)?; Ok(()) } - SpecialArgumentLayout::U64 => { + PrimitiveArgumentLayout::U64 => { deserializer.deserialize_u64(serde::de::IgnoredAny)?; Ok(()) } - SpecialArgumentLayout::U128 => { + PrimitiveArgumentLayout::U128 => { deserializer.deserialize_u128(serde::de::IgnoredAny)?; Ok(()) } - SpecialArgumentLayout::U256 => { + PrimitiveArgumentLayout::U256 => { U256::deserialize(deserializer)?; Ok(()) } - SpecialArgumentLayout::Address => { + PrimitiveArgumentLayout::Address => { SuiAddress::deserialize(deserializer)?; Ok(()) } @@ -1216,7 +1214,7 @@ impl<'d> serde::de::DeserializeSeed<'d> for &SpecialArgumentLayout { } } -struct VectorElementVisitor<'a>(&'a SpecialArgumentLayout); +struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout); impl<'d, 'a> serde::de::Visitor<'d> for VectorElementVisitor<'a> { type Value = (); @@ -1234,7 +1232,7 @@ impl<'d, 'a> serde::de::Visitor<'d> for VectorElementVisitor<'a> { } } -struct OptionElementVisitor<'a>(&'a SpecialArgumentLayout); +struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout); impl<'d, 'a> serde::de::Visitor<'d> for OptionElementVisitor<'a> { type Value = (); @@ -1258,29 +1256,29 @@ impl<'d, 'a> serde::de::Visitor<'d> for OptionElementVisitor<'a> { } } -impl fmt::Display for SpecialArgumentLayout { +impl fmt::Display for PrimitiveArgumentLayout { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - SpecialArgumentLayout::Vector(inner) => { + PrimitiveArgumentLayout::Vector(inner) => { write!(f, "vector<{inner}>") } - SpecialArgumentLayout::Option(inner) => { + PrimitiveArgumentLayout::Option(inner) => { write!(f, "std::option::Option<{inner}>") } - SpecialArgumentLayout::Ascii => { + PrimitiveArgumentLayout::Ascii => { write!(f, "std::{}::{}", RESOLVED_ASCII_STR.1, RESOLVED_ASCII_STR.2) } - SpecialArgumentLayout::UTF8 => { + PrimitiveArgumentLayout::UTF8 => { write!(f, "std::{}::{}", RESOLVED_UTF8_STR.1, RESOLVED_UTF8_STR.2) } - SpecialArgumentLayout::Bool => write!(f, "bool"), - SpecialArgumentLayout::U8 => write!(f, "u8"), - SpecialArgumentLayout::U16 => write!(f, "u16"), - SpecialArgumentLayout::U32 => write!(f, "u32"), - SpecialArgumentLayout::U64 => write!(f, "u64"), - SpecialArgumentLayout::U128 => write!(f, "u128"), - SpecialArgumentLayout::U256 => write!(f, "u256"), - SpecialArgumentLayout::Address => write!(f, "address"), + PrimitiveArgumentLayout::Bool => write!(f, "bool"), + PrimitiveArgumentLayout::U8 => write!(f, "u8"), + PrimitiveArgumentLayout::U16 => write!(f, "u16"), + PrimitiveArgumentLayout::U32 => write!(f, "u32"), + PrimitiveArgumentLayout::U64 => write!(f, "u64"), + PrimitiveArgumentLayout::U128 => write!(f, "u128"), + PrimitiveArgumentLayout::U256 => write!(f, "u256"), + PrimitiveArgumentLayout::Address => write!(f, "address"), } } } diff --git a/crates/sui-core/Cargo.toml b/crates/sui-core/Cargo.toml index 59b4690982ace..1f9ea1cfbaefb 100644 --- a/crates/sui-core/Cargo.toml +++ b/crates/sui-core/Cargo.toml @@ -88,6 +88,7 @@ serde-reflection = "0.3.6" serde_yaml = "0.8.26" pretty_assertions = "1.2.1" narwhal-test-utils = { path = "../../narwhal/test-utils" } +sui-verifier = { path = "../sui-verifier" } [target.'cfg(not(target_env = "msvc"))'.dev-dependencies] pprof = { version = "0.11.0", features = ["cpp", "frame-pointer"] } diff --git a/crates/sui-core/src/unit_tests/data/entry_point_string/Move.lock b/crates/sui-core/src/unit_tests/data/entry_point_types/Move.lock similarity index 100% rename from crates/sui-core/src/unit_tests/data/entry_point_string/Move.lock rename to crates/sui-core/src/unit_tests/data/entry_point_types/Move.lock diff --git a/crates/sui-core/src/unit_tests/data/entry_point_string/Move.toml b/crates/sui-core/src/unit_tests/data/entry_point_types/Move.toml similarity index 65% rename from crates/sui-core/src/unit_tests/data/entry_point_string/Move.toml rename to crates/sui-core/src/unit_tests/data/entry_point_types/Move.toml index 31cdcc78125c5..0aab7bc51de3f 100644 --- a/crates/sui-core/src/unit_tests/data/entry_point_string/Move.toml +++ b/crates/sui-core/src/unit_tests/data/entry_point_types/Move.toml @@ -1,9 +1,9 @@ [package] -name = "entry_point_string" +name = "entry_point_types" version = "0.0.1" [dependencies] Sui = { local = "../../../../../sui-framework" } [addresses] -entry_point_string = "0x0" +entry_point_types = "0x0" diff --git a/crates/sui-core/src/unit_tests/data/entry_point_string/sources/entry_point_string.move b/crates/sui-core/src/unit_tests/data/entry_point_types/sources/entry_point_types.move similarity index 75% rename from crates/sui-core/src/unit_tests/data/entry_point_string/sources/entry_point_string.move rename to crates/sui-core/src/unit_tests/data/entry_point_types/sources/entry_point_types.move index 62afa8fe9ab30..a3b4069aa7d54 100644 --- a/crates/sui-core/src/unit_tests/data/entry_point_string/sources/entry_point_string.move +++ b/crates/sui-core/src/unit_tests/data/entry_point_types/sources/entry_point_types.move @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -module entry_point_string::entry_point_string { +module entry_point_types::entry_point_types { use std::ascii; use std::string; use sui::tx_context::TxContext; @@ -43,4 +43,16 @@ module entry_point_string::entry_point_string { _: Option>> ) { } + + public fun drop_all(v: vector, expected_len: u64) { + let actual = 0; + while ((!vector::is_empty(&v))) { + vector::pop_back(&mut v); + actual = actual + 1; + }; + vector::destroy_empty(v); + assert!(actual == expected_len, 0); + } + + public fun id(x: T): T { x } } diff --git a/crates/sui-core/src/unit_tests/move_integration_tests.rs b/crates/sui-core/src/unit_tests/move_integration_tests.rs index 9b277542d5ef1..02c86aee83c68 100644 --- a/crates/sui-core/src/unit_tests/move_integration_tests.rs +++ b/crates/sui-core/src/unit_tests/move_integration_tests.rs @@ -7,7 +7,12 @@ use crate::authority::authority_tests::{ call_move, call_move_, execute_programmable_transaction, init_state_with_ids, send_and_confirm_transaction, TestCallArg, }; -use move_core_types::identifier::Identifier; +use move_core_types::{ + account_address::AccountAddress, + identifier::{IdentStr, Identifier}, + language_storage::StructTag, + u256::U256, +}; use sui_types::{ error::ExecutionErrorKind, object::Data, programmable_transaction_builder::ProgrammableTransactionBuilder, @@ -30,6 +35,9 @@ use std::fs::File; use std::io::Read; use std::{collections::HashSet, path::PathBuf}; use std::{env, str::FromStr}; +use sui_verifier::entry_points_verifier::{ + RESOLVED_ASCII_STR, RESOLVED_STD_OPTION, RESOLVED_UTF8_STR, +}; const MAX_GAS: u64 = 10000; @@ -1738,7 +1746,7 @@ async fn test_entry_point_string() { &sender, &sender_key, &gas, - "entry_point_string", + "entry_point_types", /* with_unpublished_deps */ false, ) .await; @@ -1753,7 +1761,7 @@ async fn test_entry_point_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "ascii_arg", vec![], vec![ @@ -1775,7 +1783,7 @@ async fn test_entry_point_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "utf8_arg", vec![], vec![ @@ -1797,7 +1805,7 @@ async fn test_entry_point_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "utf8_arg", vec![], vec![ @@ -1822,7 +1830,7 @@ async fn test_nested_string() { &sender, &sender_key, &gas, - "entry_point_string", + "entry_point_types", /* with_unpublished_deps */ false, ) .await; @@ -1836,7 +1844,7 @@ async fn test_nested_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_bcs)], @@ -1854,7 +1862,7 @@ async fn test_nested_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "vec_option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_bcs)], @@ -1872,7 +1880,7 @@ async fn test_nested_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "option_vec_option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_bcs)], @@ -1890,7 +1898,7 @@ async fn test_nested_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_bcs)], @@ -1908,7 +1916,7 @@ async fn test_nested_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "vec_option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_bcs)], @@ -1926,7 +1934,7 @@ async fn test_nested_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "vec_option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_bcs)], @@ -1944,7 +1952,7 @@ async fn test_nested_string() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "option_vec_option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_bcs)], @@ -1966,7 +1974,7 @@ async fn test_entry_point_string_vec() { &sender, &sender_key, &gas, - "entry_point_string", + "entry_point_types", /* with_unpublished_deps */ false, ) .await; @@ -1982,7 +1990,7 @@ async fn test_entry_point_string_vec() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "utf8_vec_arg", vec![], vec![ @@ -2007,7 +2015,7 @@ async fn test_entry_point_string_error() { &sender, &sender_key, &gas, - "entry_point_string", + "entry_point_types", /* with_unpublished_deps */ false, ) .await; @@ -2022,7 +2030,7 @@ async fn test_entry_point_string_error() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "ascii_arg", vec![], vec![ @@ -2057,7 +2065,7 @@ async fn test_entry_point_string_error() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "ascii_arg", vec![], vec![ @@ -2092,7 +2100,7 @@ async fn test_entry_point_string_error() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "utf8_arg", vec![], vec![ @@ -2126,7 +2134,7 @@ async fn test_entry_point_string_vec_error() { &sender, &sender_key, &gas, - "entry_point_string", + "entry_point_types", /* with_unpublished_deps */ false, ) .await; @@ -2147,7 +2155,7 @@ async fn test_entry_point_string_vec_error() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "utf8_vec_arg", vec![], vec![ @@ -2181,7 +2189,7 @@ async fn test_entry_point_string_option_error() { &sender, &sender_key, &gas, - "entry_point_string", + "entry_point_types", /* with_unpublished_deps */ false, ) .await; @@ -2195,7 +2203,7 @@ async fn test_entry_point_string_option_error() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "option_ascii_arg", vec![], vec![TestCallArg::Pure(utf_option_bcs)], @@ -2225,7 +2233,7 @@ async fn test_entry_point_string_option_error() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_vec_bcs)], @@ -2253,7 +2261,7 @@ async fn test_entry_point_string_option_error() { &sender, &sender_key, &package.0, - "entry_point_string", + "entry_point_types", "option_utf8_arg", vec![], vec![TestCallArg::Pure(utf_str_vec_bcs)], @@ -2272,6 +2280,450 @@ async fn test_entry_point_string_option_error() { ); } +async fn test_make_move_vec_for_type( + authority: &AuthorityState, + gas: &ObjectID, + sender: &SuiAddress, + sender_key: &AccountKeyPair, + package_id: ObjectID, + t: TypeTag, + value: T, +) { + fn make_and_drop( + builder: &mut ProgrammableTransactionBuilder, + package: ObjectID, + t: &TypeTag, + args: Vec, + ) { + let n = builder.pure(args.len() as u64).unwrap(); + let vec = builder.command(Command::MakeMoveVec(Some(t.clone()), args)); + builder.programmable_move_call( + package, + Identifier::new("entry_point_types").unwrap(), + Identifier::new("drop_all").unwrap(), + vec![t.clone()], + vec![vec, n], + ); + } + // empty + let mut builder = ProgrammableTransactionBuilder::new(); + make_and_drop(&mut builder, package_id, &t, vec![]); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!(effects.status(), &ExecutionStatus::Success); + assert!(effects.created().is_empty()); + assert_eq!(effects.mutated().len(), 1); + assert!(effects.unwrapped().is_empty()); + assert!(effects.deleted().is_empty()); + assert!(effects.unwrapped_then_deleted().is_empty()); + + // single + let mut builder = ProgrammableTransactionBuilder::new(); + let args = vec![builder.pure(value.clone()).unwrap()]; + make_and_drop(&mut builder, package_id, &t, args); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!(effects.status(), &ExecutionStatus::Success); + assert!(effects.created().is_empty()); + assert_eq!(effects.mutated().len(), 1); + assert!(effects.unwrapped().is_empty()); + assert!(effects.deleted().is_empty()); + assert!(effects.unwrapped_then_deleted().is_empty()); + + // two + let mut builder = ProgrammableTransactionBuilder::new(); + let args = vec![ + builder.pure(value.clone()).unwrap(), + builder.pure(value.clone()).unwrap(), + ]; + make_and_drop(&mut builder, package_id, &t, args); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!(effects.status(), &ExecutionStatus::Success); + assert!(effects.created().is_empty()); + assert_eq!(effects.mutated().len(), 1); + assert!(effects.unwrapped().is_empty()); + assert!(effects.deleted().is_empty()); + assert!(effects.unwrapped_then_deleted().is_empty()); + + // with move call value + let mut builder = ProgrammableTransactionBuilder::new(); + let arg = builder.pure(value.clone()).unwrap(); + let id_result = builder.programmable_move_call( + package_id, + Identifier::new("entry_point_types").unwrap(), + Identifier::new("id").unwrap(), + vec![t.clone()], + vec![arg], + ); + let args = vec![arg, id_result, arg]; + make_and_drop(&mut builder, package_id, &t, args); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!(effects.status(), &ExecutionStatus::Success); + assert!(effects.created().is_empty()); + assert_eq!(effects.mutated().len(), 1); + assert!(effects.unwrapped().is_empty()); + assert!(effects.deleted().is_empty()); + assert!(effects.unwrapped_then_deleted().is_empty()); + + // nested + let mut builder = ProgrammableTransactionBuilder::new(); + let arg = builder.pure(value).unwrap(); + let id_result = builder.programmable_move_call( + package_id, + Identifier::new("entry_point_types").unwrap(), + Identifier::new("id").unwrap(), + vec![t.clone()], + vec![arg], + ); + let inner_args = vec![arg, id_result, arg]; + let vec = builder.command(Command::MakeMoveVec(Some(t.clone()), inner_args)); + let args = vec![vec, vec, vec]; + make_and_drop( + &mut builder, + package_id, + &TypeTag::Vector(Box::new(t)), + args, + ); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!(effects.status(), &ExecutionStatus::Success); + assert!(effects.created().is_empty()); + assert_eq!(effects.mutated().len(), 1); + assert!(effects.unwrapped().is_empty()); + assert!(effects.deleted().is_empty()); + assert!(effects.unwrapped_then_deleted().is_empty()); +} + +macro_rules! make_vec_tests_for_type { + ($test:ident, $t:ty, $tag:expr, $value:expr) => { + #[tokio::test] + #[cfg_attr(msim, ignore)] + async fn $test() { + let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); + let gas = ObjectID::random(); + let authority = init_state_with_ids(vec![(sender, gas)]).await; + let package = build_and_publish_test_package( + &authority, + &sender, + &sender_key, + &gas, + "entry_point_types", + /* with_unpublished_deps */ false, + ) + .await; + let package_id = package.0; + test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + package_id, + $tag, + $value, + ) + .await; + test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + package_id, + TypeTag::Vector(Box::new($tag)), + Vec::<$t>::new(), + ) + .await; + test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + package_id, + TypeTag::Vector(Box::new($tag)), + vec![$value, $value], + ) + .await; + test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + package_id, + option_tag($tag), + None::<$t>, + ) + .await; + test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + package_id, + option_tag($tag), + Some($value), + ) + .await; + test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + package_id, + TypeTag::Vector(Box::new(option_tag($tag))), + vec![None, Some($value)], + ) + .await; + } + }; +} + +make_vec_tests_for_type!(test_make_move_vec_bool, bool, TypeTag::Bool, false); +make_vec_tests_for_type!(test_make_move_vec_u8, u8, TypeTag::U8, 0u8); +make_vec_tests_for_type!(test_make_move_vec_u16, u16, TypeTag::U16, 0u16); +make_vec_tests_for_type!(test_make_move_vec_u32, u32, TypeTag::U32, 0u32); +make_vec_tests_for_type!(test_make_move_vec_u64, u64, TypeTag::U64, 0u64); +make_vec_tests_for_type!(test_make_move_vec_u128, u128, TypeTag::U128, 0u128); +make_vec_tests_for_type!(test_make_move_vec_u256, U256, TypeTag::U256, U256::zero()); +make_vec_tests_for_type!( + test_make_move_vec_address, + SuiAddress, + TypeTag::Address, + SuiAddress::ZERO +); +make_vec_tests_for_type!( + test_make_move_vec_address_id, + ObjectID, + TypeTag::Struct(Box::new(sui_types::id::ID::type_())), + ObjectID::ZERO +); +make_vec_tests_for_type!(test_make_move_vec_utf8, &str, utf8_tag(), "❤️🧀"); +make_vec_tests_for_type!( + test_make_move_vec_ascii, + &str, + ascii_tag(), + "love and cheese" +); + +async fn error_test_make_move_vec_for_type( + authority: &AuthorityState, + gas: &ObjectID, + sender: &SuiAddress, + sender_key: &AccountKeyPair, + t: TypeTag, + value: T, +) { + // single without a type argument + let mut builder = ProgrammableTransactionBuilder::new(); + let arg = builder.pure(value.clone()).unwrap(); + builder.command(Command::MakeMoveVec(None, vec![arg])); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!( + effects.status(), + &ExecutionStatus::Failure { + error: ExecutionFailureStatus::command_argument_error( + CommandArgumentError::TypeMismatch, + 0 + ), + command: Some(0) + } + ); + + // invalid BCS for any Move value + const ALWAYS_INVALID_BYTES: &[u8] = &[255, 255, 255]; + + // invalid bcs + let mut builder = ProgrammableTransactionBuilder::new(); + let args = vec![builder.pure_bytes(ALWAYS_INVALID_BYTES.to_vec(), false)]; + builder.command(Command::MakeMoveVec(Some(t.clone()), args)); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!( + effects.status(), + &ExecutionStatus::Failure { + error: ExecutionFailureStatus::command_argument_error( + CommandArgumentError::InvalidBCSBytes, + 0 + ), + command: Some(0) + } + ); + + // invalid bcs bytes at end + let mut builder = ProgrammableTransactionBuilder::new(); + let args = vec![ + builder.pure(value.clone()).unwrap(), + builder.pure(value.clone()).unwrap(), + builder.pure(value).unwrap(), + builder.pure_bytes(ALWAYS_INVALID_BYTES.to_vec(), false), + ]; + builder.command(Command::MakeMoveVec(Some(t.clone()), args)); + let pt = builder.finish(); + let effects = execute_programmable_transaction(authority, gas, sender, sender_key, pt) + .await + .unwrap(); + assert_eq!( + effects.status(), + &ExecutionStatus::Failure { + error: ExecutionFailureStatus::command_argument_error( + CommandArgumentError::InvalidBCSBytes, + 3, + ), + command: Some(0) + } + ); +} + +macro_rules! make_vec_error_tests_for_type { + ($test:ident, $t:ty, $tag:expr, $value:expr) => { + #[tokio::test] + #[cfg_attr(msim, ignore)] + async fn $test() { + let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); + let gas = ObjectID::random(); + let authority = init_state_with_ids(vec![(sender, gas)]).await; + error_test_make_move_vec_for_type(&authority, &gas, &sender, &sender_key, $tag, $value) + .await; + error_test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + TypeTag::Vector(Box::new($tag)), + Vec::<$t>::new(), + ) + .await; + error_test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + TypeTag::Vector(Box::new($tag)), + vec![$value, $value], + ) + .await; + error_test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + option_tag($tag), + None::<$t>, + ) + .await; + error_test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + option_tag($tag), + Some($value), + ) + .await; + error_test_make_move_vec_for_type( + &authority, + &gas, + &sender, + &sender_key, + TypeTag::Vector(Box::new(option_tag($tag))), + vec![None, Some($value)], + ) + .await; + } + }; +} + +make_vec_error_tests_for_type!(test_error_make_move_vec_bool, bool, TypeTag::Bool, false); +make_vec_error_tests_for_type!(test_error_make_move_vec_u8, u8, TypeTag::U8, 0u8); +make_vec_error_tests_for_type!(test_error_make_move_vec_u16, u16, TypeTag::U16, 0u16); +make_vec_error_tests_for_type!(test_error_make_move_vec_u32, u32, TypeTag::U32, 0u32); +make_vec_error_tests_for_type!(test_error_make_move_vec_u64, u64, TypeTag::U64, 0u64); +make_vec_error_tests_for_type!(test_error_make_move_vec_u128, u128, TypeTag::U128, 0u128); +make_vec_error_tests_for_type!( + test_error_make_move_vec_u256, + U256, + TypeTag::U256, + U256::zero() +); +make_vec_error_tests_for_type!( + test_error_make_move_vec_address, + SuiAddress, + TypeTag::Address, + SuiAddress::ZERO +); +make_vec_error_tests_for_type!( + test_error_make_move_vec_address_id, + ObjectID, + TypeTag::Struct(Box::new(sui_types::id::ID::type_())), + ObjectID::ZERO +); +make_vec_error_tests_for_type!(test_error_make_move_vec_utf8, &str, utf8_tag(), "❤️🧀"); +make_vec_error_tests_for_type!( + test_error_make_move_vec_ascii, + &str, + ascii_tag(), + "love and cheese" +); + +#[tokio::test] +#[cfg_attr(msim, ignore)] +async fn test_make_move_vec_empty() { + let (sender, sender_key): (_, AccountKeyPair) = get_key_pair(); + let gas = ObjectID::random(); + let authority = init_state_with_ids(vec![(sender, gas)]).await; + let mut builder = ProgrammableTransactionBuilder::new(); + builder.command(Command::MakeMoveVec(None, vec![])); + let pt = builder.finish(); + let result = execute_programmable_transaction(&authority, &gas, &sender, &sender_key, pt) + .await + .unwrap_err(); + assert_eq!( + result, + SuiError::UserInputError { + error: UserInputError::EmptyCommandInput + } + ); +} + +fn resolved_struct( + (address, module, name): (&AccountAddress, &IdentStr, &IdentStr), + type_args: Vec, +) -> TypeTag { + TypeTag::Struct(Box::new(StructTag { + address: *address, + module: module.to_owned(), + name: name.to_owned(), + type_params: type_args, + })) +} + +fn option_tag(inner: TypeTag) -> TypeTag { + resolved_struct(RESOLVED_STD_OPTION, vec![inner]) +} + +fn utf8_tag() -> TypeTag { + resolved_struct(RESOLVED_UTF8_STR, vec![]) +} + +fn ascii_tag() -> TypeTag { + resolved_struct(RESOLVED_ASCII_STR, vec![]) +} + #[tokio::test] #[cfg_attr(msim, ignore)] async fn test_object_no_id_error() { diff --git a/crates/sui-types/src/programmable_transaction_builder.rs b/crates/sui-types/src/programmable_transaction_builder.rs index f32791043e152..e4df921e8b6e6 100644 --- a/crates/sui-types/src/programmable_transaction_builder.rs +++ b/crates/sui-types/src/programmable_transaction_builder.rs @@ -42,7 +42,7 @@ impl ProgrammableTransactionBuilder { ProgrammableTransaction { inputs, commands } } - fn pure_bytes(&mut self, bytes: Vec, force_separate: bool) -> Argument { + pub fn pure_bytes(&mut self, bytes: Vec, force_separate: bool) -> Argument { let arg = if force_separate { BuilderArg::ForcedNonUniquePure(self.inputs.len()) } else { diff --git a/sdk/typescript/test/e2e/entry-point-string.test.ts b/sdk/typescript/test/e2e/entry-point-string.test.ts index 3e873b64c3d28..ad57beceee7ce 100644 --- a/sdk/typescript/test/e2e/entry-point-string.test.ts +++ b/sdk/typescript/test/e2e/entry-point-string.test.ts @@ -16,7 +16,7 @@ describe('Test Move call with strings', () => { ) { const tx = new Transaction(); tx.moveCall({ - target: `${packageId}::entry_point_string::${funcName}`, + target: `${packageId}::entry_point_types::${funcName}`, arguments: [tx.pure(str), tx.pure(len)], }); const result = await toolbox.signer.signAndExecuteTransaction({ @@ -32,7 +32,7 @@ describe('Test Move call with strings', () => { toolbox = await setup(); const packagePath = __dirname + - '/../../../../crates/sui-core/src/unit_tests/data/entry_point_string'; + '/../../../../crates/sui-core/src/unit_tests/data/entry_point_types'; ({ packageId } = await publishPackage(packagePath)); });