diff --git a/aptos-move/framework/aptos-framework/doc/create_signer.md b/aptos-move/framework/aptos-framework/doc/create_signer.md index a66df38b9c601..5c15eb6bec51f 100644 --- a/aptos-move/framework/aptos-framework/doc/create_signer.md +++ b/aptos-move/framework/aptos-framework/doc/create_signer.md @@ -127,6 +127,16 @@ Convert address to singer and return.
pragma opaque;
aborts_if [abstract] false;
ensures [abstract] signer::address_of(result) == addr;
+ensures [abstract] result == spec_create_signer(addr);
+
+
+
+
+
+
+
+
+fun spec_create_signer(addr: address): signer;
diff --git a/aptos-move/framework/aptos-framework/doc/overview.md b/aptos-move/framework/aptos-framework/doc/overview.md
index 314baa3612ba9..7ad32f3ea8ec0 100644
--- a/aptos-move/framework/aptos-framework/doc/overview.md
+++ b/aptos-move/framework/aptos-framework/doc/overview.md
@@ -46,6 +46,7 @@ This is the reference documentation of the Aptos framework.
- [`0x1::object`](object.md#0x1_object)
- [`0x1::object_code_deployment`](object_code_deployment.md#0x1_object_code_deployment)
- [`0x1::optional_aggregator`](optional_aggregator.md#0x1_optional_aggregator)
+- [`0x1::permissioned_signer`](permissioned_signer.md#0x1_permissioned_signer)
- [`0x1::primary_fungible_store`](primary_fungible_store.md#0x1_primary_fungible_store)
- [`0x1::randomness`](randomness.md#0x1_randomness)
- [`0x1::randomness_api_v0_config`](randomness_api_v0_config.md#0x1_randomness_api_v0_config)
diff --git a/aptos-move/framework/aptos-framework/doc/permissioned_signer.md b/aptos-move/framework/aptos-framework/doc/permissioned_signer.md
new file mode 100644
index 0000000000000..56670d5b092fb
--- /dev/null
+++ b/aptos-move/framework/aptos-framework/doc/permissioned_signer.md
@@ -0,0 +1,1977 @@
+
+
+
+# Module `0x1::permissioned_signer`
+
+A _permissioned signer_ consists of a pair of the original signer and a generated
+address which is used store information about associated permissions.
+
+A permissioned signer is a restricted version of a signer. Functions move_to
and
+address_of
behave the same, and can be passed wherever signer is needed. However,
+code can internally query for the permissions to assert additional restrictions on
+the use of the signer.
+
+A client which is interested in restricting access granted via a signer can create a permissioned signer
+and pass on to other existing code without changes to existing APIs. Core functions in the framework, for
+example account functions, can then assert availability of permissions, effectively restricting
+existing code in a compatible way.
+
+After introducing the core functionality, examples are provided for withdraw limit on accounts, and
+for blind signing.
+
+
+- [Resource `GrantedPermissionHandles`](#0x1_permissioned_signer_GrantedPermissionHandles)
+- [Enum `PermissionedHandle`](#0x1_permissioned_signer_PermissionedHandle)
+- [Enum `StorablePermissionedHandle`](#0x1_permissioned_signer_StorablePermissionedHandle)
+- [Enum Resource `PermissionStorage`](#0x1_permissioned_signer_PermissionStorage)
+- [Enum `StoredPermission`](#0x1_permissioned_signer_StoredPermission)
+- [Enum `Permission`](#0x1_permissioned_signer_Permission)
+- [Constants](#@Constants_0)
+- [Function `create_permissioned_handle`](#0x1_permissioned_signer_create_permissioned_handle)
+- [Function `create_storable_permissioned_handle`](#0x1_permissioned_signer_create_storable_permissioned_handle)
+- [Function `destroy_permissioned_handle`](#0x1_permissioned_signer_destroy_permissioned_handle)
+- [Function `destroy_storable_permissioned_handle`](#0x1_permissioned_signer_destroy_storable_permissioned_handle)
+- [Function `destroy_permissions_storage_address`](#0x1_permissioned_signer_destroy_permissions_storage_address)
+- [Function `signer_from_permissioned_handle`](#0x1_permissioned_signer_signer_from_permissioned_handle)
+- [Function `signer_from_storable_permissioned_handle`](#0x1_permissioned_signer_signer_from_storable_permissioned_handle)
+- [Function `revoke_permission_storage_address`](#0x1_permissioned_signer_revoke_permission_storage_address)
+- [Function `revoke_all_handles`](#0x1_permissioned_signer_revoke_all_handles)
+- [Function `permissions_storage_address`](#0x1_permissioned_signer_permissions_storage_address)
+- [Function `assert_master_signer`](#0x1_permissioned_signer_assert_master_signer)
+- [Function `is_above`](#0x1_permissioned_signer_is_above)
+- [Function `consume_capacity`](#0x1_permissioned_signer_consume_capacity)
+- [Function `increase_capacity`](#0x1_permissioned_signer_increase_capacity)
+- [Function `merge`](#0x1_permissioned_signer_merge)
+- [Function `map_or`](#0x1_permissioned_signer_map_or)
+- [Function `insert_or`](#0x1_permissioned_signer_insert_or)
+- [Function `authorize`](#0x1_permissioned_signer_authorize)
+- [Function `authorize_unlimited`](#0x1_permissioned_signer_authorize_unlimited)
+- [Function `increase_limit`](#0x1_permissioned_signer_increase_limit)
+- [Function `check_permission_exists`](#0x1_permissioned_signer_check_permission_exists)
+- [Function `check_permission_capacity_above`](#0x1_permissioned_signer_check_permission_capacity_above)
+- [Function `check_permission_consume`](#0x1_permissioned_signer_check_permission_consume)
+- [Function `capacity`](#0x1_permissioned_signer_capacity)
+- [Function `revoke_permission`](#0x1_permissioned_signer_revoke_permission)
+- [Function `extract_permission`](#0x1_permissioned_signer_extract_permission)
+- [Function `extract_all_permission`](#0x1_permissioned_signer_extract_all_permission)
+- [Function `address_of`](#0x1_permissioned_signer_address_of)
+- [Function `consume_permission`](#0x1_permissioned_signer_consume_permission)
+- [Function `store_permission`](#0x1_permissioned_signer_store_permission)
+- [Function `is_permissioned_signer`](#0x1_permissioned_signer_is_permissioned_signer)
+- [Function `permission_address`](#0x1_permissioned_signer_permission_address)
+- [Function `signer_from_permissioned_handle_impl`](#0x1_permissioned_signer_signer_from_permissioned_handle_impl)
+- [Specification](#@Specification_1)
+ - [Function `create_permissioned_handle`](#@Specification_1_create_permissioned_handle)
+ - [Function `create_storable_permissioned_handle`](#@Specification_1_create_storable_permissioned_handle)
+ - [Function `destroy_permissioned_handle`](#@Specification_1_destroy_permissioned_handle)
+ - [Function `destroy_storable_permissioned_handle`](#@Specification_1_destroy_storable_permissioned_handle)
+ - [Function `revoke_permission_storage_address`](#@Specification_1_revoke_permission_storage_address)
+ - [Function `authorize`](#@Specification_1_authorize)
+ - [Function `check_permission_exists`](#@Specification_1_check_permission_exists)
+ - [Function `check_permission_capacity_above`](#@Specification_1_check_permission_capacity_above)
+ - [Function `check_permission_consume`](#@Specification_1_check_permission_consume)
+ - [Function `capacity`](#@Specification_1_capacity)
+ - [Function `consume_permission`](#@Specification_1_consume_permission)
+ - [Function `is_permissioned_signer`](#@Specification_1_is_permissioned_signer)
+ - [Function `permission_address`](#@Specification_1_permission_address)
+ - [Function `signer_from_permissioned_handle_impl`](#@Specification_1_signer_from_permissioned_handle_impl)
+
+
+use 0x1::copyable_any;
+use 0x1::create_signer;
+use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
+use 0x1::simple_map;
+use 0x1::timestamp;
+use 0x1::transaction_context;
+use 0x1::vector;
+
+
+
+
+
+
+## Resource `GrantedPermissionHandles`
+
+
+
+struct GrantedPermissionHandles has key
+
+
+
+
+active_handles: vector<address>
+enum PermissionedHandle
+
+
+
+
+master_account_addr: address
+permissions_storage_addr: address
+enum StorablePermissionedHandle has store
+
+
+
+
+master_account_addr: address
+permissions_storage_addr: address
+expiration_time: u64
+enum PermissionStorage has key
+
+
+
+
+perms: simple_map::SimpleMap<copyable_any::Any, permissioned_signer::StoredPermission>
+enum StoredPermission has copy, drop, store
+
+
+
+
+0: u256
+enum Permission<K>
+
+
+
+
+owner_address: address
+key: K
+perm: permissioned_signer::StoredPermission
+const ECANNOT_AUTHORIZE: u64 = 2;
+
+
+
+
+
+
+signer doesn't have enough capacity to extract permission.
+
+
+const ECANNOT_EXTRACT_PERMISSION: u64 = 4;
+
+
+
+
+
+
+Trying to grant permission using master signer.
+
+
+const ENOT_MASTER_SIGNER: u64 = 1;
+
+
+
+
+
+
+Access permission information from a master signer.
+
+
+const ENOT_PERMISSIONED_SIGNER: u64 = 3;
+
+
+
+
+
+
+destroying permission handle that has already been revoked or not owned by the
+given master signer.
+
+
+const E_NOT_ACTIVE: u64 = 8;
+
+
+
+
+
+
+permission handle has expired.
+
+
+const E_PERMISSION_EXPIRED: u64 = 5;
+
+
+
+
+
+
+storing extracted permission into a different signer.
+
+
+const E_PERMISSION_MISMATCH: u64 = 6;
+
+
+
+
+
+
+permission handle has been revoked by the original signer.
+
+
+const E_PERMISSION_REVOKED: u64 = 7;
+
+
+
+
+
+
+
+
+const U256_MAX: u256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
+
+
+
+
+
+
+## Function `create_permissioned_handle`
+
+Create an ephermeral permission handle based on the master signer.
+
+This handle can be used to derive a signer that can be used in the context of
+the current transaction.
+
+
+public fun create_permissioned_handle(master: &signer): permissioned_signer::PermissionedHandle
+
+
+
+
+public fun create_permissioned_handle(master: &signer): PermissionedHandle {
+ assert_master_signer(master);
+ let permissions_storage_addr = generate_auid_address();
+ let master_account_addr = signer::address_of(master);
+
+ move_to(
+ &create_signer(permissions_storage_addr),
+ PermissionStorage::V1 { perms: simple_map::new() }
+ );
+
+ PermissionedHandle::V1 { master_account_addr, permissions_storage_addr }
+}
+
+
+
+
+expiration_time
is not too far in the future.
+
+
+public(friend) fun create_storable_permissioned_handle(master: &signer, expiration_time: u64): permissioned_signer::StorablePermissionedHandle
+
+
+
+
+public(package) fun create_storable_permissioned_handle(
+ master: &signer, expiration_time: u64
+): StorablePermissionedHandle acquires GrantedPermissionHandles {
+ assert_master_signer(master);
+ let permissions_storage_addr = generate_auid_address();
+ let master_account_addr = signer::address_of(master);
+
+ assert!(
+ timestamp::now_seconds() < expiration_time,
+ error::permission_denied(E_PERMISSION_EXPIRED)
+ );
+
+ if (!exists<GrantedPermissionHandles>(master_account_addr)) {
+ move_to<GrantedPermissionHandles>(
+ master, GrantedPermissionHandles { active_handles: vector::empty() }
+ );
+ };
+
+ vector::push_back(
+ &mut borrow_global_mut<GrantedPermissionHandles>(master_account_addr).active_handles,
+ permissions_storage_addr
+ );
+
+ move_to(
+ &create_signer(permissions_storage_addr),
+ PermissionStorage::V1 { perms: simple_map::new() }
+ );
+
+ StorablePermissionedHandle::V1 {
+ master_account_addr,
+ permissions_storage_addr,
+ expiration_time
+ }
+}
+
+
+
+
+public fun destroy_permissioned_handle(p: permissioned_signer::PermissionedHandle)
+
+
+
+
+public fun destroy_permissioned_handle(p: PermissionedHandle) acquires PermissionStorage {
+ let PermissionedHandle::V1 { master_account_addr: _, permissions_storage_addr } =
+ p;
+ destroy_permissions_storage_address(permissions_storage_addr);
+}
+
+
+
+
+public(friend) fun destroy_storable_permissioned_handle(p: permissioned_signer::StorablePermissionedHandle)
+
+
+
+
+public(package) fun destroy_storable_permissioned_handle(
+ p: StorablePermissionedHandle
+) acquires PermissionStorage, GrantedPermissionHandles {
+ let StorablePermissionedHandle::V1 {
+ master_account_addr,
+ permissions_storage_addr,
+ expiration_time: _
+ } = p;
+
+ assert!(
+ exists<GrantedPermissionHandles>(master_account_addr),
+ error::permission_denied(E_PERMISSION_REVOKED),
+ );
+ let granted_permissions =
+ borrow_global_mut<GrantedPermissionHandles>(master_account_addr);
+ let (found, idx) = vector::index_of(
+ &granted_permissions.active_handles, &permissions_storage_addr
+ );
+
+ // Removing the address from the active handle list if it's still active.
+ if(found) {
+ vector::swap_remove(&mut granted_permissions.active_handles, idx);
+ };
+
+ destroy_permissions_storage_address(permissions_storage_addr);
+}
+
+
+
+
+fun destroy_permissions_storage_address(permissions_storage_addr: address)
+
+
+
+
+inline fun destroy_permissions_storage_address(
+ permissions_storage_addr: address
+) acquires PermissionStorage {
+ if (exists<PermissionStorage>(permissions_storage_addr)) {
+ let PermissionStorage::V1 { perms } =
+ move_from<PermissionStorage>(permissions_storage_addr);
+ simple_map::destroy(
+ perms,
+ |_dk| {},
+ |_dv| {}
+ );
+ }
+}
+
+
+
+
+public fun signer_from_permissioned_handle(p: &permissioned_signer::PermissionedHandle): signer
+
+
+
+
+public fun signer_from_permissioned_handle(p: &PermissionedHandle): signer {
+ signer_from_permissioned_handle_impl(
+ p.master_account_addr, p.permissions_storage_addr
+ )
+}
+
+
+
+
+public(friend) fun signer_from_storable_permissioned_handle(p: &permissioned_signer::StorablePermissionedHandle): signer
+
+
+
+
+public(package) fun signer_from_storable_permissioned_handle(
+ p: &StorablePermissionedHandle
+): signer {
+ assert!(
+ timestamp::now_seconds() < p.expiration_time,
+ error::permission_denied(E_PERMISSION_EXPIRED)
+ );
+ assert!(
+ exists<PermissionStorage>(p.permissions_storage_addr),
+ error::permission_denied(E_PERMISSION_REVOKED)
+ );
+ signer_from_permissioned_handle_impl(
+ p.master_account_addr, p.permissions_storage_addr
+ )
+}
+
+
+
+
+public entry fun revoke_permission_storage_address(s: &signer, permissions_storage_addr: address)
+
+
+
+
+public entry fun revoke_permission_storage_address(
+ s: &signer, permissions_storage_addr: address
+) acquires GrantedPermissionHandles, PermissionStorage {
+ assert!(
+ !is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER)
+ );
+ let master_account_addr = signer::address_of(s);
+
+ assert!(
+ exists<GrantedPermissionHandles>(master_account_addr),
+ error::permission_denied(E_PERMISSION_REVOKED),
+ );
+ let granted_permissions =
+ borrow_global_mut<GrantedPermissionHandles>(master_account_addr);
+ let (found, idx) = vector::index_of(
+ &granted_permissions.active_handles, &permissions_storage_addr
+ );
+
+ // The address has to be in the activated list in the master account address.
+ assert!(found, error::permission_denied(E_NOT_ACTIVE));
+ vector::swap_remove(&mut granted_permissions.active_handles, idx);
+ destroy_permissions_storage_address(permissions_storage_addr);
+}
+
+
+
+
+public entry fun revoke_all_handles(s: &signer)
+
+
+
+
+public entry fun revoke_all_handles(s: &signer) acquires GrantedPermissionHandles, PermissionStorage {
+ assert!(
+ !is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER)
+ );
+ let master_account_addr = signer::address_of(s);
+ if (!exists<GrantedPermissionHandles>(master_account_addr)) { return };
+
+ let granted_permissions =
+ borrow_global_mut<GrantedPermissionHandles>(master_account_addr);
+ let delete_list = vector::trim_reverse(
+ &mut granted_permissions.active_handles, 0
+ );
+ vector::destroy(
+ delete_list,
+ |address| {
+ destroy_permissions_storage_address(address);
+ }
+ )
+}
+
+
+
+
+public(friend) fun permissions_storage_address(p: &permissioned_signer::StorablePermissionedHandle): address
+
+
+
+
+public(package) fun permissions_storage_address(
+ p: &StorablePermissionedHandle
+): address {
+ p.permissions_storage_addr
+}
+
+
+
+
+public fun assert_master_signer(s: &signer)
+
+
+
+
+public fun assert_master_signer(s: &signer) {
+ assert!(
+ !is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER)
+ );
+}
+
+
+
+
+threshold
capacity.
+
+
+fun is_above(perm: &permissioned_signer::StoredPermission, threshold: u256): bool
+
+
+
+
+fun is_above(perm: &StoredPermission, threshold: u256): bool {
+ match (perm) {
+ StoredPermission::Capacity(capacity) => *capacity > threshold,
+ StoredPermission::Unlimited => true,
+ }
+}
+
+
+
+
+threshold
capacity from StoredPermission
+
+
+fun consume_capacity(perm: &mut permissioned_signer::StoredPermission, threshold: u256): bool
+
+
+
+
+fun consume_capacity(perm: &mut StoredPermission, threshold: u256): bool {
+ match (perm) {
+ StoredPermission::Capacity(current_capacity) => {
+ if (*current_capacity >= threshold) {
+ *current_capacity = *current_capacity - threshold;
+ true
+ } else { false }
+ }
+ StoredPermission::Unlimited => true
+ }
+}
+
+
+
+
+threshold
capacity from StoredPermission
+
+
+fun increase_capacity(perm: &mut permissioned_signer::StoredPermission, threshold: u256)
+
+
+
+
+fun increase_capacity(perm: &mut StoredPermission, threshold: u256) {
+ match (perm) {
+ StoredPermission::Capacity(current_capacity) => {
+ *current_capacity = *current_capacity + threshold;
+ }
+ StoredPermission::Unlimited => (),
+ }
+}
+
+
+
+
+fun merge(lhs: &mut permissioned_signer::StoredPermission, rhs: permissioned_signer::StoredPermission)
+
+
+
+
+fun merge(lhs: &mut StoredPermission, rhs: StoredPermission) {
+ match (rhs) {
+ StoredPermission::Capacity(new_capacity) => {
+ match (lhs) {
+ StoredPermission::Capacity(current_capacity) => {
+ *current_capacity = *current_capacity + new_capacity;
+ }
+ StoredPermission::Unlimited => (),
+ }
+ }
+ StoredPermission::Unlimited => *lhs = StoredPermission::Unlimited,
+ }
+}
+
+
+
+
+permissioned
with the given permission. This requires to have access to the master
+signer.
+
+
+fun map_or<PermKey: copy, drop, store, T>(permissioned: &signer, perm: PermKey, mutate: |&mut permissioned_signer::StoredPermission|T, default: T): T
+
+
+
+
+inline fun map_or<PermKey: copy + drop + store, T>(
+ permissioned: &signer,
+ perm: PermKey,
+ mutate: |&mut StoredPermission| T,
+ default: T,
+): T {
+ let permission_signer_addr = permission_address(permissioned);
+ assert!(
+ exists<PermissionStorage>(permission_signer_addr),
+ error::permission_denied(E_NOT_ACTIVE)
+ );
+ let perms =
+ &mut borrow_global_mut<PermissionStorage>(permission_signer_addr).perms;
+ let key = copyable_any::pack(perm);
+ if (simple_map::contains_key(perms, &key)) {
+ mutate(simple_map::borrow_mut(perms, &key))
+ } else {
+ default
+ }
+}
+
+
+
+
+fun insert_or<PermKey: copy, drop, store>(permissioned: &signer, perm: PermKey, mutate: |&mut permissioned_signer::StoredPermission|, default: permissioned_signer::StoredPermission)
+
+
+
+
+inline fun insert_or<PermKey: copy + drop + store>(
+ permissioned: &signer,
+ perm: PermKey,
+ mutate: |&mut StoredPermission|,
+ default: StoredPermission,
+) {
+ let permission_signer_addr = permission_address(permissioned);
+ assert!(
+ exists<PermissionStorage>(permission_signer_addr),
+ error::permission_denied(E_NOT_ACTIVE)
+ );
+ let perms =
+ &mut borrow_global_mut<PermissionStorage>(permission_signer_addr).perms;
+ let key = copyable_any::pack(perm);
+ if (simple_map::contains_key(perms, &key)) {
+ mutate(simple_map::borrow_mut(perms, &key));
+ } else {
+ simple_map::add(perms, key, default);
+ }
+}
+
+
+
+
+permissioned
with a given capacity.
+
+Consumption using check_permission_consume
will deduct the capacity.
+
+
+public fun authorize<PermKey: copy, drop, store>(master: &signer, permissioned: &signer, capacity: u256, perm: PermKey)
+
+
+
+
+public fun authorize<PermKey: copy + drop + store>(
+ master: &signer,
+ permissioned: &signer,
+ capacity: u256,
+ perm: PermKey
+) acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(permissioned)
+ && !is_permissioned_signer(master)
+ && signer::address_of(master) == signer::address_of(permissioned),
+ error::permission_denied(ECANNOT_AUTHORIZE)
+ );
+ insert_or(
+ permissioned,
+ perm,
+ |stored_permission| {
+ increase_capacity(stored_permission, capacity);
+ },
+ StoredPermission::Capacity(capacity),
+ )
+}
+
+
+
+
+permissioned
with the given unlimited permission.
+Unlimited permission can be consumed however many times.
+
+
+public fun authorize_unlimited<PermKey: copy, drop, store>(master: &signer, permissioned: &signer, perm: PermKey)
+
+
+
+
+public fun authorize_unlimited<PermKey: copy + drop + store>(
+ master: &signer,
+ permissioned: &signer,
+ perm: PermKey
+) acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(permissioned)
+ && !is_permissioned_signer(master)
+ && signer::address_of(master) == signer::address_of(permissioned),
+ error::permission_denied(ECANNOT_AUTHORIZE)
+ );
+ insert_or(
+ permissioned,
+ perm,
+ |stored_permission| {
+ *stored_permission = StoredPermission::Unlimited;
+ },
+ StoredPermission::Unlimited,
+ )
+}
+
+
+
+
+capacity
of a permissioned signer **without** master signer's approvoal.
+
+The caller of the module will need to make sure the witness type PermKey
can only be
+constructed within its own module, otherwise attackers can refill the permission for itself
+to bypass the checks.
+
+
+public(friend) fun increase_limit<PermKey: copy, drop, store>(permissioned: &signer, capacity: u256, perm: PermKey)
+
+
+
+
+public(package) fun increase_limit<PermKey: copy + drop + store>(
+ permissioned: &signer,
+ capacity: u256,
+ perm: PermKey
+) acquires PermissionStorage {
+ if(is_permissioned_signer(permissioned)) {
+ return;
+ };
+ insert_or(
+ permissioned,
+ perm,
+ |stored_permission| {
+ increase_capacity(stored_permission, capacity);
+ },
+ StoredPermission::Capacity(capacity),
+ )
+}
+
+
+
+
+public fun check_permission_exists<PermKey: copy, drop, store>(s: &signer, perm: PermKey): bool
+
+
+
+
+public fun check_permission_exists<PermKey: copy + drop + store>(
+ s: &signer, perm: PermKey
+): bool acquires PermissionStorage {
+ check_permission_capacity_above(s, 0, perm)
+}
+
+
+
+
+public fun check_permission_capacity_above<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+public fun check_permission_capacity_above<PermKey: copy + drop + store>(
+ s: &signer, threshold: u256, perm: PermKey
+): bool acquires PermissionStorage {
+ if (!is_permissioned_signer(s)) {
+ // master signer has all permissions
+ return true
+ };
+ map_or(
+ s,
+ perm,
+ |stored_permission| {
+ is_above(stored_permission, threshold)
+ },
+ false,
+ )
+}
+
+
+
+
+public fun check_permission_consume<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+public fun check_permission_consume<PermKey: copy + drop + store>(
+ s: &signer, threshold: u256, perm: PermKey
+): bool acquires PermissionStorage {
+ if (!is_permissioned_signer(s)) {
+ // master signer has all permissions
+ return true
+ };
+ map_or(
+ s,
+ perm,
+ |stored_permission| {
+ consume_capacity(stored_permission, threshold)
+ },
+ false,
+ )
+}
+
+
+
+
+public fun capacity<PermKey: copy, drop, store>(s: &signer, perm: PermKey): option::Option<u256>
+
+
+
+
+public fun capacity<PermKey: copy + drop + store>(
+ s: &signer, perm: PermKey
+): Option<u256> acquires PermissionStorage {
+ if (!is_permissioned_signer(s)) {
+ return option::some(U256_MAX)
+ };
+ map_or(
+ s,
+ perm,
+ |stored_permission: &mut StoredPermission| {
+ option::some(match (stored_permission) {
+ StoredPermission::Capacity(capacity) => *capacity,
+ StoredPermission::Unlimited => U256_MAX,
+ })
+ },
+ option::none(),
+ )
+}
+
+
+
+
+public fun revoke_permission<PermKey: copy, drop, store>(permissioned: &signer, perm: PermKey)
+
+
+
+
+public fun revoke_permission<PermKey: copy + drop + store>(
+ permissioned: &signer, perm: PermKey
+) acquires PermissionStorage {
+ if (!is_permissioned_signer(permissioned)) {
+ // Master signer has no permissions associated with it.
+ return
+ };
+ let addr = permission_address(permissioned);
+ if (!exists<PermissionStorage>(addr)) { return };
+ let perm_storage = &mut borrow_global_mut<PermissionStorage>(addr).perms;
+ let key = copyable_any::pack(perm);
+ if (simple_map::contains_key(perm_storage, &key)) {
+ simple_map::remove(
+ &mut borrow_global_mut<PermissionStorage>(addr).perms,
+ ©able_any::pack(perm)
+ );
+ }
+}
+
+
+
+
+public(friend) fun extract_permission<PermKey: copy, drop, store>(s: &signer, weight: u256, perm: PermKey): permissioned_signer::Permission<PermKey>
+
+
+
+
+public(package) fun extract_permission<PermKey: copy + drop + store>(
+ s: &signer, weight: u256, perm: PermKey
+): Permission<PermKey> acquires PermissionStorage {
+ assert!(
+ check_permission_consume(s, weight, perm),
+ error::permission_denied(ECANNOT_EXTRACT_PERMISSION)
+ );
+ Permission::V1 {
+ owner_address: signer::address_of(s),
+ key: perm,
+ perm: StoredPermission::Capacity(weight),
+ }
+}
+
+
+
+
+public(friend) fun extract_all_permission<PermKey: copy, drop, store>(s: &signer, perm_key: PermKey): permissioned_signer::Permission<PermKey>
+
+
+
+
+public(package) fun extract_all_permission<PermKey: copy + drop + store>(
+ s: &signer, perm_key: PermKey
+): Permission<PermKey> acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(s),
+ error::permission_denied(ECANNOT_EXTRACT_PERMISSION)
+ );
+ let addr = permission_address(s);
+ assert!(
+ exists<PermissionStorage>(addr),
+ error::permission_denied(ECANNOT_EXTRACT_PERMISSION)
+ );
+ let key = copyable_any::pack(perm_key);
+ let storage = &mut borrow_global_mut<PermissionStorage>(addr).perms;
+ let (_, value) = simple_map::remove(storage, &key);
+
+ Permission::V1 {
+ owner_address: signer::address_of(s),
+ key: perm_key,
+ perm: value,
+ }
+}
+
+
+
+
+public(friend) fun address_of<PermKey>(perm: &permissioned_signer::Permission<PermKey>): address
+
+
+
+
+public(package) fun address_of<PermKey>(perm: &Permission<PermKey>): address {
+ perm.owner_address
+}
+
+
+
+
+public(friend) fun consume_permission<PermKey: copy, drop, store>(perm: &mut permissioned_signer::Permission<PermKey>, weight: u256, perm_key: PermKey): bool
+
+
+
+
+public(package) fun consume_permission<PermKey: copy + drop + store>(
+ perm: &mut Permission<PermKey>, weight: u256, perm_key: PermKey
+): bool {
+ if (perm.key != perm_key) {
+ return false
+ };
+ consume_capacity(&mut perm.perm, weight)
+}
+
+
+
+
+public(friend) fun store_permission<PermKey: copy, drop, store>(s: &signer, perm: permissioned_signer::Permission<PermKey>)
+
+
+
+
+public(package) fun store_permission<PermKey: copy + drop + store>(
+ s: &signer, perm: Permission<PermKey>
+) acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(s),
+ error::permission_denied(ENOT_PERMISSIONED_SIGNER)
+ );
+ let Permission::V1 { key, perm, owner_address } = perm;
+
+ assert!(
+ signer::address_of(s) == owner_address,
+ error::permission_denied(E_PERMISSION_MISMATCH)
+ );
+
+ insert_or(
+ s,
+ key,
+ |stored_permission| {
+ merge(stored_permission, perm);
+ },
+ perm,
+ )
+}
+
+
+
+
+public fun is_permissioned_signer(s: &signer): bool
+
+
+
+
+public native fun is_permissioned_signer(s: &signer): bool;
+
+
+
+
+fun permission_address(permissioned: &signer): address
+
+
+
+
+native fun permission_address(permissioned: &signer): address;
+
+
+
+
+fun signer_from_permissioned_handle_impl(master_account_addr: address, permissions_storage_addr: address): signer
+
+
+
+
+native fun signer_from_permissioned_handle_impl(
+ master_account_addr: address, permissions_storage_addr: address
+): signer;
+
+
+
+
+pragma verify = false;
+axiom forall a: GrantedPermissionHandles:
+ (
+ forall i in 0..len(a.active_handles):
+ forall j in 0..len(a.active_handles):
+ i != j ==>
+ a.active_handles[i] != a.active_handles[j]
+ );
+
+
+
+
+
+
+
+
+fun spec_is_permissioned_signer(s: signer): bool;
+
+
+
+
+
+
+### Function `create_permissioned_handle`
+
+
+public fun create_permissioned_handle(master: &signer): permissioned_signer::PermissionedHandle
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract] spec_is_permissioned_signer(master);
+let permissions_storage_addr = transaction_context::spec_generate_unique_address();
+modifies global<PermissionStorage>(permissions_storage_addr);
+let master_account_addr = signer::address_of(master);
+ensures result.master_account_addr == master_account_addr;
+ensures result.permissions_storage_addr == permissions_storage_addr;
+
+
+
+
+
+
+### Function `create_storable_permissioned_handle`
+
+
+public(friend) fun create_storable_permissioned_handle(master: &signer, expiration_time: u64): permissioned_signer::StorablePermissionedHandle
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract] spec_is_permissioned_signer(master);
+let permissions_storage_addr = transaction_context::spec_generate_unique_address();
+modifies global<PermissionStorage>(permissions_storage_addr);
+let master_account_addr = signer::address_of(master);
+modifies global<GrantedPermissionHandles>(master_account_addr);
+ensures result.master_account_addr == master_account_addr;
+ensures result.permissions_storage_addr == permissions_storage_addr;
+ensures result.expiration_time == expiration_time;
+ensures vector::spec_contains(
+ global<GrantedPermissionHandles>(master_account_addr).active_handles,
+ permissions_storage_addr
+);
+ensures exists<GrantedPermissionHandles>(master_account_addr);
+
+
+
+
+
+
+### Function `destroy_permissioned_handle`
+
+
+public fun destroy_permissioned_handle(p: permissioned_signer::PermissionedHandle)
+
+
+
+
+
+ensures !exists<PermissionStorage>(p.permissions_storage_addr);
+
+
+
+
+
+
+### Function `destroy_storable_permissioned_handle`
+
+
+public(friend) fun destroy_storable_permissioned_handle(p: permissioned_signer::StorablePermissionedHandle)
+
+
+
+
+
+ensures !exists<PermissionStorage>(p.permissions_storage_addr);
+let post granted_permissions = global<GrantedPermissionHandles>(
+ p.master_account_addr
+);
+
+
+
+
+
+
+### Function `revoke_permission_storage_address`
+
+
+public entry fun revoke_permission_storage_address(s: &signer, permissions_storage_addr: address)
+
+
+
+
+
+
+
+### Function `authorize`
+
+
+public fun authorize<PermKey: copy, drop, store>(master: &signer, permissioned: &signer, capacity: u256, perm: PermKey)
+
+
+
+
+
+pragma aborts_if_is_partial;
+aborts_if !spec_is_permissioned_signer(permissioned);
+aborts_if spec_is_permissioned_signer(master);
+aborts_if signer::address_of(permissioned) != signer::address_of(master);
+ensures exists<PermissionStorage>(
+ spec_permission_address(permissioned)
+);
+
+
+
+
+
+
+### Function `check_permission_exists`
+
+
+public fun check_permission_exists<PermKey: copy, drop, store>(s: &signer, perm: PermKey): bool
+
+
+
+
+
+pragma verify = false;
+
+
+
+
+
+
+
+
+fun spec_check_permission_exists<PermKey: copy + drop + store>(s: signer, perm: PermKey): bool {
+ use aptos_std::type_info;
+ use std::bcs;
+ let addr = spec_permission_address(s);
+ let key = Any {
+ type_name: type_info::type_name<PermKey>(),
+ data: bcs::serialize(perm)
+ };
+ if (!spec_is_permissioned_signer(s)) { true }
+ else if (!exists<PermissionStorage>(addr)) { false }
+ else {
+ simple_map::spec_contains_key(global<PermissionStorage>(addr).perms, key)
+ }
+}
+
+
+
+
+
+
+### Function `check_permission_capacity_above`
+
+
+public fun check_permission_capacity_above<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+
+let permissioned_signer_addr = spec_permission_address(s);
+ensures !spec_is_permissioned_signer(s) ==> result == true;
+ensures (
+ spec_is_permissioned_signer(s)
+ && !exists<PermissionStorage>(permissioned_signer_addr)
+) ==> result == false;
+let key = Any {
+ type_name: type_info::type_name<SimpleMap<Any, u256>>(),
+ data: bcs::serialize(perm)
+};
+
+
+
+
+
+
+### Function `check_permission_consume`
+
+
+public fun check_permission_consume<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+
+let permissioned_signer_addr = spec_permission_address(s);
+ensures !spec_is_permissioned_signer(s) ==> result == true;
+ensures (
+ spec_is_permissioned_signer(s)
+ && !exists<PermissionStorage>(permissioned_signer_addr)
+) ==> result == false;
+
+
+
+
+
+
+### Function `capacity`
+
+
+public fun capacity<PermKey: copy, drop, store>(s: &signer, perm: PermKey): option::Option<u256>
+
+
+
+
+
+
+
+### Function `consume_permission`
+
+
+public(friend) fun consume_permission<PermKey: copy, drop, store>(perm: &mut permissioned_signer::Permission<PermKey>, weight: u256, perm_key: PermKey): bool
+
+
+
+
+
+
+
+### Function `is_permissioned_signer`
+
+
+public fun is_permissioned_signer(s: &signer): bool
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_is_permissioned_signer(s);
+
+
+
+
+
+
+
+
+fun spec_permission_address(s: signer): address;
+
+
+
+
+
+
+### Function `permission_address`
+
+
+fun permission_address(permissioned: &signer): address
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract]!spec_is_permissioned_signer(permissioned);
+ensures [abstract] result == spec_permission_address(permissioned);
+
+
+
+
+
+
+
+
+fun spec_signer_from_permissioned_handle_impl(
+ master_account_addr: address, permissions_storage_addr: address
+): signer;
+
+
+
+
+
+
+### Function `signer_from_permissioned_handle_impl`
+
+
+fun signer_from_permissioned_handle_impl(master_account_addr: address, permissions_storage_addr: address): signer
+
+
+
+
+
+pragma opaque;
+ensures [abstract] result
+ == spec_signer_from_permissioned_handle_impl(
+ master_account_addr, permissions_storage_addr
+ );
+
+
+
+[move-book]: https://aptos.dev/move/book/SUMMARY
diff --git a/aptos-move/framework/aptos-framework/sources/create_signer.move b/aptos-move/framework/aptos-framework/sources/create_signer.move
index 3da0c50c904f0..a92dd88845568 100644
--- a/aptos-move/framework/aptos-framework/sources/create_signer.move
+++ b/aptos-move/framework/aptos-framework/sources/create_signer.move
@@ -16,6 +16,7 @@ module aptos_framework::create_signer {
friend aptos_framework::genesis;
friend aptos_framework::multisig_account;
friend aptos_framework::object;
+ friend aptos_framework::permissioned_signer;
public(friend) native fun create_signer(addr: address): signer;
}
diff --git a/aptos-move/framework/aptos-framework/sources/permissioned_signer.move b/aptos-move/framework/aptos-framework/sources/permissioned_signer.move
new file mode 100644
index 0000000000000..457a2f868b5c5
--- /dev/null
+++ b/aptos-move/framework/aptos-framework/sources/permissioned_signer.move
@@ -0,0 +1,661 @@
+/// A _permissioned signer_ consists of a pair of the original signer and a generated
+/// address which is used store information about associated permissions.
+///
+/// A permissioned signer is a restricted version of a signer. Functions `move_to` and
+/// `address_of` behave the same, and can be passed wherever signer is needed. However,
+/// code can internally query for the permissions to assert additional restrictions on
+/// the use of the signer.
+///
+/// A client which is interested in restricting access granted via a signer can create a permissioned signer
+/// and pass on to other existing code without changes to existing APIs. Core functions in the framework, for
+/// example account functions, can then assert availability of permissions, effectively restricting
+/// existing code in a compatible way.
+///
+/// After introducing the core functionality, examples are provided for withdraw limit on accounts, and
+/// for blind signing.
+module aptos_framework::permissioned_signer {
+ use std::signer;
+ use std::error;
+ use std::vector;
+ use std::option::{Option, Self};
+ use aptos_std::copyable_any::{Self, Any};
+ use aptos_std::simple_map::{Self, SimpleMap};
+ use aptos_framework::create_signer::create_signer;
+ use aptos_framework::transaction_context::generate_auid_address;
+ use aptos_framework::timestamp;
+
+ /// Trying to grant permission using master signer.
+ const ENOT_MASTER_SIGNER: u64 = 1;
+
+ /// Cannot authorize a permission.
+ const ECANNOT_AUTHORIZE: u64 = 2;
+
+ /// Access permission information from a master signer.
+ const ENOT_PERMISSIONED_SIGNER: u64 = 3;
+
+ /// signer doesn't have enough capacity to extract permission.
+ const ECANNOT_EXTRACT_PERMISSION: u64 = 4;
+
+ /// permission handle has expired.
+ const E_PERMISSION_EXPIRED: u64 = 5;
+
+ /// storing extracted permission into a different signer.
+ const E_PERMISSION_MISMATCH: u64 = 6;
+
+ /// permission handle has been revoked by the original signer.
+ const E_PERMISSION_REVOKED: u64 = 7;
+
+ /// destroying permission handle that has already been revoked or not owned by the
+ /// given master signer.
+ const E_NOT_ACTIVE: u64 = 8;
+
+ const U256_MAX: u256 =
+ 115792089237316195423570985008687907853269984665640564039457584007913129639935;
+
+ struct GrantedPermissionHandles has key {
+ active_handles: vector
+ }
+
+ enum PermissionedHandle {
+ V1 {
+ master_account_addr: address,
+ permissions_storage_addr: address
+ }
+ }
+
+ enum StorablePermissionedHandle has store {
+ V1 {
+ master_account_addr: address,
+ permissions_storage_addr: address,
+ expiration_time: u64
+ }
+ }
+
+ enum PermissionStorage has key {
+ V1 {
+ perms: SimpleMapmove_to
and
+address_of
behave the same, and can be passed wherever signer is needed. However,
+code can internally query for the permissions to assert additional restrictions on
+the use of the signer.
+
+A client which is interested in restricting access granted via a signer can create a permissioned signer
+and pass on to other existing code without changes to existing APIs. Core functions in the framework, for
+example account functions, can then assert availability of permissions, effectively restricting
+existing code in a compatible way.
+
+After introducing the core functionality, examples are provided for withdraw limit on accounts, and
+for blind signing.
+
+
+- [Resource `GrantedPermissionHandles`](#0x1_permissioned_signer_GrantedPermissionHandles)
+- [Enum `PermissionedHandle`](#0x1_permissioned_signer_PermissionedHandle)
+- [Enum `StorablePermissionedHandle`](#0x1_permissioned_signer_StorablePermissionedHandle)
+- [Enum Resource `PermissionStorage`](#0x1_permissioned_signer_PermissionStorage)
+- [Enum `StoredPermission`](#0x1_permissioned_signer_StoredPermission)
+- [Enum `Permission`](#0x1_permissioned_signer_Permission)
+- [Constants](#@Constants_0)
+- [Function `create_permissioned_handle`](#0x1_permissioned_signer_create_permissioned_handle)
+- [Function `create_storable_permissioned_handle`](#0x1_permissioned_signer_create_storable_permissioned_handle)
+- [Function `destroy_permissioned_handle`](#0x1_permissioned_signer_destroy_permissioned_handle)
+- [Function `destroy_storable_permissioned_handle`](#0x1_permissioned_signer_destroy_storable_permissioned_handle)
+- [Function `destroy_permissions_storage_address`](#0x1_permissioned_signer_destroy_permissions_storage_address)
+- [Function `signer_from_permissioned_handle`](#0x1_permissioned_signer_signer_from_permissioned_handle)
+- [Function `signer_from_storable_permissioned_handle`](#0x1_permissioned_signer_signer_from_storable_permissioned_handle)
+- [Function `revoke_permission_storage_address`](#0x1_permissioned_signer_revoke_permission_storage_address)
+- [Function `revoke_all_handles`](#0x1_permissioned_signer_revoke_all_handles)
+- [Function `permissions_storage_address`](#0x1_permissioned_signer_permissions_storage_address)
+- [Function `assert_master_signer`](#0x1_permissioned_signer_assert_master_signer)
+- [Function `is_above`](#0x1_permissioned_signer_is_above)
+- [Function `consume_capacity`](#0x1_permissioned_signer_consume_capacity)
+- [Function `increase_capacity`](#0x1_permissioned_signer_increase_capacity)
+- [Function `merge`](#0x1_permissioned_signer_merge)
+- [Function `map_or`](#0x1_permissioned_signer_map_or)
+- [Function `insert_or`](#0x1_permissioned_signer_insert_or)
+- [Function `authorize`](#0x1_permissioned_signer_authorize)
+- [Function `authorize_unlimited`](#0x1_permissioned_signer_authorize_unlimited)
+- [Function `check_permission_exists`](#0x1_permissioned_signer_check_permission_exists)
+- [Function `check_permission_capacity_above`](#0x1_permissioned_signer_check_permission_capacity_above)
+- [Function `check_permission_consume`](#0x1_permissioned_signer_check_permission_consume)
+- [Function `capacity`](#0x1_permissioned_signer_capacity)
+- [Function `revoke_permission`](#0x1_permissioned_signer_revoke_permission)
+- [Function `extract_permission`](#0x1_permissioned_signer_extract_permission)
+- [Function `extract_all_permission`](#0x1_permissioned_signer_extract_all_permission)
+- [Function `address_of`](#0x1_permissioned_signer_address_of)
+- [Function `consume_permission`](#0x1_permissioned_signer_consume_permission)
+- [Function `store_permission`](#0x1_permissioned_signer_store_permission)
+- [Function `is_permissioned_signer`](#0x1_permissioned_signer_is_permissioned_signer)
+- [Function `permission_address`](#0x1_permissioned_signer_permission_address)
+- [Function `signer_from_permissioned_handle_impl`](#0x1_permissioned_signer_signer_from_permissioned_handle_impl)
+- [Specification](#@Specification_1)
+ - [Function `create_permissioned_handle`](#@Specification_1_create_permissioned_handle)
+ - [Function `create_storable_permissioned_handle`](#@Specification_1_create_storable_permissioned_handle)
+ - [Function `destroy_permissioned_handle`](#@Specification_1_destroy_permissioned_handle)
+ - [Function `destroy_storable_permissioned_handle`](#@Specification_1_destroy_storable_permissioned_handle)
+ - [Function `revoke_permission_storage_address`](#@Specification_1_revoke_permission_storage_address)
+ - [Function `authorize`](#@Specification_1_authorize)
+ - [Function `check_permission_exists`](#@Specification_1_check_permission_exists)
+ - [Function `check_permission_capacity_above`](#@Specification_1_check_permission_capacity_above)
+ - [Function `check_permission_consume`](#@Specification_1_check_permission_consume)
+ - [Function `capacity`](#@Specification_1_capacity)
+ - [Function `consume_permission`](#@Specification_1_consume_permission)
+ - [Function `is_permissioned_signer`](#@Specification_1_is_permissioned_signer)
+ - [Function `permission_address`](#@Specification_1_permission_address)
+ - [Function `signer_from_permissioned_handle_impl`](#@Specification_1_signer_from_permissioned_handle_impl)
+
+
+use 0x1::copyable_any;
+use 0x1::create_signer;
+use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
+use 0x1::simple_map;
+use 0x1::timestamp;
+use 0x1::transaction_context;
+use 0x1::vector;
+
+
+
+
+
+
+## Resource `GrantedPermissionHandles`
+
+
+
+struct GrantedPermissionHandles has key
+
+
+
+
+active_handles: vector<address>
+enum PermissionedHandle
+
+
+
+
+master_account_addr: address
+permissions_storage_addr: address
+enum StorablePermissionedHandle has store
+
+
+
+
+master_account_addr: address
+permissions_storage_addr: address
+expiration_time: u64
+enum PermissionStorage has key
+
+
+
+
+perms: simple_map::SimpleMap<copyable_any::Any, permissioned_signer::StoredPermission>
+enum StoredPermission has copy, drop, store
+
+
+
+
+0: u256
+enum Permission<K>
+
+
+
+
+owner_address: address
+key: K
+perm: permissioned_signer::StoredPermission
+const ECANNOT_AUTHORIZE: u64 = 2;
+
+
+
+
+
+
+signer doesn't have enough capacity to extract permission.
+
+
+const ECANNOT_EXTRACT_PERMISSION: u64 = 4;
+
+
+
+
+
+
+Trying to grant permission using master signer.
+
+
+const ENOT_MASTER_SIGNER: u64 = 1;
+
+
+
+
+
+
+Access permission information from a master signer.
+
+
+const ENOT_PERMISSIONED_SIGNER: u64 = 3;
+
+
+
+
+
+
+destroying permission handle that has already been revoked or not owned by the
+given master signer.
+
+
+const E_NOT_ACTIVE: u64 = 8;
+
+
+
+
+
+
+permission handle has expired.
+
+
+const E_PERMISSION_EXPIRED: u64 = 5;
+
+
+
+
+
+
+storing extracted permission into a different signer.
+
+
+const E_PERMISSION_MISMATCH: u64 = 6;
+
+
+
+
+
+
+permission handle has been revoked by the original signer.
+
+
+const E_PERMISSION_REVOKED: u64 = 7;
+
+
+
+
+
+
+
+
+const U256_MAX: u256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
+
+
+
+
+
+
+## Function `create_permissioned_handle`
+
+Create an ephermeral permission handle based on the master signer.
+
+This handle can be used to derive a signer that can be used in the context of
+the current transaction.
+
+
+public fun create_permissioned_handle(master: &signer): permissioned_signer::PermissionedHandle
+
+
+
+
+public fun create_permissioned_handle(master: &signer): PermissionedHandle {
+ assert_master_signer(master);
+ let permissions_storage_addr = generate_auid_address();
+ let master_account_addr = signer::address_of(master);
+
+ move_to(
+ &create_signer(permissions_storage_addr),
+ PermissionStorage::V1 { perms: simple_map::new() }
+ );
+
+ PermissionedHandle::V1 { master_account_addr, permissions_storage_addr }
+}
+
+
+
+
+expiration_time
is not too far in the future.
+
+
+public(friend) fun create_storable_permissioned_handle(master: &signer, expiration_time: u64): permissioned_signer::StorablePermissionedHandle
+
+
+
+
+public(package) fun create_storable_permissioned_handle(
+ master: &signer, expiration_time: u64
+): StorablePermissionedHandle acquires GrantedPermissionHandles {
+ assert_master_signer(master);
+ let permissions_storage_addr = generate_auid_address();
+ let master_account_addr = signer::address_of(master);
+
+ assert!(
+ timestamp::now_seconds() < expiration_time,
+ error::permission_denied(E_PERMISSION_EXPIRED)
+ );
+
+ if (!exists<GrantedPermissionHandles>(master_account_addr)) {
+ move_to<GrantedPermissionHandles>(
+ master, GrantedPermissionHandles { active_handles: vector::empty() }
+ );
+ };
+
+ vector::push_back(
+ &mut borrow_global_mut<GrantedPermissionHandles>(master_account_addr).active_handles,
+ permissions_storage_addr
+ );
+
+ move_to(
+ &create_signer(permissions_storage_addr),
+ PermissionStorage::V1 { perms: simple_map::new() }
+ );
+
+ StorablePermissionedHandle::V1 {
+ master_account_addr,
+ permissions_storage_addr,
+ expiration_time
+ }
+}
+
+
+
+
+public fun destroy_permissioned_handle(p: permissioned_signer::PermissionedHandle)
+
+
+
+
+public fun destroy_permissioned_handle(p: PermissionedHandle) acquires PermissionStorage {
+ let PermissionedHandle::V1 { master_account_addr: _, permissions_storage_addr } =
+ p;
+ destroy_permissions_storage_address(permissions_storage_addr);
+}
+
+
+
+
+public(friend) fun destroy_storable_permissioned_handle(p: permissioned_signer::StorablePermissionedHandle)
+
+
+
+
+public(package) fun destroy_storable_permissioned_handle(
+ p: StorablePermissionedHandle
+) acquires PermissionStorage, GrantedPermissionHandles {
+ let StorablePermissionedHandle::V1 {
+ master_account_addr,
+ permissions_storage_addr,
+ expiration_time: _
+ } = p;
+
+ assert!(
+ exists<GrantedPermissionHandles>(master_account_addr),
+ error::permission_denied(E_PERMISSION_REVOKED),
+ );
+ let granted_permissions =
+ borrow_global_mut<GrantedPermissionHandles>(master_account_addr);
+ let (found, idx) = vector::index_of(
+ &granted_permissions.active_handles, &permissions_storage_addr
+ );
+
+ // Removing the address from the active handle list if it's still active.
+ if(found) {
+ vector::swap_remove(&mut granted_permissions.active_handles, idx);
+ };
+
+ destroy_permissions_storage_address(permissions_storage_addr);
+}
+
+
+
+
+fun destroy_permissions_storage_address(permissions_storage_addr: address)
+
+
+
+
+inline fun destroy_permissions_storage_address(
+ permissions_storage_addr: address
+) acquires PermissionStorage {
+ if (exists<PermissionStorage>(permissions_storage_addr)) {
+ let PermissionStorage::V1 { perms } =
+ move_from<PermissionStorage>(permissions_storage_addr);
+ simple_map::destroy(
+ perms,
+ |_dk| {},
+ |_dv| {}
+ );
+ }
+}
+
+
+
+
+public fun signer_from_permissioned_handle(p: &permissioned_signer::PermissionedHandle): signer
+
+
+
+
+public fun signer_from_permissioned_handle(p: &PermissionedHandle): signer {
+ signer_from_permissioned_handle_impl(
+ p.master_account_addr, p.permissions_storage_addr
+ )
+}
+
+
+
+
+public(friend) fun signer_from_storable_permissioned_handle(p: &permissioned_signer::StorablePermissionedHandle): signer
+
+
+
+
+public(package) fun signer_from_storable_permissioned_handle(
+ p: &StorablePermissionedHandle
+): signer {
+ assert!(
+ timestamp::now_seconds() < p.expiration_time,
+ error::permission_denied(E_PERMISSION_EXPIRED)
+ );
+ assert!(
+ exists<PermissionStorage>(p.permissions_storage_addr),
+ error::permission_denied(E_PERMISSION_REVOKED)
+ );
+ signer_from_permissioned_handle_impl(
+ p.master_account_addr, p.permissions_storage_addr
+ )
+}
+
+
+
+
+public entry fun revoke_permission_storage_address(s: &signer, permissions_storage_addr: address)
+
+
+
+
+public entry fun revoke_permission_storage_address(
+ s: &signer, permissions_storage_addr: address
+) acquires GrantedPermissionHandles, PermissionStorage {
+ assert!(
+ !is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER)
+ );
+ let master_account_addr = signer::address_of(s);
+
+ assert!(
+ exists<GrantedPermissionHandles>(master_account_addr),
+ error::permission_denied(E_PERMISSION_REVOKED),
+ );
+ let granted_permissions =
+ borrow_global_mut<GrantedPermissionHandles>(master_account_addr);
+ let (found, idx) = vector::index_of(
+ &granted_permissions.active_handles, &permissions_storage_addr
+ );
+
+ // The address has to be in the activated list in the master account address.
+ assert!(found, error::permission_denied(E_NOT_ACTIVE));
+ vector::swap_remove(&mut granted_permissions.active_handles, idx);
+ destroy_permissions_storage_address(permissions_storage_addr);
+}
+
+
+
+
+public entry fun revoke_all_handles(s: &signer)
+
+
+
+
+public entry fun revoke_all_handles(s: &signer) acquires GrantedPermissionHandles, PermissionStorage {
+ assert!(
+ !is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER)
+ );
+ let master_account_addr = signer::address_of(s);
+ if (!exists<GrantedPermissionHandles>(master_account_addr)) { return };
+
+ let granted_permissions =
+ borrow_global_mut<GrantedPermissionHandles>(master_account_addr);
+ let delete_list = vector::trim_reverse(
+ &mut granted_permissions.active_handles, 0
+ );
+ vector::destroy(
+ delete_list,
+ |address| {
+ destroy_permissions_storage_address(address);
+ }
+ )
+}
+
+
+
+
+public(friend) fun permissions_storage_address(p: &permissioned_signer::StorablePermissionedHandle): address
+
+
+
+
+public(package) fun permissions_storage_address(
+ p: &StorablePermissionedHandle
+): address {
+ p.permissions_storage_addr
+}
+
+
+
+
+public fun assert_master_signer(s: &signer)
+
+
+
+
+public fun assert_master_signer(s: &signer) {
+ assert!(
+ !is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER)
+ );
+}
+
+
+
+
+threshold
capacity.
+
+
+fun is_above(perm: &permissioned_signer::StoredPermission, threshold: u256): bool
+
+
+
+
+fun is_above(perm: &StoredPermission, threshold: u256): bool {
+ match (perm) {
+ StoredPermission::Capacity(capacity) => *capacity > threshold,
+ StoredPermission::Unlimited => true,
+ }
+}
+
+
+
+
+threshold
capacity from StoredPermission
+
+
+fun consume_capacity(perm: &mut permissioned_signer::StoredPermission, threshold: u256): bool
+
+
+
+
+fun consume_capacity(perm: &mut StoredPermission, threshold: u256): bool {
+ match (perm) {
+ StoredPermission::Capacity(current_capacity) => {
+ if (*current_capacity >= threshold) {
+ *current_capacity = *current_capacity - threshold;
+ true
+ } else { false }
+ }
+ StoredPermission::Unlimited => true
+ }
+}
+
+
+
+
+threshold
capacity from StoredPermission
+
+
+fun increase_capacity(perm: &mut permissioned_signer::StoredPermission, threshold: u256)
+
+
+
+
+fun increase_capacity(perm: &mut StoredPermission, threshold: u256) {
+ match (perm) {
+ StoredPermission::Capacity(current_capacity) => {
+ *current_capacity = *current_capacity + threshold;
+ }
+ StoredPermission::Unlimited => (),
+ }
+}
+
+
+
+
+fun merge(lhs: &mut permissioned_signer::StoredPermission, rhs: permissioned_signer::StoredPermission)
+
+
+
+
+fun merge(lhs: &mut StoredPermission, rhs: StoredPermission) {
+ match (rhs) {
+ StoredPermission::Capacity(new_capacity) => {
+ match (lhs) {
+ StoredPermission::Capacity(current_capacity) => {
+ *current_capacity = *current_capacity + new_capacity;
+ }
+ StoredPermission::Unlimited => (),
+ }
+ }
+ StoredPermission::Unlimited => *lhs = StoredPermission::Unlimited,
+ }
+}
+
+
+
+
+permissioned
with the given permission. This requires to have access to the master
+signer.
+
+
+fun map_or<PermKey: copy, drop, store, T>(permissioned: &signer, perm: PermKey, mutate: |&mut permissioned_signer::StoredPermission|T, default: T): T
+
+
+
+
+inline fun map_or<PermKey: copy + drop + store, T>(
+ permissioned: &signer,
+ perm: PermKey,
+ mutate: |&mut StoredPermission| T,
+ default: T,
+): T {
+ let permission_signer_addr = permission_address(permissioned);
+ assert!(
+ exists<PermissionStorage>(permission_signer_addr),
+ error::permission_denied(E_NOT_ACTIVE)
+ );
+ let perms =
+ &mut borrow_global_mut<PermissionStorage>(permission_signer_addr).perms;
+ let key = copyable_any::pack(perm);
+ if (simple_map::contains_key(perms, &key)) {
+ mutate(simple_map::borrow_mut(perms, &key))
+ } else {
+ default
+ }
+}
+
+
+
+
+fun insert_or<PermKey: copy, drop, store>(permissioned: &signer, perm: PermKey, mutate: |&mut permissioned_signer::StoredPermission|, default: permissioned_signer::StoredPermission)
+
+
+
+
+inline fun insert_or<PermKey: copy + drop + store>(
+ permissioned: &signer,
+ perm: PermKey,
+ mutate: |&mut StoredPermission|,
+ default: StoredPermission,
+) {
+ let permission_signer_addr = permission_address(permissioned);
+ assert!(
+ exists<PermissionStorage>(permission_signer_addr),
+ error::permission_denied(E_NOT_ACTIVE)
+ );
+ let perms =
+ &mut borrow_global_mut<PermissionStorage>(permission_signer_addr).perms;
+ let key = copyable_any::pack(perm);
+ if (simple_map::contains_key(perms, &key)) {
+ mutate(simple_map::borrow_mut(perms, &key));
+ } else {
+ simple_map::add(perms, key, default);
+ }
+}
+
+
+
+
+public fun authorize<PermKey: copy, drop, store>(master: &signer, permissioned: &signer, capacity: u256, perm: PermKey)
+
+
+
+
+public fun authorize<PermKey: copy + drop + store>(
+ master: &signer,
+ permissioned: &signer,
+ capacity: u256,
+ perm: PermKey
+) acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(permissioned)
+ && !is_permissioned_signer(master)
+ && signer::address_of(master) == signer::address_of(permissioned),
+ error::permission_denied(ECANNOT_AUTHORIZE)
+ );
+ insert_or(
+ permissioned,
+ perm,
+ |stored_permission| {
+ increase_capacity(stored_permission, capacity);
+ },
+ StoredPermission::Capacity(capacity),
+ )
+}
+
+
+
+
+permissioned
with the given unlimited permission.
+Unlimited permission can be consumed however many times.
+
+
+public fun authorize_unlimited<PermKey: copy, drop, store>(master: &signer, permissioned: &signer, perm: PermKey)
+
+
+
+
+public fun authorize_unlimited<PermKey: copy + drop + store>(
+ master: &signer,
+ permissioned: &signer,
+ perm: PermKey
+) acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(permissioned)
+ && !is_permissioned_signer(master)
+ && signer::address_of(master) == signer::address_of(permissioned),
+ error::permission_denied(ECANNOT_AUTHORIZE)
+ );
+ insert_or(
+ permissioned,
+ perm,
+ |stored_permission| {
+ *stored_permission = StoredPermission::Unlimited;
+ },
+ StoredPermission::Unlimited,
+ )
+}
+
+
+
+
+public fun check_permission_exists<PermKey: copy, drop, store>(s: &signer, perm: PermKey): bool
+
+
+
+
+public fun check_permission_exists<PermKey: copy + drop + store>(
+ s: &signer, perm: PermKey
+): bool acquires PermissionStorage {
+ check_permission_capacity_above(s, 0, perm)
+}
+
+
+
+
+public fun check_permission_capacity_above<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+public fun check_permission_capacity_above<PermKey: copy + drop + store>(
+ s: &signer, threshold: u256, perm: PermKey
+): bool acquires PermissionStorage {
+ if (!is_permissioned_signer(s)) {
+ // master signer has all permissions
+ return true
+ };
+ map_or(
+ s,
+ perm,
+ |stored_permission| {
+ is_above(stored_permission, threshold)
+ },
+ false,
+ )
+}
+
+
+
+
+public fun check_permission_consume<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+public fun check_permission_consume<PermKey: copy + drop + store>(
+ s: &signer, threshold: u256, perm: PermKey
+): bool acquires PermissionStorage {
+ if (!is_permissioned_signer(s)) {
+ // master signer has all permissions
+ return true
+ };
+ map_or(
+ s,
+ perm,
+ |stored_permission| {
+ consume_capacity(stored_permission, threshold)
+ },
+ false,
+ )
+}
+
+
+
+
+public fun capacity<PermKey: copy, drop, store>(s: &signer, perm: PermKey): option::Option<u256>
+
+
+
+
+public fun capacity<PermKey: copy + drop + store>(
+ s: &signer, perm: PermKey
+): Option<u256> acquires PermissionStorage {
+ if (!is_permissioned_signer(s)) {
+ return option::some(U256_MAX)
+ };
+ map_or(
+ s,
+ perm,
+ |stored_permission: &mut StoredPermission| {
+ option::some(match (stored_permission) {
+ StoredPermission::Capacity(capacity) => *capacity,
+ StoredPermission::Unlimited => U256_MAX,
+ })
+ },
+ option::none(),
+ )
+}
+
+
+
+
+public fun revoke_permission<PermKey: copy, drop, store>(permissioned: &signer, perm: PermKey)
+
+
+
+
+public fun revoke_permission<PermKey: copy + drop + store>(
+ permissioned: &signer, perm: PermKey
+) acquires PermissionStorage {
+ if (!is_permissioned_signer(permissioned)) {
+ // Master signer has no permissions associated with it.
+ return
+ };
+ let addr = permission_address(permissioned);
+ if (!exists<PermissionStorage>(addr)) { return };
+ let perm_storage = &mut borrow_global_mut<PermissionStorage>(addr).perms;
+ let key = copyable_any::pack(perm);
+ if (simple_map::contains_key(perm_storage, &key)) {
+ simple_map::remove(
+ &mut borrow_global_mut<PermissionStorage>(addr).perms,
+ ©able_any::pack(perm)
+ );
+ }
+}
+
+
+
+
+public(friend) fun extract_permission<PermKey: copy, drop, store>(s: &signer, weight: u256, perm: PermKey): permissioned_signer::Permission<PermKey>
+
+
+
+
+public(package) fun extract_permission<PermKey: copy + drop + store>(
+ s: &signer, weight: u256, perm: PermKey
+): Permission<PermKey> acquires PermissionStorage {
+ assert!(
+ check_permission_consume(s, weight, perm),
+ error::permission_denied(ECANNOT_EXTRACT_PERMISSION)
+ );
+ Permission::V1 {
+ owner_address: signer::address_of(s),
+ key: perm,
+ perm: StoredPermission::Capacity(weight),
+ }
+}
+
+
+
+
+public(friend) fun extract_all_permission<PermKey: copy, drop, store>(s: &signer, perm_key: PermKey): permissioned_signer::Permission<PermKey>
+
+
+
+
+public(package) fun extract_all_permission<PermKey: copy + drop + store>(
+ s: &signer, perm_key: PermKey
+): Permission<PermKey> acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(s),
+ error::permission_denied(ECANNOT_EXTRACT_PERMISSION)
+ );
+ let addr = permission_address(s);
+ assert!(
+ exists<PermissionStorage>(addr),
+ error::permission_denied(ECANNOT_EXTRACT_PERMISSION)
+ );
+ let key = copyable_any::pack(perm_key);
+ let storage = &mut borrow_global_mut<PermissionStorage>(addr).perms;
+ let (_, value) = simple_map::remove(storage, &key);
+
+ Permission::V1 {
+ owner_address: signer::address_of(s),
+ key: perm_key,
+ perm: value,
+ }
+}
+
+
+
+
+public(friend) fun address_of<PermKey>(perm: &permissioned_signer::Permission<PermKey>): address
+
+
+
+
+public(package) fun address_of<PermKey>(perm: &Permission<PermKey>): address {
+ perm.owner_address
+}
+
+
+
+
+public(friend) fun consume_permission<PermKey: copy, drop, store>(perm: &mut permissioned_signer::Permission<PermKey>, weight: u256, perm_key: PermKey): bool
+
+
+
+
+public(package) fun consume_permission<PermKey: copy + drop + store>(
+ perm: &mut Permission<PermKey>, weight: u256, perm_key: PermKey
+): bool {
+ if (perm.key != perm_key) {
+ return false
+ };
+ consume_capacity(&mut perm.perm, weight)
+}
+
+
+
+
+public(friend) fun store_permission<PermKey: copy, drop, store>(s: &signer, perm: permissioned_signer::Permission<PermKey>)
+
+
+
+
+public(package) fun store_permission<PermKey: copy + drop + store>(
+ s: &signer, perm: Permission<PermKey>
+) acquires PermissionStorage {
+ assert!(
+ is_permissioned_signer(s),
+ error::permission_denied(ENOT_PERMISSIONED_SIGNER)
+ );
+ let Permission::V1 { key, perm, owner_address } = perm;
+
+ assert!(
+ signer::address_of(s) == owner_address,
+ error::permission_denied(E_PERMISSION_MISMATCH)
+ );
+
+ insert_or(
+ s,
+ key,
+ |stored_permission| {
+ merge(stored_permission, perm);
+ },
+ perm,
+ )
+}
+
+
+
+
+public fun is_permissioned_signer(s: &signer): bool
+
+
+
+
+public native fun is_permissioned_signer(s: &signer): bool;
+
+
+
+
+fun permission_address(permissioned: &signer): address
+
+
+
+
+native fun permission_address(permissioned: &signer): address;
+
+
+
+
+fun signer_from_permissioned_handle_impl(master_account_addr: address, permissions_storage_addr: address): signer
+
+
+
+
+native fun signer_from_permissioned_handle_impl(
+ master_account_addr: address, permissions_storage_addr: address
+): signer;
+
+
+
+
+pragma verify = false;
+axiom forall a: GrantedPermissionHandles:
+ (
+ forall i in 0..len(a.active_handles):
+ forall j in 0..len(a.active_handles):
+ i != j ==>
+ a.active_handles[i] != a.active_handles[j]
+ );
+
+
+
+
+
+
+
+
+fun spec_is_permissioned_signer(s: signer): bool;
+
+
+
+
+
+
+### Function `create_permissioned_handle`
+
+
+public fun create_permissioned_handle(master: &signer): permissioned_signer::PermissionedHandle
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract] spec_is_permissioned_signer(master);
+let permissions_storage_addr = transaction_context::spec_generate_unique_address();
+modifies global<PermissionStorage>(permissions_storage_addr);
+let master_account_addr = signer::address_of(master);
+ensures result.master_account_addr == master_account_addr;
+ensures result.permissions_storage_addr == permissions_storage_addr;
+
+
+
+
+
+
+### Function `create_storable_permissioned_handle`
+
+
+public(friend) fun create_storable_permissioned_handle(master: &signer, expiration_time: u64): permissioned_signer::StorablePermissionedHandle
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract] spec_is_permissioned_signer(master);
+let permissions_storage_addr = transaction_context::spec_generate_unique_address();
+modifies global<PermissionStorage>(permissions_storage_addr);
+let master_account_addr = signer::address_of(master);
+modifies global<GrantedPermissionHandles>(master_account_addr);
+ensures result.master_account_addr == master_account_addr;
+ensures result.permissions_storage_addr == permissions_storage_addr;
+ensures result.expiration_time == expiration_time;
+ensures vector::spec_contains(
+ global<GrantedPermissionHandles>(master_account_addr).active_handles,
+ permissions_storage_addr
+);
+ensures exists<GrantedPermissionHandles>(master_account_addr);
+
+
+
+
+
+
+### Function `destroy_permissioned_handle`
+
+
+public fun destroy_permissioned_handle(p: permissioned_signer::PermissionedHandle)
+
+
+
+
+
+ensures !exists<PermissionStorage>(p.permissions_storage_addr);
+
+
+
+
+
+
+### Function `destroy_storable_permissioned_handle`
+
+
+public(friend) fun destroy_storable_permissioned_handle(p: permissioned_signer::StorablePermissionedHandle)
+
+
+
+
+
+ensures !exists<PermissionStorage>(p.permissions_storage_addr);
+let post granted_permissions = global<GrantedPermissionHandles>(
+ p.master_account_addr
+);
+
+
+
+
+
+
+### Function `revoke_permission_storage_address`
+
+
+public entry fun revoke_permission_storage_address(s: &signer, permissions_storage_addr: address)
+
+
+
+
+
+
+
+### Function `authorize`
+
+
+public fun authorize<PermKey: copy, drop, store>(master: &signer, permissioned: &signer, capacity: u256, perm: PermKey)
+
+
+
+
+
+pragma aborts_if_is_partial;
+aborts_if !spec_is_permissioned_signer(permissioned);
+aborts_if spec_is_permissioned_signer(master);
+aborts_if signer::address_of(permissioned) != signer::address_of(master);
+ensures exists<PermissionStorage>(
+ spec_permission_address(permissioned)
+);
+
+
+
+
+
+
+### Function `check_permission_exists`
+
+
+public fun check_permission_exists<PermKey: copy, drop, store>(s: &signer, perm: PermKey): bool
+
+
+
+
+
+pragma verify = false;
+
+
+
+
+
+
+
+
+fun spec_check_permission_exists<PermKey: copy + drop + store>(s: signer, perm: PermKey): bool {
+ use aptos_std::type_info;
+ use std::bcs;
+ let addr = spec_permission_address(s);
+ let key = Any {
+ type_name: type_info::type_name<PermKey>(),
+ data: bcs::serialize(perm)
+ };
+ if (!spec_is_permissioned_signer(s)) { true }
+ else if (!exists<PermissionStorage>(addr)) { false }
+ else {
+ simple_map::spec_contains_key(global<PermissionStorage>(addr).perms, key)
+ }
+}
+
+
+
+
+
+
+### Function `check_permission_capacity_above`
+
+
+public fun check_permission_capacity_above<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+
+let permissioned_signer_addr = spec_permission_address(s);
+ensures !spec_is_permissioned_signer(s) ==> result == true;
+ensures (
+ spec_is_permissioned_signer(s)
+ && !exists<PermissionStorage>(permissioned_signer_addr)
+) ==> result == false;
+let key = Any {
+ type_name: type_info::type_name<SimpleMap<Any, u256>>(),
+ data: bcs::serialize(perm)
+};
+
+
+
+
+
+
+### Function `check_permission_consume`
+
+
+public fun check_permission_consume<PermKey: copy, drop, store>(s: &signer, threshold: u256, perm: PermKey): bool
+
+
+
+
+
+let permissioned_signer_addr = spec_permission_address(s);
+ensures !spec_is_permissioned_signer(s) ==> result == true;
+ensures (
+ spec_is_permissioned_signer(s)
+ && !exists<PermissionStorage>(permissioned_signer_addr)
+) ==> result == false;
+
+
+
+
+
+
+### Function `capacity`
+
+
+public fun capacity<PermKey: copy, drop, store>(s: &signer, perm: PermKey): option::Option<u256>
+
+
+
+
+
+
+
+### Function `consume_permission`
+
+
+public(friend) fun consume_permission<PermKey: copy, drop, store>(perm: &mut permissioned_signer::Permission<PermKey>, weight: u256, perm_key: PermKey): bool
+
+
+
+
+
+
+
+### Function `is_permissioned_signer`
+
+
+public fun is_permissioned_signer(s: &signer): bool
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract] false;
+ensures [abstract] result == spec_is_permissioned_signer(s);
+
+
+
+
+
+
+
+
+fun spec_permission_address(s: signer): address;
+
+
+
+
+
+
+### Function `permission_address`
+
+
+fun permission_address(permissioned: &signer): address
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract]!spec_is_permissioned_signer(permissioned);
+ensures [abstract] result == spec_permission_address(permissioned);
+
+
+
+
+
+
+
+
+fun spec_signer_from_permissioned_handle_impl(
+ master_account_addr: address, permissions_storage_addr: address
+): signer;
+
+
+
+
+
+
+### Function `signer_from_permissioned_handle_impl`
+
+
+fun signer_from_permissioned_handle_impl(master_account_addr: address, permissions_storage_addr: address): signer
+
+
+
+
+
+pragma opaque;
+ensures [abstract] result
+ == spec_signer_from_permissioned_handle_impl(
+ master_account_addr, permissions_storage_addr
+ );
+
+
+
+[move-book]: https://aptos.dev/move/book/SUMMARY
diff --git a/aptos-move/framework/aptos-framework/tests/permissioned_signer_tests.move b/aptos-move/framework/aptos-framework/tests/permissioned_signer_tests.move
new file mode 100644
index 0000000000000..ed0fe04e1e837
--- /dev/null
+++ b/aptos-move/framework/aptos-framework/tests/permissioned_signer_tests.move
@@ -0,0 +1,322 @@
+#[test_only]
+module aptos_framework::permissioned_signer_tests {
+ use aptos_framework::account::create_signer_for_test;
+ use aptos_framework::permissioned_signer;
+ use aptos_framework::timestamp;
+ use std::option;
+ use std::signer;
+
+ struct OnePermission has copy, drop, store {}
+
+ struct AddressPermission has copy, drop, store {
+ addr: address
+ }
+
+ #[test(creator = @0xcafe)]
+ fun test_permission_e2e(creator: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle = permissioned_signer::create_permissioned_handle(creator);
+ let perm_signer = permissioned_signer::signer_from_permissioned_handle(&perm_handle);
+
+ assert!(permissioned_signer::is_permissioned_signer(&perm_signer), 1);
+ assert!(!permissioned_signer::is_permissioned_signer(creator), 1);
+ assert!(signer::address_of(&perm_signer) == signer::address_of(creator), 1);
+
+ permissioned_signer::authorize(creator, &perm_signer, 100, OnePermission {});
+ assert!(
+ permissioned_signer::capacity(&perm_signer, OnePermission {})
+ == option::some(100),
+ 1
+ );
+
+ assert!(
+ permissioned_signer::check_permission_consume(
+ &perm_signer, 10, OnePermission {}
+ ),
+ 1
+ );
+ assert!(
+ permissioned_signer::capacity(&perm_signer, OnePermission {})
+ == option::some(90),
+ 1
+ );
+
+ permissioned_signer::authorize(
+ creator,
+ &perm_signer,
+ 5,
+ AddressPermission { addr: @0x1 }
+ );
+
+ assert!(
+ permissioned_signer::capacity(&perm_signer, AddressPermission { addr: @0x1 })
+ == option::some(5),
+ 1
+ );
+ assert!(
+ permissioned_signer::capacity(&perm_signer, AddressPermission { addr: @0x2 })
+ == option::none(),
+ 1
+ );
+
+ // Not enough capacity, check permission should return false
+ assert!(
+ !permissioned_signer::check_permission_consume(
+ &perm_signer, 10, AddressPermission { addr: @0x1 }
+ ),
+ 1
+ );
+
+ assert!(
+ permissioned_signer::check_permission_consume(
+ &perm_signer, 5, AddressPermission { addr: @0x1 }
+ ),
+ 1
+ );
+
+ // Remaining capacity is 0, should be viewed as non-exist.
+ assert!(
+ !permissioned_signer::check_permission_exists(
+ &perm_signer, AddressPermission { addr: @0x1 }
+ ),
+ 1
+ );
+
+ permissioned_signer::revoke_permission(&perm_signer, OnePermission {});
+ assert!(
+ permissioned_signer::capacity(&perm_signer, OnePermission {})
+ == option::none(),
+ 1
+ );
+
+ permissioned_signer::destroy_permissioned_handle(perm_handle);
+ }
+
+ #[test(creator = @0xcafe)]
+ fun test_storable_permission_e2e(creator: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle =
+ permissioned_signer::create_storable_permissioned_handle(creator, 60);
+ let perm_signer =
+ permissioned_signer::signer_from_storable_permissioned_handle(&perm_handle);
+
+ assert!(permissioned_signer::is_permissioned_signer(&perm_signer), 1);
+ assert!(!permissioned_signer::is_permissioned_signer(creator), 1);
+ assert!(signer::address_of(&perm_signer) == signer::address_of(creator), 1);
+
+ permissioned_signer::authorize(creator, &perm_signer, 100, OnePermission {});
+ assert!(
+ permissioned_signer::capacity(&perm_signer, OnePermission {})
+ == option::some(100),
+ 1
+ );
+
+ assert!(
+ permissioned_signer::check_permission_consume(
+ &perm_signer, 10, OnePermission {}
+ ),
+ 1
+ );
+ assert!(
+ permissioned_signer::capacity(&perm_signer, OnePermission {})
+ == option::some(90),
+ 1
+ );
+
+ permissioned_signer::authorize(
+ creator,
+ &perm_signer,
+ 5,
+ AddressPermission { addr: @0x1 }
+ );
+
+ assert!(
+ permissioned_signer::capacity(&perm_signer, AddressPermission { addr: @0x1 })
+ == option::some(5),
+ 1
+ );
+ assert!(
+ permissioned_signer::capacity(&perm_signer, AddressPermission { addr: @0x2 })
+ == option::none(),
+ 1
+ );
+
+ // Not enough capacity, check permission should return false
+ assert!(
+ !permissioned_signer::check_permission_consume(
+ &perm_signer, 10, AddressPermission { addr: @0x1 }
+ ),
+ 1
+ );
+
+ permissioned_signer::revoke_permission(&perm_signer, OnePermission {});
+ assert!(
+ permissioned_signer::capacity(&perm_signer, OnePermission {})
+ == option::none(),
+ 1
+ );
+
+ permissioned_signer::destroy_storable_permissioned_handle(perm_handle);
+ }
+
+ #[test(creator = @0xcafe)]
+ #[expected_failure(
+ abort_code = 0x50005, location = aptos_framework::permissioned_signer
+ )]
+ fun test_permission_expiration(creator: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle =
+ permissioned_signer::create_storable_permissioned_handle(creator, 60);
+ let _perm_signer =
+ permissioned_signer::signer_from_storable_permissioned_handle(&perm_handle);
+
+ timestamp::fast_forward_seconds(60);
+ let _perm_signer =
+ permissioned_signer::signer_from_storable_permissioned_handle(&perm_handle);
+
+ permissioned_signer::destroy_storable_permissioned_handle(perm_handle);
+ }
+
+ // invalid authorization
+ // 1. master signer is a permissioned signer
+ // 2. permissioned signer is a master signer
+ // 3. permissioned and main signer address mismatch
+ #[test(creator = @0xcafe)]
+ #[expected_failure(
+ abort_code = 0x50002, location = aptos_framework::permissioned_signer
+ )]
+ fun test_auth_1(creator: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle = permissioned_signer::create_permissioned_handle(creator);
+ let perm_signer = permissioned_signer::signer_from_permissioned_handle(&perm_handle);
+
+ permissioned_signer::authorize(
+ &perm_signer,
+ &perm_signer,
+ 100,
+ OnePermission {}
+ );
+ permissioned_signer::destroy_permissioned_handle(perm_handle);
+ }
+
+ #[test(creator = @0xcafe)]
+ #[expected_failure(
+ abort_code = 0x50002, location = aptos_framework::permissioned_signer
+ )]
+ fun test_auth_2(creator: &signer) {
+ permissioned_signer::authorize(creator, creator, 100, OnePermission {});
+ }
+
+ #[test(creator = @0xcafe, creator2 = @0xbeef)]
+ #[expected_failure(
+ abort_code = 0x50002, location = aptos_framework::permissioned_signer
+ )]
+ fun test_auth_3(creator: &signer, creator2: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle = permissioned_signer::create_permissioned_handle(creator);
+ let perm_signer = permissioned_signer::signer_from_permissioned_handle(&perm_handle);
+
+ permissioned_signer::authorize(creator2, &perm_signer, 100, OnePermission {});
+ permissioned_signer::destroy_permissioned_handle(perm_handle);
+ }
+
+ // Accessing capacity on a master signer
+ #[test(creator = @0xcafe)]
+ fun test_invalid_capacity(creator: &signer) {
+ assert!(
+ permissioned_signer::capacity(creator, OnePermission {})
+ == option::some(
+ 115792089237316195423570985008687907853269984665640564039457584007913129639935
+ ),
+ 1
+ );
+ }
+
+ // creating permission using a permissioned signer
+ #[test(creator = @0xcafe)]
+ #[expected_failure(
+ abort_code = 0x50001, location = aptos_framework::permissioned_signer
+ )]
+ fun test_invalid_creation(creator: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle = permissioned_signer::create_permissioned_handle(creator);
+ let perm_signer = permissioned_signer::signer_from_permissioned_handle(&perm_handle);
+
+ let perm_handle_2 = permissioned_signer::create_permissioned_handle(&perm_signer);
+ permissioned_signer::destroy_permissioned_handle(perm_handle);
+ permissioned_signer::destroy_permissioned_handle(perm_handle_2);
+ }
+
+ #[test(creator = @0xcafe)]
+ fun test_permission_revokation_success(creator: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle =
+ permissioned_signer::create_storable_permissioned_handle(creator, 60);
+ let _perm_signer =
+ permissioned_signer::signer_from_storable_permissioned_handle(&perm_handle);
+
+ permissioned_signer::revoke_permission_storage_address(
+ creator, permissioned_signer::permissions_storage_address(&perm_handle)
+ );
+
+ permissioned_signer::destroy_storable_permissioned_handle(perm_handle);
+ }
+
+ #[test(creator = @0xcafe)]
+ #[expected_failure(
+ abort_code = 0x50007, location = aptos_framework::permissioned_signer
+ )]
+ fun test_permission_revokation_and_access(creator: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle =
+ permissioned_signer::create_storable_permissioned_handle(creator, 60);
+ let _perm_signer =
+ permissioned_signer::signer_from_storable_permissioned_handle(&perm_handle);
+
+ permissioned_signer::revoke_permission_storage_address(
+ creator, permissioned_signer::permissions_storage_address(&perm_handle)
+ );
+ let _perm_signer =
+ permissioned_signer::signer_from_storable_permissioned_handle(&perm_handle);
+
+ permissioned_signer::destroy_storable_permissioned_handle(perm_handle);
+ }
+
+ #[test(creator1 = @0xcafe, creator2 = @0xbafe)]
+ #[expected_failure(
+ abort_code = 0x50008, location = aptos_framework::permissioned_signer
+ )]
+ fun test_permission_revoke_other(creator1: &signer, creator2: &signer) {
+ let aptos_framework = create_signer_for_test(@0x1);
+ timestamp::set_time_has_started_for_testing(&aptos_framework);
+
+ let perm_handle_1 =
+ permissioned_signer::create_storable_permissioned_handle(creator1, 60);
+
+ let perm_handle_2 =
+ permissioned_signer::create_storable_permissioned_handle(creator2, 60);
+
+ permissioned_signer::revoke_permission_storage_address(
+ creator1, permissioned_signer::permissions_storage_address(&perm_handle_2)
+ );
+
+ permissioned_signer::destroy_storable_permissioned_handle(perm_handle_1);
+ permissioned_signer::destroy_storable_permissioned_handle(perm_handle_2);
+ }
+}