Skip to content

Commit

Permalink
add perimission checks to object
Browse files Browse the repository at this point in the history
  • Loading branch information
runtian-zhou committed Nov 20, 2024
1 parent ec2c261 commit 12eac30
Show file tree
Hide file tree
Showing 5 changed files with 462 additions and 9 deletions.
110 changes: 101 additions & 9 deletions aptos-move/framework/aptos-framework/doc/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ make it so that a reference to a global object can be returned from a function.
- [Struct `TransferRef`](#0x1_object_TransferRef)
- [Struct `LinearTransferRef`](#0x1_object_LinearTransferRef)
- [Struct `DeriveRef`](#0x1_object_DeriveRef)
- [Struct `TransferPermission`](#0x1_object_TransferPermission)
- [Struct `TransferEvent`](#0x1_object_TransferEvent)
- [Struct `Transfer`](#0x1_object_Transfer)
- [Constants](#@Constants_0)
Expand Down Expand Up @@ -89,6 +90,7 @@ make it so that a reference to a global object can be returned from a function.
- [Function `is_owner`](#0x1_object_is_owner)
- [Function `owns`](#0x1_object_owns)
- [Function `root_owner`](#0x1_object_root_owner)
- [Function `grant_permission`](#0x1_object_grant_permission)
- [Specification](#@Specification_1)
- [High-level Requirements](#high-level-req)
- [Module-level Specification](#module-level-spec)
Expand Down Expand Up @@ -133,6 +135,7 @@ make it so that a reference to a global object can be returned from a function.
- [Function `is_owner`](#@Specification_1_is_owner)
- [Function `owns`](#@Specification_1_owns)
- [Function `root_owner`](#@Specification_1_root_owner)
- [Function `grant_permission`](#@Specification_1_grant_permission)


<pre><code><b>use</b> <a href="account.md#0x1_account">0x1::account</a>;
Expand All @@ -144,6 +147,7 @@ make it so that a reference to a global object can be returned from a function.
<b>use</b> <a href="../../aptos-stdlib/doc/from_bcs.md#0x1_from_bcs">0x1::from_bcs</a>;
<b>use</b> <a href="guid.md#0x1_guid">0x1::guid</a>;
<b>use</b> <a href="../../aptos-stdlib/../move-stdlib/doc/hash.md#0x1_hash">0x1::hash</a>;
<b>use</b> <a href="permissioned_signer.md#0x1_permissioned_signer">0x1::permissioned_signer</a>;
<b>use</b> <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">0x1::signer</a>;
<b>use</b> <a href="transaction_context.md#0x1_transaction_context">0x1::transaction_context</a>;
<b>use</b> <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">0x1::vector</a>;
Expand Down Expand Up @@ -496,6 +500,34 @@ Used to create derived objects from a given objects.
</dl>


</details>

<a id="0x1_object_TransferPermission"></a>

## Struct `TransferPermission`

Permission to transfer object with permissioned signer.


<pre><code><b>struct</b> <a href="object.md#0x1_object_TransferPermission">TransferPermission</a> <b>has</b> <b>copy</b>, drop, store
</code></pre>



<details>
<summary>Fields</summary>


<dl>
<dt>
<code><a href="object.md#0x1_object">object</a>: <b>address</b></code>
</dt>
<dd>

</dd>
</dl>


</details>

<a id="0x1_object_TransferEvent"></a>
Expand Down Expand Up @@ -1999,6 +2031,10 @@ hierarchy.
<b>to</b>: <b>address</b>,
) <b>acquires</b> <a href="object.md#0x1_object_ObjectCore">ObjectCore</a> {
<b>let</b> owner_address = <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(owner);
<b>assert</b>!(
<a href="permissioned_signer.md#0x1_permissioned_signer_check_permission_exists">permissioned_signer::check_permission_exists</a>(owner, <a href="object.md#0x1_object_TransferPermission">TransferPermission</a> { <a href="object.md#0x1_object">object</a> }),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="object.md#0x1_object_EOBJECT_NOT_TRANSFERRABLE">EOBJECT_NOT_TRANSFERRABLE</a>)
);
<a href="object.md#0x1_object_verify_ungated_and_descendant">verify_ungated_and_descendant</a>(owner_address, <a href="object.md#0x1_object">object</a>);
<a href="object.md#0x1_object_transfer_raw_inner">transfer_raw_inner</a>(<a href="object.md#0x1_object">object</a>, <b>to</b>);
}
Expand Down Expand Up @@ -2188,6 +2224,10 @@ Allow origin owners to reclaim any objects they previous burnt.
) <b>acquires</b> <a href="object.md#0x1_object_TombStone">TombStone</a>, <a href="object.md#0x1_object_ObjectCore">ObjectCore</a> {
<b>let</b> object_addr = <a href="object.md#0x1_object">object</a>.inner;
<b>assert</b>!(<b>exists</b>&lt;<a href="object.md#0x1_object_TombStone">TombStone</a>&gt;(object_addr), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_argument">error::invalid_argument</a>(<a href="object.md#0x1_object_EOBJECT_NOT_BURNT">EOBJECT_NOT_BURNT</a>));
<b>assert</b>!(
<a href="permissioned_signer.md#0x1_permissioned_signer_check_permission_exists">permissioned_signer::check_permission_exists</a>(original_owner, <a href="object.md#0x1_object_TransferPermission">TransferPermission</a> { <a href="object.md#0x1_object">object</a>: object_addr }),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="object.md#0x1_object_EOBJECT_NOT_TRANSFERRABLE">EOBJECT_NOT_TRANSFERRABLE</a>)
);

<b>let</b> <a href="object.md#0x1_object_TombStone">TombStone</a> { original_owner: original_owner_addr } = <b>move_from</b>&lt;<a href="object.md#0x1_object_TombStone">TombStone</a>&gt;(object_addr);
<b>assert</b>!(original_owner_addr == <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(original_owner), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="object.md#0x1_object_ENOT_OBJECT_OWNER">ENOT_OBJECT_OWNER</a>));
Expand Down Expand Up @@ -2360,6 +2400,39 @@ to determine the identity of the starting point of ownership.



</details>

<a id="0x1_object_grant_permission"></a>

## Function `grant_permission`



<pre><code><b>public</b> <b>fun</b> <a href="object.md#0x1_object_grant_permission">grant_permission</a>&lt;T&gt;(master: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>, <a href="permissioned_signer.md#0x1_permissioned_signer">permissioned_signer</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>, <a href="object.md#0x1_object">object</a>: <a href="object.md#0x1_object_Object">object::Object</a>&lt;T&gt;)
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="object.md#0x1_object_grant_permission">grant_permission</a>&lt;T&gt;(
master: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>,
<a href="permissioned_signer.md#0x1_permissioned_signer">permissioned_signer</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>,
<a href="object.md#0x1_object">object</a>: <a href="object.md#0x1_object_Object">Object</a>&lt;T&gt;,
) {
<a href="permissioned_signer.md#0x1_permissioned_signer_authorize">permissioned_signer::authorize</a>(
master,
<a href="permissioned_signer.md#0x1_permissioned_signer">permissioned_signer</a>,
1,
<a href="object.md#0x1_object_TransferPermission">TransferPermission</a> { <a href="object.md#0x1_object">object</a>: <a href="object.md#0x1_object">object</a>.inner }
)
}
</code></pre>



</details>

<a id="@Specification_1"></a>
Expand Down Expand Up @@ -2441,15 +2514,6 @@ to determine the identity of the starting point of ownership.




<a id="0x1_object_spec_exists_at"></a>


<pre><code><b>fun</b> <a href="object.md#0x1_object_spec_exists_at">spec_exists_at</a>&lt;T: key&gt;(<a href="object.md#0x1_object">object</a>: <b>address</b>): bool;
</code></pre>



<a id="@Specification_1_address_to_object"></a>

### Function `address_to_object`
Expand Down Expand Up @@ -3401,4 +3465,32 @@ to determine the identity of the starting point of ownership.
</code></pre>



<a id="@Specification_1_grant_permission"></a>

### Function `grant_permission`


<pre><code><b>public</b> <b>fun</b> <a href="object.md#0x1_object_grant_permission">grant_permission</a>&lt;T&gt;(master: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>, <a href="permissioned_signer.md#0x1_permissioned_signer">permissioned_signer</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>, <a href="object.md#0x1_object">object</a>: <a href="object.md#0x1_object_Object">object::Object</a>&lt;T&gt;)
</code></pre>




<pre><code><b>pragma</b> aborts_if_is_partial;
<b>aborts_if</b> !<a href="permissioned_signer.md#0x1_permissioned_signer_spec_is_permissioned_signer">permissioned_signer::spec_is_permissioned_signer</a>(<a href="permissioned_signer.md#0x1_permissioned_signer">permissioned_signer</a>);
<b>aborts_if</b> <a href="permissioned_signer.md#0x1_permissioned_signer_spec_is_permissioned_signer">permissioned_signer::spec_is_permissioned_signer</a>(master);
<b>aborts_if</b> <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(master) != <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(<a href="permissioned_signer.md#0x1_permissioned_signer">permissioned_signer</a>);
</code></pre>




<a id="0x1_object_spec_exists_at"></a>


<pre><code><b>fun</b> <a href="object.md#0x1_object_spec_exists_at">spec_exists_at</a>&lt;T: key&gt;(<a href="object.md#0x1_object">object</a>: <b>address</b>): bool;
</code></pre>


[move-book]: https://aptos.dev/move/book/SUMMARY
71 changes: 71 additions & 0 deletions aptos-move/framework/aptos-framework/sources/object.move
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module aptos_framework::object {
use aptos_framework::create_signer::create_signer;
use aptos_framework::event;
use aptos_framework::guid;
use aptos_framework::permissioned_signer;

friend aptos_framework::coin;
friend aptos_framework::primary_fungible_store;
Expand Down Expand Up @@ -165,6 +166,11 @@ module aptos_framework::object {
self: address,
}

/// Permission to transfer object with permissioned signer.
struct TransferPermission has copy, drop, store {
object: address,
}

/// Emitted whenever the object's owner field is changed.
struct TransferEvent has drop, store {
object: address,
Expand Down Expand Up @@ -540,6 +546,10 @@ module aptos_framework::object {
to: address,
) acquires ObjectCore {
let owner_address = signer::address_of(owner);
assert!(
permissioned_signer::check_permission_exists(owner, TransferPermission { object }),
error::permission_denied(EOBJECT_NOT_TRANSFERRABLE)
);
verify_ungated_and_descendant(owner_address, object);
transfer_raw_inner(object, to);
}
Expand Down Expand Up @@ -629,6 +639,10 @@ module aptos_framework::object {
) acquires TombStone, ObjectCore {
let object_addr = object.inner;
assert!(exists<TombStone>(object_addr), error::invalid_argument(EOBJECT_NOT_BURNT));
assert!(
permissioned_signer::check_permission_exists(original_owner, TransferPermission { object: object_addr }),
error::permission_denied(EOBJECT_NOT_TRANSFERRABLE)
);

let TombStone { original_owner: original_owner_addr } = move_from<TombStone>(object_addr);
assert!(original_owner_addr == signer::address_of(original_owner), error::permission_denied(ENOT_OBJECT_OWNER));
Expand Down Expand Up @@ -698,6 +712,19 @@ module aptos_framework::object {
obj_owner
}

public fun grant_permission<T>(
master: &signer,
permissioned_signer: &signer,
object: Object<T>,
) {
permissioned_signer::authorize(
master,
permissioned_signer,
1,
TransferPermission { object: object.inner }
)
}

#[test_only]
use std::option::{Self, Option};

Expand Down Expand Up @@ -1092,4 +1119,48 @@ module aptos_framework::object {
set_untransferable(&weapon_constructor_ref);
transfer_with_ref(linear_transfer_ref, @0x456);
}

#[test_only]
use aptos_framework::timestamp;

#[test(creator = @0x123)]
fun test_transfer_permission_e2e(
creator: &signer,
) acquires ObjectCore {
let aptos_framework = account::create_signer_for_test(@0x1);
timestamp::set_time_has_started_for_testing(&aptos_framework);

let (_, hero) = create_hero(creator);
let (_, weapon) = create_weapon(creator);

// Create a permissioned signer
let creator_permission_handle = permissioned_signer::create_permissioned_handle(creator);
let creator_permission_signer = permissioned_signer::signer_from_permissioned(&creator_permission_handle);

// Grant aaron_permission_signer permission to transfer weapon object
grant_permission(creator, &creator_permission_signer, weapon);
transfer_to_object(&creator_permission_signer, weapon, hero);

permissioned_signer::destroy_permissioned_handle(creator_permission_handle);
}

#[test(creator = @0x123)]
#[expected_failure(abort_code = 327689, location = Self)]
fun test_transfer_no_permission(
creator: &signer,
) acquires ObjectCore {
let aptos_framework = account::create_signer_for_test(@0x1);
timestamp::set_time_has_started_for_testing(&aptos_framework);

let (_, hero) = create_hero(creator);
let (_, weapon) = create_weapon(creator);

// Create a permissioned signer
let creator_permission_handle = permissioned_signer::create_permissioned_handle(creator);
let creator_permission_signer = permissioned_signer::signer_from_permissioned(&creator_permission_handle);

transfer_to_object(&creator_permission_signer, weapon, hero);

permissioned_signer::destroy_permissioned_handle(creator_permission_handle);
}
}
7 changes: 7 additions & 0 deletions aptos-move/framework/aptos-framework/sources/object.spec.move
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ spec aptos_framework::object {
pragma aborts_if_is_strict;
}

spec grant_permission {
pragma aborts_if_is_partial;
aborts_if !permissioned_signer::spec_is_permissioned_signer(permissioned_signer);
aborts_if permissioned_signer::spec_is_permissioned_signer(master);
aborts_if signer::address_of(master) != signer::address_of(permissioned_signer);
}

spec fun spec_exists_at<T: key>(object: address): bool;

spec exists_at<T: key>(object: address): bool {
Expand Down
Loading

0 comments on commit 12eac30

Please sign in to comment.