Skip to content

Commit

Permalink
Add permission check to account
Browse files Browse the repository at this point in the history
  • Loading branch information
runtian-zhou committed Sep 9, 2024
1 parent dec4a79 commit bde486a
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 1 deletion.
34 changes: 34 additions & 0 deletions aptos-move/framework/aptos-framework/doc/account.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<b>use</b> <a href="../../aptos-stdlib/../move-stdlib/doc/hash.md#0x1_hash">0x1::hash</a>;
<b>use</b> <a href="../../aptos-stdlib/doc/multi_ed25519.md#0x1_multi_ed25519">0x1::multi_ed25519</a>;
<b>use</b> <a href="../../aptos-stdlib/../move-stdlib/doc/option.md#0x1_option">0x1::option</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="system_addresses.md#0x1_system_addresses">0x1::system_addresses</a>;
<b>use</b> <a href="../../aptos-stdlib/doc/table.md#0x1_table">0x1::table</a>;
Expand Down Expand Up @@ -818,6 +819,15 @@ An attempt to create a resource account on a claimed account



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



<pre><code><b>const</b> <a href="account.md#0x1_account_EROTATION_WITH_PERMISSIONED_SIGNER">EROTATION_WITH_PERMISSIONED_SIGNER</a>: u64 = 20;
</code></pre>



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

Sequence number exceeds the maximum value for a u64
Expand Down Expand Up @@ -1167,6 +1177,10 @@ many contexts:
<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector_length">vector::length</a>(&new_auth_key) == 32,
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_invalid_argument">error::invalid_argument</a>(<a href="account.md#0x1_account_EMALFORMED_AUTHENTICATION_KEY">EMALFORMED_AUTHENTICATION_KEY</a>)
);
<b>assert</b>!(
!<a href="permissioned_signer.md#0x1_permissioned_signer_is_permissioned_signer">permissioned_signer::is_permissioned_signer</a>(<a href="account.md#0x1_account">account</a>),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="account.md#0x1_account_EROTATION_WITH_PERMISSIONED_SIGNER">EROTATION_WITH_PERMISSIONED_SIGNER</a>)
);
<b>let</b> account_resource = <b>borrow_global_mut</b>&lt;<a href="account.md#0x1_account_Account">Account</a>&gt;(addr);
account_resource.authentication_key = new_auth_key;
}
Expand Down Expand Up @@ -1259,6 +1273,10 @@ to rotate his address to Alice's address in the first place.
) <b>acquires</b> <a href="account.md#0x1_account_Account">Account</a>, <a href="account.md#0x1_account_OriginatingAddress">OriginatingAddress</a> {
<b>let</b> addr = <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(<a href="account.md#0x1_account">account</a>);
<b>assert</b>!(<a href="account.md#0x1_account_exists_at">exists_at</a>(addr), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_not_found">error::not_found</a>(<a href="account.md#0x1_account_EACCOUNT_DOES_NOT_EXIST">EACCOUNT_DOES_NOT_EXIST</a>));
<b>assert</b>!(
!<a href="permissioned_signer.md#0x1_permissioned_signer_is_permissioned_signer">permissioned_signer::is_permissioned_signer</a>(<a href="account.md#0x1_account">account</a>),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="account.md#0x1_account_EROTATION_WITH_PERMISSIONED_SIGNER">EROTATION_WITH_PERMISSIONED_SIGNER</a>)
);
<b>let</b> account_resource = <b>borrow_global_mut</b>&lt;<a href="account.md#0x1_account_Account">Account</a>&gt;(addr);

// Verify the given `from_public_key_bytes` matches this <a href="account.md#0x1_account">account</a>'s current authentication key.
Expand Down Expand Up @@ -1334,6 +1352,10 @@ to rotate his address to Alice's address in the first place.
new_public_key_bytes: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;,
cap_update_table: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;
) <b>acquires</b> <a href="account.md#0x1_account_Account">Account</a>, <a href="account.md#0x1_account_OriginatingAddress">OriginatingAddress</a> {
<b>assert</b>!(
!<a href="permissioned_signer.md#0x1_permissioned_signer_is_permissioned_signer">permissioned_signer::is_permissioned_signer</a>(delegate_signer),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="account.md#0x1_account_EROTATION_WITH_PERMISSIONED_SIGNER">EROTATION_WITH_PERMISSIONED_SIGNER</a>)
);
<b>assert</b>!(<a href="account.md#0x1_account_exists_at">exists_at</a>(rotation_cap_offerer_address), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_not_found">error::not_found</a>(<a href="account.md#0x1_account_EOFFERER_ADDRESS_DOES_NOT_EXIST">EOFFERER_ADDRESS_DOES_NOT_EXIST</a>));

// Check that there <b>exists</b> a rotation capability offer at the offerer's <a href="account.md#0x1_account">account</a> resource for the delegate.
Expand Down Expand Up @@ -1413,6 +1435,10 @@ offer, calling this function will replace the previous <code>recipient_address</
account_public_key_bytes: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;,
recipient_address: <b>address</b>,
) <b>acquires</b> <a href="account.md#0x1_account_Account">Account</a> {
<b>assert</b>!(
!<a href="permissioned_signer.md#0x1_permissioned_signer_is_permissioned_signer">permissioned_signer::is_permissioned_signer</a>(<a href="account.md#0x1_account">account</a>),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="account.md#0x1_account_EROTATION_WITH_PERMISSIONED_SIGNER">EROTATION_WITH_PERMISSIONED_SIGNER</a>)
);
<b>let</b> addr = <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(<a href="account.md#0x1_account">account</a>);
<b>assert</b>!(<a href="account.md#0x1_account_exists_at">exists_at</a>(recipient_address), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_not_found">error::not_found</a>(<a href="account.md#0x1_account_EACCOUNT_DOES_NOT_EXIST">EACCOUNT_DOES_NOT_EXIST</a>));

Expand Down Expand Up @@ -1612,6 +1638,10 @@ to the account owner's signer capability).
account_public_key_bytes: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;,
recipient_address: <b>address</b>
) <b>acquires</b> <a href="account.md#0x1_account_Account">Account</a> {
<b>assert</b>!(
!<a href="permissioned_signer.md#0x1_permissioned_signer_is_permissioned_signer">permissioned_signer::is_permissioned_signer</a>(<a href="account.md#0x1_account">account</a>),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="account.md#0x1_account_EROTATION_WITH_PERMISSIONED_SIGNER">EROTATION_WITH_PERMISSIONED_SIGNER</a>)
);
<b>let</b> source_address = <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer_address_of">signer::address_of</a>(<a href="account.md#0x1_account">account</a>);
<b>assert</b>!(<a href="account.md#0x1_account_exists_at">exists_at</a>(recipient_address), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_not_found">error::not_found</a>(<a href="account.md#0x1_account_EACCOUNT_DOES_NOT_EXIST">EACCOUNT_DOES_NOT_EXIST</a>));

Expand Down Expand Up @@ -1769,6 +1799,10 @@ at the offerer's address.


<pre><code><b>public</b> <b>fun</b> <a href="account.md#0x1_account_create_authorized_signer">create_authorized_signer</a>(<a href="account.md#0x1_account">account</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>, offerer_address: <b>address</b>): <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a> <b>acquires</b> <a href="account.md#0x1_account_Account">Account</a> {
<b>assert</b>!(
!<a href="permissioned_signer.md#0x1_permissioned_signer_is_permissioned_signer">permissioned_signer::is_permissioned_signer</a>(<a href="account.md#0x1_account">account</a>),
<a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_permission_denied">error::permission_denied</a>(<a href="account.md#0x1_account_EROTATION_WITH_PERMISSIONED_SIGNER">EROTATION_WITH_PERMISSIONED_SIGNER</a>)
);
<b>assert</b>!(<a href="account.md#0x1_account_exists_at">exists_at</a>(offerer_address), <a href="../../aptos-stdlib/../move-stdlib/doc/error.md#0x1_error_not_found">error::not_found</a>(<a href="account.md#0x1_account_EOFFERER_ADDRESS_DOES_NOT_EXIST">EOFFERER_ADDRESS_DOES_NOT_EXIST</a>));

// Check <b>if</b> there's an existing <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a> capability offer from the offerer.
Expand Down
27 changes: 27 additions & 0 deletions aptos-move/framework/aptos-framework/sources/account.move
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module aptos_framework::account {
use aptos_framework::create_signer::create_signer;
use aptos_framework::event::{Self, EventHandle};
use aptos_framework::guid;
use aptos_framework::permissioned_signer;
use aptos_framework::system_addresses;
use aptos_std::ed25519;
use aptos_std::from_bcs;
Expand Down Expand Up @@ -169,6 +170,8 @@ module aptos_framework::account {
const ENO_SIGNER_CAPABILITY_OFFERED: u64 = 19;
// This account has exceeded the allocated GUIDs it can create. It should be impossible to reach this number for real applications.
const EEXCEEDED_MAX_GUID_CREATION_NUM: u64 = 20;
// Try to rotate auth key via a permissioned signer.
const EROTATION_WITH_PERMISSIONED_SIGNER: u64 = 20;

/// Explicitly separate the GUID space between Object and Account to prevent accidental overlap.
const MAX_GUID_CREATION_NUM: u64 = 0x4000000000000;
Expand Down Expand Up @@ -282,6 +285,10 @@ module aptos_framework::account {
vector::length(&new_auth_key) == 32,
error::invalid_argument(EMALFORMED_AUTHENTICATION_KEY)
);
assert!(
!permissioned_signer::is_permissioned_signer(account),
error::permission_denied(EROTATION_WITH_PERMISSIONED_SIGNER)
);
let account_resource = borrow_global_mut<Account>(addr);
account_resource.authentication_key = new_auth_key;
}
Expand Down Expand Up @@ -334,6 +341,10 @@ module aptos_framework::account {
) acquires Account, OriginatingAddress {
let addr = signer::address_of(account);
assert!(exists_at(addr), error::not_found(EACCOUNT_DOES_NOT_EXIST));
assert!(
!permissioned_signer::is_permissioned_signer(account),
error::permission_denied(EROTATION_WITH_PERMISSIONED_SIGNER)
);
let account_resource = borrow_global_mut<Account>(addr);

// Verify the given `from_public_key_bytes` matches this account's current authentication key.
Expand Down Expand Up @@ -389,6 +400,10 @@ module aptos_framework::account {
new_public_key_bytes: vector<u8>,
cap_update_table: vector<u8>
) acquires Account, OriginatingAddress {
assert!(
!permissioned_signer::is_permissioned_signer(delegate_signer),
error::permission_denied(EROTATION_WITH_PERMISSIONED_SIGNER)
);
assert!(exists_at(rotation_cap_offerer_address), error::not_found(EOFFERER_ADDRESS_DOES_NOT_EXIST));

// Check that there exists a rotation capability offer at the offerer's account resource for the delegate.
Expand Down Expand Up @@ -448,6 +463,10 @@ module aptos_framework::account {
account_public_key_bytes: vector<u8>,
recipient_address: address,
) acquires Account {
assert!(
!permissioned_signer::is_permissioned_signer(account),
error::permission_denied(EROTATION_WITH_PERMISSIONED_SIGNER)
);
let addr = signer::address_of(account);
assert!(exists_at(recipient_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));

Expand Down Expand Up @@ -547,6 +566,10 @@ module aptos_framework::account {
account_public_key_bytes: vector<u8>,
recipient_address: address
) acquires Account {
assert!(
!permissioned_signer::is_permissioned_signer(account),
error::permission_denied(EROTATION_WITH_PERMISSIONED_SIGNER)
);
let source_address = signer::address_of(account);
assert!(exists_at(recipient_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));

Expand Down Expand Up @@ -604,6 +627,10 @@ module aptos_framework::account {
/// Return an authorized signer of the offerer, if there's an existing signer capability offer for `account`
/// at the offerer's address.
public fun create_authorized_signer(account: &signer, offerer_address: address): signer acquires Account {
assert!(
!permissioned_signer::is_permissioned_signer(account),
error::permission_denied(EROTATION_WITH_PERMISSIONED_SIGNER)
);
assert!(exists_at(offerer_address), error::not_found(EOFFERER_ADDRESS_DOES_NOT_EXIST));

// Check if there's an existing signer capability offer from the offerer.
Expand Down
104 changes: 103 additions & 1 deletion aptos-move/framework/aptos-framework/sources/fungible_asset.move
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module aptos_framework::fungible_asset {
use aptos_framework::event;
use aptos_framework::function_info::{Self, FunctionInfo};
use aptos_framework::object::{Self, Object, ConstructorRef, DeleteRef, ExtendRef};
use aptos_framework::permissioned_signer;
use std::string;
use std::features;

Expand Down Expand Up @@ -87,7 +88,8 @@ module aptos_framework::fungible_asset {
const ECONCURRENT_BALANCE_NOT_ENABLED: u64 = 32;
/// Provided derived_supply function type doesn't meet the signature requirement.
const EDERIVED_SUPPLY_FUNCTION_SIGNATURE_MISMATCH: u64 = 33;

/// signer don't have the permission to perform withdraw operation
const EWITHDRAW_PERMISSION_DENIED: u64 = 34;
//
// Constants
//
Expand Down Expand Up @@ -194,6 +196,10 @@ module aptos_framework::fungible_asset {
metadata: Object<Metadata>
}

struct WithdrawPermission has copy, drop, store {
metadata_address: address,
}

#[event]
/// Emitted when fungible assets are deposited into a store.
struct Deposit has drop, store {
Expand Down Expand Up @@ -785,9 +791,32 @@ module aptos_framework::fungible_asset {
amount: u64,
): FungibleAsset acquires FungibleStore, DispatchFunctionStore, ConcurrentFungibleBalance {
withdraw_sanity_check(owner, store, true);
withdraw_permission_check(owner, store, amount);
withdraw_internal(object::object_address(&store), amount)
}

/// Check the permission for withdraw operation.
public(friend) fun withdraw_permission_check<T: key>(
owner: &signer,
store: Object<T>,
amount: u64,
) acquires FungibleStore {
assert!(permissioned_signer::check_permission(owner, amount as u256, WithdrawPermission {
metadata_address: object::object_address(&borrow_store_resource(&store).metadata)
}), error::permission_denied(EWITHDRAW_PERMISSION_DENIED));
}

/// Check the permission for withdraw operation.
public(friend) fun withdraw_permission_check_by_address(
owner: &signer,
metadata_address: address,
amount: u64,
) {
assert!(permissioned_signer::check_permission(owner, amount as u256, WithdrawPermission {
metadata_address,
}), error::permission_denied(EWITHDRAW_PERMISSION_DENIED));
}

/// Check the permission for withdraw operation.
public(friend) fun withdraw_sanity_check<T: key>(
owner: &signer,
Expand Down Expand Up @@ -1179,6 +1208,32 @@ module aptos_framework::fungible_asset {
move_to(&object_signer, ConcurrentFungibleBalance { balance });
}

/// Permission management
///
/// Master signer grant permissioned signer ability to withdraw a given amount of fungible asset.
public fun grant_permission(
master: &signer,
permissioned: &signer,
token_type: Object<Metadata>,
amount: u64
) {
permissioned_signer::authorize(
master,
permissioned,
amount as u256,
WithdrawPermission {
metadata_address: object::object_address(&token_type),
}
)
}

/// Removing permissions from permissioned signer.
public fun revoke_permission(permissioned: &signer, token_type: Object<Metadata>) {
permissioned_signer::revoke_permission(permissioned, WithdrawPermission {
metadata_address: object::object_address(&token_type),
})
}

#[test_only]
use aptos_framework::account;

Expand Down Expand Up @@ -1234,6 +1289,9 @@ module aptos_framework::fungible_asset {
create_store(&object::create_object_from_account(owner), metadata)
}

#[test_only]
use aptos_framework::timestamp;

#[test(creator = @0xcafe)]
fun test_metadata_basic_flow(creator: &signer) acquires Metadata, Supply, ConcurrentSupply {
let (creator_ref, metadata) = create_test_token(creator);
Expand Down Expand Up @@ -1541,6 +1599,50 @@ module aptos_framework::fungible_asset {
assert!(aggregator_v2::read(&borrow_global<ConcurrentFungibleBalance>(object::object_address(&creator_store)).balance) == 30, 12);
}

#[test(creator = @0xcafe, aaron = @0xface)]
fun test_e2e_withdraw_limit(
creator: &signer,
aaron: &signer,
) acquires FungibleStore, Supply, ConcurrentSupply, DispatchFunctionStore, ConcurrentFungibleBalance {
let aptos_framework = account::create_signer_for_test(@0x1);
timestamp::set_time_has_started_for_testing(&aptos_framework);

let (mint_ref, _, _, _, test_token) = create_fungible_asset(creator);
let metadata = mint_ref.metadata;
let creator_store = create_test_store(creator, metadata);
let aaron_store = create_test_store(aaron, metadata);

assert!(supply(test_token) == option::some(0), 1);
// Mint
let fa = mint(&mint_ref, 100);
assert!(supply(test_token) == option::some(100), 2);
// Deposit
deposit(creator_store, fa);
// Withdraw
let fa = withdraw(creator, creator_store, 80);
assert!(supply(test_token) == option::some(100), 3);
deposit(aaron_store, fa);

// Create a permissioned signer
let aaron_permission_handle = permissioned_signer::create_permissioned_handle(aaron);
let aaron_permission_signer = permissioned_signer::signer_from_permissioned(&aaron_permission_handle);

// Grant aaron_permission_signer permission to withdraw 10 apt
grant_permission(aaron, &aaron_permission_signer, metadata, 10);

let fa = withdraw(&aaron_permission_signer, aaron_store, 5);
deposit(aaron_store, fa);

let fa = withdraw(&aaron_permission_signer, aaron_store, 5);
deposit(aaron_store, fa);

// aaron signer don't abide to the same limit
let fa = withdraw(aaron, aaron_store, 5);
deposit(aaron_store, fa);

permissioned_signer::destroy_permissioned_handle(aaron_permission_handle);
}

#[deprecated]
#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct FungibleAssetEvents has key {
Expand Down

0 comments on commit bde486a

Please sign in to comment.