diff --git a/aptos-move/framework/aptos-framework/doc/overview.md b/aptos-move/framework/aptos-framework/doc/overview.md
index 314baa3612ba96..7ad32f3ea8ec0d 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 00000000000000..d8196b081cf2eb
--- /dev/null
+++ b/aptos-move/framework/aptos-framework/doc/permissioned_signer.md
@@ -0,0 +1,1491 @@
+
+
+
+# Module `0x1::permissioned_signer`
+
+A _permissioned signer_ consists of a pair of the original signer and a generated
+signer which is used store information about associated permissions.
+
+A permissioned signer behaves compatible with the original signer as it comes to move_to
, address_of
, and
+existing basic signer functionality. However, the permissions can be queried 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)
+- [Struct `PermissionedHandle`](#0x1_permissioned_signer_PermissionedHandle)
+- [Struct `StorablePermissionedHandle`](#0x1_permissioned_signer_StorablePermissionedHandle)
+- [Resource `PermStorage`](#0x1_permissioned_signer_PermStorage)
+- [Struct `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_permission_address`](#0x1_permissioned_signer_destroy_permission_address)
+- [Function `remove_permission_address`](#0x1_permissioned_signer_remove_permission_address)
+- [Function `signer_from_permissioned`](#0x1_permissioned_signer_signer_from_permissioned)
+- [Function `signer_from_storable_permissioned`](#0x1_permissioned_signer_signer_from_storable_permissioned)
+- [Function `revoke_permission_handle`](#0x1_permissioned_signer_revoke_permission_handle)
+- [Function `revoke_all_handles`](#0x1_permissioned_signer_revoke_all_handles)
+- [Function `permission_address`](#0x1_permissioned_signer_permission_address)
+- [Function `assert_master_signer`](#0x1_permissioned_signer_assert_master_signer)
+- [Function `authorize`](#0x1_permissioned_signer_authorize)
+- [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 `get_key`](#0x1_permissioned_signer_get_key)
+- [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_signer`](#0x1_permissioned_signer_permission_signer)
+- [Function `signer_from_permissioned_impl`](#0x1_permissioned_signer_signer_from_permissioned_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_handle`](#@Specification_1_revoke_permission_handle)
+ - [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_signer`](#@Specification_1_permission_signer)
+ - [Function `signer_from_permissioned_impl`](#@Specification_1_signer_from_permissioned_impl)
+
+
+
use 0x1::copyable_any;
+use 0x1::create_signer;
+use 0x1::error;
+use 0x1::option;
+use 0x1::signer;
+use 0x1::smart_table;
+use 0x1::timestamp;
+use 0x1::transaction_context;
+use 0x1::vector;
+
+
+
+
+
+
+## Resource `GrantedPermissionHandles`
+
+
+
+struct GrantedPermissionHandles has key
+
+
+
+
+active_handles: vector<address>
+struct PermissionedHandle
+
+
+
+
+master_addr: address
+permission_addr: address
+struct StorablePermissionedHandle has store
+
+
+
+
+master_addr: address
+permission_addr: address
+expiration_time: u64
+struct PermStorage has key
+
+
+
+
+perms: smart_table::SmartTable<copyable_any::Any, u256>
+struct Permission<K>
+
+
+
+
+owner_address: address
+key: K
+capacity: u256
+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;
+
+
+
+
+
+
+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;
+
+
+
+
+
+
+## Function `create_permissioned_handle`
+
+
+
+public fun create_permissioned_handle(master: &signer): permissioned_signer::PermissionedHandle
+
+
+
+
+public fun create_permissioned_handle(master: &signer): PermissionedHandle {
+ assert_master_signer(master);
+ let permission_addr = generate_auid_address();
+ let master_addr = signer::address_of(master);
+
+ move_to(&create_signer(permission_addr), PermStorage { perms: smart_table::new()});
+
+ PermissionedHandle {
+ master_addr,
+ permission_addr,
+ }
+}
+
+
+
+
+public(friend) fun create_storable_permissioned_handle(master: &signer, expiration_time: u64): permissioned_signer::StorablePermissionedHandle
+
+
+
+
+public(friend) fun create_storable_permissioned_handle(master: &signer, expiration_time: u64): StorablePermissionedHandle acquires GrantedPermissionHandles {
+ assert_master_signer(master);
+ let permission_addr = generate_auid_address();
+ let master_addr = signer::address_of(master);
+
+ if(!exists<GrantedPermissionHandles>(master_addr)) {
+ move_to<GrantedPermissionHandles>(master, GrantedPermissionHandles {
+ active_handles: vector::empty(),
+ });
+ };
+
+ vector::push_back(
+ &mut borrow_global_mut<GrantedPermissionHandles>(master_addr).active_handles,
+ permission_addr
+ );
+
+ move_to(&create_signer(permission_addr), PermStorage { perms: smart_table::new()});
+
+ // Do we need to move sth similar to ObjectCore to register this address as permission address?
+ StorablePermissionedHandle {
+ master_addr,
+ permission_addr,
+ expiration_time,
+ }
+}
+
+
+
+
+public fun destroy_permissioned_handle(p: permissioned_signer::PermissionedHandle)
+
+
+
+
+public fun destroy_permissioned_handle(p: PermissionedHandle) acquires PermStorage {
+ let PermissionedHandle { master_addr: _, permission_addr } = p;
+ destroy_permission_address(permission_addr);
+}
+
+
+
+
+public fun destroy_storable_permissioned_handle(p: permissioned_signer::StorablePermissionedHandle)
+
+
+
+
+public fun destroy_storable_permissioned_handle(p: StorablePermissionedHandle) acquires PermStorage, GrantedPermissionHandles {
+ let StorablePermissionedHandle { master_addr, permission_addr, expiration_time: _ } = p;
+ destroy_permission_address(permission_addr);
+ remove_permission_address(master_addr, permission_addr);
+}
+
+
+
+
+fun destroy_permission_address(permission_addr: address)
+
+
+
+
+inline fun destroy_permission_address(permission_addr: address) acquires PermStorage {
+ if(exists<PermStorage>(permission_addr)) {
+ let PermStorage { perms } = move_from<PermStorage>(permission_addr);
+ smart_table::destroy(perms);
+ }
+}
+
+
+
+
+fun remove_permission_address(master_addr: address, permission_addr: address)
+
+
+
+
+inline fun remove_permission_address(master_addr: address, permission_addr: address) acquires GrantedPermissionHandles {
+ if(exists<GrantedPermissionHandles>(master_addr)) {
+ let granted_permissions = borrow_global_mut<GrantedPermissionHandles>(master_addr);
+ let (found, idx) = vector::index_of(&granted_permissions.active_handles, &permission_addr);
+ if(found) {
+ vector::swap_remove(&mut granted_permissions.active_handles, idx);
+ };
+ };
+}
+
+
+
+
+public fun signer_from_permissioned(p: &permissioned_signer::PermissionedHandle): signer
+
+
+
+
+public fun signer_from_permissioned(p: &PermissionedHandle): signer {
+ signer_from_permissioned_impl(p.master_addr, p.permission_addr)
+}
+
+
+
+
+public fun signer_from_storable_permissioned(p: &permissioned_signer::StorablePermissionedHandle): signer
+
+
+
+
+public fun signer_from_storable_permissioned(p: &StorablePermissionedHandle): signer {
+ assert!(
+ timestamp::now_seconds() < p.expiration_time,
+ error::permission_denied(E_PERMISSION_EXPIRED)
+ );
+ assert!(
+ exists<PermStorage>(p.permission_addr),
+ error::permission_denied(E_PERMISSION_REVOKED)
+ );
+ signer_from_permissioned_impl(p.master_addr, p.permission_addr)
+}
+
+
+
+
+public fun revoke_permission_handle(s: &signer, permission_addr: address)
+
+
+
+
+public fun revoke_permission_handle(s: &signer, permission_addr: address) acquires GrantedPermissionHandles, PermStorage {
+ assert!(!is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER));
+ let master_addr = signer::address_of(s);
+ destroy_permission_address(permission_addr);
+ remove_permission_address(master_addr, permission_addr);
+}
+
+
+
+
+public fun revoke_all_handles(s: &signer)
+
+
+
+
+public fun revoke_all_handles(s: &signer) acquires GrantedPermissionHandles, PermStorage {
+ assert!(!is_permissioned_signer(s), error::permission_denied(ENOT_MASTER_SIGNER));
+ let master_addr = signer::address_of(s);
+ if(!exists<GrantedPermissionHandles>(master_addr)) {
+ return
+ };
+
+ let granted_permissions = borrow_global_mut<GrantedPermissionHandles>(master_addr);
+ let delete_list = vector::trim_reverse(&mut granted_permissions.active_handles, 0);
+ vector::destroy(delete_list, |address| {
+ destroy_permission_address(address);
+ })
+}
+
+
+
+
+public fun permission_address(p: &permissioned_signer::StorablePermissionedHandle): address
+
+
+
+
+public fun permission_address(p: &StorablePermissionedHandle): address {
+ p.permission_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));
+}
+
+
+
+
+permissioned
with the given permission. This requires to have access to the master
+signer.
+
+
+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 PermStorage {
+ assert!(
+ is_permissioned_signer(permissioned) &&
+ !is_permissioned_signer(master) &&
+ signer::address_of(master) == signer::address_of(permissioned),
+ error::permission_denied(ECANNOT_AUTHORIZE)
+ );
+ let permission_signer = permission_signer(permissioned);
+ let permission_signer_addr = signer::address_of(&permission_signer);
+ let perms = &mut borrow_global_mut<PermStorage>(permission_signer_addr).perms;
+ let key = copyable_any::pack(perm);
+ if(smart_table::contains(perms, key)) {
+ let entry = smart_table::borrow_mut(perms, key);
+ *entry = *entry + capacity;
+ } else {
+ smart_table::add(perms, key, 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 PermStorage {
+ if (!is_permissioned_signer(s)) {
+ // master signer has all permissions
+ return true
+ };
+ let addr = signer::address_of(&permission_signer(s));
+ if(!exists<PermStorage>(addr)) {
+ return false
+ };
+ smart_table::contains(&borrow_global<PermStorage>(addr).perms, copyable_any::pack(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 PermStorage {
+ if (!is_permissioned_signer(s)) {
+ // master signer has all permissions
+ return true
+ };
+ let addr = signer::address_of(&permission_signer(s));
+ if(!exists<PermStorage>(addr)) {
+ return false
+ };
+ let key = copyable_any::pack(perm);
+ let storage = &borrow_global<PermStorage>(addr).perms;
+ if(!smart_table::contains(storage, key)) {
+ return false
+ };
+ let perm = smart_table::borrow(storage, key);
+ if(*perm > threshold) {
+ true
+ } else {
+ 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 PermStorage {
+ if (!is_permissioned_signer(s)) {
+ // master signer has all permissions
+ return true
+ };
+ let addr = signer::address_of(&permission_signer(s));
+ if(!exists<PermStorage>(addr)) {
+ return false
+ };
+ let key = copyable_any::pack(perm);
+ let storage = &mut borrow_global_mut<PermStorage>(addr).perms;
+ if(!smart_table::contains(storage, key)) {
+ return false
+ };
+ let perm = smart_table::borrow_mut(storage, key);
+ if(*perm >= threshold) {
+ *perm = *perm - threshold;
+ true
+ } else {
+ 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 PermStorage {
+ assert!(is_permissioned_signer(s), error::permission_denied(ENOT_PERMISSIONED_SIGNER));
+ let addr = signer::address_of(&permission_signer(s));
+ if(!exists<PermStorage>(addr)) {
+ return option::none()
+ };
+ let perm_storage = &borrow_global<PermStorage>(addr).perms;
+ let key = copyable_any::pack(perm);
+ if(smart_table::contains(perm_storage, key)) {
+ option::some(*smart_table::borrow(&borrow_global<PermStorage>(addr).perms, key))
+ } else {
+ 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 PermStorage {
+ if(!is_permissioned_signer(permissioned)) {
+ // Master signer has no permissions associated with it.
+ return
+ };
+ let addr = signer::address_of(&permission_signer(permissioned));
+ if(!exists<PermStorage>(addr)) {
+ return
+ };
+ smart_table::remove(&mut borrow_global_mut<PermStorage>(addr).perms, copyable_any::pack(perm));
+}
+
+
+
+
+public fun extract_permission<PermKey: copy, drop, store>(s: &signer, weight: u256, perm: PermKey): permissioned_signer::Permission<PermKey>
+
+
+
+
+public fun extract_permission<PermKey: copy + drop + store>(
+ s: &signer,
+ weight: u256,
+ perm: PermKey
+): Permission<PermKey> acquires PermStorage {
+ assert!(check_permission_consume(s, weight, perm), error::permission_denied(ECANNOT_EXTRACT_PERMISSION));
+ Permission {
+ owner_address: signer::address_of(s),
+ key: perm,
+ capacity: weight,
+ }
+}
+
+
+
+
+public fun get_key<PermKey>(perm: &permissioned_signer::Permission<PermKey>): &PermKey
+
+
+
+
+public fun get_key<PermKey>(perm: &Permission<PermKey>): &PermKey {
+ &perm.key
+}
+
+
+
+
+public fun address_of<PermKey>(perm: &permissioned_signer::Permission<PermKey>): address
+
+
+
+
+public fun address_of<PermKey>(perm: &Permission<PermKey>): address {
+ perm.owner_address
+}
+
+
+
+
+public fun consume_permission<PermKey: copy, drop, store>(perm: &mut permissioned_signer::Permission<PermKey>, weight: u256, perm_key: PermKey): bool
+
+
+
+
+public fun consume_permission<PermKey: copy + drop + store>(
+ perm: &mut Permission<PermKey>,
+ weight: u256,
+ perm_key: PermKey
+): bool {
+ if(perm.key != perm_key) {
+ return false
+ };
+ if(perm.capacity >= weight) {
+ perm.capacity = perm.capacity - weight;
+ return true
+ } else {
+ return false
+ }
+}
+
+
+
+
+public fun store_permission<PermKey: copy, drop, store>(s: &signer, perm: permissioned_signer::Permission<PermKey>)
+
+
+
+
+public fun store_permission<PermKey: copy + drop + store>(
+ s: &signer,
+ perm: Permission<PermKey>
+) acquires PermStorage {
+ assert!(is_permissioned_signer(s), error::permission_denied(ENOT_PERMISSIONED_SIGNER));
+ let Permission { key, capacity, owner_address } = perm;
+
+ assert!(signer::address_of(s) == owner_address, error::permission_denied(E_PERMISSION_MISMATCH));
+
+ let permission_signer = permission_signer(s);
+ let permission_signer_addr = signer::address_of(&permission_signer);
+ if(!exists<PermStorage>(permission_signer_addr)) {
+ move_to(&permission_signer, PermStorage { perms: smart_table::new()});
+ };
+ let perms = &mut borrow_global_mut<PermStorage>(permission_signer_addr).perms;
+ let key = copyable_any::pack(key);
+ if(smart_table::contains(perms, key)) {
+ let entry = smart_table::borrow_mut(perms, key);
+ *entry = *entry + capacity;
+ } else {
+ smart_table::add(perms, key, capacity)
+ }
+}
+
+
+
+
+public fun is_permissioned_signer(s: &signer): bool
+
+
+
+
+public native fun is_permissioned_signer(s: &signer): bool;
+
+
+
+
+fun permission_signer(permissioned: &signer): signer
+
+
+
+
+native fun permission_signer(permissioned: &signer): signer;
+
+
+
+
+fun signer_from_permissioned_impl(master_addr: address, permission_addr: address): signer
+
+
+
+
+native fun signer_from_permissioned_impl(master_addr: address, permission_addr: address): signer;
+
+
+
+
+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 permission_addr = transaction_context::spec_generate_unique_address();
+modifies global<PermStorage>(permission_addr);
+let master_addr = signer::address_of(master);
+ensures result.master_addr == master_addr;
+ensures result.permission_addr == permission_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 permission_addr = transaction_context::spec_generate_unique_address();
+modifies global<PermStorage>(permission_addr);
+let master_addr = signer::address_of(master);
+modifies global<GrantedPermissionHandles>(master_addr);
+ensures result.master_addr == master_addr;
+ensures result.permission_addr == permission_addr;
+ensures result.expiration_time == expiration_time;
+ensures vector::spec_contains(global<GrantedPermissionHandles>(master_addr).active_handles, permission_addr);
+ensures exists<GrantedPermissionHandles>(master_addr);
+
+
+
+
+
+
+### Function `destroy_permissioned_handle`
+
+
+public fun destroy_permissioned_handle(p: permissioned_signer::PermissionedHandle)
+
+
+
+
+
+ensures !exists<PermStorage>(p.permission_addr);
+
+
+
+
+
+
+### Function `destroy_storable_permissioned_handle`
+
+
+public fun destroy_storable_permissioned_handle(p: permissioned_signer::StorablePermissionedHandle)
+
+
+
+
+
+ensures !exists<PermStorage>(p.permission_addr);
+let post granted_permissions = global<GrantedPermissionHandles>(p.master_addr);
+
+
+
+
+
+
+### Function `revoke_permission_handle`
+
+
+public fun revoke_permission_handle(s: &signer, permission_addr: address)
+
+
+
+
+
+aborts_if spec_is_permissioned_signer(s);
+
+
+
+
+
+
+### 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<PermStorage>(signer::address_of(spec_permission_signer(permissioned)));
+
+
+
+
+
+
+### Function `check_permission_exists`
+
+
+public fun check_permission_exists<PermKey: copy, drop, store>(s: &signer, perm: PermKey): bool
+
+
+
+
+
+pragma opaque;
+aborts_if false;
+ensures result == spec_check_permission_exists(s, perm);
+
+
+
+
+
+
+
+
+fun spec_check_permission_exists<PermKey: copy + drop + store>(
+ s: signer,
+ perm: PermKey
+): bool {
+ use aptos_std::type_info;
+ use std::bcs;
+ let addr = signer::address_of(spec_permission_signer(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<PermStorage>(addr)) {
+ false
+ } else {
+ smart_table::spec_contains(global<PermStorage>(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 = signer::address_of(spec_permission_signer(s));
+ensures !spec_is_permissioned_signer(s) ==> result == true;
+ensures (spec_is_permissioned_signer(s) && !exists<PermStorage>(permissioned_signer_addr)) ==> result == false;
+let key = Any {
+ type_name: type_info::type_name<SmartTable<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 = signer::address_of(spec_permission_signer(s));
+ensures !spec_is_permissioned_signer(s) ==> result == true;
+ensures (spec_is_permissioned_signer(s) && !exists<PermStorage>(permissioned_signer_addr)) ==> result == false;
+
+
+
+
+
+
+### Function `capacity`
+
+
+public fun capacity<PermKey: copy, drop, store>(s: &signer, perm: PermKey): option::Option<u256>
+
+
+
+
+
+aborts_if !spec_is_permissioned_signer(s);
+let permissioned_signer_addr = signer::address_of(spec_permission_signer(s));
+ensures !exists<PermStorage>(permissioned_signer_addr) ==> option::is_none(result);
+
+
+
+
+
+
+### Function `consume_permission`
+
+
+public fun consume_permission<PermKey: copy, drop, store>(perm: &mut permissioned_signer::Permission<PermKey>, weight: u256, perm_key: PermKey): bool
+
+
+
+
+
+ensures perm.key != perm_key ==> result == false;
+ensures perm.key == perm_key && old(perm.capacity) < weight ==> result == false;
+ensures perm.key == perm_key && perm.capacity >= weight ==>
+ (perm.capacity == old(perm.capacity) - weight && result == true);
+
+
+
+
+
+
+### 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_signer(s: signer): signer;
+
+
+
+
+
+
+### Function `permission_signer`
+
+
+fun permission_signer(permissioned: &signer): signer
+
+
+
+
+
+pragma opaque;
+aborts_if [abstract] !spec_is_permissioned_signer(permissioned);
+ensures [abstract] result == spec_permission_signer(permissioned);
+
+
+
+
+
+
+
+
+fun spec_signer_from_permissioned_impl(master_addr: address, permission_addr: address): signer;
+
+
+
+
+
+
+### Function `signer_from_permissioned_impl`
+
+
+fun signer_from_permissioned_impl(master_addr: address, permission_addr: address): signer
+
+
+
+
+
+pragma opaque;
+ensures [abstract] result == spec_signer_from_permissioned_impl(master_addr, permission_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 3da0c50c904f02..a92dd888455688 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 00000000000000..edc379b760fcd1
--- /dev/null
+++ b/aptos-move/framework/aptos-framework/sources/permissioned_signer.move
@@ -0,0 +1,413 @@
+/// A _permissioned signer_ consists of a pair of the original signer and a generated
+/// signer which is used store information about associated permissions.
+///
+/// A permissioned signer behaves compatible with the original signer as it comes to `move_to`, `address_of`, and
+/// existing basic signer functionality. However, the permissions can be queried 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::smart_table::{Self, SmartTable};
+ use aptos_framework::create_signer::create_signer;
+ use aptos_framework::transaction_context::generate_auid_address;
+ use aptos_framework::timestamp;
+
+ #[test_only]
+ friend aptos_framework::permissioned_signer_tests;
+
+ /// 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;
+
+ struct GrantedPermissionHandles has key {
+ active_handles: vector,
+ }
+
+ struct PermissionedHandle {
+ master_addr: address,
+ permission_addr: address,
+ }
+
+ struct StorablePermissionedHandle has store {
+ master_addr: address,
+ permission_addr: address,
+ expiration_time: u64,
+ }
+
+ struct PermStorage has key {
+ perms: SmartTable