diff --git a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp index b32b5013808b0..29f37deaf17ea 100644 --- a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp +++ b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.exp @@ -22,7 +22,7 @@ written: object(109), object(110) task 6 'run'. lines 89-89: Error: Transaction Effects Status: Invalid Shared Child Object Usage. When a child object (either direct or indirect) of a shared object is passed by-value to an entry function, either the child object's type or the shared object's type must be defined in the same module as the called function. This is violated by object fake(109), whose ancestor fake(111) is a shared object, and neither are defined in this module.. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InvalidSharedChildUse(InvalidSharedChildUse { child: fake(109), ancestor: fake(111) }), source: Some("When a child object (either direct or indirect) of a shared object is passed by-value to an entry function, either the child object's type or the shared object's type must be defined in the same module as the called function. This is violated by object fake(109) (defined in module 'T3::O3'), whose ancestor fake(111) is a shared object (defined in module 'T2::O2'), and neither are defined in this module 'T1::O1'") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: InvalidSharedChildUse(InvalidSharedChildUse { child: fake(109), ancestor: fake(111) }), source: Some("When a child object (either direct or indirect) of a shared object is passed by-value to an entry function, either the child object's type or the shared object's type must be defined in the same module as the called function. This is violated by object fake(109) (defined in module 't3::o3'), whose ancestor fake(111) is a shared object (defined in module 't2::o2'), and neither are defined in this module 't1::o1'") } } task 7 'run'. lines 91-91: written: object(109), object(111), object(113) diff --git a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move index 48480bb20d36f..3b8cef2cf99cc 100644 --- a/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move +++ b/crates/sui-adapter-transactional-tests/tests/children/child_of_shared_object.move @@ -1,91 +1,91 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -//# init --addresses T1=0x0 T2=0x0 T3=0x0 A=0x42 +//# init --addresses t1=0x0 t2=0x0 t3=0x0 A=0x42 //# publish -module T3::O3 { +module t3::o3 { use sui::object::{Self, Info}; use sui::transfer; use sui::tx_context::{Self, TxContext}; - struct O3 has key, store { + struct Obj3 has key, store { info: Info, } public entry fun create(ctx: &mut TxContext) { - let o = O3 { info: object::new(ctx) }; + let o = Obj3 { info: object::new(ctx) }; transfer::transfer(o, tx_context::sender(ctx)) } } //# publish -module T2::O2 { +module t2::o2 { use sui::object::{Self, Info}; use sui::transfer; use sui::tx_context::{Self, TxContext}; - use T3::O3::O3; + use t3::o3::Obj3; - struct O2 has key, store { + struct Obj2 has key, store { info: Info, } - public entry fun create_shared(child: O3, ctx: &mut TxContext) { + public entry fun create_shared(child: Obj3, ctx: &mut TxContext) { transfer::share_object(new(child, ctx)) } - public entry fun create_owned(child: O3, ctx: &mut TxContext) { + public entry fun create_owned(child: Obj3, ctx: &mut TxContext) { transfer::transfer(new(child, ctx), tx_context::sender(ctx)) } - public entry fun use_o2_o3(_o2: &mut O2, o3: O3, ctx: &mut TxContext) { + public entry fun use_o2_o3(_o2: &mut Obj2, o3: Obj3, ctx: &mut TxContext) { transfer::transfer(o3, tx_context::sender(ctx)) } - fun new(child: O3, ctx: &mut TxContext): O2 { + fun new(child: Obj3, ctx: &mut TxContext): Obj2 { let info = object::new(ctx); transfer::transfer_to_object_id(child, &info); - O2 { info } + Obj2 { info } } } //# publish -module T1::O1 { +module t1::o1 { use sui::object::{Self, Info}; use sui::transfer; use sui::tx_context::{Self, TxContext}; - use T2::O2::O2; - use T3::O3::O3; + use t2::o2::Obj2; + use t3::o3::Obj3; - struct O1 has key { + struct Obj1 has key { info: Info, } - public entry fun create_shared(child: O2, ctx: &mut TxContext) { + public entry fun create_shared(child: Obj2, ctx: &mut TxContext) { transfer::share_object(new(child, ctx)) } // This function will be invalid if _o2 is a shared object and owns _o3. - public entry fun use_o2_o3(_o2: &mut O2, o3: O3, ctx: &mut TxContext) { + public entry fun use_o2_o3(_o2: &mut Obj2, o3: Obj3, ctx: &mut TxContext) { transfer::transfer(o3, tx_context::sender(ctx)) } - fun new(child: O2, ctx: &mut TxContext): O1 { + fun new(child: Obj2, ctx: &mut TxContext): Obj1 { let info = object::new(ctx); transfer::transfer_to_object_id(child, &info); - O1 { info } + Obj1 { info } } } -//# run T3::O3::create +//# run t3::o3::create -//# run T2::O2::create_shared --args object(109) +//# run t2::o2::create_shared --args object(109) -// This run should error as O2/O3 were not defined in O1 -//# run T1::O1::use_o2_o3 --args object(111) object(109) +// This run should error as Obj2/Obj3 were not defined in o1 +//# run t1::o1::use_o2_o3 --args object(111) object(109) -//# run T2::O2::use_o2_o3 --args object(111) object(109) +//# run t2::o2::use_o2_o3 --args object(111) object(109) diff --git a/crates/sui-adapter-transactional-tests/tests/publish/init_param.exp b/crates/sui-adapter-transactional-tests/tests/publish/init_param.exp index f817655238546..3646f22aaf0cb 100644 --- a/crates/sui-adapter-transactional-tests/tests/publish/init_param.exp +++ b/crates/sui-adapter-transactional-tests/tests/publish/init_param.exp @@ -2,4 +2,4 @@ processed 2 tasks task 1 'publish'. lines 5-24: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected exactly one parameter for _::M1::init of type &mut sui::tx_context::TxContext") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected last parameter for _::M1::init to be &mut sui::tx_context::TxContext, but found &mut sui::tx_context::TxContext") } } diff --git a/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.exp b/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.exp index 3486beb34f963..8c86499c2801b 100644 --- a/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.exp +++ b/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.exp @@ -14,7 +14,7 @@ written: object(106) task 4 'view-object'. lines 43-43: Owner: Shared -Contents: t2::o2::O2 {info: sui::object::Info {id: sui::object::ID {bytes: fake(107)}, version: 1u64}} +Contents: t2::o2::Obj2 {info: sui::object::Info {id: sui::object::ID {bytes: fake(107)}, version: 1u64}} task 5 'run'. lines 45-45: Error: Transaction Effects Status: Entry Argument Type Error. Error for argument at index 0: Immutable and shared objects cannot be passed by-value. diff --git a/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.move b/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.move index fb2908474ea22..05e592ec26c7e 100644 --- a/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.move +++ b/crates/sui-adapter-transactional-tests/tests/shared/by_value_shared_object.move @@ -12,17 +12,17 @@ module t2::o2 { use sui::transfer; use sui::tx_context::TxContext; - struct O2 has key, store { + struct Obj2 has key, store { info: Info, } public entry fun create(ctx: &mut TxContext) { - let o = O2 { info: object::new(ctx) }; + let o = Obj2 { info: object::new(ctx) }; transfer::share_object(o) } - public entry fun consume_o2(o2: O2) { - let O2 { info } = o2; + public entry fun consume_o2(o2: Obj2) { + let Obj2 { info } = o2; object::delete(info); } } @@ -30,9 +30,9 @@ module t2::o2 { //# publish module t1::o1 { - use t2::o2::{Self, O2}; + use t2::o2::{Self, Obj2}; - public entry fun consume_o2(o2: O2) { + public entry fun consume_o2(o2: Obj2) { o2::consume_o2(o2); } } diff --git a/crates/sui-adapter/src/adapter.rs b/crates/sui-adapter/src/adapter.rs index 69b41635ba113..27b11ac580869 100644 --- a/crates/sui-adapter/src/adapter.rs +++ b/crates/sui-adapter/src/adapter.rs @@ -37,8 +37,8 @@ use sui_types::{ SUI_SYSTEM_STATE_OBJECT_ID, }; use sui_verifier::{ - entry_points_verifier::{is_tx_context, INIT_FN_NAME, RESOLVED_STD_OPTION, RESOLVED_SUI_ID}, - verifier, + entry_points_verifier::{is_tx_context, RESOLVED_STD_OPTION, RESOLVED_SUI_ID}, + verifier, INIT_FN_NAME, }; use crate::bytecode_rewriter::ModuleHandleRewriter; @@ -264,12 +264,17 @@ pub fn store_package_and_init_modules< let modules_to_init = modules .iter() .filter_map(|module| { - module.function_defs.iter().find(|fdef| { - let fhandle = module.function_handle_at(fdef.function).name; - let fname = module.identifier_at(fhandle); - fname == INIT_FN_NAME - })?; - Some(module.self_id()) + for fdef in &module.function_defs { + let fhandle = module.function_handle_at(fdef.function); + let fname = module.identifier_at(fhandle.name); + if fname == INIT_FN_NAME { + return Some(( + module.self_id(), + module.signature_at(fhandle.parameters).len(), + )); + } + } + None }) .collect(); @@ -286,13 +291,23 @@ pub fn store_package_and_init_modules< fn init_modules + ModuleResolver + Storage>( state_view: &mut S, vm: &MoveVM, - module_ids_to_init: Vec, + module_ids_to_init: Vec<(ModuleId, usize)>, ctx: &mut TxContext, gas_status: &mut SuiGasStatus, ) -> Result<(), ExecutionError> { let init_ident = Identifier::new(INIT_FN_NAME.as_str()).unwrap(); - for module_id in module_ids_to_init { - let args = vec![ctx.to_vec()]; + for (module_id, num_args) in module_ids_to_init { + let mut args = vec![]; + // an init function can have one or two arguments, with the last one always being of type + // &mut TxContext and the additional (first) one representing a characteristic type (see + // char_type verfier pass for additional explanation) + if num_args == 2 { + // characteristic type is a struct with a single bool filed which in bcs is encoded as + // 0x01 + let bcs_char_type_value = vec![0x01]; + args.push(bcs_char_type_value); + } + args.push(ctx.to_vec()); let has_ctx_arg = true; execute_internal( diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/bool_field.exp b/crates/sui-verifier-transactional-tests/tests/char_type/bool_field.exp new file mode 100644 index 0000000000000..7c53111d341f0 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/bool_field.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-15: +created: object(103) +written: object(102) diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/bool_field.move b/crates/sui-verifier-transactional-tests/tests/char_type/bool_field.move new file mode 100644 index 0000000000000..d289fbabeffcc --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/bool_field.move @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// correct, bool field specified at source level + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop { some_field: bool } + + fun init(_: M, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/instantiate.exp b/crates/sui-verifier-transactional-tests/tests/char_type/instantiate.exp new file mode 100644 index 0000000000000..8b24573b6e884 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/instantiate.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-20: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("characteristic type _::m::M is instantiated in the _::m::pack function and must never be") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/instantiate.move b/crates/sui-verifier-transactional-tests/tests/char_type/instantiate.move new file mode 100644 index 0000000000000..2cda07b3d78ef --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/instantiate.move @@ -0,0 +1,20 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, struct type incorrectly instantiated + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop { } + + fun init(_: M, _ctx: &mut sui::tx_context::TxContext) { + } + + + fun pack(): M { + M {} + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/more_abilities.exp b/crates/sui-verifier-transactional-tests/tests/char_type/more_abilities.exp new file mode 100644 index 0000000000000..93c2866a6e5e5 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/more_abilities.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-20: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("characteristic type candidate _::m::M must have a single ability: drop") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/more_abilities.move b/crates/sui-verifier-transactional-tests/tests/char_type/more_abilities.move new file mode 100644 index 0000000000000..144669a54d6d0 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/more_abilities.move @@ -0,0 +1,20 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, struct type has abilities beyond drop + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop, copy { } + + fun init(_: M, _ctx: &mut sui::tx_context::TxContext) { + } + + + fun pack(): M { + M {} + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/no_drop.exp b/crates/sui-verifier-transactional-tests/tests/char_type/no_drop.exp new file mode 100644 index 0000000000000..65e5a5a60060c --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/no_drop.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-16: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("characteristic type candidate _::m::M must have a single ability: drop") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/no_drop.move b/crates/sui-verifier-transactional-tests/tests/char_type/no_drop.move new file mode 100644 index 0000000000000..fdc8ff95bc523 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/no_drop.move @@ -0,0 +1,16 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, char type has no drop ability + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M { } + + fun init(t: M, _: &mut sui::tx_context::TxContext) { + let M { } = t; + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/no_field.exp b/crates/sui-verifier-transactional-tests/tests/char_type/no_field.exp new file mode 100644 index 0000000000000..7c53111d341f0 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/no_field.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-15: +created: object(103) +written: object(102) diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/no_field.move b/crates/sui-verifier-transactional-tests/tests/char_type/no_field.move new file mode 100644 index 0000000000000..a63d5a26860d7 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/no_field.move @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// correct, no field specified at source level + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop { } + + fun init(_: M, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/no_init_arg.exp b/crates/sui-verifier-transactional-tests/tests/char_type/no_init_arg.exp new file mode 100644 index 0000000000000..568cca09eec24 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/no_init_arg.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-15: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("init function of a module containing characteristic type candidate must have _::m::M as the first parameter") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/no_init_arg.move b/crates/sui-verifier-transactional-tests/tests/char_type/no_init_arg.move new file mode 100644 index 0000000000000..5c5de61509393 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/no_init_arg.move @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, no char type parameter in init + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop { value: bool } + + fun init(_: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/other_mod_def.exp b/crates/sui-verifier-transactional-tests/tests/char_type/other_mod_def.exp new file mode 100644 index 0000000000000..a92489d63d633 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/other_mod_def.exp @@ -0,0 +1,9 @@ +processed 3 tasks + +task 1 'publish'. lines 8-12: +created: object(103) +written: object(102) + +task 2 'publish'. lines 14-19: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected exactly one parameter for _::n::init of type &mut sui::tx_context::TxContext") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/other_mod_def.move b/crates/sui-verifier-transactional-tests/tests/char_type/other_mod_def.move new file mode 100644 index 0000000000000..c6ab36ad4ebd7 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/other_mod_def.move @@ -0,0 +1,19 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, characteristic type candidate used in a different module + +//# init --addresses test1=0x0 test2=0x0 + +//# publish +module test1::m { + + struct M has drop { } +} + +//# publish +module test2::n { + + fun init(_: test1::m::M, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/type_param.exp b/crates/sui-verifier-transactional-tests/tests/char_type/type_param.exp new file mode 100644 index 0000000000000..9ad548e77e7a2 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/type_param.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-15: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("_::m. 'init' function cannot have type parameters") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/type_param.move b/crates/sui-verifier-transactional-tests/tests/char_type/type_param.move new file mode 100644 index 0000000000000..0caeb7143c3d3 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/type_param.move @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, struct type has type param + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop { } + + fun init(_: M, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_field_type.exp b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_field_type.exp new file mode 100644 index 0000000000000..79174bdb32100 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_field_type.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-15: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("characteristic type candidate _::m::M must have a single bool field only (or no fields)") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_field_type.move b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_field_type.move new file mode 100644 index 0000000000000..ca172f3e2a969 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_field_type.move @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, wrong struct field type + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop { value: u64 } + + fun init(_: M, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_init_type.exp b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_init_type.exp new file mode 100644 index 0000000000000..38c7563efa9ff --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_init_type.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-17: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("init function of a module containing characteristic type candidate must have _::m::M as the first parameter") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_init_type.move b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_init_type.move new file mode 100644 index 0000000000000..186ed20bb0d3d --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_init_type.move @@ -0,0 +1,17 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, wrong type of the init function's first param + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct M has drop { } + + struct N has drop { } + + fun init(_: N, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name.exp b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name.exp new file mode 100644 index 0000000000000..2000c8de2b14e --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-15: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected exactly one parameter for _::m::init of type &mut sui::tx_context::TxContext") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name.move b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name.move new file mode 100644 index 0000000000000..7f6cf86a588b6 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name.move @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, wrong characteristic type name + +//# init --addresses test=0x0 + +//# publish +module test::m { + + struct CharType has drop { } + + fun init(_: CharType, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name_format.exp b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name_format.exp new file mode 100644 index 0000000000000..d4fe0381ffd92 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name_format.exp @@ -0,0 +1,5 @@ +processed 2 tasks + +task 1 'publish'. lines 8-15: +Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected exactly one parameter for _::mod::init of type &mut sui::tx_context::TxContext") } } diff --git a/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name_format.move b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name_format.move new file mode 100644 index 0000000000000..1d46ccca8e139 --- /dev/null +++ b/crates/sui-verifier-transactional-tests/tests/char_type/wrong_name_format.move @@ -0,0 +1,15 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// invalid, wrong characteristic type name format + +//# init --addresses test=0x0 + +//# publish +module test::mod { + + struct Mod has drop { } + + fun init(_: Mod, _ctx: &mut sui::tx_context::TxContext) { + } +} diff --git a/crates/sui-verifier-transactional-tests/tests/init/must_have_txn_context.exp b/crates/sui-verifier-transactional-tests/tests/init/must_have_txn_context.exp index 9833eb38706fb..fd443b8b53bf4 100644 --- a/crates/sui-verifier-transactional-tests/tests/init/must_have_txn_context.exp +++ b/crates/sui-verifier-transactional-tests/tests/init/must_have_txn_context.exp @@ -2,7 +2,7 @@ processed 2 tasks task 0 'publish'. lines 4-11: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected exactly one parameter for _::m::init of type &mut sui::tx_context::TxContext") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected at least one and at most two parameters for _::m::init") } } task 1 'publish'. lines 14-21: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. diff --git a/crates/sui-verifier-transactional-tests/tests/init/not_txn_context.exp b/crates/sui-verifier-transactional-tests/tests/init/not_txn_context.exp index ba895e1feea73..a783c27d43e95 100644 --- a/crates/sui-verifier-transactional-tests/tests/init/not_txn_context.exp +++ b/crates/sui-verifier-transactional-tests/tests/init/not_txn_context.exp @@ -2,16 +2,16 @@ processed 4 tasks task 0 'publish'. lines 4-11: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected parameter for _::m::init to be &mut sui::tx_context::TxContext, but found u64") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected last parameter for _::m::init to be &mut sui::tx_context::TxContext, but found u64") } } task 1 'publish'. lines 13-20: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected parameter for _::tx_context::init to be &mut sui::tx_context::TxContext, but found _::tx_context::TxContext") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected last parameter for _::tx_context::init to be &mut sui::tx_context::TxContext, but found _::tx_context::TxContext") } } task 2 'publish'. lines 22-29: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected parameter for _::m::init to be &mut sui::tx_context::TxContext, but found &sui::tx_context::TxContext") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected last parameter for _::m::init to be &mut sui::tx_context::TxContext, but found &sui::tx_context::TxContext") } } task 3 'publish'. lines 32-39: Error: Transaction Effects Status: Sui Move Bytecode Verification Error. Please run the Sui Move Verifier for more information. -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected parameter for _::m::init to be &mut sui::tx_context::TxContext, but found sui::tx_context::TxContext") } } +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: SuiMoveVerificationError, source: Some("Expected last parameter for _::m::init to be &mut sui::tx_context::TxContext, but found sui::tx_context::TxContext") } } diff --git a/crates/sui-verifier/src/char_type_verifier.rs b/crates/sui-verifier/src/char_type_verifier.rs new file mode 100644 index 0000000000000..10a50e9591728 --- /dev/null +++ b/crates/sui-verifier/src/char_type_verifier.rs @@ -0,0 +1,226 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! A module can define a "characteristic type", that is a type that is instantiated only once, and +//! this property is enforced by the system. We define a characteristic type as a struct type that +//! has the same name as the module that defines it but with all the letter capitalized, and +//! possessing certain special properties specified below (please note that by convention, "regular" +//! struct type names are expressed in camel case). In other words, if a module defines a struct +//! type whose name is the same as the module name, this type MUST possess these special properties, +//! otherwise the module definition will be considered invalid and will be rejected by the +//! validator: +//! +//! - it has only one ability: drop +//! - it has only one arbitrarily named field of type boolean (since Move structs cannot be empty) +//! - its definition does not involve type parameters +//! - its only instance in existence is passed as an argument to the module initializer +//! - it is never instantiated anywhere in its defining module +//! +//! A characteristic type is one way of implementing a one-time witness pattern, where we want to +//! restrict the number of times a given type (instance) is used. Another way could be to have a set +//! data structure that can store types and use it to guarantee uniqueness. + +use move_binary_format::{ + access::ModuleAccess, + binary_views::BinaryIndexedView, + file_format::{ + Ability, AbilitySet, Bytecode, CompiledModule, FunctionDefinition, FunctionHandle, + SignatureToken, StructDefinition, StructHandle, + }, +}; +use move_core_types::{ident_str, language_storage::ModuleId}; +use sui_types::{ + base_types::{TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME}, + error::ExecutionError, + SUI_FRAMEWORK_ADDRESS, +}; + +use crate::{verification_failure, INIT_FN_NAME}; + +pub fn verify_module(module: &CompiledModule) -> Result<(), ExecutionError> { + // In Sui's framework code there is an exception to the characteristic type rule - we have a SUI + // type in the sui module but it is instantiated and the module has no initializer (the reason + // for it is that the SUI coin is only instantiated during genesis). It is easiest to simply + // special-case this module particularly that this is framework code and thus deemed correct. + if ModuleId::new(SUI_FRAMEWORK_ADDRESS, ident_str!("sui").to_owned()) == module.self_id() { + return Ok(()); + } + + let view = BinaryIndexedView::Module(module); + let mod_handle = view.module_handle_at(module.self_module_handle_idx); + let mod_name = view.identifier_at(mod_handle.name).as_str(); + let struct_defs = &module.struct_defs; + let mut char_type_candidate = None; + // find structs that can potentially represent a characteristic type + for def in struct_defs { + let struct_handle = module.struct_handle_at(def.struct_handle); + let struct_name = view.identifier_at(struct_handle.name).as_str(); + if mod_name.to_ascii_uppercase() == struct_name { + verify_char_type(module, struct_name, struct_handle, def) + .map_err(verification_failure)?; + // if we reached this point, it means we have a legitimate characteristic type candidate + // and we have to make sure that both the init function's signature reflects this and + // that this type is not instantiated in any function of the module + char_type_candidate = Some((struct_name, struct_handle, def)); + break; // no reason to look any further + } + } + for fn_def in &module.function_defs { + let fn_handle = module.function_handle_at(fn_def.function); + let fn_name = module.identifier_at(fn_handle.name); + if fn_name == INIT_FN_NAME { + if let Some((candidate_name, candidate_handle, _)) = char_type_candidate { + // only verify if init function conforms to characteristic types requirements if we + // have a characteristic type candidate + verify_init_function_char_type(module, fn_handle, candidate_name, candidate_handle) + .map_err(verification_failure)?; + } else { + // if there is no characteristic type candidate than the init function should have + // only one parameter of TxContext type + verify_init_function_single_param(module, fn_handle) + .map_err(verification_failure)?; + } + } + if let Some((candidate_name, _, def)) = char_type_candidate { + // only verify lack of characteristic types instantiations if we have a + // characteristic type candidate + verify_no_instantiations(module, fn_def, candidate_name, def) + .map_err(verification_failure)?; + } + } + + Ok(()) +} + +// Verifies all required properties of a characteristic type candidate (that is a type whose name is +// the same as the name of a +fn verify_char_type( + module: &CompiledModule, + candidate_name: &str, + candidate_handle: &StructHandle, + candidate_def: &StructDefinition, +) -> Result<(), String> { + // must have only one ability: drop + let drop_set = AbilitySet::EMPTY | Ability::Drop; + let abilities = candidate_handle.abilities; + if abilities != drop_set { + return Err(format!( + "characteristic type candidate {}::{} must have a single ability: drop", + module.self_id(), + candidate_name, + )); + } + let field_count = candidate_def.declared_field_count().map_err(|_| { + format!( + "characteristic type candidate {}::{} cannot be a native structure", + module.self_id(), + candidate_name + ) + })?; + + // unwrap below is safe as it will always be successful if declared_field_count call above is + // successful + if field_count != 1 || candidate_def.field(0).unwrap().signature.0 != SignatureToken::Bool { + return Err(format!( + "characteristic type candidate {}::{} must have a single bool field only (or no fields)", + module.self_id(), + candidate_name, + )); + } + + if !candidate_handle.type_parameters.is_empty() { + return Err(format!( + "characteristic type candidate {}::{} cannot have type parameters", + module.self_id(), + candidate_name, + )); + } + Ok(()) +} + +/// Checks if this module's `init` function conformant with the characteristic type +fn verify_init_function_char_type( + module: &CompiledModule, + fn_handle: &FunctionHandle, + candidate_name: &str, + candidate_handle: &StructHandle, +) -> Result<(), String> { + let view = &BinaryIndexedView::Module(module); + let fn_sig = view.signature_at(fn_handle.parameters); + if fn_sig.len() != 2 || !is_char_type(view, &fn_sig.0[0], candidate_handle) { + // check only the first parameter - the other one is checked in entry_points verification + // pass + return Err(format!( + "init function of a module containing characteristic type candidate must have {}::{} as the first parameter", + module.self_id(), + candidate_name, + )); + } + + Ok(()) +} + +// Checks if a given SignatureToken represents a characteristic type struct +fn is_char_type( + view: &BinaryIndexedView, + tok: &SignatureToken, + candidate_handle: &StructHandle, +) -> bool { + matches!(tok, SignatureToken::Struct(idx) if view.struct_handle_at(*idx) == candidate_handle) +} + +/// Checks if this module's `init` function has a single parameter of TxContext type only +fn verify_init_function_single_param( + module: &CompiledModule, + fn_handle: &FunctionHandle, +) -> Result<(), String> { + let view = &BinaryIndexedView::Module(module); + let fn_sig = view.signature_at(fn_handle.parameters); + if fn_sig.len() != 1 { + return Err(format!( + "Expected exactly one parameter for {}::{} of type &mut {}::{}::{}", + module.self_id(), + INIT_FN_NAME, + SUI_FRAMEWORK_ADDRESS, + TX_CONTEXT_MODULE_NAME, + TX_CONTEXT_STRUCT_NAME, + )); + } + + Ok(()) +} + +/// Checks if this module function does not contain instantiation of the characteristic type +fn verify_no_instantiations( + module: &CompiledModule, + fn_def: &FunctionDefinition, + struct_name: &str, + struct_def: &StructDefinition, +) -> Result<(), String> { + let view = &BinaryIndexedView::Module(module); + if fn_def.code.is_none() { + return Ok(()); + } + for bcode in &fn_def.code.as_ref().unwrap().code { + let struct_def_idx = match bcode { + Bytecode::Pack(idx) => idx, + _ => continue, + }; + // unwrap is safe below since we know we are getting a struct out of a module (see + // definition of struct_def_at) + if view.struct_def_at(*struct_def_idx).unwrap() == struct_def { + let fn_handle = module.function_handle_at(fn_def.function); + let fn_name = module.identifier_at(fn_handle.name); + return Err(format!( + "characteristic type {}::{} is instantiated \ + in the {}::{} function and must never be", + module.self_id(), + struct_name, + module.self_id(), + fn_name, + )); + } + } + + Ok(()) +} diff --git a/crates/sui-verifier/src/entry_points_verifier.rs b/crates/sui-verifier/src/entry_points_verifier.rs index 132643295ac47..a0a0f9ca73a3b 100644 --- a/crates/sui-verifier/src/entry_points_verifier.rs +++ b/crates/sui-verifier/src/entry_points_verifier.rs @@ -7,7 +7,7 @@ use move_binary_format::{ file_format::{AbilitySet, Bytecode, FunctionDefinition, SignatureToken, Visibility}, CompiledModule, }; -use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr}; +use move_core_types::{account_address::AccountAddress, identifier::IdentStr}; use sui_types::{ base_types::{ STD_OPTION_MODULE_NAME, STD_OPTION_STRUCT_NAME, TX_CONTEXT_MODULE_NAME, @@ -18,9 +18,7 @@ use sui_types::{ MOVE_STDLIB_ADDRESS, SUI_FRAMEWORK_ADDRESS, }; -use crate::{format_signature_token, resolve_struct, verification_failure}; - -pub const INIT_FN_NAME: &IdentStr = ident_str!("init"); +use crate::{format_signature_token, resolve_struct, verification_failure, INIT_FN_NAME}; /// Checks valid rules rules for entry points, both for module initialization and transactions /// @@ -28,7 +26,10 @@ pub const INIT_FN_NAME: &IdentStr = ident_str!("init"); /// - The existence of the function is optional /// - The function must have the name specified by `INIT_FN_NAME` /// - The function must have `Visibility::Private` -/// - The function can have a single parameter: &mut TxContext (see `is_tx_context`) +/// - The function can have at most two parameters: +/// - mandatory &mut TxContext (see `is_tx_context`) in the last position +/// - optional characteristic type (see char_type verifier pass) passed by value in the first +/// position /// /// For transaction entry points /// - The function must have `is_entry` true @@ -113,7 +114,7 @@ fn verify_init_function(module: &CompiledModule, fdef: &FunctionDefinition) -> R )); } - if !view.signature_at(fhandle.return_).0.is_empty() { + if !view.signature_at(fhandle.return_).is_empty() { return Err(format!( "{}, '{}' function cannot have return values", module.self_id(), @@ -122,22 +123,23 @@ fn verify_init_function(module: &CompiledModule, fdef: &FunctionDefinition) -> R } let parameters = &view.signature_at(fhandle.parameters).0; - if parameters.len() != 1 { + if parameters.is_empty() || parameters.len() > 2 { return Err(format!( - "Expected exactly one parameter for {}::{} of type &mut {}::{}::{}", + "Expected at least one and at most two parameters for {}::{}", module.self_id(), INIT_FN_NAME, - SUI_FRAMEWORK_ADDRESS, - TX_CONTEXT_MODULE_NAME, - TX_CONTEXT_STRUCT_NAME, )); } - if is_tx_context(view, ¶meters[0]) { + // Checking only the last (and possibly the only) parameter here. If there are two parameters, + // then the first parameter must be of a characteristic type and must be passed by value. This + // is checked by the verifier pass handling characteristic types (char_type) - please see the + // description of this pass for additional details. + if is_tx_context(view, ¶meters[parameters.len() - 1]) { Ok(()) } else { Err(format!( - "Expected parameter for {}::{} to be &mut {}::{}::{}, but found {}", + "Expected last parameter for {}::{} to be &mut {}::{}::{}, but found {}", module.self_id(), INIT_FN_NAME, SUI_FRAMEWORK_ADDRESS, diff --git a/crates/sui-verifier/src/lib.rs b/crates/sui-verifier/src/lib.rs index b2e67162360fa..28c88cfea1390 100644 --- a/crates/sui-verifier/src/lib.rs +++ b/crates/sui-verifier/src/lib.rs @@ -3,6 +3,7 @@ pub mod verifier; +pub mod char_type_verifier; pub mod entry_points_verifier; pub mod global_storage_access_verifier; pub mod id_leak_verifier; @@ -13,9 +14,11 @@ use move_binary_format::{ binary_views::BinaryIndexedView, file_format::{SignatureToken, StructHandleIndex}, }; -use move_core_types::{account_address::AccountAddress, identifier::IdentStr}; +use move_core_types::{account_address::AccountAddress, ident_str, identifier::IdentStr}; use sui_types::error::{ExecutionError, ExecutionErrorKind}; +pub const INIT_FN_NAME: &IdentStr = ident_str!("init"); + fn verification_failure(error: String) -> ExecutionError { ExecutionError::new_with_source(ExecutionErrorKind::SuiMoveVerificationError, error) } diff --git a/crates/sui-verifier/src/verifier.rs b/crates/sui-verifier/src/verifier.rs index 4826584343f58..4245e75574e36 100644 --- a/crates/sui-verifier/src/verifier.rs +++ b/crates/sui-verifier/src/verifier.rs @@ -7,8 +7,8 @@ use move_binary_format::file_format::CompiledModule; use sui_types::error::ExecutionError; use crate::{ - entry_points_verifier, global_storage_access_verifier, id_leak_verifier, private_generics, - struct_with_key_verifier, + char_type_verifier, entry_points_verifier, global_storage_access_verifier, id_leak_verifier, + private_generics, struct_with_key_verifier, }; /// Helper for a "canonical" verification of a module. @@ -17,5 +17,6 @@ pub fn verify_module(module: &CompiledModule) -> Result<(), ExecutionError> { global_storage_access_verifier::verify_module(module)?; id_leak_verifier::verify_module(module)?; private_generics::verify_module(module)?; - entry_points_verifier::verify_module(module) + entry_points_verifier::verify_module(module)?; + char_type_verifier::verify_module(module) } diff --git a/sui_programmability/examples/fungible_tokens/sources/basket.move b/sui_programmability/examples/fungible_tokens/sources/basket.move index c402d3dba2334..159bd4dc6f5e9 100644 --- a/sui_programmability/examples/fungible_tokens/sources/basket.move +++ b/sui_programmability/examples/fungible_tokens/sources/basket.move @@ -33,9 +33,9 @@ module fungible_tokens::basket { /// Needed to deposit a 1:1 ratio of SUI and MANAGED for minting, but deposited a different ratio const EBadDepositRatio: u64 = 0; - fun init(ctx: &mut TxContext) { + fun init(witness: BASKET, ctx: &mut TxContext) { // Get a treasury cap for the coin put it in the reserve - let total_supply = balance::create_supply(BASKET {}); + let total_supply = balance::create_supply(witness); transfer::share_object(Reserve { info: object::new(ctx), @@ -92,6 +92,6 @@ module fungible_tokens::basket { #[test_only] public fun init_for_testing(ctx: &mut TxContext) { - init(ctx) + init(BASKET {}, ctx) } } diff --git a/sui_programmability/examples/fungible_tokens/sources/managed.move b/sui_programmability/examples/fungible_tokens/sources/managed.move index b932afc415011..fd99db185d348 100644 --- a/sui_programmability/examples/fungible_tokens/sources/managed.move +++ b/sui_programmability/examples/fungible_tokens/sources/managed.move @@ -16,9 +16,9 @@ module fungible_tokens::managed { /// Register the managed currency to acquire its `TreasuryCap`. Because /// this is a module initializer, it ensures the currency only gets /// registered once. - fun init(ctx: &mut TxContext) { + fun init(witness: MANAGED, ctx: &mut TxContext) { // Get a treasury cap for the coin and give it to the transaction sender - let treasury_cap = coin::create_currency(MANAGED{}, ctx); + let treasury_cap = coin::create_currency(witness, ctx); transfer::transfer(treasury_cap, tx_context::sender(ctx)) } @@ -40,6 +40,6 @@ module fungible_tokens::managed { #[test_only] /// Wrapper of module initializer for testing public fun test_init(ctx: &mut TxContext) { - init(ctx) + init(MANAGED {}, ctx) } } diff --git a/sui_programmability/examples/fungible_tokens/sources/regulated_coin.move b/sui_programmability/examples/fungible_tokens/sources/regulated_coin.move index 471af16739956..fa06d0d99c068 100644 --- a/sui_programmability/examples/fungible_tokens/sources/regulated_coin.move +++ b/sui_programmability/examples/fungible_tokens/sources/regulated_coin.move @@ -84,7 +84,7 @@ module rc::regulated_coin { } } -/// ABC is a RegulatedCoin which: +/// Abc is a RegulatedCoin which: /// /// - is managed account creation (only admins can create a new balance) /// - has a denylist for addresses managed by the coin admins @@ -98,13 +98,13 @@ module abc::abc { use sui::transfer; use std::vector; - /// The ticker of ABC regulated token - struct ABC has drop {} + /// The ticker of Abc regulated token + struct Abc has drop {} - /// A restricted transfer of ABC to another account. + /// A restricted transfer of Abc to another account. struct Transfer has key { info: Info, - balance: Balance, + balance: Balance, to: address, } @@ -115,18 +115,18 @@ module abc::abc { swapped_amount: u64, } - /// For when an attempting to interact with another account's RegulatedCoin. + /// For when an attempting to interact with another account's RegulatedCoin. const ENotOwner: u64 = 1; /// For when address has been banned and someone is trying to access the balance const EAddressBanned: u64 = 2; - /// Create the ABC currency and send the TreasuryCap to the creator - /// as well as the first (and empty) balance of the RegulatedCoin. + /// Create the Abc currency and send the TreasuryCap to the creator + /// as well as the first (and empty) balance of the RegulatedCoin. /// /// Also creates a shared Registry which holds banned addresses. fun init(ctx: &mut TxContext) { - let treasury_cap = coin::create_currency(ABC {}, ctx); + let treasury_cap = coin::create_currency(Abc {}, ctx); let sender = tx_context::sender(ctx); transfer::transfer(zero(sender, ctx), sender); @@ -153,27 +153,27 @@ module abc::abc { // === Admin actions: creating balances, minting coins and banning addresses === - /// Create an empty `RCoin` instance for account `for`. TreasuryCap is passed for + /// Create an empty `RCoin` instance for account `for`. TreasuryCap is passed for /// authentification purposes - only admin can create new accounts. - public entry fun create(_: &TreasuryCap, for: address, ctx: &mut TxContext) { + public entry fun create(_: &TreasuryCap, for: address, ctx: &mut TxContext) { transfer::transfer(zero(for, ctx), for) } - /// Mint more ABC. Requires TreasuryCap for authorization, so can only be done by admins. - public entry fun mint(treasury: &mut TreasuryCap, owned: &mut RCoin, value: u64) { + /// Mint more Abc. Requires TreasuryCap for authorization, so can only be done by admins. + public entry fun mint(treasury: &mut TreasuryCap, owned: &mut RCoin, value: u64) { balance::join(borrow_mut(owned), coin::mint_balance(treasury, value)); } - /// Burn `value` amount of `RCoin`. Requires TreasuryCap for authorization, so can only be done by admins. + /// Burn `value` amount of `RCoin`. Requires TreasuryCap for authorization, so can only be done by admins. /// /// TODO: Make TreasuryCap a part of Balance module instead of Coin. - public entry fun burn(treasury: &mut TreasuryCap, owned: &mut RCoin, value: u64, ctx: &mut TxContext) { + public entry fun burn(treasury: &mut TreasuryCap, owned: &mut RCoin, value: u64, ctx: &mut TxContext) { coin::burn(treasury, coin::take(borrow_mut(owned), value, ctx)); } /// Ban some address and forbid making any transactions from or to this address. /// Only owner of the TreasuryCap can perform this action. - public entry fun ban(_cap: &TreasuryCap, registry: &mut Registry, to_ban: address) { + public entry fun ban(_cap: &TreasuryCap, registry: &mut Registry, to_ban: address) { vector::push_back(&mut registry.banned, to_ban) } @@ -183,7 +183,7 @@ module abc::abc { /// `to` account for being accepted later. /// Fails if sender is not an creator of the `RegulatedCoin` or if any of the parties is in /// the ban list in Registry. - public entry fun transfer(r: &Registry, coin: &mut RCoin, value: u64, to: address, ctx: &mut TxContext) { + public entry fun transfer(r: &Registry, coin: &mut RCoin, value: u64, to: address, ctx: &mut TxContext) { let sender = tx_context::sender(ctx); assert!(rcoin::creator(coin) == sender, ENotOwner); @@ -200,9 +200,9 @@ module abc::abc { /// Accept an incoming transfer by joining an incoming balance with an owned one. /// /// Fails if: - /// 1. the `RegulatedCoin.creator` does not match `Transfer.to`; + /// 1. the `RegulatedCoin.creator` does not match `Transfer.to`; /// 2. the address of the creator/recipient is banned; - public entry fun accept_transfer(r: &Registry, coin: &mut RCoin, transfer: Transfer, _: &mut TxContext) { + public entry fun accept_transfer(r: &Registry, coin: &mut RCoin, transfer: Transfer, _: &mut TxContext) { let Transfer { info, balance, to } = transfer; assert!(rcoin::creator(coin) == to, ENotOwner); @@ -218,9 +218,9 @@ module abc::abc { /// a `Coin`. Update `Registry` to keep track of the swapped amount. /// /// Fails if: - /// 1. `RegulatedCoin.creator` was banned; - /// 2. `RegulatedCoin` is not owned by the tx sender; - public entry fun take(r: &mut Registry, coin: &mut RCoin, value: u64, ctx: &mut TxContext) { + /// 1. `RegulatedCoin.creator` was banned; + /// 2. `RegulatedCoin` is not owned by the tx sender; + public entry fun take(r: &mut Registry, coin: &mut RCoin, value: u64, ctx: &mut TxContext) { let sender = tx_context::sender(ctx); assert!(rcoin::creator(coin) == sender, ENotOwner); @@ -235,9 +235,9 @@ module abc::abc { /// Take `Coin` and put to the `RegulatedCoin`'s balance. /// /// Fails if: - /// 1. `RegulatedCoin.creator` was banned; - /// 2. `RegulatedCoin` is not owned by the tx sender; - public entry fun put_back(r: &mut Registry, rc_coin: &mut RCoin, coin: Coin, ctx: &mut TxContext) { + /// 1. `RegulatedCoin.creator` was banned; + /// 2. `RegulatedCoin` is not owned by the tx sender; + public entry fun put_back(r: &mut Registry, rc_coin: &mut RCoin, coin: Coin, ctx: &mut TxContext) { let balance = coin::into_balance(coin); let sender = tx_context::sender(ctx); @@ -252,24 +252,24 @@ module abc::abc { // === Private implementations accessors and type morphing === - fun borrow(coin: &RCoin): &Balance { rcoin::borrow(ABC {}, coin) } - fun borrow_mut(coin: &mut RCoin): &mut Balance { rcoin::borrow_mut(ABC {}, coin) } - fun zero(creator: address, ctx: &mut TxContext): RCoin { rcoin::zero(ABC {}, creator, ctx) } + fun borrow(coin: &RCoin): &Balance { rcoin::borrow(Abc {}, coin) } + fun borrow_mut(coin: &mut RCoin): &mut Balance { rcoin::borrow_mut(Abc {}, coin) } + fun zero(creator: address, ctx: &mut TxContext): RCoin { rcoin::zero(Abc {}, creator, ctx) } - fun into_balance(coin: RCoin): Balance { rcoin::into_balance(ABC {}, coin) } - fun from_balance(balance: Balance, creator: address, ctx: &mut TxContext): RCoin { - rcoin::from_balance(ABC {}, balance, creator, ctx) + fun into_balance(coin: RCoin): Balance { rcoin::into_balance(Abc {}, coin) } + fun from_balance(balance: Balance, creator: address, ctx: &mut TxContext): RCoin { + rcoin::from_balance(Abc {}, balance, creator, ctx) } // === Testing utilities === #[test_only] public fun init_for_testing(ctx: &mut TxContext) { init(ctx) } - #[test_only] public fun borrow_for_testing(coin: &RCoin): &Balance { borrow(coin) } - #[test_only] public fun borrow_mut_for_testing(coin: &mut RCoin): &Balance { borrow_mut(coin) } + #[test_only] public fun borrow_for_testing(coin: &RCoin): &Balance { borrow(coin) } + #[test_only] public fun borrow_mut_for_testing(coin: &mut RCoin): &Balance { borrow_mut(coin) } } #[test_only] -/// Tests for the ABC module. They are sequential and based on top of each other. +/// Tests for the abc module. They are sequential and based on top of each other. /// ``` /// * - test_minting /// | +-- test_creation @@ -283,7 +283,7 @@ module abc::abc { /// | +-- test_not_owned_balance_fail /// ``` module abc::tests { - use abc::abc::{Self, ABC, Registry}; + use abc::abc::{Self, Abc, Registry}; use rc::regulated_coin::{Self as rcoin, RegulatedCoin as RCoin}; use sui::coin::{Coin, TreasuryCap}; @@ -322,7 +322,7 @@ module abc::tests { fun scenario(): Scenario { test_scenario::begin(&@0xABC) } fun people(): (address, address, address) { (@0xABC, @0xE05, @0xFACE) } - // Admin creates a regulated coin ABC and mints 1,000,000 of it. + // Admin creates a regulated coin Abc and mints 1,000,000 of it. fun test_minting_(test: &mut Scenario) { let (admin, _, _) = people(); @@ -331,8 +331,8 @@ module abc::tests { }; next_tx(test, &admin); { - let cap = test_scenario::take_owned>(test); - let coin = test_scenario::take_owned>(test); + let cap = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); abc::mint(&mut cap, &mut coin, 1000000); @@ -350,7 +350,7 @@ module abc::tests { test_minting_(test); next_tx(test, &admin); { - let cap = test_scenario::take_owned>(test); + let cap = test_scenario::take_owned>(test); abc::create(&cap, user1, ctx(test)); @@ -358,7 +358,7 @@ module abc::tests { }; next_tx(test, &user1); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); assert!(rcoin::creator(&coin) == user1, 1); assert!(rcoin::value(&coin) == 0, 2); @@ -375,7 +375,7 @@ module abc::tests { test_creation_(test); next_tx(test, &admin); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg); @@ -386,7 +386,7 @@ module abc::tests { }; next_tx(test, &user1); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); let transfer = test_scenario::take_owned(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg); @@ -400,15 +400,15 @@ module abc::tests { }; } - // Admin burns 100,000 of `RCoin` + // Admin burns 100,000 of `RCoin` fun test_burn_(test: &mut Scenario) { let (admin, _, _) = people(); test_transfer_(test); next_tx(test, &admin); { - let coin = test_scenario::take_owned>(test); - let treasury_cap = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); + let treasury_cap = test_scenario::take_owned>(test); abc::burn(&mut treasury_cap, &mut coin, 100000, ctx(test)); @@ -420,14 +420,14 @@ module abc::tests { } // User1 cashes 100,000 of his `RegulatedCoin` into a `Coin`; - // User1 sends Coin it to `user2`. + // User1 sends Coin it to `user2`. fun test_take_(test: &mut Scenario) { let (_, user1, user2) = people(); test_transfer_(test); next_tx(test, &user1); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg); @@ -441,12 +441,12 @@ module abc::tests { }; next_tx(test, &user1); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); sui::transfer::transfer(coin, user2); }; } - // User2 sends his `Coin` to `admin`. + // User2 sends his `Coin` to `admin`. // Admin puts this coin to his RegulatedCoin balance. fun test_put_back_(test: &mut Scenario) { let (admin, _, user2) = people(); @@ -454,13 +454,13 @@ module abc::tests { test_take_(test); next_tx(test, &user2); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); sui::transfer::transfer(coin, admin); }; next_tx(test, &admin); { - let coin = test_scenario::take_owned>(test); - let reg_coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); + let reg_coin = test_scenario::take_owned>(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg); @@ -478,7 +478,7 @@ module abc::tests { test_transfer_(test); next_tx(test, &admin); { - let cap = test_scenario::take_owned>(test); + let cap = test_scenario::take_owned>(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg); @@ -496,7 +496,7 @@ module abc::tests { test_ban_(test); next_tx(test, &user1); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg); @@ -514,7 +514,7 @@ module abc::tests { test_ban_(test); next_tx(test, &admin); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg); @@ -533,12 +533,12 @@ module abc::tests { test_ban_(test); next_tx(test, &user1); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); sui::transfer::transfer(coin, user2); }; next_tx(test, &user2); { - let coin = test_scenario::take_owned>(test); + let coin = test_scenario::take_owned>(test); let reg = test_scenario::take_shared(test); let reg_ref = test_scenario::borrow_mut(&mut reg);