diff --git a/aptos-move/framework/aptos-framework/doc/aptos_governance.md b/aptos-move/framework/aptos-framework/doc/aptos_governance.md index 4daf73a12bded..4598f8c382bbb 100644 --- a/aptos-move/framework/aptos-framework/doc/aptos_governance.md +++ b/aptos-move/framework/aptos-framework/doc/aptos_governance.md @@ -109,6 +109,7 @@ on a proposal multiple times as long as the total voting power of these votes do use 0x1::governance_proposal; use 0x1::math64; use 0x1::option; +use 0x1::permissioned_signer; use 0x1::randomness_config; use 0x1::reconfiguration_with_dkg; use 0x1::signer; @@ -1261,6 +1262,7 @@ Return proposal_id when a proposal is successfully created. metadata_hash: vector<u8>, is_multi_step_proposal: bool, ): u64 acquires GovernanceConfig, GovernanceEvents { + permissioned_signer::assert_master_signer(proposer); let proposer_address = signer::address_of(proposer); assert!( stake::get_delegated_voter(stake_pool) == proposer_address, @@ -1492,6 +1494,7 @@ cannot vote on the proposal even after partial governance voting is enabled. voting_power: u64, should_pass: bool, ) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents { + permissioned_signer::assert_master_signer(voter); let voter_address = signer::address_of(voter); assert!(stake::get_delegated_voter(stake_pool) == voter_address, error::invalid_argument(ENOT_DELEGATED_VOTER)); diff --git a/aptos-move/framework/aptos-framework/doc/code.md b/aptos-move/framework/aptos-framework/doc/code.md index ce18af9e16fbc..cf52525ae13a5 100644 --- a/aptos-move/framework/aptos-framework/doc/code.md +++ b/aptos-move/framework/aptos-framework/doc/code.md @@ -50,6 +50,7 @@ This module supports functionality related to code management. use 0x1::features; use 0x1::object; use 0x1::option; +use 0x1::permissioned_signer; use 0x1::signer; use 0x1::string; use 0x1::system_addresses; @@ -598,6 +599,7 @@ package.
public fun publish_package(owner: &signer, pack: PackageMetadata, code: vector<vector<u8>>) acquires PackageRegistry {
+    permissioned_signer::assert_master_signer(owner);
     // Disallow incompatible upgrade mode. Governance can decide later if this should be reconsidered.
     assert!(
         pack.upgrade_policy.policy > upgrade_policy_arbitrary().policy,
@@ -679,6 +681,7 @@ package.
 
 
 
public fun freeze_code_object(publisher: &signer, code_object: Object<PackageRegistry>) acquires PackageRegistry {
+    permissioned_signer::assert_master_signer(publisher);
     let code_object_addr = object::object_address(&code_object);
     assert!(exists<PackageRegistry>(code_object_addr), error::not_found(ECODE_OBJECT_DOES_NOT_EXIST));
     assert!(
diff --git a/aptos-move/framework/aptos-framework/doc/delegation_pool.md b/aptos-move/framework/aptos-framework/doc/delegation_pool.md
index 9ce29ab22430e..cd1cc6c6a16c5 100644
--- a/aptos-move/framework/aptos-framework/doc/delegation_pool.md
+++ b/aptos-move/framework/aptos-framework/doc/delegation_pool.md
@@ -124,6 +124,7 @@ transferred to A
 -  [Resource `BeneficiaryForOperator`](#0x1_delegation_pool_BeneficiaryForOperator)
 -  [Resource `NextCommissionPercentage`](#0x1_delegation_pool_NextCommissionPercentage)
 -  [Resource `DelegationPoolAllowlisting`](#0x1_delegation_pool_DelegationPoolAllowlisting)
+-  [Struct `DelegationPermission`](#0x1_delegation_pool_DelegationPermission)
 -  [Struct `AddStake`](#0x1_delegation_pool_AddStake)
 -  [Struct `AddStakeEvent`](#0x1_delegation_pool_AddStakeEvent)
 -  [Struct `ReactivateStake`](#0x1_delegation_pool_ReactivateStake)
@@ -171,6 +172,8 @@ transferred to A
 -  [Function `allowlisting_enabled`](#0x1_delegation_pool_allowlisting_enabled)
 -  [Function `delegator_allowlisted`](#0x1_delegation_pool_delegator_allowlisted)
 -  [Function `get_delegators_allowlist`](#0x1_delegation_pool_get_delegators_allowlist)
+-  [Function `check_signer_permission`](#0x1_delegation_pool_check_signer_permission)
+-  [Function `grant_permission`](#0x1_delegation_pool_grant_permission)
 -  [Function `initialize_delegation_pool`](#0x1_delegation_pool_initialize_delegation_pool)
 -  [Function `beneficiary_for_operator`](#0x1_delegation_pool_beneficiary_for_operator)
 -  [Function `enable_partial_governance_voting`](#0x1_delegation_pool_enable_partial_governance_voting)
@@ -245,6 +248,7 @@ transferred to A
 use 0x1::error;
 use 0x1::event;
 use 0x1::features;
+use 0x1::permissioned_signer;
 use 0x1::pool_u64_unbound;
 use 0x1::signer;
 use 0x1::smart_table;
@@ -678,6 +682,33 @@ evicted later by the pool owner.
 
 
 
+
+
+
+
+## Struct `DelegationPermission`
+
+
+
+
struct DelegationPermission has copy, drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ +
@@ -1828,6 +1859,16 @@ There is not enough active stake on the stake pool to unlock< + + +Signer does not have permission to perform delegation logic. + + +
const ENO_DELEGATION_PERMISSION: u64 = 28;
+
+ + + Changing beneficiaries for operators is not supported. @@ -2756,6 +2797,58 @@ Return allowlist or revert if allowlisting is not enabled for the provided deleg + + + + +## Function `check_signer_permission` + +Permissions + + +
fun check_signer_permission(s: &signer)
+
+ + + +
+Implementation + + +
inline fun check_signer_permission(s: &signer) {
+    assert!(
+        permissioned_signer::check_permission_exists(s, DelegationPermission {}),
+        error::permission_denied(ENO_DELEGATION_PERMISSION),
+    );
+}
+
+ + + +
+ + + +## Function `grant_permission` + + + +
public fun grant_permission(master: &signer, permissioned_signer: &signer)
+
+ + + +
+Implementation + + +
public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+    permissioned_signer::authorize(master, permissioned_signer, 1, DelegationPermission {})
+}
+
+ + +
@@ -2782,6 +2875,7 @@ Ownership over setting the operator/voter is granted to owner who h operator_commission_percentage: u64, delegation_pool_creation_seed: vector<u8>, ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage { + check_signer_permission(owner); assert!(features::delegation_pools_enabled(), error::invalid_state(EDELEGATION_POOLS_DISABLED)); let owner_address = signer::address_of(owner); assert!(!owner_cap_exists(owner_address), error::already_exists(EOWNER_CAP_ALREADY_EXISTS)); @@ -2942,6 +3036,7 @@ Vote on a proposal with a voter's voting power. To successfully vote, the follow voting_power: u64, should_pass: bool ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage { + check_signer_permission(voter); assert_partial_governance_voting_enabled(pool_address); // synchronize delegation and stake pools before any user operation. synchronize_delegation_pool(pool_address); @@ -3021,6 +3116,7 @@ voting power in THIS delegation pool must be not less than the minimum required metadata_hash: vector<u8>, is_multi_step_proposal: bool, ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage { + check_signer_permission(voter); assert_partial_governance_voting_enabled(pool_address); // synchronize delegation and stake pools before any user operation @@ -3793,6 +3889,7 @@ Allows an owner to change the operator of the underlying stake pool. owner: &signer, new_operator: address ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage { + check_signer_permission(owner); let pool_address = get_owned_pool_address(signer::address_of(owner)); // synchronize delegation and stake pools before any user operation // ensure the old operator is paid its uncommitted commission rewards @@ -3828,6 +3925,7 @@ one for each pool. operator: &signer, new_beneficiary: address ) acquires BeneficiaryForOperator { + check_signer_permission(operator); assert!(features::operator_beneficiary_change_enabled(), std::error::invalid_state( EOPERATOR_BENEFICIARY_CHANGE_NOT_SUPPORTED )); @@ -3873,6 +3971,7 @@ Allows an owner to update the commission percentage for the operator of the unde owner: &signer, new_commission_percentage: u64 ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage { + check_signer_permission(owner); assert!(features::commission_change_delegation_pool_enabled(), error::invalid_state( ECOMMISSION_RATE_CHANGE_NOT_SUPPORTED )); @@ -3938,6 +4037,7 @@ Allows an owner to change the delegated voter of the underlying stake pool. owner: &signer, new_voter: address ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage { + check_signer_permission(owner); // No one can change delegated_voter once the partial governance voting feature is enabled. assert!( !features::delegation_pool_partial_governance_voting_enabled(), @@ -3976,6 +4076,7 @@ this change won't take effects until the next lockup period. pool_address: address, new_voter: address ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage { + check_signer_permission(delegator); assert_partial_governance_voting_enabled(pool_address); // synchronize delegation and stake pools before any user operation @@ -4053,6 +4154,7 @@ Enable delegators allowlisting as the pool owner.
public entry fun enable_delegators_allowlisting(
     owner: &signer,
 ) acquires DelegationPoolOwnership, DelegationPool {
+    check_signer_permission(owner);
     assert!(
         features::delegation_pool_allowlisting_enabled(),
         error::invalid_state(EDELEGATORS_ALLOWLISTING_NOT_SUPPORTED)
@@ -4091,6 +4193,7 @@ Disable delegators allowlisting as the pool owner. The existing allowlist will b
 
public entry fun disable_delegators_allowlisting(
     owner: &signer,
 ) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
+    check_signer_permission(owner);
     let pool_address = get_owned_pool_address(signer::address_of(owner));
     assert_allowlisting_enabled(pool_address);
 
@@ -4126,6 +4229,7 @@ Allowlist a delegator as the pool owner.
     owner: &signer,
     delegator_address: address,
 ) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
+    check_signer_permission(owner);
     let pool_address = get_owned_pool_address(signer::address_of(owner));
     assert_allowlisting_enabled(pool_address);
 
@@ -4161,6 +4265,7 @@ Remove a delegator from the allowlist as the pool owner, but do not unlock their
     owner: &signer,
     delegator_address: address,
 ) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
+    check_signer_permission(owner);
     let pool_address = get_owned_pool_address(signer::address_of(owner));
     assert_allowlisting_enabled(pool_address);
 
@@ -4196,6 +4301,7 @@ Evict a delegator that is not allowlisted by unlocking their entire stake.
     owner: &signer,
     delegator_address: address,
 ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
+    check_signer_permission(owner);
     let pool_address = get_owned_pool_address(signer::address_of(owner));
     assert_allowlisting_enabled(pool_address);
     assert!(
@@ -4240,6 +4346,7 @@ Add amount of coins to the delegation pool pool_addressaddress,
     amount: u64
 ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
+    check_signer_permission(delegator);
     // short-circuit if amount to add is 0 so no event is emitted
     if (amount == 0) { return };
 
@@ -4317,6 +4424,7 @@ at most how much active stake there is on the stake pool.
     pool_address: address,
     amount: u64
 ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+    check_signer_permission(delegator);
     // short-circuit if amount to unlock is 0 so no event is emitted
     if (amount == 0) { return };
 
@@ -4418,6 +4526,7 @@ Move amount of coins from pending_inactive to active.
     pool_address: address,
     amount: u64
 ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
+    check_signer_permission(delegator);
     // short-circuit if amount to reactivate is 0 so no event is emitted
     if (amount == 0) { return };
 
@@ -4488,6 +4597,7 @@ Withdraw amount of owned inactive stake from the delegation pool at
     pool_address: address,
     amount: u64
 ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+    check_signer_permission(delegator);
     assert!(amount > 0, error::invalid_argument(EWITHDRAW_ZERO_STAKE));
     // synchronize delegation and stake pools before any user operation
     synchronize_delegation_pool(pool_address);
diff --git a/aptos-move/framework/aptos-framework/doc/managed_coin.md b/aptos-move/framework/aptos-framework/doc/managed_coin.md
index 50c2383fd111d..5f1821115f381 100644
--- a/aptos-move/framework/aptos-framework/doc/managed_coin.md
+++ b/aptos-move/framework/aptos-framework/doc/managed_coin.md
@@ -429,7 +429,8 @@ The Capabilities should not be under the signer before creating;
 The Capabilities should be under the signer after creating;
 
 
-
include coin::InitializeInternalSchema<CoinType>;
+
aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+include coin::InitializeInternalSchema<CoinType>;
 aborts_if !string::spec_internal_check_utf8(name);
 aborts_if !string::spec_internal_check_utf8(symbol);
 aborts_if exists<Capabilities<CoinType>>(signer::address_of(account));
diff --git a/aptos-move/framework/aptos-framework/doc/resource_account.md b/aptos-move/framework/aptos-framework/doc/resource_account.md
index 318d15a785de7..b292e11870952 100644
--- a/aptos-move/framework/aptos-framework/doc/resource_account.md
+++ b/aptos-move/framework/aptos-framework/doc/resource_account.md
@@ -486,6 +486,8 @@ the SignerCapability.
 
 
let source_addr = signer::address_of(origin);
 let resource_addr = account::spec_create_resource_address(source_addr, seed);
+let resource = create_signer::spec_create_signer(resource_addr);
+aborts_if permissioned_signer::spec_is_permissioned_signer(resource);
 include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIfWithoutAccountLimit;
 
@@ -547,7 +549,8 @@ the SignerCapability. -
let resource_addr = signer::address_of(resource);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(resource);
+let resource_addr = signer::address_of(resource);
 // This enforces high-level requirement 1:
 include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIf;
 // This enforces high-level requirement 2:
@@ -618,7 +621,8 @@ the SignerCapability.
 
 
 
-
// This enforces high-level requirement 6:
+
aborts_if permissioned_signer::spec_is_permissioned_signer(resource);
+// This enforces high-level requirement 6:
 aborts_if !exists<Container>(source_addr);
 let resource_addr = signer::address_of(resource);
 let container = global<Container>(source_addr);
diff --git a/aptos-move/framework/aptos-framework/doc/stake.md b/aptos-move/framework/aptos-framework/doc/stake.md
index 017d83723ad91..f8fd44e48c764 100644
--- a/aptos-move/framework/aptos-framework/doc/stake.md
+++ b/aptos-move/framework/aptos-framework/doc/stake.md
@@ -33,6 +33,7 @@ or if their stake drops below the min required, they would get removed at the en
 -  [Struct `IndividualValidatorPerformance`](#0x1_stake_IndividualValidatorPerformance)
 -  [Resource `ValidatorPerformance`](#0x1_stake_ValidatorPerformance)
 -  [Struct `RegisterValidatorCandidateEvent`](#0x1_stake_RegisterValidatorCandidateEvent)
+-  [Struct `StakePermission`](#0x1_stake_StakePermission)
 -  [Struct `RegisterValidatorCandidate`](#0x1_stake_RegisterValidatorCandidate)
 -  [Struct `SetOperatorEvent`](#0x1_stake_SetOperatorEvent)
 -  [Struct `SetOperator`](#0x1_stake_SetOperator)
@@ -65,6 +66,8 @@ or if their stake drops below the min required, they would get removed at the en
 -  [Constants](#@Constants_0)
 -  [Function `initialize_validator_fees`](#0x1_stake_initialize_validator_fees)
 -  [Function `add_transaction_fee`](#0x1_stake_add_transaction_fee)
+-  [Function `check_signer_permission`](#0x1_stake_check_signer_permission)
+-  [Function `grant_permission`](#0x1_stake_grant_permission)
 -  [Function `get_lockup_secs`](#0x1_stake_get_lockup_secs)
 -  [Function `get_remaining_lockup_secs`](#0x1_stake_get_remaining_lockup_secs)
 -  [Function `get_stake`](#0x1_stake_get_stake)
@@ -179,6 +182,7 @@ or if their stake drops below the min required, they would get removed at the en
 use 0x1::fixed_point64;
 use 0x1::math64;
 use 0x1::option;
+use 0x1::permissioned_signer;
 use 0x1::reconfiguration_state;
 use 0x1::signer;
 use 0x1::staking_config;
@@ -628,6 +632,33 @@ This allows the Stake module to mint rewards to stakers.
 
 
 
+
+
+
+
+## Struct `StakePermission`
+
+
+
+
struct StakePermission has copy, drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ +
@@ -1734,6 +1765,16 @@ Validators cannot join or leave post genesis on this test network. + + +Signer does not have permission to perform stake logic. + + +
const ENO_STAKE_PERMISSION: u64 = 28;
+
+ + + An account cannot own more than one owner capability. @@ -1942,6 +1983,59 @@ Stores the transaction fee collected to the specified validator address. + + + + +## Function `check_signer_permission` + +Permissions + + +
fun check_signer_permission(s: &signer)
+
+ + + +
+Implementation + + +
inline fun check_signer_permission(s: &signer) {
+    assert!(
+        permissioned_signer::check_permission_exists(s, StakePermission {}),
+        error::permission_denied(ENO_STAKE_PERMISSION),
+    );
+}
+
+ + + +
+ + + +## Function `grant_permission` + +Grant permission to mutate staking on behalf of the master signer. + + +
public fun grant_permission(master: &signer, permissioned_signer: &signer)
+
+ + + +
+Implementation + + +
public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+    permissioned_signer::authorize(master, permissioned_signer, 1, StakePermission {})
+}
+
+ + +
@@ -2451,6 +2545,7 @@ to set later. operator: address, voter: address, ) acquires AllowedValidators, OwnerCapability, StakePool, ValidatorSet { + check_signer_permission(owner); initialize_owner(owner); move_to(owner, ValidatorConfig { consensus_pubkey: vector::empty(), @@ -2500,6 +2595,7 @@ Initialize the validator account and give ownership to the signing account. network_addresses: vector<u8>, fullnode_addresses: vector<u8>, ) acquires AllowedValidators { + check_signer_permission(account); // Checks the public key has a valid proof-of-possession to prevent rogue-key attacks. let pubkey_from_pop = &bls12381::public_key_from_bytes_with_pop( consensus_pubkey, @@ -2537,6 +2633,7 @@ Initialize the validator account and give ownership to the signing account.
fun initialize_owner(owner: &signer) acquires AllowedValidators {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     assert!(is_allowed(owner_address), error::not_found(EINELIGIBLE_VALIDATOR));
     assert!(!stake_pool_exists(owner_address), error::already_exists(EALREADY_REGISTERED));
@@ -2591,6 +2688,7 @@ Extract and return owner capability from the signing account.
 
 
 
public fun extract_owner_cap(owner: &signer): OwnerCapability acquires OwnerCapability {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
     move_from<OwnerCapability>(owner_address)
@@ -2619,6 +2717,7 @@ staking pool.
 
 
 
public fun deposit_owner_cap(owner: &signer, owner_cap: OwnerCapability) {
+    check_signer_permission(owner);
     assert!(!exists<OwnerCapability>(signer::address_of(owner)), error::not_found(EOWNER_CAP_ALREADY_EXISTS));
     move_to(owner, owner_cap);
 }
@@ -2670,6 +2769,7 @@ Allows an owner to change the operator of the stake pool.
 
 
 
public entry fun set_operator(owner: &signer, new_operator: address) acquires OwnerCapability, StakePool {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
     let ownership_cap = borrow_global<OwnerCapability>(owner_address);
@@ -2746,6 +2846,7 @@ Allows an owner to change the delegated voter of the stake pool.
 
 
 
public entry fun set_delegated_voter(owner: &signer, new_voter: address) acquires OwnerCapability, StakePool {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
     let ownership_cap = borrow_global<OwnerCapability>(owner_address);
@@ -2802,6 +2903,7 @@ Add amount of coins from the public entry fun add_stake(owner: &signer, amount: u64) acquires OwnerCapability, StakePool, ValidatorSet {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
     let ownership_cap = borrow_global<OwnerCapability>(owner_address);
@@ -2902,6 +3004,7 @@ Move amount of coins from pending_inactive to active.
 
 
 
public entry fun reactivate_stake(owner: &signer, amount: u64) acquires OwnerCapability, StakePool {
+    check_signer_permission(owner);
     assert_reconfig_not_in_progress();
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
@@ -2989,6 +3092,7 @@ Rotate the consensus key of the validator, it'll take effect in next epoch.
     new_consensus_pubkey: vector<u8>,
     proof_of_possession: vector<u8>,
 ) acquires StakePool, ValidatorConfig {
+    check_signer_permission(operator);
     assert_reconfig_not_in_progress();
     assert_stake_pool_exists(pool_address);
 
@@ -3052,6 +3156,7 @@ Update the network and full node addresses of the validator. This only takes eff
     new_network_addresses: vector<u8>,
     new_fullnode_addresses: vector<u8>,
 ) acquires StakePool, ValidatorConfig {
+    check_signer_permission(operator);
     assert_reconfig_not_in_progress();
     assert_stake_pool_exists(pool_address);
     let stake_pool = borrow_global_mut<StakePool>(pool_address);
@@ -3109,6 +3214,7 @@ Similar to increase_lockup_with_cap but will use ownership capability from the s
 
 
 
public entry fun increase_lockup(owner: &signer) acquires OwnerCapability, StakePool {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
     let ownership_cap = borrow_global<OwnerCapability>(owner_address);
@@ -3192,6 +3298,7 @@ This can only called by the operator of the validator/staking pool.
     operator: &signer,
     pool_address: address
 ) acquires StakePool, ValidatorConfig, ValidatorSet {
+    check_signer_permission(operator);
     assert!(
         staking_config::get_allow_validator_set_change(&staking_config::get()),
         error::invalid_argument(ENO_POST_GENESIS_VALIDATOR_SET_CHANGE_ALLOWED),
@@ -3294,6 +3401,7 @@ Similar to unlock_with_cap but will use ownership capability from the signing ac
 
 
 
public entry fun unlock(owner: &signer, amount: u64) acquires OwnerCapability, StakePool {
+    check_signer_permission(owner);
     assert_reconfig_not_in_progress();
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
@@ -3381,6 +3489,7 @@ Withdraw from account's inacti
     owner: &signer,
     withdraw_amount: u64
 ) acquires OwnerCapability, StakePool, ValidatorSet {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     assert_owner_cap_exists(owner_address);
     let ownership_cap = borrow_global<OwnerCapability>(owner_address);
@@ -3479,6 +3588,7 @@ Can only be called by the operator of the validator/staking pool.
     operator: &signer,
     pool_address: address
 ) acquires StakePool, ValidatorSet {
+    check_signer_permission(operator);
     assert_reconfig_not_in_progress();
     let config = staking_config::get();
     assert!(
@@ -4640,6 +4750,59 @@ Returns validator's next epoch voting power, including pending_active, active, a
 
 
 
+
+
+
+
+
fun spec_get_reward_rate_1(config: StakingConfig): num {
+   if (features::spec_periodical_reward_rate_decrease_enabled()) {
+       let epoch_rewards_rate = global<staking_config::StakingRewardsConfig>(@aptos_framework).rewards_rate;
+       if (epoch_rewards_rate.value == 0) {
+           0
+       } else {
+           let denominator_0 = aptos_std::fixed_point64::spec_divide_u128(staking_config::MAX_REWARDS_RATE, epoch_rewards_rate);
+           let denominator = if (denominator_0 > MAX_U64) {
+               MAX_U64
+           } else {
+               denominator_0
+           };
+           let nominator = aptos_std::fixed_point64::spec_multiply_u128(denominator, epoch_rewards_rate);
+           nominator
+       }
+   } else {
+           config.rewards_rate
+   }
+}
+
+ + + + + + + +
fun spec_get_reward_rate_2(config: StakingConfig): num {
+   if (features::spec_periodical_reward_rate_decrease_enabled()) {
+       let epoch_rewards_rate = global<staking_config::StakingRewardsConfig>(@aptos_framework).rewards_rate;
+       if (epoch_rewards_rate.value == 0) {
+           1
+       } else {
+           let denominator_0 = aptos_std::fixed_point64::spec_divide_u128(staking_config::MAX_REWARDS_RATE, epoch_rewards_rate);
+           let denominator = if (denominator_0 > MAX_U64) {
+               MAX_U64
+           } else {
+               denominator_0
+           };
+           denominator
+       }
+   } else {
+           config.rewards_rate_denominator
+   }
+}
+
+ + + ### Resource `ValidatorSet` @@ -5000,7 +5163,8 @@ Returns validator's next epoch voting power, including pending_active, active, a -
let owner_address = signer::address_of(owner);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(owner);
+let owner_address = signer::address_of(owner);
 aborts_if exists<OwnerCapability>(owner_address);
 ensures exists<OwnerCapability>(owner_address);
 ensures global<OwnerCapability>(owner_address) == owner_cap;
@@ -5126,7 +5290,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
 
 
 
-
let pre_stake_pool = global<StakePool>(pool_address);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(operator);
+let pre_stake_pool = global<StakePool>(pool_address);
 let post validator_info = global<ValidatorConfig>(pool_address);
 aborts_if reconfiguration_state::spec_is_in_progress();
 aborts_if !exists<StakePool>(pool_address);
@@ -5155,7 +5320,8 @@ Returns validator's next epoch voting power, including pending_active, active, a
 
 
 
-
let pre_stake_pool = global<StakePool>(pool_address);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(operator);
+let pre_stake_pool = global<StakePool>(pool_address);
 let post validator_info = global<ValidatorConfig>(pool_address);
 modifies global<ValidatorConfig>(pool_address);
 include StakedValueNochange;
@@ -5711,59 +5877,6 @@ Returns validator's next epoch voting power, including pending_active, active, a
 
 
 
-
-
-
-
-
fun spec_get_reward_rate_1(config: StakingConfig): num {
-   if (features::spec_periodical_reward_rate_decrease_enabled()) {
-       let epoch_rewards_rate = global<staking_config::StakingRewardsConfig>(@aptos_framework).rewards_rate;
-       if (epoch_rewards_rate.value == 0) {
-           0
-       } else {
-           let denominator_0 = aptos_std::fixed_point64::spec_divide_u128(staking_config::MAX_REWARDS_RATE, epoch_rewards_rate);
-           let denominator = if (denominator_0 > MAX_U64) {
-               MAX_U64
-           } else {
-               denominator_0
-           };
-           let nominator = aptos_std::fixed_point64::spec_multiply_u128(denominator, epoch_rewards_rate);
-           nominator
-       }
-   } else {
-           config.rewards_rate
-   }
-}
-
- - - - - - - -
fun spec_get_reward_rate_2(config: StakingConfig): num {
-   if (features::spec_periodical_reward_rate_decrease_enabled()) {
-       let epoch_rewards_rate = global<staking_config::StakingRewardsConfig>(@aptos_framework).rewards_rate;
-       if (epoch_rewards_rate.value == 0) {
-           1
-       } else {
-           let denominator_0 = aptos_std::fixed_point64::spec_divide_u128(staking_config::MAX_REWARDS_RATE, epoch_rewards_rate);
-           let denominator = if (denominator_0 > MAX_U64) {
-               MAX_U64
-           } else {
-               denominator_0
-           };
-           denominator
-       }
-   } else {
-           config.rewards_rate_denominator
-   }
-}
-
- - - ### Function `update_stake_pool` diff --git a/aptos-move/framework/aptos-framework/doc/staking_proxy.md b/aptos-move/framework/aptos-framework/doc/staking_proxy.md index c05adb7f26803..e8a756c4039fc 100644 --- a/aptos-move/framework/aptos-framework/doc/staking_proxy.md +++ b/aptos-move/framework/aptos-framework/doc/staking_proxy.md @@ -5,6 +5,10 @@ +- [Struct `StakeProxyPermission`](#0x1_staking_proxy_StakeProxyPermission) +- [Constants](#@Constants_0) +- [Function `check_signer_permission`](#0x1_staking_proxy_check_signer_permission) +- [Function `grant_permission`](#0x1_staking_proxy_grant_permission) - [Function `set_operator`](#0x1_staking_proxy_set_operator) - [Function `set_voter`](#0x1_staking_proxy_set_voter) - [Function `set_vesting_contract_operator`](#0x1_staking_proxy_set_vesting_contract_operator) @@ -13,20 +17,22 @@ - [Function `set_vesting_contract_voter`](#0x1_staking_proxy_set_vesting_contract_voter) - [Function `set_staking_contract_voter`](#0x1_staking_proxy_set_staking_contract_voter) - [Function `set_stake_pool_voter`](#0x1_staking_proxy_set_stake_pool_voter) -- [Specification](#@Specification_0) +- [Specification](#@Specification_1) - [High-level Requirements](#high-level-req) - [Module-level Specification](#module-level-spec) - - [Function `set_operator`](#@Specification_0_set_operator) - - [Function `set_voter`](#@Specification_0_set_voter) - - [Function `set_vesting_contract_operator`](#@Specification_0_set_vesting_contract_operator) - - [Function `set_staking_contract_operator`](#@Specification_0_set_staking_contract_operator) - - [Function `set_stake_pool_operator`](#@Specification_0_set_stake_pool_operator) - - [Function `set_vesting_contract_voter`](#@Specification_0_set_vesting_contract_voter) - - [Function `set_staking_contract_voter`](#@Specification_0_set_staking_contract_voter) - - [Function `set_stake_pool_voter`](#@Specification_0_set_stake_pool_voter) - - -
use 0x1::signer;
+    -  [Function `set_operator`](#@Specification_1_set_operator)
+    -  [Function `set_voter`](#@Specification_1_set_voter)
+    -  [Function `set_vesting_contract_operator`](#@Specification_1_set_vesting_contract_operator)
+    -  [Function `set_staking_contract_operator`](#@Specification_1_set_staking_contract_operator)
+    -  [Function `set_stake_pool_operator`](#@Specification_1_set_stake_pool_operator)
+    -  [Function `set_vesting_contract_voter`](#@Specification_1_set_vesting_contract_voter)
+    -  [Function `set_staking_contract_voter`](#@Specification_1_set_staking_contract_voter)
+    -  [Function `set_stake_pool_voter`](#@Specification_1_set_stake_pool_voter)
+
+
+
use 0x1::error;
+use 0x1::permissioned_signer;
+use 0x1::signer;
 use 0x1::stake;
 use 0x1::staking_contract;
 use 0x1::vesting;
@@ -34,6 +40,101 @@
 
 
 
+
+
+## Struct `StakeProxyPermission`
+
+
+
+
struct StakeProxyPermission has copy, drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ + +
+ + + +## Constants + + + + +Signer does not have permission to perform stake proxy logic. + + +
const ENO_STAKE_PERMISSION: u64 = 28;
+
+ + + + + +## Function `check_signer_permission` + +Permissions + + +
fun check_signer_permission(s: &signer)
+
+ + + +
+Implementation + + +
inline fun check_signer_permission(s: &signer) {
+    assert!(
+        permissioned_signer::check_permission_exists(s, StakeProxyPermission {}),
+        error::permission_denied(ENO_STAKE_PERMISSION),
+    );
+}
+
+ + + +
+ + + +## Function `grant_permission` + +Grant permission to mutate staking on behalf of the master signer. + + +
public fun grant_permission(master: &signer, permissioned_signer: &signer)
+
+ + + +
+Implementation + + +
public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+    permissioned_signer::authorize(master, permissioned_signer, 1, StakeProxyPermission {})
+}
+
+ + + +
+ ## Function `set_operator` @@ -102,6 +203,7 @@
public entry fun set_vesting_contract_operator(owner: &signer, old_operator: address, new_operator: address) {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     let vesting_contracts = &vesting::vesting_contracts(owner_address);
     vector::for_each_ref(vesting_contracts, |vesting_contract| {
@@ -134,6 +236,7 @@
 
 
 
public entry fun set_staking_contract_operator(owner: &signer, old_operator: address, new_operator: address) {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     if (staking_contract::staking_contract_exists(owner_address, old_operator)) {
         let current_commission_percentage = staking_contract::commission_percentage(owner_address, old_operator);
@@ -162,6 +265,7 @@
 
 
 
public entry fun set_stake_pool_operator(owner: &signer, new_operator: address) {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     if (stake::stake_pool_exists(owner_address)) {
         stake::set_operator(owner, new_operator);
@@ -189,6 +293,7 @@
 
 
 
public entry fun set_vesting_contract_voter(owner: &signer, operator: address, new_voter: address) {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     let vesting_contracts = &vesting::vesting_contracts(owner_address);
     vector::for_each_ref(vesting_contracts, |vesting_contract| {
@@ -220,6 +325,7 @@
 
 
 
public entry fun set_staking_contract_voter(owner: &signer, operator: address, new_voter: address) {
+    check_signer_permission(owner);
     let owner_address = signer::address_of(owner);
     if (staking_contract::staking_contract_exists(owner_address, operator)) {
         staking_contract::update_voter(owner, operator, new_voter);
@@ -247,6 +353,7 @@
 
 
 
public entry fun set_stake_pool_voter(owner: &signer, new_voter: address) {
+    check_signer_permission(owner);
     if (stake::stake_pool_exists(signer::address_of(owner))) {
         stake::set_delegated_voter(owner, new_voter);
     };
@@ -257,7 +364,7 @@
 
 
 
-
+
 
 ## Specification
 
@@ -329,7 +436,7 @@
 
 
 
-
+
 
 ### Function `set_operator`
 
@@ -349,7 +456,7 @@ Aborts if conditions of SetStakePoolOperator are not met
 
 
 
-
+
 
 ### Function `set_voter`
 
@@ -368,7 +475,7 @@ Aborts if conditions of SetStackingContractVoter and SetStackPoolVoterAbortsIf a
 
 
 
-
+
 
 ### Function `set_vesting_contract_operator`
 
@@ -384,7 +491,7 @@ Aborts if conditions of SetStackingContractVoter and SetStackPoolVoterAbortsIf a
 
 
 
-
+
 
 ### Function `set_staking_contract_operator`
 
@@ -438,7 +545,7 @@ Aborts if conditions of SetStackingContractVoter and SetStackPoolVoterAbortsIf a
 
 
 
-
+
 
 ### Function `set_stake_pool_operator`
 
@@ -463,6 +570,7 @@ One of them are not exists
 
schema SetStakePoolOperator {
     owner: &signer;
     new_operator: address;
+    aborts_if permissioned_signer::spec_is_permissioned_signer(owner);
     let owner_address = signer::address_of(owner);
     let ownership_cap = borrow_global<stake::OwnerCapability>(owner_address);
     let pool_address = ownership_cap.pool_address;
@@ -473,7 +581,7 @@ One of them are not exists
 
 
 
-
+
 
 ### Function `set_vesting_contract_voter`
 
@@ -489,7 +597,7 @@ One of them are not exists
 
 
 
-
+
 
 ### Function `set_staking_contract_voter`
 
@@ -531,7 +639,7 @@ Then abort if the resource is not exist
 
 
 
-
+
 
 ### Function `set_stake_pool_voter`
 
@@ -554,6 +662,7 @@ Then abort if the resource is not exist
 
schema SetStakePoolVoterAbortsIf {
     owner: &signer;
     new_voter: address;
+    aborts_if permissioned_signer::spec_is_permissioned_signer(owner);
     let owner_address = signer::address_of(owner);
     let ownership_cap = global<stake::OwnerCapability>(owner_address);
     let pool_address = ownership_cap.pool_address;
diff --git a/aptos-move/framework/aptos-framework/doc/vesting.md b/aptos-move/framework/aptos-framework/doc/vesting.md
index 6f2d17e23f75f..b3ecfc2cd6158 100644
--- a/aptos-move/framework/aptos-framework/doc/vesting.md
+++ b/aptos-move/framework/aptos-framework/doc/vesting.md
@@ -169,6 +169,7 @@ withdrawable, admin can call admin_withdraw to withdraw all funds to the vesting
 use 0x1::features;
 use 0x1::fixed_point32;
 use 0x1::math64;
+use 0x1::permissioned_signer;
 use 0x1::pool_u64;
 use 0x1::signer;
 use 0x1::simple_map;
@@ -3229,6 +3230,7 @@ This address should be deterministic for the same admin and vesting contract cre
 
 
 
fun verify_admin(admin: &signer, vesting_contract: &VestingContract) {
+    permissioned_signer::assert_master_signer(admin);
     assert!(signer::address_of(admin) == vesting_contract.admin, error::unauthenticated(ENOT_ADMIN));
 }
 
@@ -4318,7 +4320,8 @@ This address should be deterministic for the same admin and vesting contract cre -
// This enforces high-level requirement 9:
+
aborts_if permissioned_signer::spec_is_permissioned_signer(admin);
+// This enforces high-level requirement 9:
 aborts_if signer::address_of(admin) != vesting_contract.admin;
 
@@ -4499,6 +4502,7 @@ This address should be deterministic for the same admin and vesting contract cre
schema VerifyAdminAbortsIf {
     contract_address: address;
     admin: signer;
+    aborts_if permissioned_signer::spec_is_permissioned_signer(admin);
     aborts_if !exists<VestingContract>(contract_address);
     let vesting_contract = global<VestingContract>(contract_address);
     aborts_if signer::address_of(admin) != vesting_contract.admin;
diff --git a/aptos-move/framework/aptos-framework/doc/voting.md b/aptos-move/framework/aptos-framework/doc/voting.md
index 946e707b6e2b2..f861c6f925df5 100644
--- a/aptos-move/framework/aptos-framework/doc/voting.md
+++ b/aptos-move/framework/aptos-framework/doc/voting.md
@@ -98,6 +98,7 @@ the resolution process.
 use 0x1::features;
 use 0x1::from_bcs;
 use 0x1::option;
+use 0x1::permissioned_signer;
 use 0x1::signer;
 use 0x1::simple_map;
 use 0x1::string;
@@ -796,6 +797,7 @@ Key used to track the resolvable time in the proposal's metadata.
 
 
 
public fun register<ProposalType: store>(account: &signer) {
+    permissioned_signer::assert_master_signer(account);
     let addr = signer::address_of(account);
     assert!(!exists<VotingForum<ProposalType>>(addr), error::already_exists(EVOTING_FORUM_ALREADY_REGISTERED));
 
@@ -1923,7 +1925,8 @@ Return true if the voting period of the given proposal has already ended.
 
 
 
-
let addr = signer::address_of(account);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+let addr = signer::address_of(account);
 aborts_if exists<VotingForum<ProposalType>>(addr);
 aborts_if !exists<account::Account>(addr);
 let register_account = global<account::Account>(addr);
diff --git a/aptos-move/framework/aptos-framework/sources/aptos_governance.move b/aptos-move/framework/aptos-framework/sources/aptos_governance.move
index 19c8d45c92753..dc75f8c2a1c3c 100644
--- a/aptos-move/framework/aptos-framework/sources/aptos_governance.move
+++ b/aptos-move/framework/aptos-framework/sources/aptos_governance.move
@@ -31,6 +31,7 @@ module aptos_framework::aptos_governance {
     use aptos_framework::system_addresses;
     use aptos_framework::aptos_coin::{Self, AptosCoin};
     use aptos_framework::consensus_config;
+    use aptos_framework::permissioned_signer;
     use aptos_framework::randomness_config;
     use aptos_framework::reconfiguration_with_dkg;
     use aptos_framework::timestamp;
@@ -375,6 +376,7 @@ module aptos_framework::aptos_governance {
         metadata_hash: vector,
         is_multi_step_proposal: bool,
     ): u64 acquires GovernanceConfig, GovernanceEvents {
+        permissioned_signer::assert_master_signer(proposer);
         let proposer_address = signer::address_of(proposer);
         assert!(
             stake::get_delegated_voter(stake_pool) == proposer_address,
@@ -506,6 +508,7 @@ module aptos_framework::aptos_governance {
         voting_power: u64,
         should_pass: bool,
     ) acquires ApprovedExecutionHashes, VotingRecords, VotingRecordsV2, GovernanceEvents {
+        permissioned_signer::assert_master_signer(voter);
         let voter_address = signer::address_of(voter);
         assert!(stake::get_delegated_voter(stake_pool) == voter_address, error::invalid_argument(ENOT_DELEGATED_VOTER));
 
diff --git a/aptos-move/framework/aptos-framework/sources/code.move b/aptos-move/framework/aptos-framework/sources/code.move
index ef884c9695d1c..3e4d2d7866d71 100644
--- a/aptos-move/framework/aptos-framework/sources/code.move
+++ b/aptos-move/framework/aptos-framework/sources/code.move
@@ -13,6 +13,7 @@ module aptos_framework::code {
     use std::string;
     use aptos_framework::event;
     use aptos_framework::object::{Self, Object};
+    use aptos_framework::permissioned_signer;
 
     // ----------------------------------------------------------------------
     // Code Publishing
@@ -145,6 +146,7 @@ module aptos_framework::code {
     /// Publishes a package at the given signer's address. The caller must provide package metadata describing the
     /// package.
     public fun publish_package(owner: &signer, pack: PackageMetadata, code: vector>) acquires PackageRegistry {
+        permissioned_signer::assert_master_signer(owner);
         // Disallow incompatible upgrade mode. Governance can decide later if this should be reconsidered.
         assert!(
             pack.upgrade_policy.policy > upgrade_policy_arbitrary().policy,
@@ -206,6 +208,7 @@ module aptos_framework::code {
     }
 
     public fun freeze_code_object(publisher: &signer, code_object: Object) acquires PackageRegistry {
+        permissioned_signer::assert_master_signer(publisher);
         let code_object_addr = object::object_address(&code_object);
         assert!(exists(code_object_addr), error::not_found(ECODE_OBJECT_DOES_NOT_EXIST));
         assert!(
diff --git a/aptos-move/framework/aptos-framework/sources/coin.spec.move b/aptos-move/framework/aptos-framework/sources/coin.spec.move
index 2564bc0daa8c6..aa4da455af04d 100644
--- a/aptos-move/framework/aptos-framework/sources/coin.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/coin.spec.move
@@ -395,6 +395,7 @@ spec aptos_framework::coin {
     /// The creator of `CoinType` must be `@aptos_framework`.
     /// `SupplyConfig` allow upgrade.
     spec upgrade_supply(account: &signer) {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(account);
         let account_addr = signer::address_of(account);
         let coin_address = type_info::type_of().account_address;
         aborts_if coin_address != account_addr;
@@ -423,6 +424,7 @@ spec aptos_framework::coin {
     }
 
     spec initialize {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(account);
         let account_addr = signer::address_of(account);
         /// [high-level-req-1.2]
         aborts_if type_info::type_of().account_address != account_addr;
@@ -441,6 +443,7 @@ spec aptos_framework::coin {
     monitor_supply: bool,
     ): (BurnCapability, FreezeCapability, MintCapability) {
         use aptos_framework::aggregator_factory;
+        aborts_if permissioned_signer::spec_is_permissioned_signer(account);
         let addr = signer::address_of(account);
         aborts_if addr != @aptos_framework;
         aborts_if monitor_supply && !exists(@aptos_framework);
@@ -473,6 +476,7 @@ spec aptos_framework::coin {
     monitor_supply: bool,
     parallelizable: bool,
     ): (BurnCapability, FreezeCapability, MintCapability) {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(account);
         include InitializeInternalSchema {
             name: name.bytes,
             symbol: symbol.bytes
diff --git a/aptos-move/framework/aptos-framework/sources/create_signer.spec.move b/aptos-move/framework/aptos-framework/sources/create_signer.spec.move
index 1bb4c0ffa9fd6..dab59d30da2db 100644
--- a/aptos-move/framework/aptos-framework/sources/create_signer.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/create_signer.spec.move
@@ -41,5 +41,8 @@ spec aptos_framework::create_signer {
         pragma opaque;
         aborts_if [abstract] false;
         ensures [abstract] signer::address_of(result) == addr;
+        ensures [abstract] result == spec_create_signer(addr);
     }
+
+    spec fun spec_create_signer(addr: address): signer;
 }
diff --git a/aptos-move/framework/aptos-framework/sources/delegation_pool.move b/aptos-move/framework/aptos-framework/sources/delegation_pool.move
index be1643ca6b197..869669cf0e739 100644
--- a/aptos-move/framework/aptos-framework/sources/delegation_pool.move
+++ b/aptos-move/framework/aptos-framework/sources/delegation_pool.move
@@ -124,6 +124,7 @@ module aptos_framework::delegation_pool {
     use aptos_framework::aptos_governance;
     use aptos_framework::coin;
     use aptos_framework::event::{Self, EventHandle, emit};
+    use aptos_framework::permissioned_signer;
     use aptos_framework::stake;
     use aptos_framework::stake::get_operator;
     use aptos_framework::staking_config;
@@ -215,6 +216,9 @@ module aptos_framework::delegation_pool {
     /// Cannot unlock the accumulated active stake of NULL_SHAREHOLDER(0x0).
     const ECANNOT_UNLOCK_NULL_SHAREHOLDER: u64 = 27;
 
+    /// Signer does not have permission to perform delegation logic.
+    const ENO_DELEGATION_PERMISSION: u64 = 28;
+
     const MAX_U64: u64 = 18446744073709551615;
 
     /// Maximum operator percentage fee(of double digit precision): 22.85% is represented as 2285
@@ -346,6 +350,8 @@ module aptos_framework::delegation_pool {
         allowlist: SmartTable,
     }
 
+    struct DelegationPermission has copy, drop, store {}
+
     #[event]
     struct AddStake has drop, store {
         pool_address: address,
@@ -832,6 +838,18 @@ module aptos_framework::delegation_pool {
         allowlist
     }
 
+    /// Permissions
+    inline fun check_signer_permission(s: &signer) {
+        assert!(
+            permissioned_signer::check_permission_exists(s, DelegationPermission {}),
+            error::permission_denied(ENO_DELEGATION_PERMISSION),
+        );
+    }
+
+    public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+        permissioned_signer::authorize(master, permissioned_signer, 1, DelegationPermission {})
+    }
+
     /// Initialize a delegation pool of custom fixed `operator_commission_percentage`.
     /// A resource account is created from `owner` signer and its supplied `delegation_pool_creation_seed`
     /// to host the delegation pool resource and own the underlying stake pool.
@@ -841,6 +859,7 @@ module aptos_framework::delegation_pool {
         operator_commission_percentage: u64,
         delegation_pool_creation_seed: vector,
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(owner);
         assert!(features::delegation_pools_enabled(), error::invalid_state(EDELEGATION_POOLS_DISABLED));
         let owner_address = signer::address_of(owner);
         assert!(!owner_cap_exists(owner_address), error::already_exists(EOWNER_CAP_ALREADY_EXISTS));
@@ -941,6 +960,7 @@ module aptos_framework::delegation_pool {
         voting_power: u64,
         should_pass: bool
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(voter);
         assert_partial_governance_voting_enabled(pool_address);
         // synchronize delegation and stake pools before any user operation.
         synchronize_delegation_pool(pool_address);
@@ -1000,6 +1020,7 @@ module aptos_framework::delegation_pool {
         metadata_hash: vector,
         is_multi_step_proposal: bool,
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(voter);
         assert_partial_governance_voting_enabled(pool_address);
 
         // synchronize delegation and stake pools before any user operation
@@ -1292,6 +1313,7 @@ module aptos_framework::delegation_pool {
         owner: &signer,
         new_operator: address
     ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(owner);
         let pool_address = get_owned_pool_address(signer::address_of(owner));
         // synchronize delegation and stake pools before any user operation
         // ensure the old operator is paid its uncommitted commission rewards
@@ -1307,6 +1329,7 @@ module aptos_framework::delegation_pool {
         operator: &signer,
         new_beneficiary: address
     ) acquires BeneficiaryForOperator {
+        check_signer_permission(operator);
         assert!(features::operator_beneficiary_change_enabled(), std::error::invalid_state(
             EOPERATOR_BENEFICIARY_CHANGE_NOT_SUPPORTED
         ));
@@ -1332,6 +1355,7 @@ module aptos_framework::delegation_pool {
         owner: &signer,
         new_commission_percentage: u64
     ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(owner);
         assert!(features::commission_change_delegation_pool_enabled(), error::invalid_state(
             ECOMMISSION_RATE_CHANGE_NOT_SUPPORTED
         ));
@@ -1377,6 +1401,7 @@ module aptos_framework::delegation_pool {
         owner: &signer,
         new_voter: address
     ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(owner);
         // No one can change delegated_voter once the partial governance voting feature is enabled.
         assert!(
             !features::delegation_pool_partial_governance_voting_enabled(),
@@ -1395,6 +1420,7 @@ module aptos_framework::delegation_pool {
         pool_address: address,
         new_voter: address
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(delegator);
         assert_partial_governance_voting_enabled(pool_address);
 
         // synchronize delegation and stake pools before any user operation
@@ -1452,6 +1478,7 @@ module aptos_framework::delegation_pool {
     public entry fun enable_delegators_allowlisting(
         owner: &signer,
     ) acquires DelegationPoolOwnership, DelegationPool {
+        check_signer_permission(owner);
         assert!(
             features::delegation_pool_allowlisting_enabled(),
             error::invalid_state(EDELEGATORS_ALLOWLISTING_NOT_SUPPORTED)
@@ -1470,6 +1497,7 @@ module aptos_framework::delegation_pool {
     public entry fun disable_delegators_allowlisting(
         owner: &signer,
     ) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
+        check_signer_permission(owner);
         let pool_address = get_owned_pool_address(signer::address_of(owner));
         assert_allowlisting_enabled(pool_address);
 
@@ -1485,6 +1513,7 @@ module aptos_framework::delegation_pool {
         owner: &signer,
         delegator_address: address,
     ) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
+        check_signer_permission(owner);
         let pool_address = get_owned_pool_address(signer::address_of(owner));
         assert_allowlisting_enabled(pool_address);
 
@@ -1500,6 +1529,7 @@ module aptos_framework::delegation_pool {
         owner: &signer,
         delegator_address: address,
     ) acquires DelegationPoolOwnership, DelegationPoolAllowlisting {
+        check_signer_permission(owner);
         let pool_address = get_owned_pool_address(signer::address_of(owner));
         assert_allowlisting_enabled(pool_address);
 
@@ -1515,6 +1545,7 @@ module aptos_framework::delegation_pool {
         owner: &signer,
         delegator_address: address,
     ) acquires DelegationPoolOwnership, DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
+        check_signer_permission(owner);
         let pool_address = get_owned_pool_address(signer::address_of(owner));
         assert_allowlisting_enabled(pool_address);
         assert!(
@@ -1539,6 +1570,7 @@ module aptos_framework::delegation_pool {
         pool_address: address,
         amount: u64
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
+        check_signer_permission(delegator);
         // short-circuit if amount to add is 0 so no event is emitted
         if (amount == 0) { return };
 
@@ -1596,6 +1628,7 @@ module aptos_framework::delegation_pool {
         pool_address: address,
         amount: u64
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(delegator);
         // short-circuit if amount to unlock is 0 so no event is emitted
         if (amount == 0) { return };
 
@@ -1657,6 +1690,7 @@ module aptos_framework::delegation_pool {
         pool_address: address,
         amount: u64
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage, DelegationPoolAllowlisting {
+        check_signer_permission(delegator);
         // short-circuit if amount to reactivate is 0 so no event is emitted
         if (amount == 0) { return };
 
@@ -1707,6 +1741,7 @@ module aptos_framework::delegation_pool {
         pool_address: address,
         amount: u64
     ) acquires DelegationPool, GovernanceRecords, BeneficiaryForOperator, NextCommissionPercentage {
+        check_signer_permission(delegator);
         assert!(amount > 0, error::invalid_argument(EWITHDRAW_ZERO_STAKE));
         // synchronize delegation and stake pools before any user operation
         synchronize_delegation_pool(pool_address);
diff --git a/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move b/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move
index 344c9744f7c97..8569fa284d4a9 100644
--- a/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move
@@ -97,6 +97,8 @@ spec aptos_framework::managed_coin {
         decimals: u8,
         monitor_supply: bool,
     ) {
+        use aptos_framework::permissioned_signer;
+        aborts_if permissioned_signer::spec_is_permissioned_signer(account);
         include coin::InitializeInternalSchema;
         aborts_if !string::spec_internal_check_utf8(name);
         aborts_if !string::spec_internal_check_utf8(symbol);
diff --git a/aptos-move/framework/aptos-framework/sources/resource_account.spec.move b/aptos-move/framework/aptos-framework/sources/resource_account.spec.move
index 847e77853bdc4..902a15fec660d 100644
--- a/aptos-move/framework/aptos-framework/sources/resource_account.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/resource_account.spec.move
@@ -68,8 +68,12 @@ spec aptos_framework::resource_account {
         seed: vector,
         optional_auth_key: vector,
     ) {
+        use aptos_framework::permissioned_signer;
+        use aptos_framework::create_signer;
         let source_addr = signer::address_of(origin);
         let resource_addr = account::spec_create_resource_address(source_addr, seed);
+        let resource = create_signer::spec_create_signer(resource_addr);
+        aborts_if permissioned_signer::spec_is_permissioned_signer(resource);
         include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIfWithoutAccountLimit;
     }
 
@@ -116,6 +120,8 @@ spec aptos_framework::resource_account {
         resource_signer_cap: account::SignerCapability,
         optional_auth_key: vector,
     ) {
+        use aptos_framework::permissioned_signer;
+        aborts_if permissioned_signer::spec_is_permissioned_signer(resource);
         let resource_addr = signer::address_of(resource);
         /// [high-level-req-1]
         include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIf;
@@ -172,6 +178,8 @@ spec aptos_framework::resource_account {
         resource: &signer,
         source_addr: address,
     ) : account::SignerCapability  {
+        use aptos_framework::permissioned_signer;
+        aborts_if permissioned_signer::spec_is_permissioned_signer(resource);
         /// [high-level-req-6]
         aborts_if !exists(source_addr);
         let resource_addr = signer::address_of(resource);
diff --git a/aptos-move/framework/aptos-framework/sources/stake.move b/aptos-move/framework/aptos-framework/sources/stake.move
index 0b65fa5f4eb7d..3070b2eaeaebb 100644
--- a/aptos-move/framework/aptos-framework/sources/stake.move
+++ b/aptos-move/framework/aptos-framework/sources/stake.move
@@ -34,6 +34,7 @@ module aptos_framework::stake {
     use aptos_framework::system_addresses;
     use aptos_framework::staking_config::{Self, StakingConfig, StakingRewardsConfig};
     use aptos_framework::chain_status;
+    use aptos_framework::permissioned_signer;
 
     friend aptos_framework::block;
     friend aptos_framework::genesis;
@@ -81,6 +82,8 @@ module aptos_framework::stake {
     const EFEES_TABLE_ALREADY_EXISTS: u64 = 19;
     /// Validator set change temporarily disabled because of in-progress reconfiguration.
     const ERECONFIGURATION_IN_PROGRESS: u64 = 20;
+    /// Signer does not have permission to perform stake logic.
+    const ENO_STAKE_PERMISSION: u64 = 28;
 
     /// Validator status enum. We can switch to proper enum later once Move supports it.
     const VALIDATOR_STATUS_PENDING_ACTIVE: u64 = 1;
@@ -204,6 +207,8 @@ module aptos_framework::stake {
         pool_address: address,
     }
 
+    struct StakePermission has copy, drop, store {}
+
     #[event]
     struct RegisterValidatorCandidate has drop, store {
         pool_address: address,
@@ -366,6 +371,19 @@ module aptos_framework::stake {
         }
     }
 
+    /// Permissions
+    inline fun check_signer_permission(s: &signer) {
+        assert!(
+            permissioned_signer::check_permission_exists(s, StakePermission {}),
+            error::permission_denied(ENO_STAKE_PERMISSION),
+        );
+    }
+
+    /// Grant permission to mutate staking on behalf of the master signer.
+    public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+        permissioned_signer::authorize(master, permissioned_signer, 1, StakePermission {})
+    }
+
     #[view]
     /// Return the lockup expiration of the stake pool at `pool_address`.
     /// This will throw an error if there's no stake pool at `pool_address`.
@@ -558,6 +576,7 @@ module aptos_framework::stake {
         operator: address,
         voter: address,
     ) acquires AllowedValidators, OwnerCapability, StakePool, ValidatorSet {
+        check_signer_permission(owner);
         initialize_owner(owner);
         move_to(owner, ValidatorConfig {
             consensus_pubkey: vector::empty(),
@@ -587,6 +606,7 @@ module aptos_framework::stake {
         network_addresses: vector,
         fullnode_addresses: vector,
     ) acquires AllowedValidators {
+        check_signer_permission(account);
         // Checks the public key has a valid proof-of-possession to prevent rogue-key attacks.
         let pubkey_from_pop = &bls12381::public_key_from_bytes_with_pop(
             consensus_pubkey,
@@ -604,6 +624,7 @@ module aptos_framework::stake {
     }
 
     fun initialize_owner(owner: &signer) acquires AllowedValidators {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         assert!(is_allowed(owner_address), error::not_found(EINELIGIBLE_VALIDATOR));
         assert!(!stake_pool_exists(owner_address), error::already_exists(EALREADY_REGISTERED));
@@ -638,6 +659,7 @@ module aptos_framework::stake {
 
     /// Extract and return owner capability from the signing account.
     public fun extract_owner_cap(owner: &signer): OwnerCapability acquires OwnerCapability {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
         move_from(owner_address)
@@ -646,6 +668,7 @@ module aptos_framework::stake {
     /// Deposit `owner_cap` into `account`. This requires `account` to not already have ownership of another
     /// staking pool.
     public fun deposit_owner_cap(owner: &signer, owner_cap: OwnerCapability) {
+        check_signer_permission(owner);
         assert!(!exists(signer::address_of(owner)), error::not_found(EOWNER_CAP_ALREADY_EXISTS));
         move_to(owner, owner_cap);
     }
@@ -657,6 +680,7 @@ module aptos_framework::stake {
 
     /// Allows an owner to change the operator of the stake pool.
     public entry fun set_operator(owner: &signer, new_operator: address) acquires OwnerCapability, StakePool {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
         let ownership_cap = borrow_global(owner_address);
@@ -693,6 +717,7 @@ module aptos_framework::stake {
 
     /// Allows an owner to change the delegated voter of the stake pool.
     public entry fun set_delegated_voter(owner: &signer, new_voter: address) acquires OwnerCapability, StakePool {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
         let ownership_cap = borrow_global(owner_address);
@@ -709,6 +734,7 @@ module aptos_framework::stake {
 
     /// Add `amount` of coins from the `account` owning the StakePool.
     public entry fun add_stake(owner: &signer, amount: u64) acquires OwnerCapability, StakePool, ValidatorSet {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
         let ownership_cap = borrow_global(owner_address);
@@ -769,6 +795,7 @@ module aptos_framework::stake {
 
     /// Move `amount` of coins from pending_inactive to active.
     public entry fun reactivate_stake(owner: &signer, amount: u64) acquires OwnerCapability, StakePool {
+        check_signer_permission(owner);
         assert_reconfig_not_in_progress();
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
@@ -816,6 +843,7 @@ module aptos_framework::stake {
         new_consensus_pubkey: vector,
         proof_of_possession: vector,
     ) acquires StakePool, ValidatorConfig {
+        check_signer_permission(operator);
         assert_reconfig_not_in_progress();
         assert_stake_pool_exists(pool_address);
 
@@ -859,6 +887,7 @@ module aptos_framework::stake {
         new_network_addresses: vector,
         new_fullnode_addresses: vector,
     ) acquires StakePool, ValidatorConfig {
+        check_signer_permission(operator);
         assert_reconfig_not_in_progress();
         assert_stake_pool_exists(pool_address);
         let stake_pool = borrow_global_mut(pool_address);
@@ -896,6 +925,7 @@ module aptos_framework::stake {
 
     /// Similar to increase_lockup_with_cap but will use ownership capability from the signing account.
     public entry fun increase_lockup(owner: &signer) acquires OwnerCapability, StakePool {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
         let ownership_cap = borrow_global(owner_address);
@@ -939,6 +969,7 @@ module aptos_framework::stake {
         operator: &signer,
         pool_address: address
     ) acquires StakePool, ValidatorConfig, ValidatorSet {
+        check_signer_permission(operator);
         assert!(
             staking_config::get_allow_validator_set_change(&staking_config::get()),
             error::invalid_argument(ENO_POST_GENESIS_VALIDATOR_SET_CHANGE_ALLOWED),
@@ -1001,6 +1032,7 @@ module aptos_framework::stake {
 
     /// Similar to unlock_with_cap but will use ownership capability from the signing account.
     public entry fun unlock(owner: &signer, amount: u64) acquires OwnerCapability, StakePool {
+        check_signer_permission(owner);
         assert_reconfig_not_in_progress();
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
@@ -1048,6 +1080,7 @@ module aptos_framework::stake {
         owner: &signer,
         withdraw_amount: u64
     ) acquires OwnerCapability, StakePool, ValidatorSet {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         assert_owner_cap_exists(owner_address);
         let ownership_cap = borrow_global(owner_address);
@@ -1106,6 +1139,7 @@ module aptos_framework::stake {
         operator: &signer,
         pool_address: address
     ) acquires StakePool, ValidatorSet {
+        check_signer_permission(operator);
         assert_reconfig_not_in_progress();
         let config = staking_config::get();
         assert!(
diff --git a/aptos-move/framework/aptos-framework/sources/stake.spec.move b/aptos-move/framework/aptos-framework/sources/stake.spec.move
index 44101fbcb134c..3ed44f710fa41 100644
--- a/aptos-move/framework/aptos-framework/sources/stake.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/stake.spec.move
@@ -310,6 +310,7 @@ spec aptos_framework::stake {
     }
 
     spec deposit_owner_cap(owner: &signer, owner_cap: OwnerCapability) {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(owner);
         let owner_address = signer::address_of(owner);
         aborts_if exists(owner_address);
         ensures exists(owner_address);
@@ -358,6 +359,7 @@ spec aptos_framework::stake {
         new_network_addresses: vector,
         new_fullnode_addresses: vector,
     ) {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(operator);
         let pre_stake_pool = global(pool_address);
         let post validator_info = global(pool_address);
         modifies global(pool_address);
@@ -404,6 +406,7 @@ spec aptos_framework::stake {
         new_consensus_pubkey: vector,
         proof_of_possession: vector,
     ) {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(operator);
         let pre_stake_pool = global(pool_address);
         let post validator_info = global(pool_address);
         aborts_if reconfiguration_state::spec_is_in_progress();
diff --git a/aptos-move/framework/aptos-framework/sources/staking_proxy.move b/aptos-move/framework/aptos-framework/sources/staking_proxy.move
index 26d1aa33372ce..598b51cae7a3c 100644
--- a/aptos-move/framework/aptos-framework/sources/staking_proxy.move
+++ b/aptos-move/framework/aptos-framework/sources/staking_proxy.move
@@ -1,11 +1,31 @@
 module aptos_framework::staking_proxy {
+    use std::error;
     use std::signer;
     use std::vector;
 
+    use aptos_framework::permissioned_signer;
     use aptos_framework::stake;
     use aptos_framework::staking_contract;
     use aptos_framework::vesting;
 
+    struct StakeProxyPermission has copy, drop, store {}
+
+    /// Signer does not have permission to perform stake proxy logic.
+    const ENO_STAKE_PERMISSION: u64 = 28;
+
+    /// Permissions
+    inline fun check_signer_permission(s: &signer) {
+        assert!(
+            permissioned_signer::check_permission_exists(s, StakeProxyPermission {}),
+            error::permission_denied(ENO_STAKE_PERMISSION),
+        );
+    }
+
+    /// Grant permission to mutate staking on behalf of the master signer.
+    public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+        permissioned_signer::authorize(master, permissioned_signer, 1, StakeProxyPermission {})
+    }
+
     public entry fun set_operator(owner: &signer, old_operator: address, new_operator: address) {
         set_vesting_contract_operator(owner, old_operator, new_operator);
         set_staking_contract_operator(owner, old_operator, new_operator);
@@ -19,6 +39,7 @@ module aptos_framework::staking_proxy {
     }
 
     public entry fun set_vesting_contract_operator(owner: &signer, old_operator: address, new_operator: address) {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         let vesting_contracts = &vesting::vesting_contracts(owner_address);
         vector::for_each_ref(vesting_contracts, |vesting_contract| {
@@ -31,6 +52,7 @@ module aptos_framework::staking_proxy {
     }
 
     public entry fun set_staking_contract_operator(owner: &signer, old_operator: address, new_operator: address) {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         if (staking_contract::staking_contract_exists(owner_address, old_operator)) {
             let current_commission_percentage = staking_contract::commission_percentage(owner_address, old_operator);
@@ -39,6 +61,7 @@ module aptos_framework::staking_proxy {
     }
 
     public entry fun set_stake_pool_operator(owner: &signer, new_operator: address) {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         if (stake::stake_pool_exists(owner_address)) {
             stake::set_operator(owner, new_operator);
@@ -46,6 +69,7 @@ module aptos_framework::staking_proxy {
     }
 
     public entry fun set_vesting_contract_voter(owner: &signer, operator: address, new_voter: address) {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         let vesting_contracts = &vesting::vesting_contracts(owner_address);
         vector::for_each_ref(vesting_contracts, |vesting_contract| {
@@ -57,6 +81,7 @@ module aptos_framework::staking_proxy {
     }
 
     public entry fun set_staking_contract_voter(owner: &signer, operator: address, new_voter: address) {
+        check_signer_permission(owner);
         let owner_address = signer::address_of(owner);
         if (staking_contract::staking_contract_exists(owner_address, operator)) {
             staking_contract::update_voter(owner, operator, new_voter);
@@ -64,6 +89,7 @@ module aptos_framework::staking_proxy {
     }
 
     public entry fun set_stake_pool_voter(owner: &signer, new_voter: address) {
+        check_signer_permission(owner);
         if (stake::stake_pool_exists(signer::address_of(owner))) {
             stake::set_delegated_voter(owner, new_voter);
         };
diff --git a/aptos-move/framework/aptos-framework/sources/staking_proxy.spec.move b/aptos-move/framework/aptos-framework/sources/staking_proxy.spec.move
index a1da120f0eed4..d5b6ba29ad82e 100644
--- a/aptos-move/framework/aptos-framework/sources/staking_proxy.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/staking_proxy.spec.move
@@ -125,9 +125,12 @@ spec aptos_framework::staking_proxy {
     }
 
     spec schema SetStakePoolOperator {
+        use aptos_framework::permissioned_signer;
+
         owner: &signer;
         new_operator: address;
 
+        aborts_if permissioned_signer::spec_is_permissioned_signer(owner);
         let owner_address = signer::address_of(owner);
         let ownership_cap = borrow_global(owner_address);
         let pool_address = ownership_cap.pool_address;
@@ -169,9 +172,12 @@ spec aptos_framework::staking_proxy {
     }
 
     spec schema SetStakePoolVoterAbortsIf {
+        use aptos_framework::permissioned_signer;
+
         owner: &signer;
         new_voter: address;
 
+        aborts_if permissioned_signer::spec_is_permissioned_signer(owner);
         let owner_address = signer::address_of(owner);
         let ownership_cap = global(owner_address);
         let pool_address = ownership_cap.pool_address;
diff --git a/aptos-move/framework/aptos-framework/sources/vesting.move b/aptos-move/framework/aptos-framework/sources/vesting.move
index 9ede3acfa3d03..1a25757b81903 100644
--- a/aptos-move/framework/aptos-framework/sources/vesting.move
+++ b/aptos-move/framework/aptos-framework/sources/vesting.move
@@ -53,6 +53,7 @@ module aptos_framework::vesting {
     use aptos_framework::staking_contract;
     use aptos_framework::system_addresses;
     use aptos_framework::timestamp;
+    use aptos_framework::permissioned_signer;
 
     friend aptos_framework::genesis;
 
@@ -1142,6 +1143,7 @@ module aptos_framework::vesting {
     }
 
     fun verify_admin(admin: &signer, vesting_contract: &VestingContract) {
+        permissioned_signer::assert_master_signer(admin);
         assert!(signer::address_of(admin) == vesting_contract.admin, error::unauthenticated(ENOT_ADMIN));
     }
 
diff --git a/aptos-move/framework/aptos-framework/sources/vesting.spec.move b/aptos-move/framework/aptos-framework/sources/vesting.spec.move
index 3bcbcbb11c0de..1aa491d30089f 100644
--- a/aptos-move/framework/aptos-framework/sources/vesting.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/vesting.spec.move
@@ -530,6 +530,7 @@ spec aptos_framework::vesting {
     }
 
     spec verify_admin(admin: &signer, vesting_contract: &VestingContract) {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(admin);
         /// [high-level-req-9]
         aborts_if signer::address_of(admin) != vesting_contract.admin;
     }
@@ -630,6 +631,8 @@ spec aptos_framework::vesting {
     spec schema VerifyAdminAbortsIf {
         contract_address: address;
         admin: signer;
+
+        aborts_if permissioned_signer::spec_is_permissioned_signer(admin);
         aborts_if !exists(contract_address);
         let vesting_contract = global(contract_address);
         aborts_if signer::address_of(admin) != vesting_contract.admin;
diff --git a/aptos-move/framework/aptos-framework/sources/voting.move b/aptos-move/framework/aptos-framework/sources/voting.move
index a10e795b7369f..4dd70ad10e580 100644
--- a/aptos-move/framework/aptos-framework/sources/voting.move
+++ b/aptos-move/framework/aptos-framework/sources/voting.move
@@ -34,6 +34,7 @@ module aptos_framework::voting {
 
     use aptos_framework::account;
     use aptos_framework::event::{Self, EventHandle};
+    use aptos_framework::permissioned_signer;
     use aptos_framework::timestamp;
     use aptos_framework::transaction_context;
     use aptos_std::from_bcs;
@@ -189,6 +190,7 @@ module aptos_framework::voting {
     }
 
     public fun register(account: &signer) {
+        permissioned_signer::assert_master_signer(account);
         let addr = signer::address_of(account);
         assert!(!exists>(addr), error::already_exists(EVOTING_FORUM_ALREADY_REGISTERED));
 
diff --git a/aptos-move/framework/aptos-framework/sources/voting.spec.move b/aptos-move/framework/aptos-framework/sources/voting.spec.move
index a1c05091e54b6..29681b5fa54bb 100644
--- a/aptos-move/framework/aptos-framework/sources/voting.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/voting.spec.move
@@ -44,6 +44,7 @@ spec aptos_framework::voting {
     }
 
     spec register(account: &signer) {
+        aborts_if permissioned_signer::spec_is_permissioned_signer(account);
         let addr = signer::address_of(account);
 
         // Will abort if there's already a `VotingForum` under addr