From 54581c5637cc32e3f6ecd7f6dc0f6d4248ddae82 Mon Sep 17 00:00:00 2001 From: runtianz Date: Mon, 9 Sep 2024 10:38:26 -0700 Subject: [PATCH] TMP --- .../framework/aptos-framework/doc/account.md | 129 +++++++++++++- .../aptos-framework/sources/account.move | 167 ++++++++++++++++++ .../aptos-framework/sources/account.spec.move | 5 +- 3 files changed, 293 insertions(+), 8 deletions(-) diff --git a/aptos-move/framework/aptos-framework/doc/account.md b/aptos-move/framework/aptos-framework/doc/account.md index 9566844a0f7394..447fb207802d1d 100644 --- a/aptos-move/framework/aptos-framework/doc/account.md +++ b/aptos-move/framework/aptos-framework/doc/account.md @@ -19,7 +19,10 @@ - [Struct `SignerCapabilityOfferProofChallenge`](#0x1_account_SignerCapabilityOfferProofChallenge) - [Struct `RotationCapabilityOfferProofChallengeV2`](#0x1_account_RotationCapabilityOfferProofChallengeV2) - [Struct `SignerCapabilityOfferProofChallengeV2`](#0x1_account_SignerCapabilityOfferProofChallengeV2) +- [Struct `AccountPermission`](#0x1_account_AccountPermission) - [Constants](#@Constants_0) +- [Function `check_signer_permission`](#0x1_account_check_signer_permission) +- [Function `grant_permission`](#0x1_account_grant_permission) - [Function `initialize`](#0x1_account_initialize) - [Function `create_account_if_does_not_exist`](#0x1_account_create_account_if_does_not_exist) - [Function `create_account`](#0x1_account_create_account) @@ -110,6 +113,7 @@ use 0x1::hash; use 0x1::multi_ed25519; use 0x1::option; +use 0x1::permissioned_signer; use 0x1::signer; use 0x1::system_addresses; use 0x1::table; @@ -639,6 +643,33 @@ This V2 struct adds the chain_id + + + + +## Struct `AccountPermission` + + + +
struct AccountPermission has copy, drop, store
+
+ + + +
+Fields + + +
+
+dummy_field: bool +
+
+ +
+
+ +
@@ -798,6 +829,16 @@ The current authentication key and the new authentication key are the same + + +Current permissioned signer cannot perform the privilaged operations. + + +
const ENO_ACCOUNT_PERMISSION: u64 = 23;
+
+ + + The caller does not have a digital-signature-based capability to call this function @@ -926,6 +967,61 @@ Scheme identifier for MultiEd25519 signatures used to derive authentication 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, AccountPermission {}),
+        error::permission_denied(ENO_ACCOUNT_PERMISSION),
+    );
+}
+
+ + + +
+ + + +## Function `grant_permission` + +Grant permission to perform key rotations on behalf of the master signer. + +This is **extermely dangerous** and should be granted only when it's absolutely needed. + + +
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, AccountPermission {})
+}
+
+ + + +
+ ## Function `initialize` @@ -1256,6 +1352,7 @@ many contexts: vector::length(&new_auth_key) == 32, error::invalid_argument(EMALFORMED_AUTHENTICATION_KEY) ); + check_signer_permission(account); let account_resource = borrow_global_mut<Account>(addr); account_resource.authentication_key = new_auth_key; } @@ -1351,6 +1448,7 @@ to rotate his address to Alice's address in the first place. ) acquires Account, OriginatingAddress { let addr = signer::address_of(account); assert!(exists_at(addr), error::not_found(EACCOUNT_DOES_NOT_EXIST)); + check_signer_permission(account); let account_resource = borrow_global_mut<Account>(addr); // Verify the given `from_public_key_bytes` matches this account's current authentication key. @@ -1426,6 +1524,7 @@ to rotate his address to Alice's address in the first place. new_public_key_bytes: vector<u8>, cap_update_table: vector<u8> ) acquires Account, OriginatingAddress { + check_signer_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. @@ -1505,6 +1604,7 @@ offer, calling this function will replace the previous recipient_addressvector<u8>, recipient_address: address, ) acquires Account { + check_signer_permission(account); let addr = signer::address_of(account); assert!(exists_at(recipient_address), error::not_found(EACCOUNT_DOES_NOT_EXIST)); @@ -1683,6 +1783,7 @@ Revoke the rotation capability offer given to to_be_revoked_recipient_addr
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_signer_permission(account);
     let addr = signer::address_of(account);
     let account_resource = borrow_global<Account>(addr);
     assert!(
@@ -1714,6 +1815,7 @@ Revoke any rotation capability offer in the specified account.
 
 
 
public entry fun revoke_any_rotation_capability(account: &signer) acquires Account {
+    check_signer_permission(account);
     let account_resource = borrow_global_mut<Account>(signer::address_of(account));
     option::extract(&mut account_resource.rotation_capability_offer.for);
 }
@@ -1754,6 +1856,7 @@ to the account owner's signer capability).
     account_public_key_bytes: vector<u8>,
     recipient_address: address
 ) acquires Account {
+    check_signer_permission(account);
     let source_address = signer::address_of(account);
     assert!(exists_at(recipient_address), error::not_found(EACCOUNT_DOES_NOT_EXIST));
 
@@ -1853,6 +1956,7 @@ has a signer capability offer from 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_signer_permission(account);
     let addr = signer::address_of(account);
     let account_resource = borrow_global<Account>(addr);
     assert!(
@@ -1884,6 +1988,7 @@ Revoke any signer capability offer in the specified account.
 
 
 
public entry fun revoke_any_signer_capability(account: &signer) acquires Account {
+    check_signer_permission(account);
     let account_resource = borrow_global_mut<Account>(signer::address_of(account));
     option::extract(&mut account_resource.signer_capability_offer.for);
 }
@@ -1911,6 +2016,7 @@ at the offerer's address.
 
 
 
public fun create_authorized_signer(account: &signer, offerer_address: address): signer acquires Account {
+    check_signer_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.
@@ -2754,6 +2860,7 @@ The length of new_auth_key is 32.
 let post account_resource = global<Account>(addr);
 aborts_if !exists<Account>(addr);
 aborts_if vector::length(new_auth_key) != 32;
+aborts_if permissioned_signer::spec_is_permissioned_signer(account);
 modifies global<Account>(addr);
 ensures account_resource.authentication_key == new_auth_key;
 
@@ -2776,6 +2883,7 @@ The length of new_auth_key is 32. let post account_resource = global<Account>(addr); aborts_if !exists<Account>(addr); aborts_if vector::length(new_auth_key) != 32; +aborts_if permissioned_signer::spec_is_permissioned_signer(account); modifies global<Account>(addr); ensures account_resource.authentication_key == new_auth_key;
@@ -2807,6 +2915,7 @@ The authentication scheme is ED25519_SCHEME and MULTI_ED25519_SCHEME
let addr = signer::address_of(account);
 let account_resource = global<Account>(addr);
 aborts_if !exists<Account>(addr);
+aborts_if permissioned_signer::spec_is_permissioned_signer(account);
 // This enforces high-level requirement 6:
 include from_scheme == ED25519_SCHEME ==> ed25519::NewUnvalidatedPublicKeyFromBytesAbortsIf { bytes: from_public_key_bytes };
 aborts_if from_scheme == ED25519_SCHEME && ({
@@ -2871,7 +2980,8 @@ The authentication scheme is ED25519_SCHEME and MULTI_ED25519_SCHEME
 
 
 
-
aborts_if !exists<Account>(rotation_cap_offerer_address);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(delegate_signer);
+aborts_if !exists<Account>(rotation_cap_offerer_address);
 let delegate_address = signer::address_of(delegate_signer);
 let offerer_account_resource = global<Account>(rotation_cap_offerer_address);
 aborts_if !from_bcs::deserializable<address>(offerer_account_resource.authentication_key);
@@ -2930,6 +3040,7 @@ The authentication scheme is ED25519_SCHEME and MULTI_ED25519_SCHEME
     source_address,
     recipient_address,
 };
+aborts_if permissioned_signer::spec_is_permissioned_signer(account);
 aborts_if !exists<chain_id::ChainId>(@aptos_framework);
 aborts_if !exists<Account>(recipient_address);
 aborts_if !exists<Account>(source_address);
@@ -3028,7 +3139,8 @@ The authentication scheme is ED25519_SCHEME and MULTI_ED25519_SCHEME
 
 
 
-
aborts_if !exists<Account>(to_be_revoked_address);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+aborts_if !exists<Account>(to_be_revoked_address);
 let addr = signer::address_of(account);
 let account_resource = global<Account>(addr);
 aborts_if !exists<Account>(addr);
@@ -3052,7 +3164,8 @@ The authentication scheme is ED25519_SCHEME and MULTI_ED25519_SCHEME
 
 
 
-
let addr = signer::address_of(account);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+let addr = signer::address_of(account);
 modifies global<Account>(addr);
 aborts_if !exists<Account>(addr);
 let account_resource = global<Account>(addr);
@@ -3084,6 +3197,7 @@ The authentication scheme is ED25519_SCHEME and MULTI_ED25519_SCHEME.
     source_address,
     recipient_address,
 };
+aborts_if permissioned_signer::spec_is_permissioned_signer(account);
 aborts_if !exists<Account>(recipient_address);
 aborts_if !exists<Account>(source_address);
 include account_scheme == ED25519_SCHEME ==> ed25519::NewUnvalidatedPublicKeyFromBytesAbortsIf { bytes: account_public_key_bytes };
@@ -3167,7 +3281,8 @@ The Account existed under the signer.
 The value of signer_capability_offer.for of Account resource under the signer is to_be_revoked_address.
 
 
-
aborts_if !exists<Account>(to_be_revoked_address);
+
aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+aborts_if !exists<Account>(to_be_revoked_address);
 let addr = signer::address_of(account);
 let account_resource = global<Account>(addr);
 aborts_if !exists<Account>(addr);
@@ -3190,6 +3305,7 @@ The value of signer_capability_offer.for of Account resource under the signer is
 
 
 
modifies global<Account>(signer::address_of(account));
+aborts_if permissioned_signer::spec_is_permissioned_signer(account);
 // This enforces high-level requirement 7:
 aborts_if !exists<Account>(signer::address_of(account));
 let account_resource = global<Account>(signer::address_of(account));
@@ -3211,7 +3327,8 @@ The Account existed under the signer.
 The value of signer_capability_offer.for of Account resource under the signer is offerer_address.
 
 
-
// This enforces high-level requirement 8:
+
aborts_if permissioned_signer::spec_is_permissioned_signer(account);
+// This enforces high-level requirement 8:
 include AccountContainsAddr{
     account,
     address: offerer_address,
@@ -3372,6 +3489,8 @@ The value of signer_capability_offer.for of Account resource under the signer is
 
 
let source_addr = signer::address_of(source);
 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 len(ZERO_AUTH_KEY) != 32;
 include exists_at(resource_addr) ==> CreateResourceAccountAbortsIf;
 include !exists_at(resource_addr) ==> CreateAccountAbortsIf {addr: resource_addr};
diff --git a/aptos-move/framework/aptos-framework/sources/account.move b/aptos-move/framework/aptos-framework/sources/account.move
index 744ffa18861e92..9af50f3817e53d 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 36e7a5740027ed..d872dd82b0278c 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);