diff --git a/aptos-move/framework/aptos-framework/doc/account.md b/aptos-move/framework/aptos-framework/doc/account.md index 276d6bd47b6244..6b40dbd5d2eec6 100644 --- a/aptos-move/framework/aptos-framework/doc/account.md +++ b/aptos-move/framework/aptos-framework/doc/account.md @@ -105,6 +105,7 @@ use 0x1::hash; use 0x1::multi_ed25519; use 0x1::option; +use 0x1::permissioned_signer; use 0x1::signer; use 0x1::system_addresses; use 0x1::table; @@ -818,6 +819,15 @@ An attempt to create a resource account on a claimed account + + + + +
const EROTATION_WITH_PERMISSIONED_SIGNER: u64 = 20;
+
+
+
+
Sequence number exceeds the maximum value for a u64
@@ -1167,6 +1177,10 @@ many contexts:
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;
}
@@ -1259,6 +1273,10 @@ to rotate his address to Alice's address in the first place.
) 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.
@@ -1334,6 +1352,10 @@ to rotate his address to Alice's address in the first place.
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.
@@ -1413,6 +1435,10 @@ offer, calling this function will replace the previous recipient_address
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));
@@ -1612,6 +1638,10 @@ to the account owner's signer capability).
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));
@@ -1769,6 +1799,10 @@ 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.
diff --git a/aptos-move/framework/aptos-framework/sources/account.move b/aptos-move/framework/aptos-framework/sources/account.move
index a249fbb2d3d09a..f9050583f718a9 100644
--- a/aptos-move/framework/aptos-framework/sources/account.move
+++ b/aptos-move/framework/aptos-framework/sources/account.move
@@ -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;
@@ -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;
@@ -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(addr);
account_resource.authentication_key = new_auth_key;
}
@@ -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(addr);
// Verify the given `from_public_key_bytes` matches this account's current authentication key.
@@ -389,6 +400,10 @@ module aptos_framework::account {
new_public_key_bytes: vector,
cap_update_table: vector
) 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.
@@ -448,6 +463,10 @@ module aptos_framework::account {
account_public_key_bytes: vector,
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));
@@ -547,6 +566,10 @@ module aptos_framework::account {
account_public_key_bytes: vector,
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));
@@ -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.
diff --git a/aptos-move/framework/aptos-framework/sources/fungible_asset.move b/aptos-move/framework/aptos-framework/sources/fungible_asset.move
index 946d7b05eb415c..8a72c88ba1a2f0 100644
--- a/aptos-move/framework/aptos-framework/sources/fungible_asset.move
+++ b/aptos-move/framework/aptos-framework/sources/fungible_asset.move
@@ -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;
@@ -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
//
@@ -194,6 +196,10 @@ module aptos_framework::fungible_asset {
metadata: Object
}
+ struct WithdrawPermission has copy, drop, store {
+ metadata_address: address,
+ }
+
#[event]
/// Emitted when fungible assets are deposited into a store.
struct Deposit has drop, store {
@@ -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(
+ owner: &signer,
+ store: Object,
+ 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(
owner: &signer,
@@ -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,
+ 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) {
+ permissioned_signer::revoke_permission(permissioned, WithdrawPermission {
+ metadata_address: object::object_address(&token_type),
+ })
+ }
+
#[test_only]
use aptos_framework::account;
@@ -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);
@@ -1541,6 +1599,50 @@ module aptos_framework::fungible_asset {
assert!(aggregator_v2::read(&borrow_global(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 {