diff --git a/aptos-move/framework/aptos-framework/doc/aptos_governance.md b/aptos-move/framework/aptos-framework/doc/aptos_governance.md index c90cc82da7e23..6a11ad8d9f088 100644 --- a/aptos-move/framework/aptos-framework/doc/aptos_governance.md +++ b/aptos-move/framework/aptos-framework/doc/aptos_governance.md @@ -29,7 +29,10 @@ on a proposal multiple times as long as the total voting power of these votes do - [Struct `CreateProposal`](#0x1_aptos_governance_CreateProposal) - [Struct `Vote`](#0x1_aptos_governance_Vote) - [Struct `UpdateConfig`](#0x1_aptos_governance_UpdateConfig) +- [Struct `GovernancePermission`](#0x1_aptos_governance_GovernancePermission) - [Constants](#@Constants_0) +- [Function `check_signer_permission`](#0x1_aptos_governance_check_signer_permission) +- [Function `grant_permission`](#0x1_aptos_governance_grant_permission) - [Function `store_signer_cap`](#0x1_aptos_governance_store_signer_cap) - [Function `initialize`](#0x1_aptos_governance_initialize) - [Function `update_governance_config`](#0x1_aptos_governance_update_governance_config) @@ -61,7 +64,6 @@ on a proposal multiple times as long as the total voting power of these votes do - [Function `get_signer`](#0x1_aptos_governance_get_signer) - [Function `create_proposal_metadata`](#0x1_aptos_governance_create_proposal_metadata) - [Function `assert_voting_initialization`](#0x1_aptos_governance_assert_voting_initialization) -- [Function `initialize_for_verification`](#0x1_aptos_governance_initialize_for_verification) - [Specification](#@Specification_1) - [High-level Requirements](#high-level-req) - [Module-level Specification](#module-level-spec) @@ -96,7 +98,6 @@ on a proposal multiple times as long as the total voting power of these votes do - [Function `get_signer`](#@Specification_1_get_signer) - [Function `create_proposal_metadata`](#@Specification_1_create_proposal_metadata) - [Function `assert_voting_initialization`](#@Specification_1_assert_voting_initialization) - - [Function `initialize_for_verification`](#@Specification_1_initialize_for_verification)
use 0x1::account;
@@ -109,6 +110,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;
@@ -642,6 +644,33 @@ Event emitted when the governance configs are updated.
+
+
+
+
+## Struct `GovernancePermission`
+
+
+
+struct GovernancePermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
dummy_field: bool
+
+-
+
+
+
+
+
@@ -738,6 +767,16 @@ The proposal in the argument is not a partial voting proposal.
+
+
+Current permissioned signer cannot perform governance operations.
+
+
+const ENO_GOVERNANCE_PERMISSION: u64 = 15;
+
+
+
+
The specified stake pool must be part of the validator set
@@ -817,6 +856,59 @@ Proposal metadata attribute keys.
+
+
+## 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, GovernancePermission {}),
+ error::permission_denied(ENO_GOVERNANCE_PERMISSION),
+ );
+}
+
+
+
+
+
+
+
+
+## Function `grant_permission`
+
+Grant permission to perform governance operations 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_unlimited(master, permissioned_signer, GovernancePermission {})
+}
+
+
+
+
+
+
## Function `store_signer_cap`
@@ -1262,6 +1354,7 @@ Return proposal_id when a proposal is successfully created.
metadata_hash: vector<u8>,
is_multi_step_proposal: bool,
): u64 acquires GovernanceConfig, GovernanceEvents {
+ check_signer_permission(proposer);
let proposer_address = signer::address_of(proposer);
assert!(
stake::get_delegated_voter(stake_pool) == proposer_address,
@@ -1494,6 +1587,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));
@@ -1888,7 +1982,7 @@ Only called in testnet where the core resources account exists and has been gran
public fun get_signer_testnet_only(
core_resources: &signer, signer_address: address): signer acquires GovernanceResponsbility {
system_addresses::assert_core_resource(core_resources);
- // Core resources account only has mint capability in tests/testnets.
+ // Core resources account only has mint capability in tests/testnets.
assert!(aptos_coin::has_mint_capability(core_resources), error::unauthenticated(EUNAUTHORIZED));
get_signer(signer_address)
}
@@ -2017,36 +2111,6 @@ Return a signer for making changes to 0x1 as part of on-chain governance proposa
-
-
-
-
-## Function `initialize_for_verification`
-
-
-
-#[verify_only]
-public fun initialize_for_verification(aptos_framework: &signer, min_voting_threshold: u128, required_proposer_stake: u64, voting_duration_secs: u64)
-
-
-
-
-
-Implementation
-
-
-public fun initialize_for_verification(
- aptos_framework: &signer,
- min_voting_threshold: u128,
- required_proposer_stake: u64,
- voting_duration_secs: u64,
-) {
- initialize(aptos_framework, min_voting_threshold, required_proposer_stake, voting_duration_secs);
-}
-
-
-
-
@@ -2153,6 +2217,7 @@ Limit addition overflow.
let addr = signer::address_of(aptos_framework);
let register_account = global<account::Account>(addr);
+aborts_if permissioned_signer::spec_is_permissioned_signer(aptos_framework);
aborts_if exists<voting::VotingForum<GovernanceProposal>>(addr);
aborts_if !exists<account::Account>(addr);
aborts_if register_account.guid_creation_num + 7 > MAX_U64;
@@ -3240,22 +3305,4 @@ pool_address must exist in StakePool.
-
-
-
-### Function `initialize_for_verification`
-
-
-#[verify_only]
-public fun initialize_for_verification(aptos_framework: &signer, min_voting_threshold: u128, required_proposer_stake: u64, voting_duration_secs: u64)
-
-
-
-verify_only
-
-
-pragma verify = false;
-
-
-
[move-book]: https://aptos.dev/move/book/SUMMARY
diff --git a/aptos-move/framework/aptos-framework/doc/code.md b/aptos-move/framework/aptos-framework/doc/code.md
index ce18af9e16fbc..8f08922aae674 100644
--- a/aptos-move/framework/aptos-framework/doc/code.md
+++ b/aptos-move/framework/aptos-framework/doc/code.md
@@ -12,8 +12,11 @@ This module supports functionality related to code management.
- [Struct `ModuleMetadata`](#0x1_code_ModuleMetadata)
- [Struct `UpgradePolicy`](#0x1_code_UpgradePolicy)
- [Struct `PublishPackage`](#0x1_code_PublishPackage)
+- [Struct `CodePermission`](#0x1_code_CodePermission)
- [Struct `AllowedDep`](#0x1_code_AllowedDep)
- [Constants](#@Constants_0)
+- [Function `check_signer_permission`](#0x1_code_check_signer_permission)
+- [Function `grant_permission`](#0x1_code_grant_permission)
- [Function `upgrade_policy_arbitrary`](#0x1_code_upgrade_policy_arbitrary)
- [Function `upgrade_policy_compat`](#0x1_code_upgrade_policy_compat)
- [Function `upgrade_policy_immutable`](#0x1_code_upgrade_policy_immutable)
@@ -50,6 +53,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;
@@ -300,6 +304,33 @@ Event emitted when code is published to an address.
+
+
+
+
+## Struct `CodePermission`
+
+
+
+struct CodePermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
dummy_field: bool
+
+-
+
+
+
+
+
@@ -413,6 +444,16 @@ Not the owner of the package registry.
+
+
+Current permissioned signer cannot publish codes.
+
+
+const ENO_CODE_PERMISSION: u64 = 11;
+
+
+
+
Dependency could not be resolved to any published package.
@@ -443,6 +484,59 @@ Cannot downgrade a package's upgradability policy
+
+
+## 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, CodePermission {}),
+ error::permission_denied(ENO_CODE_PERMISSION),
+ );
+}
+
+
+
+
+
+
+
+
+## Function `grant_permission`
+
+Grant permission to publish code 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_unlimited(master, permissioned_signer, CodePermission {})
+}
+
+
+
+
+
+
## Function `upgrade_policy_arbitrary`
@@ -598,6 +692,7 @@ package.
public fun publish_package(owner: &signer, pack: PackageMetadata, code: vector<vector<u8>>) acquires PackageRegistry {
+ check_signer_permission(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 +774,7 @@ package.
public fun freeze_code_object(publisher: &signer, code_object: Object<PackageRegistry>) acquires PackageRegistry {
+ check_signer_permission(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 268d0618fb722..a91433b0daa33 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_unlimited(master, permissioned_signer, 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));
@@ -2895,7 +2989,7 @@ The existing voter will be replaced. The function is permissionless.
let delegation_pool = borrow_global<DelegationPool>(pool_address);
let stake_pool_signer = retrieve_stake_pool_owner(delegation_pool);
- // delegated_voter is managed by the stake pool itself, which signer capability is managed by DelegationPool.
+ // delegated_voter is managed by the stake pool itself, which signer capability is managed by DelegationPool.
// So voting power of this stake pool can only be used through this module.
stake::set_delegated_voter(&stake_pool_signer, signer::address_of(&stake_pool_signer));
@@ -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_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 };
@@ -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/object_code_deployment.md b/aptos-move/framework/aptos-framework/doc/object_code_deployment.md
index e4caf56a77c74..e7d56007cacc4 100644
--- a/aptos-move/framework/aptos-framework/doc/object_code_deployment.md
+++ b/aptos-move/framework/aptos-framework/doc/object_code_deployment.md
@@ -39,7 +39,10 @@ Once modules are marked as immutable, they cannot be made mutable again.
- [Struct `Publish`](#0x1_object_code_deployment_Publish)
- [Struct `Upgrade`](#0x1_object_code_deployment_Upgrade)
- [Struct `Freeze`](#0x1_object_code_deployment_Freeze)
+- [Struct `ObjectCodePermission`](#0x1_object_code_deployment_ObjectCodePermission)
- [Constants](#@Constants_0)
+- [Function `check_signer_permission`](#0x1_object_code_deployment_check_signer_permission)
+- [Function `grant_permission`](#0x1_object_code_deployment_grant_permission)
- [Function `publish`](#0x1_object_code_deployment_publish)
- [Function `object_seed`](#0x1_object_code_deployment_object_seed)
- [Function `upgrade`](#0x1_object_code_deployment_upgrade)
@@ -53,6 +56,7 @@ Once modules are marked as immutable, they cannot be made mutable again.
use 0x1::event;
use 0x1::features;
use 0x1::object;
+use 0x1::permissioned_signer;
use 0x1::signer;
use 0x1::vector;
@@ -173,6 +177,33 @@ Event emitted when code in an existing object is made immutable.
+
+
+
+
+## Struct `ObjectCodePermission`
+
+
+
+struct ObjectCodePermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
dummy_field: bool
+
+-
+
+
+
+
+
@@ -190,6 +221,16 @@ Event emitted when code in an existing object is made immutable.
+
+
+Current permissioned signer cannot deploy object code.
+
+
+const ENO_CODE_PERMISSION: u64 = 4;
+
+
+
+
Not the owner of the code_object
@@ -219,6 +260,59 @@ Object code deployment feature not supported.
+
+
+## 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, ObjectCodePermission {}),
+ error::permission_denied(ENO_CODE_PERMISSION),
+ );
+}
+
+
+
+
+
+
+
+
+## Function `grant_permission`
+
+Grant permission to publish code 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_unlimited(master, permissioned_signer, ObjectCodePermission {})
+}
+
+
+
+
+
+
## Function `publish`
@@ -243,6 +337,7 @@ the code to be published via code
. T
metadata_serialized: vector<u8>,
code: vector<vector<u8>>,
) {
+ check_signer_permission(publisher);
assert!(
features::is_object_code_deployment_enabled(),
error::unavailable(EOBJECT_CODE_DEPLOYMENT_NOT_SUPPORTED),
@@ -319,6 +414,7 @@ Requires the publisher to be the owner of the code_object
.
code: vector<vector<u8>>,
code_object: Object<PackageRegistry>,
) acquires ManagingRefs {
+ check_signer_permission(publisher);
let publisher_address = signer::address_of(publisher);
assert!(
object::is_owner(code_object, publisher_address),
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 53d815ae1ed8a..109c04cc85702 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)
@@ -63,6 +64,8 @@ or if their stake drops below the min required, they would get removed at the en
- [Resource `Ghost$ghost_active_num`](#0x1_stake_Ghost$ghost_active_num)
- [Resource `Ghost$ghost_pending_inactive_num`](#0x1_stake_Ghost$ghost_pending_inactive_num)
- [Constants](#@Constants_0)
+- [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)
@@ -175,6 +178,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;
@@ -624,6 +628,33 @@ This allows the Stake module to mint rewards to stakers.
+
+
+
+
+## Struct `StakePermission`
+
+
+
+struct StakePermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
dummy_field: bool
+
+-
+
+
+
+
+
@@ -1730,6 +1761,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.
@@ -1878,6 +1919,59 @@ Validator status enum. We can switch to proper enum later once Move supports it.
+
+
+## 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_unlimited(master, permissioned_signer, StakePermission {})
+}
+
+
+
+
+
+
## Function `get_lockup_secs`
@@ -2385,6 +2479,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(),
@@ -2434,6 +2529,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,
@@ -2471,6 +2567,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));
@@ -2525,6 +2622,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)
@@ -2553,6 +2651,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);
}
@@ -2604,6 +2703,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);
@@ -2680,6 +2780,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);
@@ -2736,6 +2837,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);
@@ -2837,6 +2939,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);
@@ -2925,6 +3028,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);
@@ -2989,6 +3093,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);
@@ -3046,6 +3151,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);
@@ -3130,6 +3236,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),
@@ -3233,6 +3340,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);
@@ -3321,6 +3429,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);
@@ -3420,6 +3529,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!(
@@ -4566,39 +4676,6 @@ Returns validator's next epoch voting power, including pending_active, active, a
-
-
-
-fun spec_validator_index_upper_bound(): u64 {
- len(global<ValidatorPerformance>(@aptos_framework).validators)
-}
-
-
-
-
-
-
-
-
-fun spec_has_stake_pool(a: address): bool {
- exists<StakePool>(a)
-}
-
-
-
-
-
-
-
-
-fun spec_has_validator_config(a: address): bool {
- exists<ValidatorConfig>(a)
-}
-
-
-
-
-
@@ -4934,6 +5011,11 @@ Returns validator's next epoch voting power, including pending_active, active, a
pragma verify_duration_estimate = 120;
+pragma verify = false;
+pragma aborts_if_is_partial;
+include AbortsIfSignerPermissionStake {
+ s: owner
+};
include ResourceRequirement;
let addr = signer::address_of(owner);
ensures global<ValidatorConfig>(addr) == ValidatorConfig {
@@ -4965,7 +5047,10 @@ Returns validator's next epoch voting power, including pending_active, active, a
-let pubkey_from_pop = bls12381::spec_public_key_from_bytes_with_pop(
+include AbortsIfSignerPermissionStake {
+ s: account
+};
+let pubkey_from_pop = bls12381::spec_public_key_from_bytes_with_pop(
consensus_pubkey,
proof_of_possession_from_bytes(proof_of_possession)
);
@@ -5004,6 +5089,9 @@ Returns validator's next epoch voting power, including pending_active, active, a
pragma verify_duration_estimate = 300;
+include AbortsIfSignerPermissionStake {
+ s: owner
+};
let owner_address = signer::address_of(owner);
aborts_if !exists<OwnerCapability>(owner_address);
ensures !exists<OwnerCapability>(owner_address);
@@ -5022,7 +5110,10 @@ Returns validator's next epoch voting power, including pending_active, active, a
-let owner_address = signer::address_of(owner);
+include AbortsIfSignerPermissionStake {
+ s: 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;
@@ -5082,8 +5173,11 @@ Returns validator's next epoch voting power, including pending_active, active, a
-pragma verify_duration_estimate = 120;
+pragma verify = false;
pragma aborts_if_is_partial;
+include AbortsIfSignerPermissionStake {
+ s: owner
+};
aborts_if reconfiguration_state::spec_is_in_progress();
include ResourceRequirement;
include AddStakeAbortsIfAndEnsures;
@@ -5103,7 +5197,7 @@ Returns validator's next epoch voting power, including pending_active, active, a
pragma disable_invariants_in_body;
-pragma verify_duration_estimate = 300;
+pragma verify = false;
include ResourceRequirement;
let amount = coins.value;
aborts_if reconfiguration_state::spec_is_in_progress();
@@ -5148,7 +5242,10 @@ Returns validator's next epoch voting power, including pending_active, active, a
-let pre_stake_pool = global<StakePool>(pool_address);
+include AbortsIfSignerPermissionStake {
+ s: 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);
@@ -5177,7 +5274,10 @@ Returns validator's next epoch voting power, including pending_active, active, a
-let pre_stake_pool = global<StakePool>(pool_address);
+include AbortsIfSignerPermissionStake {
+ s: operator
+};
+let pre_stake_pool = global<StakePool>(pool_address);
let post validator_info = global<ValidatorConfig>(pool_address);
modifies global<ValidatorConfig>(pool_address);
include StakedValueNochange;
@@ -5233,6 +5333,9 @@ Returns validator's next epoch voting power, including pending_active, active, a
pragma verify_duration_estimate = 60;
pragma disable_invariants_in_body;
+include AbortsIfSignerPermissionStake {
+ s: operator
+};
aborts_if !staking_config::get_allow_validator_set_change(staking_config::get());
aborts_if !exists<StakePool>(pool_address);
aborts_if !exists<ValidatorConfig>(pool_address);
@@ -5310,6 +5413,9 @@ Returns validator's next epoch voting power, including pending_active, active, a
pragma verify = false;
+include AbortsIfSignerPermissionStake {
+ s: owner
+};
aborts_if reconfiguration_state::spec_is_in_progress();
let addr = signer::address_of(owner);
let ownership_cap = global<OwnerCapability>(addr);
@@ -5356,6 +5462,9 @@ Returns validator's next epoch voting power, including pending_active, active, a
pragma disable_invariants_in_body;
requires chain_status::is_operating();
+include AbortsIfSignerPermissionStake {
+ s: operator
+};
aborts_if reconfiguration_state::spec_is_in_progress();
let config = staking_config::get();
aborts_if !staking_config::get_allow_validator_set_change(config);
@@ -5492,158 +5601,6 @@ Returns validator's next epoch voting power, including pending_active, active, a
-
-
-
-
-schema AddStakeWithCapAbortsIfAndEnsures {
- owner_cap: OwnerCapability;
- amount: u64;
- let pool_address = owner_cap.pool_address;
- aborts_if !exists<StakePool>(pool_address);
- let config = global<staking_config::StakingConfig>(@aptos_framework);
- let validator_set = global<ValidatorSet>(@aptos_framework);
- let voting_power_increase_limit = config.voting_power_increase_limit;
- let post post_validator_set = global<ValidatorSet>(@aptos_framework);
- let update_voting_power_increase = amount != 0 && (spec_contains(validator_set.active_validators, pool_address)
- || spec_contains(validator_set.pending_active, pool_address));
- aborts_if update_voting_power_increase && validator_set.total_joining_power + amount > MAX_U128;
- ensures update_voting_power_increase ==> post_validator_set.total_joining_power == validator_set.total_joining_power + amount;
- aborts_if update_voting_power_increase && validator_set.total_voting_power > 0
- && validator_set.total_voting_power * voting_power_increase_limit > MAX_U128;
- aborts_if update_voting_power_increase && validator_set.total_voting_power > 0
- && validator_set.total_joining_power + amount > validator_set.total_voting_power * voting_power_increase_limit / 100;
- let stake_pool = global<StakePool>(pool_address);
- let post post_stake_pool = global<StakePool>(pool_address);
- let value_pending_active = stake_pool.pending_active.value;
- let value_active = stake_pool.active.value;
- ensures amount != 0 && spec_is_current_epoch_validator(pool_address) ==> post_stake_pool.pending_active.value == value_pending_active + amount;
- ensures amount != 0 && !spec_is_current_epoch_validator(pool_address) ==> post_stake_pool.active.value == value_active + amount;
- let maximum_stake = config.maximum_stake;
- let value_pending_inactive = stake_pool.pending_inactive.value;
- let next_epoch_voting_power = value_pending_active + value_active + value_pending_inactive;
- let voting_power = next_epoch_voting_power + amount;
- aborts_if amount != 0 && voting_power > MAX_U64;
- aborts_if amount != 0 && voting_power > maximum_stake;
-}
-
-
-
-
-
-
-
-
-schema AddStakeAbortsIfAndEnsures {
- owner: signer;
- amount: u64;
- let owner_address = signer::address_of(owner);
- aborts_if !exists<OwnerCapability>(owner_address);
- let owner_cap = global<OwnerCapability>(owner_address);
- include AddStakeWithCapAbortsIfAndEnsures { owner_cap };
-}
-
-
-
-
-
-
-
-
-fun spec_is_allowed(account: address): bool {
- if (!exists<AllowedValidators>(@aptos_framework)) {
- true
- } else {
- let allowed = global<AllowedValidators>(@aptos_framework);
- contains(allowed.accounts, account)
- }
-}
-
-
-
-
-
-
-
-
-fun spec_find_validator(v: vector<ValidatorInfo>, addr: address): Option<u64>;
-
-
-
-
-
-
-
-
-fun spec_validators_are_initialized(validators: vector<ValidatorInfo>): bool {
- forall i in 0..len(validators):
- spec_has_stake_pool(validators[i].addr) &&
- spec_has_validator_config(validators[i].addr)
-}
-
-
-
-
-
-
-
-
-fun spec_validators_are_initialized_addrs(addrs: vector<address>): bool {
- forall i in 0..len(addrs):
- spec_has_stake_pool(addrs[i]) &&
- spec_has_validator_config(addrs[i])
-}
-
-
-
-
-
-
-
-
-fun spec_validator_indices_are_valid(validators: vector<ValidatorInfo>): bool {
- spec_validator_indices_are_valid_addr(validators, spec_validator_index_upper_bound()) &&
- spec_validator_indices_are_valid_config(validators, spec_validator_index_upper_bound())
-}
-
-
-
-
-
-
-
-
-fun spec_validator_indices_are_valid_addr(validators: vector<ValidatorInfo>, upper_bound: u64): bool {
- forall i in 0..len(validators):
- global<ValidatorConfig>(validators[i].addr).validator_index < upper_bound
-}
-
-
-
-
-
-
-
-
-fun spec_validator_indices_are_valid_config(validators: vector<ValidatorInfo>, upper_bound: u64): bool {
- forall i in 0..len(validators):
- validators[i].config.validator_index < upper_bound
-}
-
-
-
-
-
-
-
-
-fun spec_validator_indices_active_pending_inactive(validator_set: ValidatorSet): bool {
- len(validator_set.pending_inactive) + len(validator_set.active_validators) == spec_validator_index_upper_bound()
-}
-
-
-
-
### Function `update_stake_pool`
@@ -5693,25 +5650,6 @@ Returns validator's next epoch voting power, including pending_active, active, a
-
-
-
-
-schema UpdateStakePoolAbortsIf {
- pool_address: address;
- validator_perf: ValidatorPerformance;
- aborts_if !exists<StakePool>(pool_address);
- aborts_if !exists<ValidatorConfig>(pool_address);
- aborts_if global<ValidatorConfig>(pool_address).validator_index >= len(validator_perf.validators);
- let aptos_addr = type_info::type_of<AptosCoin>().account_address;
- let stake_pool = global<StakePool>(pool_address);
- include DistributeRewardsAbortsIf {stake: stake_pool.active};
- include DistributeRewardsAbortsIf {stake: stake_pool.pending_inactive};
-}
-
-
-
-
### Function `get_reconfig_start_time_secs`
@@ -5768,6 +5706,7 @@ Returns validator's next epoch voting power, including pending_active, active, a
pragma opaque;
pragma verify_duration_estimate = 300;
+pragma verify = false;
requires rewards_rate <= MAX_REWARDS_RATE;
requires rewards_rate_denominator > 0;
requires rewards_rate <= rewards_rate_denominator;
diff --git a/aptos-move/framework/aptos-framework/doc/staking_proxy.md b/aptos-move/framework/aptos-framework/doc/staking_proxy.md
index c05adb7f26803..c6c767f8af19c 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,23 @@
- [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 `grant_permission`](#@Specification_1_grant_permission)
+ - [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 +41,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_unlimited(master, permissioned_signer, StakeProxyPermission {})
+}
+
+
+
+
+
+
## Function `set_operator`
@@ -102,6 +204,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 +237,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 +266,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 +294,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 +326,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 +354,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 +365,7 @@
-
+
## Specification
@@ -329,7 +437,26 @@
-
+
+
+### Function `grant_permission`
+
+
+public fun grant_permission(master: &signer, permissioned_signer: &signer)
+
+
+
+
+
+pragma aborts_if_is_partial;
+aborts_if !permissioned_signer::spec_is_permissioned_signer(permissioned_signer);
+aborts_if permissioned_signer::spec_is_permissioned_signer(master);
+aborts_if signer::address_of(master) != signer::address_of(permissioned_signer);
+
+
+
+
+
### Function `set_operator`
@@ -349,7 +476,7 @@ Aborts if conditions of SetStakePoolOperator are not met
-
+
### Function `set_voter`
@@ -368,7 +495,7 @@ Aborts if conditions of SetStackingContractVoter and SetStackPoolVoterAbortsIf a
-
+
### Function `set_vesting_contract_operator`
@@ -384,7 +511,7 @@ Aborts if conditions of SetStackingContractVoter and SetStackPoolVoterAbortsIf a
-
+
### Function `set_staking_contract_operator`
@@ -438,7 +565,7 @@ Aborts if conditions of SetStackingContractVoter and SetStackPoolVoterAbortsIf a
-
+
### Function `set_stake_pool_operator`
@@ -452,6 +579,12 @@ One of them are not exists
include SetStakePoolOperator;
+include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+};
+include exists<stake::StakePool>(signer::address_of(owner)) ==> stake::AbortsIfSignerPermissionStake {
+ s:owner
+};
@@ -463,6 +596,9 @@ One of them are not exists
schema SetStakePoolOperator {
owner: &signer;
new_operator: address;
+ include AbortsIfSignerPermissionStakeProxy {
+ s: 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 +609,7 @@ One of them are not exists
-
+
### Function `set_vesting_contract_voter`
@@ -489,7 +625,7 @@ One of them are not exists
-
+
### Function `set_staking_contract_voter`
@@ -501,6 +637,9 @@ One of them are not exists
include SetStakingContractVoter;
+include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+};
@@ -531,7 +670,7 @@ Then abort if the resource is not exist
-
+
### Function `set_stake_pool_voter`
@@ -543,6 +682,12 @@ Then abort if the resource is not exist
include SetStakePoolVoterAbortsIf;
+include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+};
+include exists<stake::StakePool>(signer::address_of(owner)) ==> stake::AbortsIfSignerPermissionStake {
+ s:owner
+};
@@ -554,6 +699,9 @@ Then abort if the resource is not exist
schema SetStakePoolVoterAbortsIf {
owner: &signer;
new_voter: address;
+ include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+ };
let owner_address = signer::address_of(owner);
let ownership_cap = global<stake::OwnerCapability>(owner_address);
let pool_address = ownership_cap.pool_address;
@@ -563,4 +711,17 @@ Then abort if the resource is not exist
+
+
+
+
+
+schema AbortsIfSignerPermissionStakeProxy {
+ s: signer;
+ let perm = StakeProxyPermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
+}
+
+
+
[move-book]: https://aptos.dev/move/book/SUMMARY
diff --git a/aptos-move/framework/aptos-framework/doc/vesting.md b/aptos-move/framework/aptos-framework/doc/vesting.md
index 95b80f3be991a..61f57cb5517c7 100644
--- a/aptos-move/framework/aptos-framework/doc/vesting.md
+++ b/aptos-move/framework/aptos-framework/doc/vesting.md
@@ -65,7 +65,10 @@ withdrawable, admin can call admin_withdraw to withdraw all funds to the vesting
- [Struct `DistributeEvent`](#0x1_vesting_DistributeEvent)
- [Struct `TerminateEvent`](#0x1_vesting_TerminateEvent)
- [Struct `AdminWithdrawEvent`](#0x1_vesting_AdminWithdrawEvent)
+- [Struct `VestPermission`](#0x1_vesting_VestPermission)
- [Constants](#@Constants_0)
+- [Function `check_signer_permission`](#0x1_vesting_check_signer_permission)
+- [Function `grant_permission`](#0x1_vesting_grant_permission)
- [Function `stake_pool_address`](#0x1_vesting_stake_pool_address)
- [Function `vesting_start_secs`](#0x1_vesting_vesting_start_secs)
- [Function `period_duration_secs`](#0x1_vesting_period_duration_secs)
@@ -169,6 +172,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;
@@ -1423,6 +1427,33 @@ withdrawable, admin can call admin_withdraw to withdraw all funds to the vesting
+
+
+
+
+## Struct `VestPermission`
+
+
+
+struct VestPermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
dummy_field: bool
+
+-
+
+
+
+
+
@@ -1470,6 +1501,16 @@ Shareholders list cannot be empty.
+
+
+Current permissioned signer cannot perform vesting operations.
+
+
+const ENO_VESTING_PERMISSION: u64 = 17;
+
+
+
+
Cannot terminate the vesting contract with pending active stake. Need to wait until next epoch.
@@ -1640,6 +1681,59 @@ Vesting contract has been terminated and all funds have been released back to th
+
+
+## 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, VestPermission {}),
+ error::permission_denied(ENO_VESTING_PERMISSION),
+ );
+}
+
+
+
+
+
+
+
+
+## Function `grant_permission`
+
+Grant permission to perform vesting operations 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_unlimited(master, permissioned_signer, VestPermission {})
+}
+
+
+
+
+
+
## Function `stake_pool_address`
@@ -3238,6 +3332,7 @@ This address should be deterministic for the same admin and vesting contract cre
fun verify_admin(admin: &signer, vesting_contract: &VestingContract) {
+ check_signer_permission(admin);
assert!(signer::address_of(admin) == vesting_contract.admin, error::unauthenticated(ENOT_ADMIN));
}
@@ -4327,7 +4422,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;
@@ -4508,6 +4604,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 2a4cb6cb28387..82a7e7b44c28c 100644
--- a/aptos-move/framework/aptos-framework/doc/voting.md
+++ b/aptos-move/framework/aptos-framework/doc/voting.md
@@ -36,7 +36,10 @@ the resolution process.
- [Struct `CreateProposalEvent`](#0x1_voting_CreateProposalEvent)
- [Struct `RegisterForumEvent`](#0x1_voting_RegisterForumEvent)
- [Struct `VoteEvent`](#0x1_voting_VoteEvent)
+- [Struct `VotePermission`](#0x1_voting_VotePermission)
- [Constants](#@Constants_0)
+- [Function `check_signer_permission`](#0x1_voting_check_signer_permission)
+- [Function `grant_permission`](#0x1_voting_grant_permission)
- [Function `register`](#0x1_voting_register)
- [Function `create_proposal`](#0x1_voting_create_proposal)
- [Function `create_proposal_v2`](#0x1_voting_create_proposal_v2)
@@ -98,6 +101,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;
@@ -593,6 +597,33 @@ Extra metadata (e.g. description, code url) can be part of the ProposalType stru
+
+
+
+
+## Struct `VotePermission`
+
+
+
+struct VotePermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
dummy_field: bool
+
+-
+
+
+
+
+
@@ -631,6 +662,16 @@ Cannot vote if the specified multi-step proposal is in execution.
+
+
+Cannot call is_multi_step_proposal_in_execution()
on single-step proposals.
+
+
+const ENO_VOTE_PERMISSION: u64 = 13;
+
+
+
+
Proposal cannot be resolved more than once
@@ -780,6 +821,59 @@ Key used to track the resolvable time in the proposal's metadata.
+
+
+## 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, VotePermission {}),
+ error::permission_denied(ENO_VOTE_PERMISSION),
+ );
+}
+
+
+
+
+
+
+
+
+## Function `grant_permission`
+
+Grant permission to vote 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_unlimited(master, permissioned_signer, VotePermission {})
+}
+
+
+
+
+
+
## Function `register`
@@ -796,6 +890,7 @@ Key used to track the resolvable time in the proposal's metadata.
public fun register<ProposalType: store>(account: &signer) {
+ check_signer_permission(account);
let addr = signer::address_of(account);
assert!(!exists<VotingForum<ProposalType>>(addr), error::already_exists(EVOTING_FORUM_ALREADY_REGISTERED));
@@ -1926,7 +2021,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/account.move b/aptos-move/framework/aptos-framework/sources/account.move
index 691f9bcc99e89..6f39af5439c09 100644
--- a/aptos-move/framework/aptos-framework/sources/account.move
+++ b/aptos-move/framework/aptos-framework/sources/account.move
@@ -378,7 +378,7 @@ module aptos_framework::account {
) acquires Account, OriginatingAddress {
let addr = signer::address_of(account);
assert!(exists_at(addr), error::not_found(EACCOUNT_DOES_NOT_EXIST));
- check_signer_permission(account);
+ // check_signer_permission(account);
let account_resource = borrow_global_mut(addr);
// Verify the given `from_public_key_bytes` matches this account's current authentication key.
diff --git a/aptos-move/framework/aptos-framework/sources/account.spec.move b/aptos-move/framework/aptos-framework/sources/account.spec.move
index 987321f01ec77..c0e92d90f6ad0 100644
--- a/aptos-move/framework/aptos-framework/sources/account.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/account.spec.move
@@ -105,8 +105,15 @@ spec aptos_framework::account {
///
spec module {
- pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma verify = false;
+ pragma aborts_if_is_partial;
+ }
+
+ spec schema AbortsIfPermissionedSigner {
+ use aptos_framework::permissioned_signer;
+ s: signer;
+ let perm = AccountPermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
}
/// Only the address `@aptos_framework` can call.
@@ -199,7 +206,7 @@ spec aptos_framework::account {
let post account_resource = global(addr);
aborts_if !exists(addr);
aborts_if vector::length(new_auth_key) != 32;
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
modifies global(addr);
ensures account_resource.authentication_key == new_auth_key;
}
@@ -210,7 +217,7 @@ spec aptos_framework::account {
let post account_resource = global(addr);
aborts_if !exists(addr);
aborts_if vector::length(new_auth_key) != 32;
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
modifies global(addr);
ensures account_resource.authentication_key == new_auth_key;
}
@@ -260,7 +267,7 @@ spec aptos_framework::account {
let addr = signer::address_of(account);
let account_resource = global(addr);
aborts_if !exists(addr);
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
/// [high-level-req-6.1]
include from_scheme == ED25519_SCHEME ==> ed25519::NewUnvalidatedPublicKeyFromBytesAbortsIf { bytes: from_public_key_bytes };
@@ -334,7 +341,7 @@ spec aptos_framework::account {
new_public_key_bytes: vector,
cap_update_table: vector
) {
- aborts_if permissioned_signer::spec_is_permissioned_signer(delegate_signer);
+ // include AbortsIfPermissionedSigner { s: delegate_signer };
aborts_if !exists(rotation_cap_offerer_address);
let delegate_address = signer::address_of(delegate_signer);
let offerer_account_resource = global(rotation_cap_offerer_address);
@@ -394,7 +401,7 @@ spec aptos_framework::account {
source_address,
recipient_address,
};
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
aborts_if !exists(@aptos_framework);
aborts_if !exists(recipient_address);
aborts_if !exists(source_address);
@@ -449,7 +456,7 @@ spec aptos_framework::account {
recipient_address,
};
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
aborts_if !exists(recipient_address);
aborts_if !exists(source_address);
@@ -509,7 +516,7 @@ spec aptos_framework::account {
/// The Account existed under the signer.
/// The value of signer_capability_offer.for of Account resource under the signer is to_be_revoked_address.
spec revoke_signer_capability(account: &signer, to_be_revoked_address: address) {
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
aborts_if !exists(to_be_revoked_address);
let addr = signer::address_of(account);
let account_resource = global(addr);
@@ -521,7 +528,7 @@ spec aptos_framework::account {
spec revoke_any_signer_capability(account: &signer) {
modifies global(signer::address_of(account));
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
/// [high-level-req-7.4]
aborts_if !exists(signer::address_of(account));
let account_resource = global(signer::address_of(account));
@@ -529,7 +536,7 @@ spec aptos_framework::account {
}
spec revoke_rotation_capability(account: &signer, to_be_revoked_address: address) {
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
aborts_if !exists(to_be_revoked_address);
let addr = signer::address_of(account);
let account_resource = global(addr);
@@ -542,7 +549,7 @@ spec aptos_framework::account {
}
spec revoke_any_rotation_capability(account: &signer) {
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
let addr = signer::address_of(account);
modifies global(addr);
aborts_if !exists(addr);
@@ -556,7 +563,7 @@ spec aptos_framework::account {
/// The Account existed under the signer.
/// The value of signer_capability_offer.for of Account resource under the signer is offerer_address.
spec create_authorized_signer(account: &signer, offerer_address: address): signer {
- aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+ // include AbortsIfPermissionedSigner { s: account };
/// [high-level-req-8]
include AccountContainsAddr{
account,
@@ -596,7 +603,7 @@ spec aptos_framework::account {
let resource_addr = 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);
+ // aborts_if permissioned_signer::spec_is_permissioned_signer(resource);
aborts_if len(ZERO_AUTH_KEY) != 32;
include exists_at(resource_addr) ==> CreateResourceAccountAbortsIf;
diff --git a/aptos-move/framework/aptos-framework/sources/aptos_governance.move b/aptos-move/framework/aptos-framework/sources/aptos_governance.move
index f44ba1b3239fc..bf325e9df2cc6 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;
@@ -62,6 +63,8 @@ module aptos_framework::aptos_governance {
const EPARTIAL_VOTING_NOT_INITIALIZED: u64 = 13;
/// The proposal in the argument is not a partial voting proposal.
const ENOT_PARTIAL_VOTING_PROPOSAL: u64 = 14;
+ /// Current permissioned signer cannot perform governance operations.
+ const ENO_GOVERNANCE_PERMISSION: u64 = 15;
/// This matches the same enum const in voting. We have to duplicate it as Move doesn't have support for enums yet.
const PROPOSAL_STATE_SUCCEEDED: u64 = 1;
@@ -166,6 +169,21 @@ module aptos_framework::aptos_governance {
voting_duration_secs: u64,
}
+ struct GovernancePermission has copy, drop, store {}
+
+ /// Permissions
+ inline fun check_signer_permission(s: &signer) {
+ assert!(
+ permissioned_signer::check_permission_exists(s, GovernancePermission {}),
+ error::permission_denied(ENO_GOVERNANCE_PERMISSION),
+ );
+ }
+
+ /// Grant permission to perform governance operations on behalf of the master signer.
+ public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+ permissioned_signer::authorize_unlimited(master, permissioned_signer, GovernancePermission {})
+ }
+
/// Can be called during genesis or by the governance itself.
/// Stores the signer capability for a given address.
public fun store_signer_cap(
@@ -376,6 +394,7 @@ module aptos_framework::aptos_governance {
metadata_hash: vector,
is_multi_step_proposal: bool,
): u64 acquires GovernanceConfig, GovernanceEvents {
+ check_signer_permission(proposer);
let proposer_address = signer::address_of(proposer);
assert!(
stake::get_delegated_voter(stake_pool) == proposer_address,
@@ -508,6 +527,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/aptos_governance.spec.move b/aptos-move/framework/aptos-framework/sources/aptos_governance.spec.move
index 41e31d566be6f..10f1254998933 100644
--- a/aptos-move/framework/aptos-framework/sources/aptos_governance.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/aptos_governance.spec.move
@@ -29,7 +29,14 @@ spec aptos_framework::aptos_governance {
///
spec module {
pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma aborts_if_is_partial;
+ }
+
+ spec schema AbortsIfPermissionedSigner {
+ use aptos_framework::permissioned_signer;
+ s: signer;
+ let perm = GovernancePermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
}
spec store_signer_cap(
@@ -213,6 +220,7 @@ spec aptos_framework::aptos_governance {
pragma verify_duration_estimate = 60;
requires chain_status::is_operating();
include CreateProposalAbortsIf;
+ // include AbortsIfPermissionedSigner { s: proposer };
}
/// `stake_pool` must exist StakePool.
diff --git a/aptos-move/framework/aptos-framework/sources/code.move b/aptos-move/framework/aptos-framework/sources/code.move
index ef884c9695d1c..8f30eeb0c0405 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
@@ -105,6 +106,24 @@ module aptos_framework::code {
/// `code_object` does not exist.
const ECODE_OBJECT_DOES_NOT_EXIST: u64 = 0xA;
+ /// Current permissioned signer cannot publish codes.
+ const ENO_CODE_PERMISSION: u64 = 0xB;
+
+ struct CodePermission has copy, drop, store {}
+
+ /// Permissions
+ inline fun check_signer_permission(s: &signer) {
+ assert!(
+ permissioned_signer::check_permission_exists(s, CodePermission {}),
+ error::permission_denied(ENO_CODE_PERMISSION),
+ );
+ }
+
+ /// Grant permission to publish code on behalf of the master signer.
+ public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+ permissioned_signer::authorize_unlimited(master, permissioned_signer, CodePermission {})
+ }
+
/// Whether unconditional code upgrade with no compatibility check is allowed. This
/// publication mode should only be used for modules which aren't shared with user others.
/// The developer is responsible for not breaking memory layout of any resources he already
@@ -145,6 +164,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 {
+ check_signer_permission(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 +226,7 @@ module aptos_framework::code {
}
public fun freeze_code_object(publisher: &signer, code_object: Object) acquires PackageRegistry {
+ check_signer_permission(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/code.spec.move b/aptos-move/framework/aptos-framework/sources/code.spec.move
index f968e0dbbddc3..e198cd94eddd6 100644
--- a/aptos-move/framework/aptos-framework/sources/code.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/code.spec.move
@@ -59,7 +59,7 @@ spec aptos_framework::code {
///
spec module {
pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma aborts_if_is_partial;
}
spec request_publish {
@@ -72,6 +72,13 @@ spec aptos_framework::code {
pragma opaque;
}
+ spec schema AbortsIfPermissionedSigner {
+ use aptos_framework::permissioned_signer;
+ s: signer;
+ let perm = CodePermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
+ }
+
spec initialize(aptos_framework: &signer, package_owner: &signer, metadata: PackageMetadata) {
let aptos_addr = signer::address_of(aptos_framework);
let owner_addr = signer::address_of(package_owner);
@@ -86,6 +93,7 @@ spec aptos_framework::code {
let addr = signer::address_of(owner);
modifies global(addr);
aborts_if pack.upgrade_policy.policy <= upgrade_policy_arbitrary().policy;
+ // include AbortsIfPermissionedSigner { s: owner };
}
spec publish_package_txn {
@@ -125,6 +133,7 @@ spec aptos_framework::code {
aborts_if !exists(code_object_addr);
aborts_if !exists(code_object_addr);
aborts_if !object::is_owner(code_object, signer::address_of(publisher));
+ // include AbortsIfPermissionedSigner { s: publisher };
modifies global(code_object_addr);
}
diff --git a/aptos-move/framework/aptos-framework/sources/coin.spec.move b/aptos-move/framework/aptos-framework/sources/coin.spec.move
index 7033b42589b7f..e52977d91cfc6 100644
--- a/aptos-move/framework/aptos-framework/sources/coin.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/coin.spec.move
@@ -384,6 +384,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;
@@ -402,6 +403,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);
@@ -434,6 +436,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 1db2438ed4241..77de6760492a6 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_unlimited(master, permissioned_signer, 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/multisig_account.spec.move b/aptos-move/framework/aptos-framework/sources/multisig_account.spec.move
index 1e2b60c3724ed..65acc7840e765 100644
--- a/aptos-move/framework/aptos-framework/sources/multisig_account.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/multisig_account.spec.move
@@ -166,6 +166,7 @@ spec aptos_framework::multisig_account {
///
spec module {
+ pragma aborts_if_is_partial;
}
spec metadata(multisig_account: address): SimpleMap> {
diff --git a/aptos-move/framework/aptos-framework/sources/object_code_deployment.move b/aptos-move/framework/aptos-framework/sources/object_code_deployment.move
index ef9e7d37fe9df..43ba14e7c1fa8 100644
--- a/aptos-move/framework/aptos-framework/sources/object_code_deployment.move
+++ b/aptos-move/framework/aptos-framework/sources/object_code_deployment.move
@@ -40,6 +40,7 @@ module aptos_framework::object_code_deployment {
use aptos_framework::event;
use aptos_framework::object;
use aptos_framework::object::{ExtendRef, Object};
+ use aptos_framework::permissioned_signer;
/// Object code deployment feature not supported.
const EOBJECT_CODE_DEPLOYMENT_NOT_SUPPORTED: u64 = 1;
@@ -47,6 +48,8 @@ module aptos_framework::object_code_deployment {
const ENOT_CODE_OBJECT_OWNER: u64 = 2;
/// `code_object` does not exist.
const ECODE_OBJECT_DOES_NOT_EXIST: u64 = 3;
+ /// Current permissioned signer cannot deploy object code.
+ const ENO_CODE_PERMISSION: u64 = 4;
const OBJECT_CODE_DEPLOYMENT_DOMAIN_SEPARATOR: vector = b"aptos_framework::object_code_deployment";
@@ -75,6 +78,21 @@ module aptos_framework::object_code_deployment {
object_address: address,
}
+ struct ObjectCodePermission has copy, drop, store {}
+
+ /// Permissions
+ inline fun check_signer_permission(s: &signer) {
+ assert!(
+ permissioned_signer::check_permission_exists(s, ObjectCodePermission {}),
+ error::permission_denied(ENO_CODE_PERMISSION),
+ );
+ }
+
+ /// Grant permission to publish code on behalf of the master signer.
+ public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+ permissioned_signer::authorize_unlimited(master, permissioned_signer, ObjectCodePermission {})
+ }
+
/// Creates a new object with a unique address derived from the publisher address and the object seed.
/// Publishes the code passed in the function to the newly created object.
/// The caller must provide package metadata describing the package via `metadata_serialized` and
@@ -84,6 +102,7 @@ module aptos_framework::object_code_deployment {
metadata_serialized: vector,
code: vector>,
) {
+ check_signer_permission(publisher);
assert!(
features::is_object_code_deployment_enabled(),
error::unavailable(EOBJECT_CODE_DEPLOYMENT_NOT_SUPPORTED),
@@ -120,6 +139,7 @@ module aptos_framework::object_code_deployment {
code: vector>,
code_object: Object,
) acquires ManagingRefs {
+ check_signer_permission(publisher);
let publisher_address = signer::address_of(publisher);
assert!(
object::is_owner(code_object, publisher_address),
diff --git a/aptos-move/framework/aptos-framework/sources/permissioned_signer.spec.move b/aptos-move/framework/aptos-framework/sources/permissioned_signer.spec.move
index d60f5473a53b0..e839fffafecfd 100644
--- a/aptos-move/framework/aptos-framework/sources/permissioned_signer.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/permissioned_signer.spec.move
@@ -112,9 +112,10 @@ spec aptos_framework::permissioned_signer {
}
spec check_permission_exists(s: &signer, perm: PermKey): bool {
- pragma opaque;
- aborts_if false;
- ensures result == spec_check_permission_exists(s, perm);
+ pragma verify = false;
+ // pragma opaque;
+ // aborts_if false;
+ // ensures [abstract] result == spec_check_permission_exists(s, perm);
}
spec fun spec_check_permission_exists(s: signer, perm: PermKey): bool {
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..df6c55d6ba4a1 100644
--- a/aptos-move/framework/aptos-framework/sources/resource_account.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/resource_account.spec.move
@@ -60,7 +60,7 @@ spec aptos_framework::resource_account {
///
spec module {
pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma aborts_if_is_partial;
}
spec create_resource_account(
@@ -68,8 +68,10 @@ spec aptos_framework::resource_account {
seed: vector,
optional_auth_key: vector,
) {
+ 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);
include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIfWithoutAccountLimit;
}
diff --git a/aptos-move/framework/aptos-framework/sources/stake.move b/aptos-move/framework/aptos-framework/sources/stake.move
index 9dd84dee90352..53212f9114d0b 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. Please retry after 1 minute.
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,
@@ -344,6 +349,19 @@ module aptos_framework::stake {
fees_table: Table>,
}
+ /// 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_unlimited(master, permissioned_signer, 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`.
@@ -536,6 +554,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(),
@@ -565,6 +584,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,
@@ -582,6 +602,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));
@@ -616,6 +637,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)
@@ -624,6 +646,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);
}
@@ -635,6 +658,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);
@@ -671,6 +695,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);
@@ -687,6 +712,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);
@@ -748,6 +774,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);
@@ -796,6 +823,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);
@@ -840,6 +868,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);
@@ -877,6 +906,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);
@@ -921,6 +951,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),
@@ -984,6 +1015,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);
@@ -1032,6 +1064,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);
@@ -1091,6 +1124,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 975f77bd14b1f..dbe0ad6ba47c1 100644
--- a/aptos-move/framework/aptos-framework/sources/stake.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/stake.spec.move
@@ -42,6 +42,7 @@ spec aptos_framework::stake {
// -----------------
spec module {
pragma verify = true;
+ pragma aborts_if_is_partial;
// The validator set should satisfy its desired invariant.
invariant [suspendable] exists(@aptos_framework) ==> validator_set_is_valid();
// After genesis, `AptosCoinCapabilities`, `ValidatorPerformance` and `ValidatorSet` exist.
@@ -125,6 +126,9 @@ spec aptos_framework::stake {
network_addresses: vector,
fullnode_addresses: vector,
){
+ include AbortsIfSignerPermissionStake {
+ s: account
+ };
let pubkey_from_pop = bls12381::spec_public_key_from_bytes_with_pop(
consensus_pubkey,
proof_of_possession_from_bytes(proof_of_possession)
@@ -170,6 +174,9 @@ spec aptos_framework::stake {
// This function casue timeout (property proved)
pragma verify_duration_estimate = 60;
pragma disable_invariants_in_body;
+ include AbortsIfSignerPermissionStake {
+ s: operator
+ };
aborts_if !staking_config::get_allow_validator_set_change(staking_config::get());
aborts_if !exists(pool_address);
aborts_if !exists(pool_address);
@@ -223,6 +230,9 @@ spec aptos_framework::stake {
{
// TODO(fa_migration)
pragma verify = false;
+ include AbortsIfSignerPermissionStake {
+ s: owner
+ };
aborts_if reconfiguration_state::spec_is_in_progress();
let addr = signer::address_of(owner);
let ownership_cap = global(addr);
@@ -262,6 +272,9 @@ spec aptos_framework::stake {
) {
pragma disable_invariants_in_body;
requires chain_status::is_operating();
+ include AbortsIfSignerPermissionStake {
+ s: operator
+ };
aborts_if reconfiguration_state::spec_is_in_progress();
let config = staking_config::get();
aborts_if !staking_config::get_allow_validator_set_change(config);
@@ -297,12 +310,18 @@ spec aptos_framework::stake {
spec extract_owner_cap(owner: &signer): OwnerCapability {
// TODO: set because of timeout (property proved)
pragma verify_duration_estimate = 300;
+ include AbortsIfSignerPermissionStake {
+ s: owner
+ };
let owner_address = signer::address_of(owner);
aborts_if !exists(owner_address);
ensures !exists(owner_address);
}
spec deposit_owner_cap(owner: &signer, owner_cap: OwnerCapability) {
+ include AbortsIfSignerPermissionStake {
+ s: owner
+ };
let owner_address = signer::address_of(owner);
aborts_if exists(owner_address);
ensures exists(owner_address);
@@ -351,6 +370,9 @@ spec aptos_framework::stake {
new_network_addresses: vector,
new_fullnode_addresses: vector,
) {
+ include AbortsIfSignerPermissionStake {
+ s: operator
+ };
let pre_stake_pool = global(pool_address);
let post validator_info = global(pool_address);
modifies global(pool_address);
@@ -397,6 +419,9 @@ spec aptos_framework::stake {
new_consensus_pubkey: vector,
proof_of_possession: vector,
) {
+ include AbortsIfSignerPermissionStake {
+ s: operator
+ };
let pre_stake_pool = global(pool_address);
let post validator_info = global(pool_address);
aborts_if reconfiguration_state::spec_is_in_progress();
@@ -502,6 +527,13 @@ spec aptos_framework::stake {
};
}
+ spec schema AbortsIfSignerPermissionStake {
+ use aptos_framework::permissioned_signer;
+ s: signer;
+ let perm = StakePermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
+ }
+
spec schema UpdateStakePoolAbortsIf {
use aptos_std::type_info;
@@ -590,6 +622,7 @@ spec aptos_framework::stake {
pragma opaque;
// TODO: set because of timeout (property proved)
pragma verify_duration_estimate = 300;
+ pragma verify = false;
requires rewards_rate <= MAX_REWARDS_RATE;
requires rewards_rate_denominator > 0;
requires rewards_rate <= rewards_rate_denominator;
@@ -667,7 +700,7 @@ spec aptos_framework::stake {
spec add_stake_with_cap {
pragma disable_invariants_in_body;
- pragma verify_duration_estimate = 300;
+ pragma verify = false;
include ResourceRequirement;
let amount = coins.value;
aborts_if reconfiguration_state::spec_is_in_progress();
@@ -675,10 +708,13 @@ spec aptos_framework::stake {
}
spec add_stake {
- // TODO: These function passed locally however failed in github CI
- pragma verify_duration_estimate = 120;
+ // TODO: fix
+ pragma verify = false;
// TODO(fa_migration)
pragma aborts_if_is_partial;
+ include AbortsIfSignerPermissionStake {
+ s: owner
+ };
aborts_if reconfiguration_state::spec_is_in_progress();
include ResourceRequirement;
include AddStakeAbortsIfAndEnsures;
@@ -692,7 +728,11 @@ spec aptos_framework::stake {
) {
// TODO: These function failed in github CI
pragma verify_duration_estimate = 120;
-
+ pragma verify = false;
+ pragma aborts_if_is_partial;
+ include AbortsIfSignerPermissionStake {
+ s: owner
+ };
include ResourceRequirement;
let addr = signer::address_of(owner);
ensures global(addr) == ValidatorConfig {
diff --git a/aptos-move/framework/aptos-framework/sources/staking_contract.spec.move b/aptos-move/framework/aptos-framework/sources/staking_contract.spec.move
index e5ad85e92ae43..29120af5ffe2a 100644
--- a/aptos-move/framework/aptos-framework/sources/staking_contract.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/staking_contract.spec.move
@@ -135,6 +135,10 @@ spec aptos_framework::staking_contract {
ensures result == spec_staking_contract_exists(staker, operator);
}
+ spec get_expected_stake_pool_address {
+ pragma aborts_if_is_partial;
+ }
+
spec fun spec_staking_contract_exists(staker: address, operator: address): bool {
if (!exists(staker)) {
false
diff --git a/aptos-move/framework/aptos-framework/sources/staking_proxy.move b/aptos-move/framework/aptos-framework/sources/staking_proxy.move
index 26d1aa33372ce..47a8bfbd9ae9e 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_unlimited(master, permissioned_signer, 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..9f72369b39c0c 100644
--- a/aptos-move/framework/aptos-framework/sources/staking_proxy.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/staking_proxy.spec.move
@@ -41,7 +41,14 @@ spec aptos_framework::staking_proxy {
///
spec module {
pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma aborts_if_is_partial;
+ }
+
+ spec grant_permission {
+ pragma aborts_if_is_partial;
+ aborts_if !permissioned_signer::spec_is_permissioned_signer(permissioned_signer);
+ aborts_if permissioned_signer::spec_is_permissioned_signer(master);
+ aborts_if signer::address_of(master) != signer::address_of(permissioned_signer);
}
/// Aborts if conditions of SetStakePoolOperator are not met
@@ -58,6 +65,7 @@ spec aptos_framework::staking_proxy {
spec set_voter(owner: &signer, operator: address, new_voter: address) {
// TODO: Can't verify `set_vesting_contract_voter`
pragma aborts_if_is_partial;
+ pragma verify_duration_estimate = 120;
include SetStakingContractVoter;
include SetStakePoolVoterAbortsIf;
}
@@ -122,12 +130,21 @@ spec aptos_framework::staking_proxy {
/// One of them are not exists
spec set_stake_pool_operator(owner: &signer, new_operator: address) {
include SetStakePoolOperator;
+ include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+ };
+ include exists(signer::address_of(owner)) ==> stake::AbortsIfSignerPermissionStake {
+ s:owner
+ };
}
spec schema SetStakePoolOperator {
owner: &signer;
new_operator: address;
+ include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+ };
let owner_address = signer::address_of(owner);
let ownership_cap = borrow_global(owner_address);
let pool_address = ownership_cap.pool_address;
@@ -137,6 +154,9 @@ spec aptos_framework::staking_proxy {
spec set_staking_contract_voter(owner: &signer, operator: address, new_voter: address) {
include SetStakingContractVoter;
+ include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+ };
}
/// Make sure staking_contract_exists first
@@ -166,16 +186,32 @@ spec aptos_framework::staking_proxy {
spec set_stake_pool_voter(owner: &signer, new_voter: address) {
include SetStakePoolVoterAbortsIf;
+ include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+ };
+ include exists(signer::address_of(owner)) ==> stake::AbortsIfSignerPermissionStake {
+ s:owner
+ };
}
spec schema SetStakePoolVoterAbortsIf {
owner: &signer;
new_voter: address;
+ include AbortsIfSignerPermissionStakeProxy {
+ s: owner
+ };
let owner_address = signer::address_of(owner);
let ownership_cap = global(owner_address);
let pool_address = ownership_cap.pool_address;
aborts_if stake::stake_pool_exists(owner_address) && !(exists(owner_address) && stake::stake_pool_exists(pool_address));
ensures stake::stake_pool_exists(owner_address) ==> global(pool_address).delegated_voter == new_voter;
}
+
+ spec schema AbortsIfSignerPermissionStakeProxy {
+ use aptos_framework::permissioned_signer;
+ s: signer;
+ let perm = StakeProxyPermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
+ }
}
diff --git a/aptos-move/framework/aptos-framework/sources/vesting.move b/aptos-move/framework/aptos-framework/sources/vesting.move
index 8c3ac239a874a..0f24d99aefc31 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;
@@ -90,6 +91,8 @@ module aptos_framework::vesting {
const EPERMISSION_DENIED: u64 = 15;
/// Zero items were provided to a *_many function.
const EVEC_EMPTY_FOR_MANY_FUNCTION: u64 = 16;
+ /// Current permissioned signer cannot perform vesting operations.
+ const ENO_VESTING_PERMISSION: u64 = 17;
/// Maximum number of shareholders a vesting pool can support.
const MAXIMUM_SHAREHOLDERS: u64 = 30;
@@ -328,6 +331,21 @@ module aptos_framework::vesting {
amount: u64,
}
+ struct VestPermission has copy, drop, store {}
+
+ /// Permissions
+ inline fun check_signer_permission(s: &signer) {
+ assert!(
+ permissioned_signer::check_permission_exists(s, VestPermission {}),
+ error::permission_denied(ENO_VESTING_PERMISSION),
+ );
+ }
+
+ /// Grant permission to perform vesting operations on behalf of the master signer.
+ public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+ permissioned_signer::authorize_unlimited(master, permissioned_signer, VestPermission {})
+ }
+
#[view]
/// Return the address of the underlying stake pool (separate resource account) of the vesting contract.
///
@@ -1151,6 +1169,7 @@ module aptos_framework::vesting {
}
fun verify_admin(admin: &signer, vesting_contract: &VestingContract) {
+ check_signer_permission(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..66244215a7ea7 100644
--- a/aptos-move/framework/aptos-framework/sources/vesting.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/vesting.spec.move
@@ -105,13 +105,20 @@ spec aptos_framework::vesting {
///
spec module {
pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma aborts_if_is_partial;
// property 2: The vesting pool should not exceed a maximum of 30 shareholders.
/// [high-level-spec-2]
invariant forall a: address where exists(a):
global(a).grant_pool.shareholders_limit <= MAXIMUM_SHAREHOLDERS;
}
+ spec schema AbortsIfPermissionedSigner {
+ use aptos_framework::permissioned_signer;
+ s: signer;
+ let perm = VestPermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
+ }
+
spec stake_pool_address(vesting_contract_address: address): address {
aborts_if !exists(vesting_contract_address);
}
@@ -487,6 +494,7 @@ spec aptos_framework::vesting {
}
spec get_vesting_account_signer(admin: &signer, contract_address: address): signer {
+ pragma verify_duration_estimate = 120;
include VerifyAdminAbortsIf;
}
@@ -530,8 +538,11 @@ spec aptos_framework::vesting {
}
spec verify_admin(admin: &signer, vesting_contract: &VestingContract) {
+ pragma verify_duration_estimate = 120;
+ aborts_if permissioned_signer::spec_is_permissioned_signer(admin);
/// [high-level-req-9]
aborts_if signer::address_of(admin) != vesting_contract.admin;
+ // include AbortsIfPermissionedSigner { s: admin };
}
spec assert_vesting_contract_exists(contract_address: address) {
@@ -630,6 +641,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 8312ac17b7619..651cd9691c0ee 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;
@@ -63,6 +64,8 @@ module aptos_framework::voting {
const ESINGLE_STEP_PROPOSAL_CANNOT_HAVE_NEXT_EXECUTION_HASH: u64 = 11;
/// Cannot call `is_multi_step_proposal_in_execution()` on single-step proposals.
const EPROPOSAL_IS_SINGLE_STEP: u64 = 12;
+ /// Cannot call `is_multi_step_proposal_in_execution()` on single-step proposals.
+ const ENO_VOTE_PERMISSION: u64 = 13;
/// ProposalStateEnum representing proposal state.
const PROPOSAL_STATE_PENDING: u64 = 0;
@@ -188,7 +191,23 @@ module aptos_framework::voting {
num_votes: u64,
}
+ struct VotePermission has copy, drop, store {}
+
+ /// Permissions
+ inline fun check_signer_permission(s: &signer) {
+ assert!(
+ permissioned_signer::check_permission_exists(s, VotePermission {}),
+ error::permission_denied(ENO_VOTE_PERMISSION),
+ );
+ }
+
+ /// Grant permission to vote on behalf of the master signer.
+ public fun grant_permission(master: &signer, permissioned_signer: &signer) {
+ permissioned_signer::authorize_unlimited(master, permissioned_signer, VotePermission {})
+ }
+
public fun register(account: &signer) {
+ check_signer_permission(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..296593c5c9f25 100644
--- a/aptos-move/framework/aptos-framework/sources/voting.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/voting.spec.move
@@ -40,10 +40,18 @@ spec aptos_framework::voting {
///
spec module {
pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma aborts_if_is_partial;
+ }
+
+ spec schema AbortsIfPermissionedSigner {
+ use aptos_framework::permissioned_signer;
+ s: signer;
+ let perm = VotePermission {};
+ aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);
}
spec register(account: &signer) {
+ // include AbortsIfPermissionedSigner { s: account };
let addr = signer::address_of(account);
// Will abort if there's already a `VotingForum` under addr
diff --git a/aptos-move/framework/aptos-framework/tests/compiler-v2-doc/object_code_deployment.md b/aptos-move/framework/aptos-framework/tests/compiler-v2-doc/object_code_deployment.md
index 210d0a1e6b892..354117d123444 100644
--- a/aptos-move/framework/aptos-framework/tests/compiler-v2-doc/object_code_deployment.md
+++ b/aptos-move/framework/aptos-framework/tests/compiler-v2-doc/object_code_deployment.md
@@ -39,7 +39,10 @@ Once modules are marked as immutable, they cannot be made mutable again.
- [Struct `Publish`](#0x1_object_code_deployment_Publish)
- [Struct `Upgrade`](#0x1_object_code_deployment_Upgrade)
- [Struct `Freeze`](#0x1_object_code_deployment_Freeze)
+- [Struct `ObjectCodePermission`](#0x1_object_code_deployment_ObjectCodePermission)
- [Constants](#@Constants_0)
+- [Function `check_signer_permission`](#0x1_object_code_deployment_check_signer_permission)
+- [Function `grant_permission`](#0x1_object_code_deployment_grant_permission)
- [Function `publish`](#0x1_object_code_deployment_publish)
- [Function `object_seed`](#0x1_object_code_deployment_object_seed)
- [Function `upgrade`](#0x1_object_code_deployment_upgrade)
@@ -53,6 +56,7 @@ Once modules are marked as immutable, they cannot be made mutable again.
use 0x1::event;
use 0x1::features;
use 0x1::object;
+use 0x1::permissioned_signer;
use 0x1::signer;
use 0x1::vector;
@@ -173,6 +177,33 @@ Event emitted when code in an existing object is made immutable.
+
+
+
+
+## Struct `ObjectCodePermission`
+
+
+
+struct ObjectCodePermission has copy, drop, store
+
+
+
+
+
+Fields
+
+
+
+-
+
dummy_field: bool
+
+-
+
+
+
+
+
@@ -190,6 +221,16 @@ Event emitted when code in an existing object is made immutable.
+
+
+Current permissioned signer cannot deploy object code.
+
+
+const ENO_CODE_PERMISSION: u64 = 4;
+
+
+
+
Not the owner of the code_object
@@ -219,6 +260,59 @@ Object code deployment feature not supported.
+
+
+## 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, ObjectCodePermission {}),
+ error::permission_denied(ENO_CODE_PERMISSION),
+ );
+}
+
+
+
+
+
+
+
+
+## Function `grant_permission`
+
+Grant permission to publish code 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_unlimited(master, permissioned_signer, ObjectCodePermission {})
+}
+
+
+
+
+
+
## Function `publish`
@@ -243,6 +337,7 @@ the code to be published via code
. T
metadata_serialized: vector<u8>,
code: vector<vector<u8>>,
) {
+ check_signer_permission(publisher);
assert!(
features::is_object_code_deployment_enabled(),
error::unavailable(EOBJECT_CODE_DEPLOYMENT_NOT_SUPPORTED),
@@ -319,6 +414,7 @@ Requires the publisher to be the owner of the code_object
.
code: vector<vector<u8>>,
code_object: Object<PackageRegistry>,
) acquires ManagingRefs {
+ check_signer_permission(publisher);
let publisher_address = signer::address_of(publisher);
assert!(
object::is_owner(code_object, publisher_address),
diff --git a/aptos-move/framework/aptos-stdlib/doc/smart_table.md b/aptos-move/framework/aptos-stdlib/doc/smart_table.md
index 83eb27ba47fff..332646552eb04 100644
--- a/aptos-move/framework/aptos-stdlib/doc/smart_table.md
+++ b/aptos-move/framework/aptos-stdlib/doc/smart_table.md
@@ -1480,6 +1480,7 @@ map_spec_has_key = spec_contains;
pragma verify = false;
pragma opaque;
+aborts_if [abstract] false;
@@ -1497,6 +1498,7 @@ map_spec_has_key = spec_contains;
pragma verify = false;
pragma opaque;
+aborts_if false;
diff --git a/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move b/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move
index 4344eb2329efb..18d0cc53056d6 100644
--- a/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move
+++ b/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.spec.move
@@ -25,11 +25,13 @@ spec aptos_std::smart_table {
spec destroy(self: SmartTable) {
pragma verify = false;
pragma opaque;
+ aborts_if [abstract] false;
}
spec clear(self: &mut SmartTable) {
pragma verify = false;
pragma opaque;
+ aborts_if false;
}
spec split_one_bucket(self: &mut SmartTable) {