diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.move index 931930df6a0..e9603680909 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.move @@ -20,14 +20,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.snap index 5a77d296a1b..f79cc9fb255 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/gasless.snap @@ -6,13 +6,13 @@ processed 5 tasks init: A: object(0,0) -task 1, lines 8-35: +task 1, lines 8-33: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 11225200, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 10032000, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 37-39: +task 2, lines 35-37: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate" //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -20,7 +20,7 @@ created: object(2,0), object(2,1) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5677200, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 41: +task 3, line 39: //# view-object 2,1 Owner: Shared( 3 ) Version: 3 @@ -32,6 +32,6 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 4, lines 43-45: +task 4, lines 41-43: //# abstract --account immshared(2,1) --ptb-inputs 100 @A Error: Error checking transaction input objects: GasObjectNotOwnedObject { owner: Shared { initial_shared_version: SequenceNumber(3) } } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.move index 33908bbecbd..928e550d27b 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.move @@ -20,14 +20,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.snap index 9e3b3bd7a3b..192ca3f4df9 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/mutable.snap @@ -6,13 +6,13 @@ processed 6 tasks init: A: object(0,0) -task 1, lines 8-35: +task 1, lines 8-33: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 11225200, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 10032000, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 37-41: +task 2, lines 35-39: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6657600, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 43: +task 3, line 41: //# view-object 2,0 Owner: Account Address ( fake(2,2) ) Version: 3 @@ -37,7 +37,7 @@ Contents: iota::coin::Coin { }, } -task 4, line 45: +task 4, line 43: //# view-object 2,2 Owner: Shared( 3 ) Version: 3 @@ -49,6 +49,6 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 5, lines 47-48: +task 5, lines 45-46: //# abstract --account object(2,2) --gas-payment 2,0 --ptb-inputs 100 @A Error: Error checking transaction input objects: Unsupported("MoveAuthenticator cannot authenticate mutable shared objects") diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.move index 20cd1912e81..57ddb109afb 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.move @@ -22,14 +22,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.snap index 31beeaa81bc..40c7247fae8 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/receive_object.snap @@ -6,13 +6,13 @@ processed 8 tasks init: A: object(0,0) -task 1, lines 8-46: +task 1, lines 8-44: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12737600, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 11696400, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 48-52: +task 2, lines 46-50: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6657600, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 54: +task 3, line 52: //# view-object 2,2 Owner: Shared( 3 ) Version: 3 @@ -34,7 +34,7 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 5, lines 58-60: +task 5, lines 56-58: //# programmable --sender A --inputs 2000000000 @a_account //> 0: SplitCoins(Gas, [Input(0)]); //> 1: TransferObjects([Result(0)], Input(1)); @@ -42,12 +42,12 @@ created: object(5,0) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 1960800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 6, lines 62-63: +task 6, lines 60-61: //# abstract --account immshared(2,2) --gas-payment 2,0 --ptb-inputs object(2,2) receiving(5,0) mutated: object(2,0), object(2,2), object(5,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 3382000, storage_rebate: 3382000, non_refundable_storage_fee: 0 -task 7, line 65: +task 7, line 63: //# view-object 5,0 Owner: Account Address ( a_account ) Version: 5 diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.move index 0ac62ab791d..e26c80e4a66 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.move @@ -20,13 +20,9 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ) { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); - iota::transfer::share_object(account); + let account = AbstractAccount { id: object::new(ctx) }; + + account::create_shared_account_v1(account, authenticator); } #[authenticator] diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.snap index 8fedd634d6a..2a812ffdb84 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/account/sponsor.snap @@ -6,13 +6,13 @@ processed 6 tasks init: A: object(0,0) -task 1, lines 8-33: +task 1, lines 8-29: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 10966800, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 9720400, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 35-37: +task 2, lines 31-33: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -20,7 +20,7 @@ created: object(2,0), object(2,1) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5677200, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 39: +task 3, line 35: //# view-object 2,1 Owner: Shared( 3 ) Version: 3 @@ -32,14 +32,14 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 4, lines 41-43: +task 4, lines 37-39: //# abstract --account immshared(2,1) --sponsor A --ptb-inputs 100 @A created: object(4,0) mutated: object(0,0) unchanged_shared: object(2,1) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 1960800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 5, line 45: +task 5, line 41: //# view-object 4,0 Owner: Account Address ( A ) Version: 4 diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.move index 70a0274690e..b90d17a641a 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.move @@ -25,14 +25,13 @@ public fun create( ctx: &mut TxContext, ): address { let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + dynamic_field::add(&mut account.id, OwnerPublicKey {}, public_key); + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.snap index 4f926d30147..bcdd9897526 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519.snap @@ -6,13 +6,13 @@ processed 7 tasks init: A: object(0,0) -task 1, lines 8-57: +task 1, lines 8-56: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 13322800, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12182800, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 59-63: +task 2, lines 58-62: //# programmable --sender A --inputs x"cc62332e34bb2d5cd69f60efbb2a36cb916c7eb458301ea36636c4dbb012bd88" object(1,1) "abstract_account" "authenticate_ed25519" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2), object(2,3) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 8816000, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 65: +task 3, line 64: //# view-object 2,3 Owner: Shared( 3 ) Version: 3 @@ -34,7 +34,7 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 4, line 67: +task 4, line 66: //# view-object 2,0 Owner: Account Address ( fake(2,3) ) Version: 3 @@ -49,14 +49,14 @@ Contents: iota::coin::Coin { }, } -task 5, lines 69-71: +task 5, lines 68-70: //# abstract --account immshared(2,3) --gas-payment 2,0 --auth-inputs x"cce72947906dbae4c166fc01fd096432784032be43db540909bc901dbc057992b4d655ca4f4355cf0868e1266baacf6919902969f063e74162f8f04bc4056105" x"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3" --ptb-inputs 100 @A created: object(5,0) mutated: object(2,0) unchanged_shared: object(2,3) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 1960800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 6, line 73: +task 6, line 72: //# view-object 5,0 Owner: Account Address ( A ) Version: 4 diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.move index d1ab56773ef..23087b69fd4 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.move @@ -25,14 +25,13 @@ public fun create( ctx: &mut TxContext, ): address { let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + dynamic_field::add(&mut account.id, OwnerPublicKey {}, public_key); + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } @@ -68,4 +67,4 @@ public fun authenticate_ed25519( //# abstract --account immshared(2,3) --gas-payment 2,0 --auth-inputs x"cce72947906dbae4c166fc01fd096432784032be43db540909bc901dbc057992b4d655ca4f4355cf0868e1266baacf6919902969f063e74162f8f04bc4056105" x"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c10000edd3" --ptb-inputs 100 @A //> 0: SplitCoins(Gas, [Input(0)]); -//> 1: TransferObjects([Result(0)], Input(1)); \ No newline at end of file +//> 1: TransferObjects([Result(0)], Input(1)); diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.snap index 2423f429710..dc67d328f06 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_digest.snap @@ -6,13 +6,13 @@ processed 6 tasks init: A: object(0,0) -task 1, lines 8-57: +task 1, lines 8-56: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 13322800, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12182800, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 59-63: +task 2, lines 58-62: //# programmable --sender A --inputs x"cc62332e34bb2d5cd69f60efbb2a36cb916c7eb458301ea36636c4dbb012bd88" object(1,1) "abstract_account" "authenticate_ed25519" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2), object(2,3) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 8816000, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 65: +task 3, line 64: //# view-object 2,3 Owner: Shared( 3 ) Version: 3 @@ -34,7 +34,7 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 4, line 67: +task 4, line 66: //# view-object 2,0 Owner: Account Address ( fake(2,3) ) Version: 3 @@ -49,6 +49,6 @@ Contents: iota::coin::Coin { }, } -task 5, lines 69-71: +task 5, lines 68-70: //# abstract --account immshared(2,3) --gas-payment 2,0 --auth-inputs x"cce72947906dbae4c166fc01fd096432784032be43db540909bc901dbc057992b4d655ca4f4355cf0868e1266baacf6919902969f063e74162f8f04bc4056105" x"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c10000edd3" --ptb-inputs 100 @A Error: Failed to execute the Move authenticator, reason: "ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveAbort(MoveLocation { module: ModuleId { address: object(1,0), name: Identifier(\"abstract_account\") }, function: 1, instruction: 11, function_name: Some(\"authenticate_ed25519\") }, 0), source: Some(VMError { major_status: ABORTED, sub_status: Some(0), message: Some(\"object(1,0)::abstract_account::authenticate_ed25519 at offset 11\"), exec_state: None, location: Module(ModuleId { address: object(1,0), name: Identifier(\"abstract_account\") }), indices: [], offsets: [(FunctionDefinitionIndex(1), 11)] }), command: Some(0) } }". diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.move index 30ce06ee588..148feac9c34 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.move @@ -25,14 +25,13 @@ public fun create( ctx: &mut TxContext, ): address { let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + dynamic_field::add(&mut account.id, OwnerPublicKey {}, public_key); + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.snap index 4cdca8c6beb..84eb8c1420a 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/ed25519_wrong_signature.snap @@ -6,13 +6,13 @@ processed 6 tasks init: A: object(0,0) -task 1, lines 8-57: +task 1, lines 8-56: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 13322800, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12182800, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 59-63: +task 2, lines 58-62: //# programmable --sender A --inputs x"cc62332e34bb2d5cd69f60efbb2a36cb916c7eb458301ea36636c4dbb012bd88" object(1,1) "abstract_account" "authenticate_ed25519" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2), object(2,3) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 8816000, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 65: +task 3, line 64: //# view-object 2,3 Owner: Shared( 3 ) Version: 3 @@ -34,7 +34,7 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 4, line 67: +task 4, line 66: //# view-object 2,0 Owner: Account Address ( fake(2,3) ) Version: 3 @@ -49,6 +49,6 @@ Contents: iota::coin::Coin { }, } -task 5, lines 69-71: +task 5, lines 68-70: //# abstract --account immshared(2,3) --gas-payment 2,0 --auth-inputs x"cce72947906dbae4c166fc01fd096432784032be43db540909bc901dbc057992b4d655ca4f4355cf0868e1266baacf6919902969f063e74162f8f04bc4052345" x"315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3" --ptb-inputs 100 @A Error: Failed to execute the Move authenticator, reason: "ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveAbort(MoveLocation { module: ModuleId { address: object(1,0), name: Identifier(\"abstract_account\") }, function: 1, instruction: 11, function_name: Some(\"authenticate_ed25519\") }, 0), source: Some(VMError { major_status: ABORTED, sub_status: Some(0), message: Some(\"object(1,0)::abstract_account::authenticate_ed25519 at offset 11\"), exec_state: None, location: Module(ModuleId { address: object(1,0), name: Identifier(\"abstract_account\") }), indices: [], offsets: [(FunctionDefinitionIndex(1), 11)] }), command: Some(0) } }". diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.move index f53c37b760a..2f19026658f 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.move @@ -21,14 +21,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.snap index c21e49c756d..2ab2ffd83f5 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/several_authenticators.snap @@ -6,13 +6,13 @@ processed 7 tasks init: A: object(0,0) -task 1, lines 8-53: +task 1, lines 8-51: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 13961200, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12775600, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 55-59: +task 2, lines 53-57: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate_hello_world" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6748800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 61: +task 3, line 59: //# view-object 2,0 Owner: Account Address ( fake(2,2) ) Version: 3 @@ -37,7 +37,7 @@ Contents: iota::coin::Coin { }, } -task 4, line 63: +task 4, line 61: //# view-object 2,2 Owner: Shared( 3 ) Version: 3 @@ -49,14 +49,14 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 5, lines 65-67: +task 5, lines 63-65: //# abstract --account immshared(2,2) --gas-payment 2,0 --auth-inputs "HelloWorld" --ptb-inputs 100 @A created: object(5,0) mutated: object(2,0) unchanged_shared: object(2,2) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 1960800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 6, line 69: +task 6, line 67: //# view-object 5,0 Owner: Account Address ( A ) Version: 4 diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.move index 01128d90c9f..0bb4b39276a 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.move @@ -21,14 +21,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.snap index aa49540c62d..d519188c723 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple.snap @@ -6,13 +6,13 @@ processed 7 tasks init: A: object(0,0) -task 1, lines 8-43: +task 1, lines 8-41: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12334800, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 11149200, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 45-49: +task 2, lines 43-47: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate_hello_world" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6748800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 51: +task 3, line 49: //# view-object 2,0 Owner: Account Address ( fake(2,2) ) Version: 3 @@ -37,7 +37,7 @@ Contents: iota::coin::Coin { }, } -task 4, line 53: +task 4, line 51: //# view-object 2,2 Owner: Shared( 3 ) Version: 3 @@ -49,14 +49,14 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 5, lines 55-57: +task 5, lines 53-55: //# abstract --account immshared(2,2) --gas-payment 2,0 --auth-inputs "HelloWorld" --ptb-inputs 100 @A created: object(5,0) mutated: object(2,0) unchanged_shared: object(2,2) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 1960800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 6, line 59: +task 6, line 57: //# view-object 5,0 Owner: Account Address ( A ) Version: 4 diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.move index 6cd4877cb1e..22484bb9820 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.move @@ -21,14 +21,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.snap index e74542cde5a..7ea124530a4 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/simple_fail.snap @@ -6,13 +6,13 @@ processed 6 tasks init: A: object(0,0) -task 1, lines 8-43: +task 1, lines 8-41: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12334800, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 11149200, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 45-49: +task 2, lines 43-47: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate_hello_world" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -22,7 +22,7 @@ created: object(2,0), object(2,1), object(2,2) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6748800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 51: +task 3, line 49: //# view-object 2,0 Owner: Account Address ( fake(2,2) ) Version: 3 @@ -37,7 +37,7 @@ Contents: iota::coin::Coin { }, } -task 4, line 53: +task 4, line 51: //# view-object 2,2 Owner: Shared( 3 ) Version: 3 @@ -49,6 +49,6 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 5, lines 55-56: +task 5, lines 53-54: //# abstract --account immshared(2,2) --gas-payment 2,0 --auth-inputs "test" --ptb-inputs 100 @A Error: Failed to execute the Move authenticator, reason: "ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveAbort(MoveLocation { module: ModuleId { address: object(1,0), name: Identifier(\"abstract_account\") }, function: 1, instruction: 7, function_name: Some(\"authenticate_hello_world\") }, 0), source: Some(VMError { major_status: ABORTED, sub_status: Some(0), message: Some(\"object(1,0)::abstract_account::authenticate_hello_world at offset 7\"), exec_state: None, location: Module(ModuleId { address: object(1,0), name: Identifier(\"abstract_account\") }), indices: [], offsets: [(FunctionDefinitionIndex(1), 7)] }), command: Some(0) } }". diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.move index a68d184db9a..896230d818e 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.move @@ -21,14 +21,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } @@ -45,4 +43,4 @@ public fun authenticate_hello_world( //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); //> 2: SplitCoins(Gas, [Input(4)]); -//> 3: TransferObjects([Result(2)], Result(1)); \ No newline at end of file +//> 3: TransferObjects([Result(2)], Result(1)); diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.snap index 8e99fc9861a..ded6841963d 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/without_attribute.snap @@ -6,13 +6,13 @@ processed 3 tasks init: A: object(0,0) -task 1, lines 8-42: +task 1, lines 8-40: //# publish --sender A created: object(1,0) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 8816000, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 7630400, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 44-48: +task 2, lines 42-46: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate_hello_world" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.move b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.move index 3ef695903be..c6742908006 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.move +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.move @@ -24,14 +24,9 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ) { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); - iota::transfer::share_object(account); + let account = AbstractAccount { id: object::new(ctx) }; + + account::create_shared_account_v1(account, authenticator); } #[authenticator] diff --git a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.snap b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.snap index acd07e3c3bc..7377dd40357 100644 --- a/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.snap +++ b/crates/iota-adapter-transactional-tests/tests/abstract_account/authenticator/wrong_type.snap @@ -6,15 +6,15 @@ processed 3 tasks init: A: object(0,0) -task 1, lines 8-38: +task 1, lines 8-33: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 11666000, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 10419600, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 40-42: +task 2, lines 35-37: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate" //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); -Error: Transaction Effects Status: Move Runtime Abort. Location: iota::account::create_auth_info_v1 (function index 0) at offset 15, Abort Code: 13835902772270465031 -Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveAbort(MoveLocation { module: ModuleId { address: iota, name: Identifier("account") }, function: 0, instruction: 15, function_name: Some("create_auth_info_v1") }, 13835902772270465031), source: Some(VMError { major_status: ABORTED, sub_status: Some(13835902772270465031), message: Some("iota::account::create_auth_info_v1 at offset 15"), exec_state: None, location: Module(ModuleId { address: iota, name: Identifier("account") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 15)] }), command: Some(0) } } +Error: Transaction Effects Status: Move Runtime Abort. Location: iota::account::create_auth_info_v1 (function index 0) at offset 15, Abort Code: 13835621258638917637 +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: MoveAbort(MoveLocation { module: ModuleId { address: iota, name: Identifier("account") }, function: 0, instruction: 15, function_name: Some("create_auth_info_v1") }, 13835621258638917637), source: Some(VMError { major_status: ABORTED, sub_status: Some(13835621258638917637), message: Some("iota::account::create_auth_info_v1 at offset 15"), exec_state: None, location: Module(ModuleId { address: iota, name: Identifier("account") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 15)] }), command: Some(0) } } diff --git a/crates/iota-adapter-transactional-tests/tests/programmable/private_account_functions.move b/crates/iota-adapter-transactional-tests/tests/programmable/private_account_functions.move new file mode 100644 index 00000000000..a0be2fdf69f --- /dev/null +++ b/crates/iota-adapter-transactional-tests/tests/programmable/private_account_functions.move @@ -0,0 +1,33 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// tests calling private account functions + +//# init --addresses test=0x0 --accounts A + +//# publish +module test::account; + +use iota::auth_context::AuthContext; + +public struct Account has key { id: UID } + +public fun create(ctx: &mut TxContext): Account { Account { id: object::new(ctx) } } + +#[authenticator] +public fun authenticate(_: &Account, _auth_ctx: &AuthContext, _ctx: &TxContext) {} + +//# programmable --inputs object(1,1) "account" "authenticate" +//> 0: test::account::create(); +//> 1: iota::account::create_auth_info_v1(Input(0), Input(1), Input(2)); +//> 2: iota::account::create_shared_account_v1(Result(0), Result(1)); + +//# programmable --inputs object(1,1) "account" "authenticate" +//> 0: test::account::create(); +//> 1: iota::account::create_auth_info_v1(Input(0), Input(1), Input(2)); +//> 2: iota::account::create_immutable_account_v1(Result(0), Result(1)); + +//# programmable --inputs object(1,1) "account" "authenticate" +//> 0: test::account::create(); +//> 1: iota::account::create_auth_info_v1(Input(0), Input(1), Input(2)); +//> 2: iota::account::rotate_auth_info_v1(Result(0), Result(1)); diff --git a/crates/iota-adapter-transactional-tests/tests/programmable/private_account_functions.snap b/crates/iota-adapter-transactional-tests/tests/programmable/private_account_functions.snap new file mode 100644 index 00000000000..90c5c81ae38 --- /dev/null +++ b/crates/iota-adapter-transactional-tests/tests/programmable/private_account_functions.snap @@ -0,0 +1,37 @@ +--- +source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs +--- +processed 5 tasks + +init: +A: object(0,0) + +task 1, lines 8-18: +//# publish +created: object(1,0), object(1,1) +mutated: object(0,1) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 8466400, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 2, lines 20-23: +//# programmable --inputs object(1,1) "account" "authenticate" +//> 0: test::account::create(); +//> 1: iota::account::create_auth_info_v1(Input(0), Input(1), Input(2)); +//> 2: iota::account::create_shared_account_v1(Result(0), Result(1)); +Error: Transaction Effects Status: Non Entry Function Invoked. Move Call must start with an entry function +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: NonEntryFunctionInvoked, source: Some("Cannot directly call iota::account::create_shared_account_v1."), command: Some(2) } } + +task 3, lines 25-28: +//# programmable --inputs object(1,1) "account" "authenticate" +//> 0: test::account::create(); +//> 1: iota::account::create_auth_info_v1(Input(0), Input(1), Input(2)); +//> 2: iota::account::create_immutable_account_v1(Result(0), Result(1)); +Error: Transaction Effects Status: Non Entry Function Invoked. Move Call must start with an entry function +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: NonEntryFunctionInvoked, source: Some("Cannot directly call iota::account::create_immutable_account_v1."), command: Some(2) } } + +task 4, lines 30-33: +//# programmable --inputs object(1,1) "account" "authenticate" +//> 0: test::account::create(); +//> 1: iota::account::create_auth_info_v1(Input(0), Input(1), Input(2)); +//> 2: iota::account::rotate_auth_info_v1(Result(0), Result(1)); +Error: Transaction Effects Status: Non Entry Function Invoked. Move Call must start with an entry function +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: NonEntryFunctionInvoked, source: Some("Cannot directly call iota::account::rotate_auth_info_v1."), command: Some(2) } } diff --git a/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/abstract_account.move b/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/abstract_account.move index f966ce752b4..a12ee64b76b 100644 --- a/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/abstract_account.move +++ b/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/abstract_account.move @@ -25,6 +25,7 @@ const ETransactionSenderIsNotTheAccount: vector = b"Transaction must be sign /// add the desired authenticator info and dynamic fields. public struct AbstractAccountBuilder { account: AbstractAccount, + authenticator: AuthenticatorInfoV1, } /// This struct represents an abstract account. @@ -50,16 +51,10 @@ public fun builder( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): AbstractAccountBuilder { - let mut builder = AbstractAccountBuilder { + AbstractAccountBuilder { account: AbstractAccount { id: object::new(ctx) }, - }; - - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &builder.account, authenticator, - ); - account::attach_auth_info_v1(&mut builder.account.id, authenticator_compatibility_proof); - builder + } } /// Attach a `Value` as a dynamic field to the account being built. @@ -73,9 +68,9 @@ public fun add_dynamic_field( } /// Finish building the `AbstractAccount` and share the object. -public fun finish(self: AbstractAccountBuilder): AbstractAccount { - let AbstractAccountBuilder { account } = self; - account +public fun build_shared(self: AbstractAccountBuilder) { + let AbstractAccountBuilder { account, authenticator } = self; + account::create_shared_account_v1(account, authenticator); } /// Share AbstractAccount. @@ -157,11 +152,7 @@ public fun rotate_auth_info_v1( ): AuthenticatorInfoV1 { ensure_tx_sender_is_account(self, ctx); - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - self, - authenticator, - ); - account::rotate_auth_info_v1(&mut self.id, authenticator_compatibility_proof) + account::rotate_auth_info_v1(self, authenticator) } // === Public-View Functions === diff --git a/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/basic_keyed_aa.move b/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/basic_keyed_aa.move index 0bf8297b4cc..a64fd0d2879 100644 --- a/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/basic_keyed_aa.move +++ b/crates/iota-e2e-tests/tests/abstract_account/abstract_account/sources/basic_keyed_aa.move @@ -50,10 +50,9 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ) { - let account = abstract_account::builder(authenticator, ctx) + abstract_account::builder(authenticator, ctx) .add_dynamic_field(OwnerPublicKey {}, public_key) - .finish(); - account.share(); + .build_shared(); } /// Rotates the account owner public key to a new one as well as the authenticator. diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/16/0x0000000000000000000000000000000000000000000000000000000000000002 b/crates/iota-framework-snapshot/bytecode_snapshot/16/0x0000000000000000000000000000000000000000000000000000000000000002 index 6b9133ccf62..80b2a3e40a6 100644 Binary files a/crates/iota-framework-snapshot/bytecode_snapshot/16/0x0000000000000000000000000000000000000000000000000000000000000002 and b/crates/iota-framework-snapshot/bytecode_snapshot/16/0x0000000000000000000000000000000000000000000000000000000000000002 differ diff --git a/crates/iota-framework-snapshot/manifest.json b/crates/iota-framework-snapshot/manifest.json index 8908359efde..f392ddc6ab2 100644 --- a/crates/iota-framework-snapshot/manifest.json +++ b/crates/iota-framework-snapshot/manifest.json @@ -415,7 +415,7 @@ ] }, "16": { - "git_revision": "1a516653b610f38977cd4465d3392e69d7b92523", + "git_revision": "59eb660ce613d0d43f01bf2e9b6935cd14cb5c07", "packages": [ { "name": "MoveStdlib", diff --git a/crates/iota-framework/packages/iota-framework/sources/account.move b/crates/iota-framework/packages/iota-framework/sources/account.move index 7b5e6b2c748..51153eed526 100644 --- a/crates/iota-framework/packages/iota-framework/sources/account.move +++ b/crates/iota-framework/packages/iota-framework/sources/account.move @@ -15,10 +15,7 @@ const EAuthenticatorInfoV1AlreadyAttached: vector = const EAuthenticatorInfoV1NotAttached: vector = b"'AuthenticatorInfoV1' is not attached to the account."; #[error(code = 2)] -const EAuthenticatorInfoV1CompatibilityNotProven: vector = - b"An `AuthenticatorInfoV1` instance is not verified to be attached to the account."; -#[error(code = 3)] -const EAuthenticatorInfoNotCompatibileWithAccount: vector = +const EAuthenticatorInfoNotCompatibleWithAccount: vector = b"The provided `AuthenticatorInfoV1` is not compatible with the account type."; /// Dynamic field key, where the system will look for a potential @@ -33,12 +30,6 @@ public struct AuthenticatorInfoV1 has copy, drop, store { function_name: ascii::String, } -/// Represents a proof of compatibility between `AuthenticatorInfoV1` and an account. -public struct AuthenticatorInfoV1CompatibilityProof has drop { - account_id: ID, - authenticator: AuthenticatorInfoV1, -} - /// Create an "AuthenticatorInfoV1" using an `authenticate` function defined outside of this version of the package /// /// The referred `package`, `module_name`, `function_name` can refer to any valid `authenticate` function, @@ -65,7 +56,7 @@ public fun create_auth_info_v1( assert!( type_name::get() == authenticator_metadata.account_type(), - EAuthenticatorInfoNotCompatibileWithAccount, + EAuthenticatorInfoNotCompatibleWithAccount, ); AuthenticatorInfoV1 { package: package_metadata.storage_id(), @@ -74,38 +65,42 @@ public fun create_auth_info_v1( } } -/// Checks that the provided `authenticator` is compatible with the given `account`. -/// Returns a proof that can be used to attach or rotate the `authenticator` to the `account`. -public fun check_auth_info_v1_compatibility( - account: &Account, +/// Create a mutable shared account with the provided `authenticator`. +/// The `authenticator` instance will be added to the account as a dynamic field specified by the `AuthenticatorInfoV1Key` name. +/// This function has custom rules performed by the IOTA Move bytecode verifier that ensures +/// that `Account` is an object defined in the module where `create_shared_account_v1` is invoked. +public fun create_shared_account_v1( + mut account: Account, authenticator: AuthenticatorInfoV1, -): AuthenticatorInfoV1CompatibilityProof { - AuthenticatorInfoV1CompatibilityProof { - account_id: object::id(account), - authenticator, - } +) { + attach_auth_info_v1(&mut account, authenticator); + + create_shared_account_v1_impl(account); } -/// Attach the `authenticator` instance to the account. It uses a `AuthenticatorInfoV1CompatibilityProof` to obtain that instance. -/// It will be added as a dynamic field specified by the `AuthenticatorInfoV1Key` name. -public fun attach_auth_info_v1( - account_id: &mut UID, - proof: AuthenticatorInfoV1CompatibilityProof, +/// Create an immutable account with the provided `authenticator`. +/// The `authenticator` instance will be added to the account as a dynamic field specified by the `AuthenticatorInfoV1Key` name. +/// This function has custom rules performed by the IOTA Move bytecode verifier that ensures +/// that `Account` is an object defined in the module where `create_immutable_account_v1` is invoked. +public fun create_immutable_account_v1( + mut account: Account, + authenticator: AuthenticatorInfoV1, ) { - assert!(account_id.as_inner() == proof.account_id, EAuthenticatorInfoV1CompatibilityNotProven); - assert!(!has_auth_info_v1(account_id), EAuthenticatorInfoV1AlreadyAttached); + attach_auth_info_v1(&mut account, authenticator); - dynamic_field::add(account_id, auth_info_v1_key(), proof.authenticator); + create_immutable_account_v1_impl(account); } /// Rotate the account-related authenticator. -/// The `authenticator` instance will replace the account dynamic field specified by the `AuthenticatorInfoV1Key` name; -/// It uses a `AuthenticatorInfoV1CompatibilityProof` to obtain the new instance. +/// The `authenticator` instance will replace the account dynamic field specified by the `AuthenticatorInfoV1Key` name. +/// This function has custom rules performed by the IOTA Move bytecode verifier that ensures +/// that `Account` is an object defined in the module where `rotate_auth_info_v1` is invoked. public fun rotate_auth_info_v1( - account_id: &mut UID, - proof: AuthenticatorInfoV1CompatibilityProof, + account: &mut Account, + authenticator: AuthenticatorInfoV1, ): AuthenticatorInfoV1 { - assert!(account_id.as_inner() == proof.account_id, EAuthenticatorInfoV1CompatibilityNotProven); + let account_id = borrow_account_uid_mut(account); + assert!(has_auth_info_v1(account_id), EAuthenticatorInfoV1NotAttached); let name = auth_info_v1_key(); @@ -114,7 +109,7 @@ public fun rotate_auth_info_v1( account_id, name, ); - dynamic_field::add(account_id, name, proof.authenticator); + dynamic_field::add(account_id, name, authenticator); previous_authenticator_info } @@ -134,7 +129,36 @@ fun auth_info_v1_key(): AuthenticatorInfoV1Key { AuthenticatorInfoV1Key {} } -/// Creates an `AuthenticatorInfoV1` instance for testing, skipping validation. +/// Add `authenticator` as a dynamic field to `account`. +/// This function must be called only from the account functions protected by the compiler +/// from being called outside the `Account` module. +fun attach_auth_info_v1( + account: &mut Account, + authenticator: AuthenticatorInfoV1, +) { + let account_id = borrow_account_uid_mut(account); + + assert!(!has_auth_info_v1(account_id), EAuthenticatorInfoV1AlreadyAttached); + + dynamic_field::add(account_id, auth_info_v1_key(), authenticator); +} + +/// Borrow the account `UID` mutably. +/// This function must be called only from the functions protected by the IOTA Move bytecode verifier +/// from being called outside the `Account` module. +native fun borrow_account_uid_mut(account: &mut Account): &mut UID; + +/// Turn `account` into a mutable shared object. +/// This function must be called only from the functions protected by the IOTA Move bytecode verifier +/// from being called outside the `Account` module. +native fun create_shared_account_v1_impl(account: Account); + +/// Turn `account` into an immutable object. +/// This function must be called only from the functions protected by the IOTA Move bytecode verifier +/// from being called outside the `Account` module. +native fun create_immutable_account_v1_impl(account: Account); + +/// Create an `AuthenticatorInfoV1` instance for testing, skipping validation. #[test_only] public fun create_auth_info_v1_for_testing( package: address, diff --git a/crates/iota-framework/packages/iota-framework/tests/account_tests.move b/crates/iota-framework/packages/iota-framework/tests/account_tests.move index 85eac11a33c..d926540a50d 100644 --- a/crates/iota-framework/packages/iota-framework/tests/account_tests.move +++ b/crates/iota-framework/packages/iota-framework/tests/account_tests.move @@ -18,24 +18,20 @@ fun id(self: &TestAccount): &UID { &self.id } -fun id_mut(self: &mut TestAccount): &mut UID { - &mut self.id -} - #[test] -fun authenticator_info_v1_happy_path() { - account_test_mut!(|_, account| { +fun authenticator_info_v1_shared_account_happy_path() { + account_test!(|scenario, account| { let default_authenticator_info = create_default_authenticator_info_v1_for_testing(); // Check that there is no an attached `AuthenticatorInfoV1` just after creation. assert_eq(account::has_auth_info_v1(account.id()), false); - // Attach an `AuthenticatorInfoV1` instance to the account. - let compatibility_proof = account::check_auth_info_v1_compatibility( - account, - default_authenticator_info, - ); - account::attach_auth_info_v1(account.id_mut(), compatibility_proof); + // Create a shared account with an attached `AuthenticatorInfoV1` instance. + account::create_shared_account_v1(account, default_authenticator_info); + + scenario.next_tx(@0x0); + + let mut account = scenario.take_shared(); assert_eq(account::has_auth_info_v1(account.id()), true); assert_ref_eq(account::borrow_auth_info_v1(account.id()), &default_authenticator_info); @@ -46,27 +42,46 @@ fun authenticator_info_v1_happy_path() { ascii::string(b"module2"), ascii::string(b"function2"), ); - - let compatibility_proof = account::check_auth_info_v1_compatibility( - account, - updated_authenticator_info, - ); let previous_authenticator_info = account::rotate_auth_info_v1( - account.id_mut(), - compatibility_proof, + &mut account, + updated_authenticator_info, ); assert_eq(previous_authenticator_info, default_authenticator_info); assert_eq(account::has_auth_info_v1(account.id()), true); assert_ref_eq(account::borrow_auth_info_v1(account.id()), &updated_authenticator_info); + + test_scenario::return_shared(account); + }); +} + +#[test] +fun authenticator_info_v1_immutable_account_happy_path() { + account_test!(|scenario, account| { + let default_authenticator_info = create_default_authenticator_info_v1_for_testing(); + + // Check that there is no an attached `AuthenticatorInfoV1` just after creation. + assert_eq(account::has_auth_info_v1(account.id()), false); + + // Create an immutable account with an attached `AuthenticatorInfoV1` instance. + account::create_immutable_account_v1(account, default_authenticator_info); + + scenario.next_tx(@0x0); + + let account = scenario.take_immutable(); + + assert_eq(account::has_auth_info_v1(account.id()), true); + assert_ref_eq(account::borrow_auth_info_v1(account.id()), &default_authenticator_info); + + test_scenario::return_immutable(account); }); } #[test] #[expected_failure(abort_code = account::EAuthenticatorInfoV1AlreadyAttached)] -fun authenticator_info_v1_double_attach() { - account_test_mut!(|_, account| { +fun authenticator_info_v1_double_shared_account_creation() { + account_test!(|scenario, account| { let authenticator_info_1 = create_default_authenticator_info_v1_for_testing(); let authenticator_info_2 = account::create_auth_info_v1_for_testing( @0x2, @@ -74,80 +89,58 @@ fun authenticator_info_v1_double_attach() { ascii::string(b"function2"), ); - let compatibility_proof_1 = account::check_auth_info_v1_compatibility( - account, - authenticator_info_1, - ); - account::attach_auth_info_v1(account.id_mut(), compatibility_proof_1); - // Attach another `AuthenticatorInfoV1` instance that is forbidden. - let compatibility_proof_2 = account::check_auth_info_v1_compatibility( - account, - authenticator_info_2, - ); - account::attach_auth_info_v1(account.id_mut(), compatibility_proof_2); - }); -} + account::create_shared_account_v1(account, authenticator_info_1); -#[test] -#[expected_failure(abort_code = account::EAuthenticatorInfoV1CompatibilityNotProven)] -fun authenticator_info_v1_not_proven_attach() { - account_test_mut!(|scenario, account| { - let authenticator_info = create_default_authenticator_info_v1_for_testing(); + scenario.next_tx(@0x0); - let account_2 = create_test_account(scenario); - let compatibility_proof = account::check_auth_info_v1_compatibility( - &account_2, - authenticator_info, - ); - // Attach a not proven `AuthenticatorInfoV1` instance. - account::attach_auth_info_v1(account.id_mut(), compatibility_proof); - test_utils::destroy(account_2); + let account = scenario.take_shared(); + + // Call `account::create_account_v1` one more time for the same object that is forbidden. + account::create_shared_account_v1(account, authenticator_info_2); }); } #[test] -#[expected_failure(abort_code = account::EAuthenticatorInfoV1NotAttached)] -fun authenticator_info_v1_borrow_non_existent() { - account_test!(|_, account_id| { - // Borrow a non-existing `AuthenticatorInfoV1` instance. - account::borrow_auth_info_v1(account_id); +#[expected_failure(abort_code = account::EAuthenticatorInfoV1AlreadyAttached)] +fun authenticator_info_v1_double_immutable_account_creation() { + account_test!(|scenario, account| { + let authenticator_info_1 = create_default_authenticator_info_v1_for_testing(); + let authenticator_info_2 = account::create_auth_info_v1_for_testing( + @0x2, + ascii::string(b"module2"), + ascii::string(b"function2"), + ); + + account::create_immutable_account_v1(account, authenticator_info_1); + + scenario.next_tx(@0x0); + + let account = scenario.take_immutable(); + // Call `account::create_account_v1` one more time for the same object that is forbidden. + account::create_immutable_account_v1(account, authenticator_info_2); }); } #[test] #[expected_failure(abort_code = account::EAuthenticatorInfoV1NotAttached)] -fun authenticator_info_v1_rotate_non_existent() { - account_test_mut!(|_, account| { - let authenticator_info = create_default_authenticator_info_v1_for_testing(); +fun authenticator_info_v1_borrow_non_attached() { + account_test!(|_, account| { + // Borrow a non-attached `AuthenticatorInfoV1` instance. + account::borrow_auth_info_v1(account.id()); - let compatibility_proof = account::check_auth_info_v1_compatibility( - account, - authenticator_info, - ); - account::rotate_auth_info_v1(account.id_mut(), compatibility_proof); + test_utils::destroy(account); }); } #[test] -#[expected_failure(abort_code = account::EAuthenticatorInfoV1CompatibilityNotProven)] -fun authenticator_info_v1_rotate_not_proven() { - account_test_mut!(|scenario, account| { +#[expected_failure(abort_code = account::EAuthenticatorInfoV1NotAttached)] +fun authenticator_info_v1_rotate_non_attached() { + account_test!(|_, mut account| { let authenticator_info = create_default_authenticator_info_v1_for_testing(); - let compatibility_proof = account::check_auth_info_v1_compatibility( - account, - authenticator_info, - ); - account::attach_auth_info_v1(account.id_mut(), compatibility_proof); + account::rotate_auth_info_v1(&mut account, authenticator_info); - let account_2 = create_test_account(scenario); - let compatibility_proof = account::check_auth_info_v1_compatibility( - &account_2, - authenticator_info, - ); - // Rotate a not proven `AuthenticatorInfoV1` instance. - account::rotate_auth_info_v1(account.id_mut(), compatibility_proof); - test_utils::destroy(account_2); + test_utils::destroy(account); }); } @@ -163,26 +156,12 @@ fun create_default_authenticator_info_v1_for_testing(): AuthenticatorInfoV1 Result<(), anyhow::Error> { "coin_manager", "config", "deny_list", + "derived_object", "display", "dynamic_field", "dynamic_object_field", @@ -60,6 +61,7 @@ async fn get_normalized_move_modules_by_package() -> Result<(), anyhow::Error> { "object_bag", "object_table", "package", + "package_metadata", "pay", "poseidon", "priority_queue", diff --git a/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap b/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap index 0432ac0daf6..3c28a7cb862 100644 --- a/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap +++ b/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap @@ -8,7 +8,7 @@ system_state_version: 1 iota_treasury_cap: inner: id: - id: "0x87c5ea4916fc3b461c09c7658e0c1bc26d0870ab3d94e3585beea7c25124e97a" + id: "0x5ffc85c55b68c12d076506721f93de9243e4ac6d684aaa8a96bdedcfc5b763aa" total_supply: value: "751500000000000000" validators: @@ -244,13 +244,13 @@ validators: next_epoch_primary_address: ~ extra_fields: id: - id: "0x948ecfff10a09397e826bc81f9ea7012b0cabec165c29b68c269c6c4582fdcd4" + id: "0x5bd7558660a6451c910a69fe3dd52e45ed720505b4184083f9e3bb7c41edbb07" size: 0 voting_power: 10000 - operation_cap_id: "0x171ac0a062f7463e9f52122b6adc40dd4cf0ac0f97f5bf8acfc4dcb2aa94e53d" + operation_cap_id: "0x401f07ca6d62e3739307782b5d333d52aa78d67a9fb5720063897f58956b25f4" gas_price: 1000 staking_pool: - id: "0x6e1fa56e748c508a5eba8e097d70f369b8f01fcf5507696ce1dc03048739bfa1" + id: "0x8567589b65404fd49e088ad46fae36905682bda04877c6db4998a851efc0bdab" activation_epoch: 0 deactivation_epoch: ~ iota_balance: 1500000000000000 @@ -258,14 +258,14 @@ validators: value: 0 pool_token_balance: 1500000000000000 exchange_rates: - id: "0x888114e0d92bbd1afe58010b4f69db71e9b3bcffcac599d6b00a9f489574817b" + id: "0x3fb8206da20d6c6838a8fdf9d839b4bfb1b5fce4d7c65ee1a4f19d72ac389003" size: 1 pending_stake: 0 pending_total_iota_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0x23a265df00831684eec17a1079b3a702a8ae6ce926b22bee3e08c3f27b0ef1bd" + id: "0xe5683c287c7012bfd936a6e98934296d536f4ae4fd26d78fcbf98ca2c5df743e" size: 0 commission_rate: 200 next_epoch_stake: 1500000000000000 @@ -273,27 +273,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0x269cb2c93c70d886f3b46a5d7ca00d99be9d876d5008370a1512ec777f5839a8" + id: "0xd2a6b42dc9f1afd09e5f105916ac609bc8a35215461511209076b3103eb714a4" size: 0 pending_active_validators: contents: - id: "0x499ae2276ba137a096e04c8be9d782110fbb905a15352f769f7f69349e124a9a" + id: "0x7e239969e82f7cc4acbac675b322deedd482c7aba53f77257c126149868954f4" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x59f538e0c227ae703f862e2b81e901f39326f3987bb74ba55fae02f67223a2a7" + id: "0x77f6672eaebed1fafda7ea9b98f919a4f4ff515828b2a34ccb904cf05693ec0c" size: 1 inactive_validators: - id: "0x0ef65b097fd491266ed3424d18b443f953b18f8879dea0dd738043f768cd22e0" + id: "0x48b2ac17505c6dfc04cda863d2c3dce37e3b3ba3ad39308831f4d950e641c1b7" size: 0 validator_candidates: - id: "0xca36d5ab23d0485c71c35e8191a1c806c39996dc1608044a857298e07d728820" + id: "0x6298cdfc1e8477f993be4980398731844aed4227a9ed58d38b58328b79e67ab5" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0xc7b233c28a5ffc1e2bc9e77bd5e34021e7ebcc105681b16a3bdbb4cafd275551" + id: "0xadd13f0b0b3cda929c5bde9d0f9622d86b20af58da1a939ba9ff1a0095c823c5" size: 0 storage_fund: total_object_storage_rebates: @@ -310,7 +310,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0xc08d1f4d667924fbed5042fbaec90a2d0df5428cbb31a8bbc0756e3f2c0df603" + id: "0x178f95dbe7dd72e20596414b254c7895563a1032e72d11d980a6b2282c12e6b9" size: 0 iota_system_admin_cap: dummy_field: false @@ -327,5 +327,5 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0x4c58f26ad1b83a5fb2bb76e94d34f7a24e3f22bb24f4610a86b3a4a12834d59d" + id: "0x124dff9baec7fe9380969b347fe8b482a524a2726469588096ae05aff2d4cea6" size: 0 diff --git a/crates/iota-transactional-test-runner/syntax.md b/crates/iota-transactional-test-runner/syntax.md index 0e810c0c956..cea3b8f8c1b 100644 --- a/crates/iota-transactional-test-runner/syntax.md +++ b/crates/iota-transactional-test-runner/syntax.md @@ -1467,14 +1467,12 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): address { - let mut account = AbstractAccount { id: object::new(ctx) }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = AbstractAccount { id: object::new(ctx) }; + let account_address = object::id_address(&account); - iota::transfer::share_object(account); + + account::create_shared_account_v1(account, authenticator); + account_address } @@ -1527,13 +1525,13 @@ processed 7 tasks init: A: object(0,0) -task 1, lines 8-43: +task 1, lines 8-41: //# publish --sender A created: object(1,0), object(1,1) mutated: object(0,0) -gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 12334800, storage_rebate: 0, non_refundable_storage_fee: 0 +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 11149200, storage_rebate: 0, non_refundable_storage_fee: 0 -task 2, lines 45-49: +task 2, lines 43-47: //# programmable --sender A --inputs x"10" object(1,1) "abstract_account" "authenticate_hello_world" 7000000000 //> 0: iota::account::create_auth_info_v1(Input(1), Input(2), Input(3)); //> 1: test::abstract_account::create(Input(0), Result(0)); @@ -1543,7 +1541,7 @@ created: object(2,0), object(2,1), object(2,2) mutated: object(0,0) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6748800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 3, line 51: +task 3, line 49: //# view-object 2,0 Owner: Account Address ( fake(2,2) ) Version: 3 @@ -1558,7 +1556,7 @@ Contents: iota::coin::Coin { }, } -task 4, line 53: +task 4, line 51: //# view-object 2,2 Owner: Shared( 3 ) Version: 3 @@ -1570,14 +1568,14 @@ Contents: test::abstract_account::AbstractAccount { }, } -task 5, lines 55-57: +task 5, lines 53-55: //# abstract --account immshared(2,2) --gas-payment 2,0 --auth-inputs "HelloWorld" --ptb-inputs 100 @A created: object(5,0) mutated: object(2,0) unchanged_shared: object(2,2) gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 1960800, storage_rebate: 980400, non_refundable_storage_fee: 0 -task 6, line 59: +task 6, line 57: //# view-object 5,0 Owner: Account Address ( A ) Version: 4 diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account.mvir b/crates/iota-verifier-transactional-tests/tests/account_functions/account.mvir new file mode 100644 index 00000000000..414e5e2dead --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account.mvir @@ -0,0 +1,62 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//# publish +module 0x0.m { + import 0x2.account; + import 0x2.object; + import 0x2.package_metadata; + import 0x1.ascii; + + struct Account has key { + id: object.UID, + } + + t1( + package_metadata: &package_metadata.PackageMetadataV1, + module_name: ascii.String, + function_name: ascii.String, + ): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.create_auth_info_v1(move(package_metadata), move(module_name), move(function_name)); + return move(a); + } + + t2(account: &Self.Account): &account.AuthenticatorInfoV1 { + let a: &account.AuthenticatorInfoV1; + let id: &object.UID; + label l0: + id = &move(account).Account::id; + a = account.borrow_auth_info_v1(move(id)); + return move(a); + } + + t3(account: &Self.Account): bool { + let b: bool; + let id: &object.UID; + label l0: + id = &move(account).Account::id; + b = account.has_auth_info_v1(move(id)); + return move(b); + } + + t4(account: Self.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_shared_account_v1(move(account), move(authenticator)); + return; + } + + t5(account: Self.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_immutable_account_v1(move(account), move(authenticator)); + return; + } + + t6(account: &mut Self.Account, authenticator: account.AuthenticatorInfoV1): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.rotate_auth_info_v1(move(account), move(authenticator)); + return move(a); + } +} diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account.snap b/crates/iota-verifier-transactional-tests/tests/account_functions/account.snap new file mode 100644 index 00000000000..8bbd57514f0 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account.snap @@ -0,0 +1,10 @@ +--- +source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs +--- +processed 1 task + +task 0, lines 4-62: +//# publish +created: object(1,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 8420800, storage_rebate: 0, non_refundable_storage_fee: 0 diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account_generic.mvir b/crates/iota-verifier-transactional-tests/tests/account_functions/account_generic.mvir new file mode 100644 index 00000000000..24a3f41c562 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account_generic.mvir @@ -0,0 +1,56 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//# publish +module 0x0.m { + import 0x2.account; + import 0x2.package_metadata; + import 0x1.ascii; + + t1( + package_metadata: &package_metadata.PackageMetadataV1, + module_name: ascii.String, + function_name: ascii.String, + ): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.create_auth_info_v1(move(package_metadata), move(module_name), move(function_name)); + return move(a); + } +} + + +//# publish +module 0x0.m { + import 0x2.account; + + t2(account: T0, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_shared_account_v1(move(account), move(authenticator)); + return; + } +} + + +//# publish +module 0x0.m { + import 0x2.account; + + t3(account: T0, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_immutable_account_v1(move(account), move(authenticator)); + return; + } +} + +//# publish +module 0x0.m { + import 0x2.account; + + t4(account: &mut T0, authenticator: account.AuthenticatorInfoV1): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.rotate_auth_info_v1(move(account), move(authenticator)); + return move(a); + } +} diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account_generic.snap b/crates/iota-verifier-transactional-tests/tests/account_functions/account_generic.snap new file mode 100644 index 00000000000..8b74dc7ae11 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account_generic.snap @@ -0,0 +1,25 @@ +--- +source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs +--- +processed 4 tasks + +task 0, lines 4-20: +//# publish +created: object(1,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5069200, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 1, lines 23-32: +//# publish +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t2. Invalid call to 'iota::account::create_shared_account_v1' on an object of type 'T0'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 2, lines 35-44: +//# publish +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t3. Invalid call to 'iota::account::create_immutable_account_v1' on an object of type 'T0'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 3, lines 46-56: +//# publish +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t4. Invalid call to 'iota::account::rotate_auth_info_v1' on an object of type 'T0'. The account object's type must be defined in the current module."), command: Some(0) } } diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account_store.mvir b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store.mvir new file mode 100644 index 00000000000..733ee299686 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store.mvir @@ -0,0 +1,62 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//# publish +module 0x0.m { + import 0x2.account; + import 0x2.object; + import 0x2.package_metadata; + import 0x1.ascii; + + struct Account has key, store { + id: object.UID, + } + + t1( + package_metadata: &package_metadata.PackageMetadataV1, + module_name: ascii.String, + function_name: ascii.String, + ): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.create_auth_info_v1(move(package_metadata), move(module_name), move(function_name)); + return move(a); + } + + t2(account: &Self.Account): &account.AuthenticatorInfoV1 { + let a: &account.AuthenticatorInfoV1; + let id: &object.UID; + label l0: + id = &move(account).Account::id; + a = account.borrow_auth_info_v1(move(id)); + return move(a); + } + + t3(account: &Self.Account): bool { + let b: bool; + let id: &object.UID; + label l0: + id = &move(account).Account::id; + b = account.has_auth_info_v1(move(id)); + return move(b); + } + + t4(account: Self.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_shared_account_v1(move(account), move(authenticator)); + return; + } + + t5(account: Self.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_immutable_account_v1(move(account), move(authenticator)); + return; + } + + t6(account: &mut Self.Account, authenticator: account.AuthenticatorInfoV1): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.rotate_auth_info_v1(move(account), move(authenticator)); + return move(a); + } +} diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account_store.snap b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store.snap new file mode 100644 index 00000000000..8bbd57514f0 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store.snap @@ -0,0 +1,10 @@ +--- +source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs +--- +processed 1 task + +task 0, lines 4-62: +//# publish +created: object(1,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 8420800, storage_rebate: 0, non_refundable_storage_fee: 0 diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account_store_generic.mvir b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store_generic.mvir new file mode 100644 index 00000000000..95157189014 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store_generic.mvir @@ -0,0 +1,56 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//# publish +module 0x0.m { + import 0x2.account; + import 0x2.package_metadata; + import 0x1.ascii; + + t1( + package_metadata: &package_metadata.PackageMetadataV1, + module_name: ascii.String, + function_name: ascii.String, + ): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.create_auth_info_v1(move(package_metadata), move(module_name), move(function_name)); + return move(a); + } +} + + +//# publish +module 0x0.m { + import 0x2.account; + + t2(account: T0, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_shared_account_v1(move(account), move(authenticator)); + return; + } +} + + +//# publish +module 0x0.m { + import 0x2.account; + + t3(account: T0, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_immutable_account_v1(move(account), move(authenticator)); + return; + } +} + +//# publish +module 0x0.m { + import 0x2.account; + + t4(account: &mut T0, authenticator: account.AuthenticatorInfoV1): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.rotate_auth_info_v1(move(account), move(authenticator)); + return move(a); + } +} diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/account_store_generic.snap b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store_generic.snap new file mode 100644 index 00000000000..8b74dc7ae11 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/account_store_generic.snap @@ -0,0 +1,25 @@ +--- +source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs +--- +processed 4 tasks + +task 0, lines 4-20: +//# publish +created: object(1,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5069200, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 1, lines 23-32: +//# publish +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t2. Invalid call to 'iota::account::create_shared_account_v1' on an object of type 'T0'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 2, lines 35-44: +//# publish +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t3. Invalid call to 'iota::account::create_immutable_account_v1' on an object of type 'T0'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 3, lines 46-56: +//# publish +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t4. Invalid call to 'iota::account::rotate_auth_info_v1' on an object of type 'T0'. The account object's type must be defined in the current module."), command: Some(0) } } diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/external_account.mvir b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account.mvir new file mode 100644 index 00000000000..75f66d89824 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account.mvir @@ -0,0 +1,111 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//# init --addresses test=0x0 + +//# publish +module test.external { + import 0x2.object; + + struct Account has key { + id: object.UID, + } + + public uid(self: &Self.Account): &object.UID { + let id: &object.UID; + label l0: + id = &move(self).Account::id; + return move(id); + } + +} + +//# set-address test object(1,0) + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import 0x2.package_metadata; + import 0x1.ascii; + import test.external; + + t1( + package_metadata: &package_metadata.PackageMetadataV1, + module_name: ascii.String, + function_name: ascii.String, + ): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.create_auth_info_v1(move(package_metadata), move(module_name), move(function_name)); + return move(a); + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import 0x2.object; + import test.external; + + t2(account: &external.Account): &account.AuthenticatorInfoV1 { + let a: &account.AuthenticatorInfoV1; + let id: &object.UID; + label l0: + id = external.uid(move(account)); + a = account.borrow_auth_info_v1(move(id)); + return move(a); + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import 0x2.object; + import test.external; + + t3(account: &external.Account): bool { + let b: bool; + let id: &object.UID; + label l0: + id = external.uid(move(account)); + b = account.has_auth_info_v1(move(id)); + return move(b); + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import test.external; + + t4(account: external.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_shared_account_v1(move(account), move(authenticator)); + return; + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import test.external; + + t5(account: external.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_immutable_account_v1(move(account), move(authenticator)); + return; + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import test.external; + + t6(account: &mut external.Account, authenticator: account.AuthenticatorInfoV1): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.rotate_auth_info_v1(move(account), move(authenticator)); + return move(a); + } +} diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/external_account.snap b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account.snap new file mode 100644 index 00000000000..4a11ab21d7f --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account.snap @@ -0,0 +1,43 @@ +--- +source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs +--- +processed 9 tasks + +task 1, lines 6-21: +//# publish +created: object(1,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 4575200, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, lines 25-42: +//# publish --dependencies test +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6080000, storage_rebate: 980400, non_refundable_storage_fee: 0 + +task 4, lines 44-58: +//# publish --dependencies test +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5684800, storage_rebate: 980400, non_refundable_storage_fee: 0 + +task 5, lines 60-74: +//# publish --dependencies test +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5259200, storage_rebate: 980400, non_refundable_storage_fee: 0 + +task 6, lines 76-86: +//# publish --dependencies test +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t4. Invalid call to 'iota::account::create_shared_account_v1' on an object of type 'test::external::Account'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 7, lines 88-98: +//# publish --dependencies test +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t5. Invalid call to 'iota::account::create_immutable_account_v1' on an object of type 'test::external::Account'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 8, lines 100-111: +//# publish --dependencies test +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t6. Invalid call to 'iota::account::rotate_auth_info_v1' on an object of type 'test::external::Account'. The account object's type must be defined in the current module."), command: Some(0) } } diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/external_account_store.mvir b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account_store.mvir new file mode 100644 index 00000000000..68b477eab14 --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account_store.mvir @@ -0,0 +1,111 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +//# init --addresses test=0x0 + +//# publish +module test.external { + import 0x2.object; + + struct Account has key, store { + id: object.UID, + } + + public uid(self: &Self.Account): &object.UID { + let id: &object.UID; + label l0: + id = &move(self).Account::id; + return move(id); + } + +} + +//# set-address test object(1,0) + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import 0x2.package_metadata; + import 0x1.ascii; + import test.external; + + t1( + package_metadata: &package_metadata.PackageMetadataV1, + module_name: ascii.String, + function_name: ascii.String, + ): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.create_auth_info_v1(move(package_metadata), move(module_name), move(function_name)); + return move(a); + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import 0x2.object; + import test.external; + + t2(account: &external.Account): &account.AuthenticatorInfoV1 { + let a: &account.AuthenticatorInfoV1; + let id: &object.UID; + label l0: + id = external.uid(move(account)); + a = account.borrow_auth_info_v1(move(id)); + return move(a); + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import 0x2.object; + import test.external; + + t3(account: &external.Account): bool { + let b: bool; + let id: &object.UID; + label l0: + id = external.uid(move(account)); + b = account.has_auth_info_v1(move(id)); + return move(b); + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import test.external; + + t4(account: external.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_shared_account_v1(move(account), move(authenticator)); + return; + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import test.external; + + t5(account: external.Account, authenticator: account.AuthenticatorInfoV1) { + label l0: + account.create_immutable_account_v1(move(account), move(authenticator)); + return; + } +} + +//# publish --dependencies test +module 0x0.m { + import 0x2.account; + import test.external; + + t6(account: &mut external.Account, authenticator: account.AuthenticatorInfoV1): account.AuthenticatorInfoV1 { + let a: account.AuthenticatorInfoV1; + label l0: + a = account.rotate_auth_info_v1(move(account), move(authenticator)); + return move(a); + } +} diff --git a/crates/iota-verifier-transactional-tests/tests/account_functions/external_account_store.snap b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account_store.snap new file mode 100644 index 00000000000..4a11ab21d7f --- /dev/null +++ b/crates/iota-verifier-transactional-tests/tests/account_functions/external_account_store.snap @@ -0,0 +1,43 @@ +--- +source: external-crates/move/crates/move-transactional-test-runner/src/framework.rs +--- +processed 9 tasks + +task 1, lines 6-21: +//# publish +created: object(1,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 4575200, storage_rebate: 0, non_refundable_storage_fee: 0 + +task 3, lines 25-42: +//# publish --dependencies test +created: object(3,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 6080000, storage_rebate: 980400, non_refundable_storage_fee: 0 + +task 4, lines 44-58: +//# publish --dependencies test +created: object(4,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5684800, storage_rebate: 980400, non_refundable_storage_fee: 0 + +task 5, lines 60-74: +//# publish --dependencies test +created: object(5,0) +mutated: object(0,0) +gas summary: computation_cost: 1000000, computation_cost_burned: 1000000, storage_cost: 5259200, storage_rebate: 980400, non_refundable_storage_fee: 0 + +task 6, lines 76-86: +//# publish --dependencies test +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t4. Invalid call to 'iota::account::create_shared_account_v1' on an object of type 'test::external::Account'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 7, lines 88-98: +//# publish --dependencies test +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t5. Invalid call to 'iota::account::create_immutable_account_v1' on an object of type 'test::external::Account'. The account object's type must be defined in the current module."), command: Some(0) } } + +task 8, lines 100-111: +//# publish --dependencies test +Error: Transaction Effects Status: IOTA Move Bytecode Verification Error. Please run the IOTA Move Verifier for more information. +Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: IotaMoveVerificationError, source: Some("_::m::t6. Invalid call to 'iota::account::rotate_auth_info_v1' on an object of type 'test::external::Account'. The account object's type must be defined in the current module."), command: Some(0) } } diff --git a/examples/move/dynamic_multisig_account/sources/dynamic_multisig_account.move b/examples/move/dynamic_multisig_account/sources/dynamic_multisig_account.move index 86cec7237bd..2f63f2eeef3 100644 --- a/examples/move/dynamic_multisig_account/sources/dynamic_multisig_account.move +++ b/examples/move/dynamic_multisig_account/sources/dynamic_multisig_account.move @@ -64,16 +64,10 @@ public fun create( dynamic_field::add(&mut id, threshold_key(), threshold); dynamic_field::add(&mut id, transactions_key(), transactions::create(ctx)); - let mut account = DynamicMultisigAccount { id }; - - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); + let account = DynamicMultisigAccount { id }; // Create a mutable shared account object. - iota::transfer::share_object(account); + account::create_shared_account_v1(account, authenticator); } // --------------------------------------- View Functions --------------------------------------- @@ -195,17 +189,13 @@ public fun update_account_data( // Verify the provided data consistency. verify_threshold(&members, threshold); - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - self, - authenticator, - ); - let account_id = &mut self.id; // Update the dynamic fields. It is expected that the fields already exist. update_dynamic_field(account_id, members_key(), members); update_dynamic_field(account_id, threshold_key(), threshold); - account::rotate_auth_info_v1(account_id, authenticator_compatibility_proof); + + account::rotate_auth_info_v1(self, authenticator); } /// A transaction authenticator. diff --git a/examples/move/function_keys/sources/function_keys.move b/examples/move/function_keys/sources/function_keys.move index f6a14c913f2..2b20c87541b 100644 --- a/examples/move/function_keys/sources/function_keys.move +++ b/examples/move/function_keys/sources/function_keys.move @@ -66,11 +66,10 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ) { - let account = builder(authenticator, ctx) + builder(authenticator, ctx) .add_dynamic_field(OwnerPublicKey {}, public_key) .add_dynamic_field(fk_store_key(), build_fn_keys_store(ctx)) - .finish(); - account.share(); + .build_shared(); } /// Grants (allows) a `FunctionKey` under a specific `pub_key`. @@ -180,8 +179,5 @@ public fun create_without_fk_store( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ) { - let account = builder(authenticator, ctx) - .add_dynamic_field(OwnerPublicKey {}, public_key) - .finish(); - account.share(); + builder(authenticator, ctx).add_dynamic_field(OwnerPublicKey {}, public_key).build_shared(); } diff --git a/examples/move/iotaccount/sources/iotaccount.move b/examples/move/iotaccount/sources/iotaccount.move index 9f23c2b44e2..ae8b50fb06c 100644 --- a/examples/move/iotaccount/sources/iotaccount.move +++ b/examples/move/iotaccount/sources/iotaccount.move @@ -23,6 +23,7 @@ const ETransactionSenderIsNotTheAccount: vector = b"Transaction must be sign /// add the desired authenticator info and dynamic fields. public struct IOTAccountBuilder { account: IOTAccount, + authenticator: AuthenticatorInfoV1, } /// This struct represents an abstract IOTA account. @@ -48,16 +49,10 @@ public fun builder( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ): IOTAccountBuilder { - let mut builder = IOTAccountBuilder { + IOTAccountBuilder { account: IOTAccount { id: object::new(ctx) }, - }; - - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &builder.account, authenticator, - ); - account::attach_auth_info_v1(&mut builder.account.id, authenticator_compatibility_proof); - builder + } } /// Attach a `Value` as a dynamic field to the account being built. @@ -70,15 +65,15 @@ public fun add_dynamic_field( self } -/// Finish building the `IOTAccount` and share the object. -public fun finish(self: IOTAccountBuilder): IOTAccount { - let IOTAccountBuilder { account } = self; - account -} +/// Finish building a shared `IOTAccount` instance. +public fun build_shared(self: IOTAccountBuilder): address { + let IOTAccountBuilder { account, authenticator } = self; -/// Share IOTAccount. -public fun share(self: IOTAccount) { - iota::transfer::share_object(self); + let account_address = account.account_address(); + + account::create_shared_account_v1(account, authenticator); + + account_address } /// Adds a new dynamic field to the account. @@ -155,11 +150,7 @@ public fun rotate_auth_info_v1( ): AuthenticatorInfoV1 { ensure_tx_sender_is_account(self, ctx); - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - self, - authenticator, - ); - account::rotate_auth_info_v1(&mut self.id, authenticator_compatibility_proof) + account::rotate_auth_info_v1(self, authenticator) } // === Public-View Functions === diff --git a/examples/move/iotaccount/sources/keyed_iotaccount.move b/examples/move/iotaccount/sources/keyed_iotaccount.move index ff9a358d7a3..f2abca94abc 100644 --- a/examples/move/iotaccount/sources/keyed_iotaccount.move +++ b/examples/move/iotaccount/sources/keyed_iotaccount.move @@ -50,10 +50,9 @@ public fun create( authenticator: AuthenticatorInfoV1, ctx: &mut TxContext, ) { - let account = iotaccount::builder(authenticator, ctx) + iotaccount::builder(authenticator, ctx) .add_dynamic_field(OwnerPublicKey {}, public_key) - .finish(); - account.share(); + .build_shared(); } /// Ed25519 signature authenticator. diff --git a/examples/move/iotaccount/tests/iotaccount_builder_tests.move b/examples/move/iotaccount/tests/iotaccount_builder_tests.move index 10311632dba..c551815e48d 100644 --- a/examples/move/iotaccount/tests/iotaccount_builder_tests.move +++ b/examples/move/iotaccount/tests/iotaccount_builder_tests.move @@ -26,10 +26,7 @@ fun builder_all_mandatory_fields_set() { let authenticator = create_authenticator_info_v1_for_testing(); // Any field value can be set as a dynamic field, and for the purposes of this test // the exact value doesn't matter. - let account = iotaccount::builder(authenticator, ctx) - .add_dynamic_field(dynamic_field_key, 6) - .finish(); - account.share(); + iotaccount::builder(authenticator, ctx).add_dynamic_field(dynamic_field_key, 6).build_shared(); scenario.next_tx(@0x0); { @@ -62,14 +59,13 @@ fun attempting_to_add_same_dynamic_field_twice() { let authenticator = create_authenticator_info_v1_for_testing(); let field_name = b"SomeData".to_ascii_string(); - let account = iotaccount::builder(authenticator, ctx) + iotaccount::builder(authenticator, ctx) .add_dynamic_field(field_name, 3) .add_dynamic_field( field_name, 3, ) - .finish(); - account.share(); + .build_shared(); test_scenario::end(scenario_val); } @@ -87,7 +83,7 @@ fun dynamic_fields_observe_the_value_not_just_the_type() { // are different. let field_name = b"SomeData".to_ascii_string(); let another_name = b"DifferentData".to_ascii_string(); - let account = iotaccount::builder(authenticator, ctx) + iotaccount::builder(authenticator, ctx) .add_dynamic_field( field_name, 3, @@ -96,8 +92,7 @@ fun dynamic_fields_observe_the_value_not_just_the_type() { another_name, 3, ) - .finish(); - account.share(); + .build_shared(); test_scenario::end(scenario_val); } diff --git a/examples/move/iotaccount/tests/test_utils.move b/examples/move/iotaccount/tests/test_utils.move index c6732c4fa91..d7dad56f3c8 100644 --- a/examples/move/iotaccount/tests/test_utils.move +++ b/examples/move/iotaccount/tests/test_utils.move @@ -5,21 +5,14 @@ module iotaccount::test_utils; use iota::account; -use iotaccount::iotaccount::{builder, share, IOTAccount}; +use iotaccount::iotaccount::{builder, IOTAccount}; public fun create_iotaccount_for_testing(scenario: &mut iota::test_scenario::Scenario): address { let ctx = iota::test_scenario::ctx(scenario); let authenticator = create_authenticator_info_v1_for_testing(); - let account = builder(authenticator, ctx) - .add_dynamic_field(b"SomeData".to_ascii_string(), 3u8) - .finish(); - let account_address = account.account_address(); - - share(account); - - account_address + builder(authenticator, ctx).add_dynamic_field(b"SomeData".to_ascii_string(), 3u8).build_shared() } public fun create_authenticator_info_v1_for_testing(): account::AuthenticatorInfoV1 { diff --git a/examples/move/time_locked/sources/account.move b/examples/move/time_locked/sources/account.move index d7fa08fc474..6d5a4788d27 100644 --- a/examples/move/time_locked/sources/account.move +++ b/examples/move/time_locked/sources/account.move @@ -45,15 +45,9 @@ public fun create( owner_public_key::attach(&mut id, public_key); unlock_time::attach(&mut id, unlock_time); - let mut account = TimeLocked { id }; + let account = TimeLocked { id }; - let authenticator_compatibility_proof = account::check_auth_info_v1_compatibility( - &account, - authenticator, - ); - account::attach_auth_info_v1(&mut account.id, authenticator_compatibility_proof); - - iota::transfer::share_object(account); + account::create_shared_account_v1(account, authenticator); } /// Authenticate access for the `Time locked account`. diff --git a/external-crates/move/crates/move-compiler/src/iota_mode/mod.rs b/external-crates/move/crates/move-compiler/src/iota_mode/mod.rs index b1e1cd0da66..451df6de20a 100644 --- a/external-crates/move/crates/move-compiler/src/iota_mode/mod.rs +++ b/external-crates/move/crates/move-compiler/src/iota_mode/mod.rs @@ -73,6 +73,12 @@ pub const SHARE_FUNCTION_NAME: Symbol = symbol!("share_object"); pub const RECEIVE_FUNCTION_NAME: Symbol = symbol!("receive"); pub const RECEIVING_TYPE_NAME: Symbol = symbol!("Receiving"); +pub const ACCOUNT_MODULE_NAME: Symbol = symbol!("account"); +pub const CREATE_SHARED_ACCOUNT_V1_FUNCTION_NAME: Symbol = symbol!("create_shared_account_v1"); +pub const CREATE_IMMUTABLE_ACCOUNT_V1_FUNCTION_NAME: Symbol = + symbol!("create_immutable_account_v1"); +pub const ROTATE_AUTH_INFO_V1_FUNCTION_NAME: Symbol = symbol!("rotate_auth_info_v1"); + pub const PRIVATE_TRANSFER_FUNCTIONS: &[Symbol] = &[ TRANSFER_FUNCTION_NAME, FREEZE_FUNCTION_NAME, @@ -80,6 +86,12 @@ pub const PRIVATE_TRANSFER_FUNCTIONS: &[Symbol] = &[ RECEIVE_FUNCTION_NAME, ]; +pub const PRIVATE_ACCOUNT_FUNCTIONS: &[Symbol] = &[ + CREATE_SHARED_ACCOUNT_V1_FUNCTION_NAME, + CREATE_IMMUTABLE_ACCOUNT_V1_FUNCTION_NAME, + ROTATE_AUTH_INFO_V1_FUNCTION_NAME, +]; + //************************************************************************************************** // Diagnostics //************************************************************************************************** @@ -172,6 +184,15 @@ pub const PRIVATE_TRANSFER_CALL_DIAG: DiagnosticInfo = custom( 9, "invalid private transfer call", ); +pub const PRIVATE_ACCOUNT_CALL_DIAG: DiagnosticInfo = custom( + IOTA_DIAG_PREFIX, + Severity::NonblockingError, + // category + TYPING, + // code + 10, + "invalid private account call", +); // Bridge supported asset pub const BRIDGE_SUPPORTED_ASSET: &[&str] = &["btc", "eth", "usdc", "usdt"]; diff --git a/external-crates/move/crates/move-compiler/src/iota_mode/typing.rs b/external-crates/move/crates/move-compiler/src/iota_mode/typing.rs index e01d2085a51..8279575aa9d 100644 --- a/external-crates/move/crates/move-compiler/src/iota_mode/typing.rs +++ b/external-crates/move/crates/move-compiler/src/iota_mode/typing.rs @@ -979,6 +979,10 @@ fn exp(context: &mut Context, e: &T::Exp) { if is_transfer_module && PRIVATE_TRANSFER_FUNCTIONS.contains(&name.value()) { check_private_transfer(context, e.exp.loc, mcall) } + let is_account_module = module.value.is(&IOTA_ADDR_VALUE, ACCOUNT_MODULE_NAME); + if is_account_module && PRIVATE_ACCOUNT_FUNCTIONS.contains(&name.value()) { + check_private_account(context, e.exp.loc, mcall) + } } T::UnannotatedExp_::Pack(m, s, _, _) => { if !context.in_test @@ -1050,7 +1054,7 @@ fn check_private_transfer(context: &mut Context, loc: Loc, mcall: &ModuleCall) { let current_module = context.current_module(); if current_module .value - .is(&IOTA_ADDR_VALUE, TRANSFER_FUNCTION_NAME) + .is(&IOTA_ADDR_VALUE, TRANSFER_MODULE_NAME) { // inside the transfer module, so no private transfer rules return; @@ -1114,6 +1118,54 @@ fn check_private_transfer(context: &mut Context, loc: Loc, mcall: &ModuleCall) { } } +fn check_private_account(context: &mut Context, loc: Loc, mcall: &ModuleCall) { + let ModuleCall { + module, + name, + type_arguments, + .. + } = mcall; + let current_module = context.current_module(); + if current_module + .value + .is(&IOTA_ADDR_VALUE, ACCOUNT_MODULE_NAME) + { + // inside the account module, so no private account rules + return; + } + let Some(first_ty) = type_arguments.first() else { + // invalid arity + debug_assert!(false, "ICE arity should have been expanded for errors"); + return; + }; + let (in_current_module, first_ty_tn) = match first_ty.value.type_name() { + Some(sp!(_, TypeName_::Multiple(_))) | Some(sp!(_, TypeName_::Builtin(_))) | None => { + (false, None) + } + Some(sp!(_, TypeName_::ModuleType(m, n))) => (m == current_module, Some((m, n))), + }; + if !in_current_module { + let mut msg = format!( + "Invalid private account call. \ + The function '{}::{}' is restricted to being called in the object's module", + module, name, + ); + if let Some((first_ty_module, _)) = &first_ty_tn { + msg = format!("{}, '{}'", msg, first_ty_module); + }; + let ty_msg = format!( + "The type {} is not declared in the current module", + error_format(first_ty, &Subst::empty()), + ); + let diag = diag!( + PRIVATE_ACCOUNT_CALL_DIAG, + (loc, msg), + (first_ty.loc, ty_msg) + ); + context.add_diag(diag) + } +} + /// Checks the `authenticator` attribute for a valid version field. /// Only accepts #[authenticator], #[authenticator = ], or /// #[authenticator(version = )]. diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account.move b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account.move new file mode 100644 index 00000000000..c6f9856b941 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account.move @@ -0,0 +1,53 @@ +// can use private account internal functions inside of the defining module + +module a::m { + use iota::account::{Self, AuthenticatorInfoV1}; + + struct A has key { + id: iota::object::UID, + } + + public fun t1(account: A, authenticator: AuthenticatorInfoV1) { + account::create_shared_account_v1(account, authenticator); + } + + public fun t2(account: A, authenticator: AuthenticatorInfoV1) { + account::create_immutable_account_v1(account, authenticator); + } + + public fun t3(account: &mut A, authenticator: AuthenticatorInfoV1): AuthenticatorInfoV1 { + account::rotate_auth_info_v1(account, authenticator) + } +} + +module iota::object { + struct UID has store { + id: address, + } +} + +module iota::account { + use iota::object::UID; + + struct AuthenticatorInfoV1 { + id: UID, + } + + public fun create_shared_account_v1(_: Account, _: AuthenticatorInfoV1) { + abort 0 + } + + public fun create_immutable_account_v1( + _: Account, + _: AuthenticatorInfoV1, + ) { + abort 0 + } + + public fun rotate_auth_info_v1( + _: &mut Account, + _: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + abort 0 + } +} diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account.snap b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account.snap new file mode 100644 index 00000000000..5b31e67da5c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account.snap @@ -0,0 +1,8 @@ +--- +source: crates/move-compiler/tests/move_check_testsuite.rs +info: + flavor: iota + edition: legacy + lint: false +--- + diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_generic.move b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_generic.move new file mode 100644 index 00000000000..a635d92ef76 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_generic.move @@ -0,0 +1,52 @@ +// tests modules cannot use private account internal functions outside of the defining module + +module a::m { + use iota::account::{Self, AuthenticatorInfoV1}; + + public fun t1(account: A, authenticator: AuthenticatorInfoV1) { + account::create_shared_account_v1(account, authenticator); + } + + public fun t2(account: A, authenticator: AuthenticatorInfoV1) { + account::create_immutable_account_v1(account, authenticator); + } + + public fun t3( + account: &mut A, + authenticator: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + account::rotate_auth_info_v1(account, authenticator) + } +} + +module iota::object { + struct UID has store { + id: address, + } +} + +module iota::account { + use iota::object::UID; + + struct AuthenticatorInfoV1 { + id: UID, + } + + public fun create_shared_account_v1(_: Account, _: AuthenticatorInfoV1) { + abort 0 + } + + public fun create_immutable_account_v1( + _: Account, + _: AuthenticatorInfoV1, + ) { + abort 0 + } + + public fun rotate_auth_info_v1( + _: &mut Account, + _: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + abort 0 + } +} diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_generic.snap b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_generic.snap new file mode 100644 index 00000000000..c8d1d0af81f --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_generic.snap @@ -0,0 +1,31 @@ +--- +source: crates/move-compiler/tests/move_check_testsuite.rs +info: + flavor: iota + edition: legacy + lint: false +--- +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/account_generic.move:7:9 + │ +6 │ public fun t1(account: A, authenticator: AuthenticatorInfoV1) { + │ - The type 'A' is not declared in the current module +7 │ account::create_shared_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_shared_account_v1' is restricted to being called in the object's module + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/account_generic.move:11:9 + │ +10 │ public fun t2(account: A, authenticator: AuthenticatorInfoV1) { + │ - The type 'A' is not declared in the current module +11 │ account::create_immutable_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_immutable_account_v1' is restricted to being called in the object's module + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/account_generic.move:18:9 + │ +15 │ account: &mut A, + │ - The type 'A' is not declared in the current module + · +18 │ account::rotate_auth_info_v1(account, authenticator) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::rotate_auth_info_v1' is restricted to being called in the object's module diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store.move b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store.move new file mode 100644 index 00000000000..a51f92776a2 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store.move @@ -0,0 +1,53 @@ +// can use private account internal functions inside of the defining module if it has store + +module a::m { + use iota::account::{Self, AuthenticatorInfoV1}; + + struct A has key, store { + id: iota::object::UID, + } + + public fun t1(account: A, authenticator: AuthenticatorInfoV1) { + account::create_shared_account_v1(account, authenticator); + } + + public fun t2(account: A, authenticator: AuthenticatorInfoV1) { + account::create_immutable_account_v1(account, authenticator); + } + + public fun t3(account: &mut A, authenticator: AuthenticatorInfoV1): AuthenticatorInfoV1 { + account::rotate_auth_info_v1(account, authenticator) + } +} + +module iota::object { + struct UID has store { + id: address, + } +} + +module iota::account { + use iota::object::UID; + + struct AuthenticatorInfoV1 { + id: UID, + } + + public fun create_shared_account_v1(_: Account, _: AuthenticatorInfoV1) { + abort 0 + } + + public fun create_immutable_account_v1( + _: Account, + _: AuthenticatorInfoV1, + ) { + abort 0 + } + + public fun rotate_auth_info_v1( + _: &mut Account, + _: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + abort 0 + } +} diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store.snap b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store.snap new file mode 100644 index 00000000000..5b31e67da5c --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store.snap @@ -0,0 +1,8 @@ +--- +source: crates/move-compiler/tests/move_check_testsuite.rs +info: + flavor: iota + edition: legacy + lint: false +--- + diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store_generic.move b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store_generic.move new file mode 100644 index 00000000000..9f7c056b075 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store_generic.move @@ -0,0 +1,53 @@ +// tests modules cannot use private account internal functions outside of the defining module +// even if it has store + +module a::m { + use iota::account::{Self, AuthenticatorInfoV1}; + + public fun t1(account: A, authenticator: AuthenticatorInfoV1) { + account::create_shared_account_v1(account, authenticator); + } + + public fun t2(account: A, authenticator: AuthenticatorInfoV1) { + account::create_immutable_account_v1(account, authenticator); + } + + public fun t3( + account: &mut A, + authenticator: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + account::rotate_auth_info_v1(account, authenticator) + } +} + +module iota::object { + struct UID has store { + id: address, + } +} + +module iota::account { + use iota::object::UID; + + struct AuthenticatorInfoV1 { + id: UID, + } + + public fun create_shared_account_v1(_: Account, _: AuthenticatorInfoV1) { + abort 0 + } + + public fun create_immutable_account_v1( + _: Account, + _: AuthenticatorInfoV1, + ) { + abort 0 + } + + public fun rotate_auth_info_v1( + _: &mut Account, + _: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + abort 0 + } +} diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store_generic.snap b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store_generic.snap new file mode 100644 index 00000000000..211f420f814 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/account_store_generic.snap @@ -0,0 +1,31 @@ +--- +source: crates/move-compiler/tests/move_check_testsuite.rs +info: + flavor: iota + edition: legacy + lint: false +--- +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/account_store_generic.move:8:9 + │ +7 │ public fun t1(account: A, authenticator: AuthenticatorInfoV1) { + │ - The type 'A' is not declared in the current module +8 │ account::create_shared_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_shared_account_v1' is restricted to being called in the object's module + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/account_store_generic.move:12:9 + │ +11 │ public fun t2(account: A, authenticator: AuthenticatorInfoV1) { + │ - The type 'A' is not declared in the current module +12 │ account::create_immutable_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_immutable_account_v1' is restricted to being called in the object's module + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/account_store_generic.move:19:9 + │ +16 │ account: &mut A, + │ - The type 'A' is not declared in the current module + · +19 │ account::rotate_auth_info_v1(account, authenticator) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::rotate_auth_info_v1' is restricted to being called in the object's module diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account.move b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account.move new file mode 100644 index 00000000000..51d937a8f15 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account.move @@ -0,0 +1,59 @@ +// tests modules cannot use private account internal functions outside of the defining module + +module a::m { + use a::other; + use iota::account::{Self, AuthenticatorInfoV1}; + + public fun t1(account: other::A, authenticator: AuthenticatorInfoV1) { + account::create_shared_account_v1(account, authenticator); + } + + public fun t2(account: other::A, authenticator: AuthenticatorInfoV1) { + account::create_immutable_account_v1(account, authenticator); + } + + public fun t3( + account: &mut other::A, + authenticator: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + account::rotate_auth_info_v1(account, authenticator) + } +} + +module a::other { + struct A has key { + id: iota::object::UID, + } +} + +module iota::object { + struct UID has store { + id: address, + } +} + +module iota::account { + use iota::object::UID; + + struct AuthenticatorInfoV1 { + id: UID, + } + + public fun create_shared_account_v1(_: Account, _: AuthenticatorInfoV1) { + abort 0 + } + + public fun create_immutable_account_v1( + _: Account, + _: AuthenticatorInfoV1, + ) { + abort 0 + } + + public fun rotate_auth_info_v1( + _: &mut Account, + _: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + abort 0 + } +} diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account.snap b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account.snap new file mode 100644 index 00000000000..db9b224d1a2 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account.snap @@ -0,0 +1,31 @@ +--- +source: crates/move-compiler/tests/move_check_testsuite.rs +info: + flavor: iota + edition: legacy + lint: false +--- +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/external_account.move:8:9 + │ +7 │ public fun t1(account: other::A, authenticator: AuthenticatorInfoV1) { + │ -------- The type 'a::other::A' is not declared in the current module +8 │ account::create_shared_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_shared_account_v1' is restricted to being called in the object's module, 'a::other' + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/external_account.move:12:9 + │ +11 │ public fun t2(account: other::A, authenticator: AuthenticatorInfoV1) { + │ -------- The type 'a::other::A' is not declared in the current module +12 │ account::create_immutable_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_immutable_account_v1' is restricted to being called in the object's module, 'a::other' + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/external_account.move:19:9 + │ +16 │ account: &mut other::A, + │ -------- The type 'a::other::A' is not declared in the current module + · +19 │ account::rotate_auth_info_v1(account, authenticator) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::rotate_auth_info_v1' is restricted to being called in the object's module, 'a::other' diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account_store.move b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account_store.move new file mode 100644 index 00000000000..ab9cb7e7dbf --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account_store.move @@ -0,0 +1,60 @@ +// tests modules cannot use private account internal functions outside of the defining module +// even if it has store + +module a::m { + use a::other; + use iota::account::{Self, AuthenticatorInfoV1}; + + public fun t1(account: other::A, authenticator: AuthenticatorInfoV1) { + account::create_shared_account_v1(account, authenticator); + } + + public fun t2(account: other::A, authenticator: AuthenticatorInfoV1) { + account::create_immutable_account_v1(account, authenticator); + } + + public fun t3( + account: &mut other::A, + authenticator: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + account::rotate_auth_info_v1(account, authenticator) + } +} + +module a::other { + struct A has key, store { + id: iota::object::UID, + } +} + +module iota::object { + struct UID has store { + id: address, + } +} + +module iota::account { + use iota::object::UID; + + struct AuthenticatorInfoV1 { + id: UID, + } + + public fun create_shared_account_v1(_: Account, _: AuthenticatorInfoV1) { + abort 0 + } + + public fun create_immutable_account_v1( + _: Account, + _: AuthenticatorInfoV1, + ) { + abort 0 + } + + public fun rotate_auth_info_v1( + _: &mut Account, + _: AuthenticatorInfoV1, + ): AuthenticatorInfoV1 { + abort 0 + } +} diff --git a/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account_store.snap b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account_store.snap new file mode 100644 index 00000000000..9f03768ffe8 --- /dev/null +++ b/external-crates/move/crates/move-compiler/tests/iota_mode/account/external_account_store.snap @@ -0,0 +1,31 @@ +--- +source: crates/move-compiler/tests/move_check_testsuite.rs +info: + flavor: iota + edition: legacy + lint: false +--- +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/external_account_store.move:9:9 + │ +8 │ public fun t1(account: other::A, authenticator: AuthenticatorInfoV1) { + │ -------- The type 'a::other::A' is not declared in the current module +9 │ account::create_shared_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_shared_account_v1' is restricted to being called in the object's module, 'a::other' + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/external_account_store.move:13:9 + │ +12 │ public fun t2(account: other::A, authenticator: AuthenticatorInfoV1) { + │ -------- The type 'a::other::A' is not declared in the current module +13 │ account::create_immutable_account_v1(account, authenticator); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::create_immutable_account_v1' is restricted to being called in the object's module, 'a::other' + +error[Iota E02010]: invalid private account call + ┌─ tests/iota_mode/account/external_account_store.move:20:9 + │ +17 │ account: &mut other::A, + │ -------- The type 'a::other::A' is not declared in the current module + · +20 │ account::rotate_auth_info_v1(account, authenticator) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Invalid private account call. The function 'iota::account::rotate_auth_info_v1' is restricted to being called in the object's module, 'a::other' diff --git a/external-crates/move/crates/move-symbol-pool/src/lib.rs b/external-crates/move/crates/move-symbol-pool/src/lib.rs index 9624017bf3e..c966a87235d 100644 --- a/external-crates/move/crates/move-symbol-pool/src/lib.rs +++ b/external-crates/move/crates/move-symbol-pool/src/lib.rs @@ -96,6 +96,10 @@ static_symbols!( "migration", "beta", "development", + "account", + "create_shared_account_v1", + "create_immutable_account_v1", + "rotate_auth_info_v1", ); /// The global, unique cache of strings. diff --git a/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs b/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs index b7258bdfe9d..5304a9d0584 100644 --- a/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs +++ b/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs @@ -40,7 +40,10 @@ mod checked { }; use iota_verifier::{ INIT_FN_NAME, - private_generics::{EVENT_MODULE, PRIVATE_TRANSFER_FUNCTIONS, TRANSFER_MODULE}, + private_generics::{ + ACCOUNT_MODULE, EVENT_MODULE, PRIVATE_ACCOUNT_FUNCTIONS, PRIVATE_TRANSFER_FUNCTIONS, + TRANSFER_MODULE, + }, }; use move_binary_format::{ CompiledModule, @@ -1470,6 +1473,16 @@ mod checked { )); } + if module_ident == (&IOTA_FRAMEWORK_ADDRESS, ACCOUNT_MODULE) + && PRIVATE_ACCOUNT_FUNCTIONS.contains(&function) + { + let msg = format!("Cannot directly call iota::{ACCOUNT_MODULE}::{function}."); + return Err(ExecutionError::new_with_source( + ExecutionErrorKind::NonEntryFunctionInvoked, + msg, + )); + } + Ok(()) } diff --git a/iota-execution/latest/iota-move-natives/src/lib.rs b/iota-execution/latest/iota-move-natives/src/lib.rs index 671e92fd65f..47d6f6f72fa 100644 --- a/iota-execution/latest/iota-move-natives/src/lib.rs +++ b/iota-execution/latest/iota-move-natives/src/lib.rs @@ -777,6 +777,21 @@ pub fn all_natives(silent: bool, protocol_config: &ProtocolConfig) -> NativeFunc ("address", "from_bytes", make_native!(address::from_bytes)), ("address", "to_u256", make_native!(address::to_u256)), ("address", "from_u256", make_native!(address::from_u256)), + ( + "account", + "borrow_account_uid_mut", + make_native!(object::borrow_uid), + ), + ( + "account", + "create_shared_account_v1_impl", + make_native!(transfer::share_object), + ), + ( + "account", + "create_immutable_account_v1_impl", + make_native!(transfer::freeze_object), + ), ("hash", "blake2b256", make_native!(hash::blake2b256)), ( "bls12381", diff --git a/iota-execution/latest/iota-verifier/src/private_generics.rs b/iota-execution/latest/iota-verifier/src/private_generics.rs index 3d7f3154ed7..545cd3022f5 100644 --- a/iota-execution/latest/iota-verifier/src/private_generics.rs +++ b/iota-execution/latest/iota-verifier/src/private_generics.rs @@ -16,6 +16,7 @@ use move_core_types::{account_address::AccountAddress, ident_str, identifier::Id use crate::{TEST_SCENARIO_MODULE_NAME, verification_failure}; pub const TRANSFER_MODULE: &IdentStr = ident_str!("transfer"); +pub const ACCOUNT_MODULE: &IdentStr = ident_str!("account"); pub const EVENT_MODULE: &IdentStr = ident_str!("event"); pub const EVENT_FUNCTION: &IdentStr = ident_str!("emit"); pub const GET_EVENTS_TEST_FUNCTION: &IdentStr = ident_str!("events_by_type"); @@ -39,6 +40,17 @@ pub const TRANSFER_IMPL_FUNCTIONS: &[&IdentStr] = &[ ident_str!("receive_impl"), ]; +pub const PUBLIC_ACCOUNT_FUNCTIONS: &[&IdentStr] = &[ + ident_str!("create_auth_info_v1"), + ident_str!("borrow_auth_info_v1"), + ident_str!("has_auth_info_v1"), +]; +pub const PRIVATE_ACCOUNT_FUNCTIONS: &[&IdentStr] = &[ + ident_str!("create_shared_account_v1"), + ident_str!("create_immutable_account_v1"), + ident_str!("rotate_auth_info_v1"), +]; + /// All transfer functions (the functions in `iota::transfer`) are "private" in /// that they are restricted to the module. /// For example, with `transfer::transfer(...)`, either: @@ -90,16 +102,18 @@ fn verify_function(view: &CompiledModule, fdef: &FunctionDefinition) -> Result<( let type_arguments = &view.signature_at(*type_parameters).0; let ident = addr_module(view, mhandle); if ident == (IOTA_FRAMEWORK_ADDRESS, TRANSFER_MODULE) { - verify_private_transfer(view, fhandle, type_arguments)? + verify_private_transfer_module_functions(view, fhandle, type_arguments)? } else if ident == (IOTA_FRAMEWORK_ADDRESS, EVENT_MODULE) { verify_private_event_emit(view, fhandle, type_arguments)? + } else if ident == (IOTA_FRAMEWORK_ADDRESS, ACCOUNT_MODULE) { + verify_private_account_module_functions(view, fhandle, type_arguments)? } } } Ok(()) } -fn verify_private_transfer( +fn verify_private_transfer_module_functions( view: &CompiledModule, fhandle: &FunctionHandle, type_arguments: &[SignatureToken], @@ -140,6 +154,46 @@ fn verify_private_transfer( Ok(()) } +fn verify_private_account_module_functions( + view: &CompiledModule, + fhandle: &FunctionHandle, + type_arguments: &[SignatureToken], +) -> Result<(), String> { + let self_handle = view.module_handle_at(view.self_handle_idx()); + if addr_module(view, self_handle) == (IOTA_FRAMEWORK_ADDRESS, ACCOUNT_MODULE) { + return Ok(()); + } + let fident = view.identifier_at(fhandle.name); + // public account functions have no additional rules + if PUBLIC_ACCOUNT_FUNCTIONS.contains(&fident) { + return Ok(()); + } + if !PRIVATE_ACCOUNT_FUNCTIONS.contains(&fident) { + // unknown function, so a bug in the implementation here + debug_assert!(false, "unknown account function {fident}"); + return Err(format!("Calling unknown account function, {fident}")); + }; + + if type_arguments.len() != 1 { + debug_assert!(false, "Expected 1 type argument for {fident}"); + return Err(format!("Expected 1 type argument for {fident}")); + } + + let type_arg = &type_arguments[0]; + if !is_defined_in_current_module(view, type_arg) { + return Err(format!( + "Invalid call to '{iota}::{account}::{f}' on an object of type '{t}'. \ + The account object's type must be defined in the current module.", + iota = IOTA_FRAMEWORK_ADDRESS, + account = ACCOUNT_MODULE, + f = fident, + t = format_signature_token(view, type_arg), + )); + } + + Ok(()) +} + fn verify_private_event_emit( view: &CompiledModule, fhandle: &FunctionHandle,