accoun
public entry fun revoke_signer_capability(account: &signer, to_be_revoked_address: address) acquires Account {
assert!(exists_at(to_be_revoked_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));
+ check_offering_permission(account);
let addr = signer::address_of(account);
let account_resource = borrow_global<Account>(addr);
assert!(
@@ -1884,6 +2066,7 @@ Revoke any signer capability offer in the specified account.
public entry fun revoke_any_signer_capability(account: &signer) acquires Account {
+ check_offering_permission(account);
let account_resource = borrow_global_mut<Account>(signer::address_of(account));
option::extract(&mut account_resource.signer_capability_offer.for);
}
@@ -1911,6 +2094,7 @@ at the offerer's address.
public fun create_authorized_signer(account: &signer, offerer_address: address): signer acquires Account {
+ check_offering_permission(account);
assert!(exists_at(offerer_address), error::not_found(EOFFERER_ADDRESS_DOES_NOT_EXIST));
// Check if there's an existing signer capability offer from the offerer.
@@ -2514,8 +2698,8 @@ Capability based functions for efficient use.
### Module-level Specification
-pragma verify = true;
-pragma aborts_if_is_strict;
+pragma verify = false;
+pragma aborts_if_is_partial;
diff --git a/aptos-move/framework/aptos-framework/doc/big_ordered_map.md b/aptos-move/framework/aptos-framework/doc/big_ordered_map.md
index dac43b4483ca1..2a32bfce29e72 100644
--- a/aptos-move/framework/aptos-framework/doc/big_ordered_map.md
+++ b/aptos-move/framework/aptos-framework/doc/big_ordered_map.md
@@ -363,51 +363,51 @@ The BigOrderedMap data structure.
## Constants
-
+
-Map key already exists
+Internal errors.
-const EKEY_ALREADY_EXISTS: u64 = 1;
+const EINTERNAL_INVARIANT_BROKEN: u64 = 20;
-
+
-Map key is not found
-const EKEY_NOT_FOUND: u64 = 2;
+const NULL_INDEX: u64 = 0;
-
+
-Internal errors.
+Trying to do an operation on an IteratorPtr that would go out of bounds
-const EINTERNAL_INVARIANT_BROKEN: u64 = 20;
+const EITER_OUT_OF_BOUNDS: u64 = 3;
-
+
+Map key already exists
-const NULL_INDEX: u64 = 0;
+const EKEY_ALREADY_EXISTS: u64 = 1;
-
+
-Trying to do an operation on an IteratorPtr that would go out of bounds
+Map key is not found
-const EITER_OUT_OF_BOUNDS: u64 = 3;
+const EKEY_NOT_FOUND: u64 = 2;
diff --git a/aptos-move/framework/aptos-framework/doc/multisig_account.md b/aptos-move/framework/aptos-framework/doc/multisig_account.md
index 0b6aa0e44dd7a..4e9e9de51191b 100644
--- a/aptos-move/framework/aptos-framework/doc/multisig_account.md
+++ b/aptos-move/framework/aptos-framework/doc/multisig_account.md
@@ -4166,7 +4166,8 @@ Add new owners, remove owners to remove, update signatures required.
-aborts_if !exists<account::Account>(creator);
+pragma aborts_if_is_partial;
+aborts_if !exists<account::Account>(creator);
let owner_nonce = global<account::Account>(creator).sequence_number;
diff --git a/aptos-move/framework/aptos-framework/doc/ordered_map.md b/aptos-move/framework/aptos-framework/doc/ordered_map.md
index 85c456769972d..cd90763cdbfe8 100644
--- a/aptos-move/framework/aptos-framework/doc/ordered_map.md
+++ b/aptos-move/framework/aptos-framework/doc/ordered_map.md
@@ -221,31 +221,31 @@ TODO: Once fields can be (mutable) references, this class will be deprecated.
## Constants
-
+
-Map key already exists
-const EKEY_ALREADY_EXISTS: u64 = 1;
+const EITER_OUT_OF_BOUNDS: u64 = 3;
-
+
-Map key is not found
+Map key already exists
-const EKEY_NOT_FOUND: u64 = 2;
+const EKEY_ALREADY_EXISTS: u64 = 1;
-
+
+Map key is not found
-const EITER_OUT_OF_BOUNDS: u64 = 3;
+const EKEY_NOT_FOUND: u64 = 2;
diff --git a/aptos-move/framework/aptos-framework/doc/resource_account.md b/aptos-move/framework/aptos-framework/doc/resource_account.md
index 318d15a785de7..45d7d6ebc4914 100644
--- a/aptos-move/framework/aptos-framework/doc/resource_account.md
+++ b/aptos-move/framework/aptos-framework/doc/resource_account.md
@@ -468,7 +468,7 @@ the SignerCapability.
pragma verify = true;
-pragma aborts_if_is_strict;
+pragma aborts_if_is_partial;
@@ -547,7 +547,8 @@ the SignerCapability.
-let resource_addr = signer::address_of(resource);
+pragma aborts_if_is_partial;
+let resource_addr = signer::address_of(resource);
// This enforces high-level requirement 1:
include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIf;
// This enforces high-level requirement 2:
@@ -618,7 +619,8 @@ the SignerCapability.
-// This enforces high-level requirement 6:
+pragma aborts_if_is_partial;
+// 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/staking_contract.md b/aptos-move/framework/aptos-framework/doc/staking_contract.md
index 09ecf20686f86..43d969b13b988 100644
--- a/aptos-move/framework/aptos-framework/doc/staking_contract.md
+++ b/aptos-move/framework/aptos-framework/doc/staking_contract.md
@@ -99,6 +99,7 @@ pool.
- [Function `pending_distribution_counts`](#@Specification_1_pending_distribution_counts)
- [Function `staking_contract_exists`](#@Specification_1_staking_contract_exists)
- [Function `beneficiary_for_operator`](#@Specification_1_beneficiary_for_operator)
+ - [Function `get_expected_stake_pool_address`](#@Specification_1_get_expected_stake_pool_address)
- [Function `create_staking_contract`](#@Specification_1_create_staking_contract)
- [Function `create_staking_contract_with_coins`](#@Specification_1_create_staking_contract_with_coins)
- [Function `add_stake`](#@Specification_1_add_stake)
@@ -2958,35 +2959,52 @@ Staking_contract exists the stacker/operator pair.
+
-
+### Function `beneficiary_for_operator`
-fun spec_staking_contract_exists(staker: address, operator: address): bool {
- if (!exists<Store>(staker)) {
- false
- } else {
- let store = global<Store>(staker);
- simple_map::spec_contains_key(store.staking_contracts, operator)
- }
-}
+#[view]
+public fun beneficiary_for_operator(operator: address): address
-
-### Function `beneficiary_for_operator`
+pragma verify = false;
+
+
+
+
+
+
+### Function `get_expected_stake_pool_address`
#[view]
-public fun beneficiary_for_operator(operator: address): address
+public fun get_expected_stake_pool_address(staker: address, operator: address, contract_creation_seed: vector<u8>): address
-pragma verify = false;
+pragma aborts_if_is_partial;
+
+
+
+
+
+
+
+
+fun spec_staking_contract_exists(staker: address, operator: address): bool {
+ if (!exists<Store>(staker)) {
+ false
+ } else {
+ let store = global<Store>(staker);
+ simple_map::spec_contains_key(store.staking_contracts, operator)
+ }
+}
diff --git a/aptos-move/framework/aptos-framework/sources/account.move b/aptos-move/framework/aptos-framework/sources/account.move
index 744ffa18861e9..9af50f3817e53 100644
--- a/aptos-move/framework/aptos-framework/sources/account.move
+++ b/aptos-move/framework/aptos-framework/sources/account.move
@@ -9,6 +9,7 @@ module aptos_framework::account {
use aptos_framework::create_signer::create_signer;
use aptos_framework::event::{Self, EventHandle};
use aptos_framework::guid;
+ use aptos_framework::permissioned_signer;
use aptos_framework::system_addresses;
use aptos_std::ed25519;
use aptos_std::from_bcs;
@@ -179,6 +180,8 @@ module aptos_framework::account {
const ENEW_AUTH_KEY_ALREADY_MAPPED: u64 = 21;
/// The current authentication key and the new authentication key are the same
const ENEW_AUTH_KEY_SAME_AS_CURRENT: u64 = 22;
+ /// Current permissioned signer cannot perform the privilaged operations.
+ const ENO_ACCOUNT_PERMISSION: u64 = 23;
/// Explicitly separate the GUID space between Object and Account to prevent accidental overlap.
const MAX_GUID_CREATION_NUM: u64 = 0x4000000000000;
@@ -187,6 +190,43 @@ module aptos_framework::account {
/// Create signer for testing, independently of an Aptos-style `Account`.
public fun create_signer_for_test(addr: address): signer { create_signer(addr) }
+ enum AccountPermission has copy, drop, store {
+ /// Permission to rotate a key.
+ KeyRotation,
+ /// Permission to offer another address to act like your address
+ Offering,
+ }
+
+ /// Permissions
+ ///
+ inline fun check_rotation_permission(s: &signer) {
+ assert!(
+ permissioned_signer::check_permission_exists(s, AccountPermission::KeyRotation {}),
+ error::permission_denied(ENO_ACCOUNT_PERMISSION),
+ );
+ }
+
+ inline fun check_offering_permission(s: &signer) {
+ assert!(
+ permissioned_signer::check_permission_exists(s, AccountPermission::Offering {}),
+ error::permission_denied(ENO_ACCOUNT_PERMISSION),
+ );
+ }
+
+ /// Grant permission to perform key rotations on behalf of the master signer.
+ ///
+ /// This is **extremely dangerous** and should be granted only when it's absolutely needed.
+ public fun grant_key_rotation_permission(master: &signer, permissioned_signer: &signer) {
+ permissioned_signer::authorize_unlimited(master, permissioned_signer, AccountPermission::KeyRotation {})
+ }
+
+ /// Grant permission to use offered address's signer on behalf of the master signer.
+ ///
+ /// This is **extremely dangerous** and should be granted only when it's absolutely needed.
+ public fun grant_key_offering_permission(master: &signer, permissioned_signer: &signer) {
+ permissioned_signer::authorize_unlimited(master, permissioned_signer, AccountPermission::Offering {})
+ }
+
/// Only called during genesis to initialize system resources for this module.
public(friend) fun initialize(aptos_framework: &signer) {
system_addresses::assert_aptos_framework(aptos_framework);
@@ -302,6 +342,7 @@ module aptos_framework::account {
vector::length(&new_auth_key) == 32,
error::invalid_argument(EMALFORMED_AUTHENTICATION_KEY)
);
+ check_rotation_permission(account);
let account_resource = borrow_global_mut(addr);
account_resource.authentication_key = new_auth_key;
}
@@ -357,6 +398,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_rotation_permission(account);
let account_resource = borrow_global_mut(addr);
// Verify the given `from_public_key_bytes` matches this account's current authentication key.
@@ -412,6 +454,7 @@ module aptos_framework::account {
new_public_key_bytes: vector,
cap_update_table: vector
) acquires Account, OriginatingAddress {
+ check_rotation_permission(delegate_signer);
assert!(exists_at(rotation_cap_offerer_address), error::not_found(EOFFERER_ADDRESS_DOES_NOT_EXIST));
// Check that there exists a rotation capability offer at the offerer's account resource for the delegate.
@@ -471,6 +514,7 @@ module aptos_framework::account {
account_public_key_bytes: vector,
recipient_address: address,
) acquires Account {
+ check_rotation_permission(account);
let addr = signer::address_of(account);
assert!(exists_at(recipient_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));
@@ -569,6 +613,7 @@ module aptos_framework::account {
/// Revoke the rotation capability offer given to `to_be_revoked_recipient_address` from `account`
public entry fun revoke_rotation_capability(account: &signer, to_be_revoked_address: address) acquires Account {
assert!(exists_at(to_be_revoked_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));
+ check_rotation_permission(account);
let addr = signer::address_of(account);
let account_resource = borrow_global(addr);
assert!(
@@ -580,6 +625,7 @@ module aptos_framework::account {
/// Revoke any rotation capability offer in the specified account.
public entry fun revoke_any_rotation_capability(account: &signer) acquires Account {
+ check_rotation_permission(account);
let account_resource = borrow_global_mut(signer::address_of(account));
option::extract(&mut account_resource.rotation_capability_offer.for);
}
@@ -600,6 +646,7 @@ module aptos_framework::account {
account_public_key_bytes: vector,
recipient_address: address
) acquires Account {
+ check_offering_permission(account);
let source_address = signer::address_of(account);
assert!(exists_at(recipient_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));
@@ -639,6 +686,7 @@ module aptos_framework::account {
/// has a signer capability offer from `account` but will be revoked in this function).
public entry fun revoke_signer_capability(account: &signer, to_be_revoked_address: address) acquires Account {
assert!(exists_at(to_be_revoked_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));
+ check_offering_permission(account);
let addr = signer::address_of(account);
let account_resource = borrow_global(addr);
assert!(
@@ -650,6 +698,7 @@ module aptos_framework::account {
/// Revoke any signer capability offer in the specified account.
public entry fun revoke_any_signer_capability(account: &signer) acquires Account {
+ check_offering_permission(account);
let account_resource = borrow_global_mut(signer::address_of(account));
option::extract(&mut account_resource.signer_capability_offer.for);
}
@@ -657,6 +706,7 @@ module aptos_framework::account {
/// Return an authorized signer of the offerer, if there's an existing signer capability offer for `account`
/// at the offerer's address.
public fun create_authorized_signer(account: &signer, offerer_address: address): signer acquires Account {
+ check_offering_permission(account);
assert!(exists_at(offerer_address), error::not_found(EOFFERER_ADDRESS_DOES_NOT_EXIST));
// Check if there's an existing signer capability offer from the offerer.
@@ -1202,6 +1252,123 @@ module aptos_framework::account {
assert!(signer::address_of(&signer) == signer::address_of(&alice), 0);
}
+ #[test(bob = @0x345)]
+ public entry fun test_valid_check_signer_capability_and_create_authorized_signer_with_permission(bob: signer) acquires Account {
+ let (alice_sk, alice_pk) = ed25519::generate_keys();
+ let alice_pk_bytes = ed25519::validated_public_key_to_bytes(&alice_pk);
+ let alice = create_account_from_ed25519_public_key(alice_pk_bytes);
+ let alice_addr = signer::address_of(&alice);
+
+ let bob_addr = signer::address_of(&bob);
+ create_account(bob_addr);
+
+ let challenge = SignerCapabilityOfferProofChallengeV2 {
+ sequence_number: borrow_global(alice_addr).sequence_number,
+ source_address: alice_addr,
+ recipient_address: bob_addr,
+ };
+
+ let alice_signer_capability_offer_sig = ed25519::sign_struct(&alice_sk, challenge);
+
+ let alice_permission_handle = permissioned_signer::create_permissioned_handle(&alice);
+ let alice_permission_signer = permissioned_signer::signer_from_permissioned_handle(&alice_permission_handle);
+
+ grant_key_offering_permission(&alice, &alice_permission_signer);
+
+ offer_signer_capability(
+ &alice_permission_signer,
+ ed25519::signature_to_bytes(&alice_signer_capability_offer_sig),
+ 0,
+ alice_pk_bytes,
+ bob_addr
+ );
+
+ assert!(option::contains(&borrow_global(alice_addr).signer_capability_offer.for, &bob_addr), 0);
+
+ let signer = create_authorized_signer(&bob, alice_addr);
+ assert!(signer::address_of(&signer) == signer::address_of(&alice), 0);
+
+ permissioned_signer::destroy_permissioned_handle(alice_permission_handle);
+ }
+
+ #[test(bob = @0x345)]
+ #[expected_failure(abort_code = 0x50017, location = Self)]
+ public entry fun test_valid_check_signer_capability_and_create_authorized_signer_with_no_permission(bob: signer) acquires Account {
+ let (alice_sk, alice_pk) = ed25519::generate_keys();
+ let alice_pk_bytes = ed25519::validated_public_key_to_bytes(&alice_pk);
+ let alice = create_account_from_ed25519_public_key(alice_pk_bytes);
+ let alice_addr = signer::address_of(&alice);
+
+ let bob_addr = signer::address_of(&bob);
+ create_account(bob_addr);
+
+ let challenge = SignerCapabilityOfferProofChallengeV2 {
+ sequence_number: borrow_global(alice_addr).sequence_number,
+ source_address: alice_addr,
+ recipient_address: bob_addr,
+ };
+
+ let alice_signer_capability_offer_sig = ed25519::sign_struct(&alice_sk, challenge);
+
+ let alice_permission_handle = permissioned_signer::create_permissioned_handle(&alice);
+ let alice_permission_signer = permissioned_signer::signer_from_permissioned_handle(&alice_permission_handle);
+
+ offer_signer_capability(
+ &alice_permission_signer,
+ ed25519::signature_to_bytes(&alice_signer_capability_offer_sig),
+ 0,
+ alice_pk_bytes,
+ bob_addr
+ );
+
+ assert!(option::contains(&borrow_global(alice_addr).signer_capability_offer.for, &bob_addr), 0);
+
+ let signer = create_authorized_signer(&bob, alice_addr);
+ assert!(signer::address_of(&signer) == signer::address_of(&alice), 0);
+
+ permissioned_signer::destroy_permissioned_handle(alice_permission_handle);
+ }
+
+ #[test(bob = @0x345)]
+ #[expected_failure(abort_code = 0x50017, location = Self)]
+ public entry fun test_valid_check_signer_capability_and_create_authorized_signer_with_wrong_permission(bob: signer) acquires Account {
+ let (alice_sk, alice_pk) = ed25519::generate_keys();
+ let alice_pk_bytes = ed25519::validated_public_key_to_bytes(&alice_pk);
+ let alice = create_account_from_ed25519_public_key(alice_pk_bytes);
+ let alice_addr = signer::address_of(&alice);
+
+ let bob_addr = signer::address_of(&bob);
+ create_account(bob_addr);
+
+ let challenge = SignerCapabilityOfferProofChallengeV2 {
+ sequence_number: borrow_global(alice_addr).sequence_number,
+ source_address: alice_addr,
+ recipient_address: bob_addr,
+ };
+
+ let alice_signer_capability_offer_sig = ed25519::sign_struct(&alice_sk, challenge);
+
+ let alice_permission_handle = permissioned_signer::create_permissioned_handle(&alice);
+ let alice_permission_signer = permissioned_signer::signer_from_permissioned_handle(&alice_permission_handle);
+
+ grant_key_rotation_permission(&alice, &alice_permission_signer);
+
+ offer_signer_capability(
+ &alice_permission_signer,
+ ed25519::signature_to_bytes(&alice_signer_capability_offer_sig),
+ 0,
+ alice_pk_bytes,
+ bob_addr
+ );
+
+ assert!(option::contains(&borrow_global(alice_addr).signer_capability_offer.for, &bob_addr), 0);
+
+ let signer = create_authorized_signer(&bob, alice_addr);
+ assert!(signer::address_of(&signer) == signer::address_of(&alice), 0);
+
+ permissioned_signer::destroy_permissioned_handle(alice_permission_handle);
+ }
+
#[test(bob = @0x345)]
public entry fun test_get_signer_cap_and_is_signer_cap(bob: signer) acquires Account {
let (alice_sk, alice_pk) = ed25519::generate_keys();
diff --git a/aptos-move/framework/aptos-framework/sources/account.spec.move b/aptos-move/framework/aptos-framework/sources/account.spec.move
index 36e7a5740027e..d872dd82b0278 100644
--- a/aptos-move/framework/aptos-framework/sources/account.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/account.spec.move
@@ -105,8 +105,8 @@ spec aptos_framework::account {
///
spec module {
- pragma verify = true;
- pragma aborts_if_is_strict;
+ pragma verify = false;
+ pragma aborts_if_is_partial;
}
/// Only the address `@aptos_framework` can call.
@@ -390,7 +390,6 @@ spec aptos_framework::account {
source_address,
recipient_address,
};
-
aborts_if !exists(@aptos_framework);
aborts_if !exists(recipient_address);
aborts_if !exists(source_address);
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..f9ebf96b25838 100644
--- a/aptos-move/framework/aptos-framework/sources/multisig_account.spec.move
+++ b/aptos-move/framework/aptos-framework/sources/multisig_account.spec.move
@@ -207,6 +207,7 @@ spec aptos_framework::multisig_account {
}
spec get_next_multisig_account_address(creator: address): address {
+ pragma aborts_if_is_partial;
aborts_if !exists(creator);
let owner_nonce = global(creator).sequence_number;
}
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..e5cce243cd84b 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(
@@ -116,6 +116,8 @@ spec aptos_framework::resource_account {
resource_signer_cap: account::SignerCapability,
optional_auth_key: vector,
) {
+ pragma aborts_if_is_partial;
+
let resource_addr = signer::address_of(resource);
/// [high-level-req-1]
include RotateAccountAuthenticationKeyAndStoreCapabilityAbortsIf;
@@ -172,6 +174,8 @@ spec aptos_framework::resource_account {
resource: &signer,
source_addr: address,
) : account::SignerCapability {
+ pragma aborts_if_is_partial;
+
/// [high-level-req-6]
aborts_if !exists(source_addr);
let resource_addr = signer::address_of(resource);
diff --git a/aptos-move/framework/aptos-framework/sources/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