-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5eb0f1d
commit 887d7a2
Showing
4 changed files
with
183 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
aptos-move/framework/aptos-framework/sources/permissioned_signer.move
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/// 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::address_of; | ||
use std::error; | ||
use std::string::String; | ||
use aptos_std::copyable_any::{Self, Any}; | ||
use aptos_std::simple_map::{Self, SimpleMap}; | ||
use aptos_std::smart_table::{Self, SmartTable}; | ||
use aptos_std::type_info::type_name; | ||
use aptos_framework::transaction_context::generate_auid_address; | ||
|
||
|
||
/// Trying to grant permission using master signer. | ||
const ENOT_MASTER_SIGNER: u64 = 1; | ||
|
||
/// Cannot authorize a permission. | ||
const ECANNOT_AUTHORIZE: u64 = 2; | ||
|
||
struct PermissionedHandle has store { | ||
master_addr: address, | ||
permission_addr: address, | ||
} | ||
|
||
|
||
// Do we define a resource group similar to ObjectGroup so that all permission objects can be stored in the same slot? | ||
|
||
public fun create_permissioned_signer(master: &signer): PermissionedHandle { | ||
assert!(!is_permissioned_signer(master), error::permission_denied(ENOT_MASTER_SIGNER)); | ||
// Do we need to move sth similar to ObjectCore to register this address as permission address? | ||
PermissionedHandle { | ||
master_addr: address_of(master), | ||
permission_addr: generate_auid_address(), | ||
} | ||
} | ||
|
||
public fun destroy_permissioned_signer(p: PermissionedHandle) acquires PermStorage { | ||
let PermissionedHandle { master_addr: _, permission_addr } = p; | ||
let PermStorage { perms } = move_from<PermStorage>(permission_addr); | ||
smart_table::destroy(perms); | ||
} | ||
|
||
/// ===================================================================================================== | ||
/// Permission Management | ||
/// | ||
struct PermStorage has key { | ||
perms: SmartTable<Any, u256>, | ||
} | ||
|
||
/// Authorizes `permissioned` with the given permission. This requires to have access to the `master` | ||
/// signer. | ||
public fun authorize<PermKey: copy + drop + store>(permissioned: &signer, capacity: u256, master: &signer, perm: PermKey) acquires PermStorage { | ||
assert!( | ||
is_permissioned_signer(permissioned) && | ||
!is_permissioned_signer(master) && | ||
address_of(master) == address_of(permissioned), | ||
error::permission_denied(ECANNOT_AUTHORIZE) | ||
); | ||
let permission_signer = permission_signer(permissioned); | ||
let permission_signer_addr = 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; | ||
smart_table::add(perms, copyable_any::pack(perm), capacity); | ||
} | ||
/// Asserts that the given signer has permission `PermKey`, and the capacity | ||
/// to handle `weight`, which will be subtracted from capacity. | ||
public fun check_permission<PermKey: copy + drop + store>(s: &signer, weight: u256, perm: PermKey): bool acquires PermStorage { | ||
if (!is_permissioned_signer(s)) { | ||
// master signer has all permissions | ||
return true | ||
}; | ||
let addr = address_of(permission_signer(s)); | ||
if(!exists<PermStorage>(addr)) { | ||
return false | ||
}; | ||
let perm = smart_table::borrow_mut(&mut borrow_global_mut<PermStorage>(addr).perms, copyable_any::pack(perm)); | ||
if(*perm < weight) { | ||
return false | ||
}; | ||
*perm = *perm - weight; | ||
return true | ||
} | ||
|
||
public fun capacity<PermKey: copy + drop + store>(s: &signer, perm: PermKey): u256 acquires PermStorage { | ||
*smart_table::borrow(&borrow_global<PermStorage>(address_of(permission_signer(s))).perms, copyable_any::pack(perm)) | ||
} | ||
|
||
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 = address_of(permission_signer(permissioned)); | ||
if(!exists<PermStorage>(addr)) { | ||
return; | ||
}; | ||
smart_table::remove(&mut borrow_global_mut<PermStorage>(addr).perms, copyable_any::pack(perm)); | ||
} | ||
|
||
// ===================================================================================================== | ||
// Native Functions | ||
|
||
/// Creates a permissioned signer from an existing universal signer. The function aborts if the | ||
/// given signer is already a permissioned signer. | ||
/// | ||
/// The implementation of this function requires to extend the value representation for signers in the VM. | ||
/// | ||
/// Check whether this is a permissioned signer. | ||
native fun is_permissioned_signer(s: &signer): bool; | ||
/// Return the signer used for storing permissions. Aborts if not a permissioned signer. | ||
native fun permission_signer(permissioned: &signer): &signer; | ||
/// | ||
/// invariants: | ||
/// address_of(master) == address_of(signer_from_permissioned(create_permissioned_signer(master))), | ||
/// | ||
public native fun signer_from_permissioned(p: &PermissionedHandle): signer; | ||
} |