Skip to content

Commit

Permalink
[framework] introduce private entry function for creating multisig ac…
Browse files Browse the repository at this point in the history
…count

This eliminates the need for generating the complex proof the existing
create_with_existing_account functions require.
  • Loading branch information
yeptos committed Sep 9, 2024
1 parent d5f9496 commit cb6f3a8
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 1 deletion.
2 changes: 1 addition & 1 deletion aptos-move/framework/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ The overall structure of the Aptos Framework is as follows:
├── aptos-token # Sources, testing and generated documentation for Aptos token component
├── aptos-stdlib # Sources, testing and generated documentation for Aptos stdlib component
├── move-stdlib # Sources, testing and generated documentation for Move stdlib component
├── cached-packages # Tooling to generate SDK from mvoe sources.
├── cached-packages # Tooling to generate SDK from move sources.
├── src # Compilation and generation of information from Move source files in the Aptos Framework. Not designed to be used as a Rust library
├── releases # Move release bundles
└── tests
Expand Down
101 changes: 101 additions & 0 deletions aptos-move/framework/aptos-framework/doc/multisig_account.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ and implement the governance voting logic on top.
- [Function `next_sequence_number`](#0x1_multisig_account_next_sequence_number)
- [Function `vote`](#0x1_multisig_account_vote)
- [Function `available_transaction_queue_capacity`](#0x1_multisig_account_available_transaction_queue_capacity)
- [Function `create_with_existing_account_call`](#0x1_multisig_account_create_with_existing_account_call)
- [Function `create_with_existing_account`](#0x1_multisig_account_create_with_existing_account)
- [Function `create_with_existing_account_and_revoke_auth_key_call`](#0x1_multisig_account_create_with_existing_account_and_revoke_auth_key_call)
- [Function `create_with_existing_account_and_revoke_auth_key`](#0x1_multisig_account_create_with_existing_account_and_revoke_auth_key)
- [Function `create`](#0x1_multisig_account_create)
- [Function `create_with_owners`](#0x1_multisig_account_create_with_owners)
Expand Down Expand Up @@ -1932,6 +1934,50 @@ Return a bool tuple indicating whether an owner has voted and if so, whether the



</details>

<a id="0x1_multisig_account_create_with_existing_account_call"></a>

## Function `create_with_existing_account_call`

Private entry function that creates a new multisig account on top of an existing account.

This offers a migration path for any existing account.

Note that this does not revoke auth key-based control over the account. Owners should separately rotate the auth
key after they are fully migrated to the new multisig account. Alternatively, they can call
create_with_existing_account_and_revoke_auth_key_call instead.


<pre><code>entry <b>fun</b> <a href="multisig_account.md#0x1_multisig_account_create_with_existing_account_call">create_with_existing_account_call</a>(<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>, owners: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<b>address</b>&gt;, num_signatures_required: u64, metadata_keys: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<a href="../../aptos-stdlib/../move-stdlib/doc/string.md#0x1_string_String">string::String</a>&gt;, metadata_values: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;&gt;)
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code>entry <b>fun</b> <a href="multisig_account.md#0x1_multisig_account_create_with_existing_account_call">create_with_existing_account_call</a>(
<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>,
owners: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<b>address</b>&gt;,
num_signatures_required: u64,
metadata_keys: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;String&gt;,
metadata_values: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;&gt;,
) <b>acquires</b> <a href="multisig_account.md#0x1_multisig_account_MultisigAccount">MultisigAccount</a> {
<a href="multisig_account.md#0x1_multisig_account_create_with_owners_internal">create_with_owners_internal</a>(
<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>,
owners,
num_signatures_required,
<a href="../../aptos-stdlib/../move-stdlib/doc/option.md#0x1_option_none">option::none</a>&lt;SignerCapability&gt;(),
metadata_keys,
metadata_values,
);
}
</code></pre>



</details>

<a id="0x1_multisig_account_create_with_existing_account"></a>
Expand Down Expand Up @@ -2002,6 +2048,61 @@ create_with_existing_account_and_revoke_auth_key instead.



</details>

<a id="0x1_multisig_account_create_with_existing_account_and_revoke_auth_key_call"></a>

## Function `create_with_existing_account_and_revoke_auth_key_call`

Private entry function that creates a new multisig account on top of an existing account and immediately rotate
the origin auth key to 0x0.

Note: If the original account is a resource account, this does not revoke all control over it as if any
SignerCapability of the resource account still exists, it can still be used to generate the signer for the
account.


<pre><code>entry <b>fun</b> <a href="multisig_account.md#0x1_multisig_account_create_with_existing_account_and_revoke_auth_key_call">create_with_existing_account_and_revoke_auth_key_call</a>(<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>, owners: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<b>address</b>&gt;, num_signatures_required: u64, metadata_keys: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<a href="../../aptos-stdlib/../move-stdlib/doc/string.md#0x1_string_String">string::String</a>&gt;, metadata_values: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;&gt;)
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code>entry <b>fun</b> <a href="multisig_account.md#0x1_multisig_account_create_with_existing_account_and_revoke_auth_key_call">create_with_existing_account_and_revoke_auth_key_call</a>(
<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>: &<a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a>,
owners: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<b>address</b>&gt;,
num_signatures_required: u64,
metadata_keys: <a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;String&gt;,
metadata_values:<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;<a href="../../aptos-stdlib/../move-stdlib/doc/vector.md#0x1_vector">vector</a>&lt;u8&gt;&gt;,
) <b>acquires</b> <a href="multisig_account.md#0x1_multisig_account_MultisigAccount">MultisigAccount</a> {
<a href="multisig_account.md#0x1_multisig_account_create_with_owners_internal">create_with_owners_internal</a>(
<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>,
owners,
num_signatures_required,
<a href="../../aptos-stdlib/../move-stdlib/doc/option.md#0x1_option_none">option::none</a>&lt;SignerCapability&gt;(),
metadata_keys,
metadata_values,
);

// Rotate the <a href="account.md#0x1_account">account</a>'s auth key <b>to</b> 0x0, which effectively revokes control via auth key.
<b>let</b> multisig_address = address_of(<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>);
<a href="account.md#0x1_account_rotate_authentication_key_internal">account::rotate_authentication_key_internal</a>(<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>, <a href="multisig_account.md#0x1_multisig_account_ZERO_AUTH_KEY">ZERO_AUTH_KEY</a>);
// This also needs <b>to</b> revoke <a href="../../aptos-stdlib/doc/any.md#0x1_any">any</a> <a href="../../aptos-stdlib/../move-stdlib/doc/signer.md#0x1_signer">signer</a> capability or rotation capability that <b>exists</b> for the <a href="account.md#0x1_account">account</a> <b>to</b>
// completely remove all access <b>to</b> the <a href="account.md#0x1_account">account</a>.
<b>if</b> (<a href="account.md#0x1_account_is_signer_capability_offered">account::is_signer_capability_offered</a>(multisig_address)) {
<a href="account.md#0x1_account_revoke_any_signer_capability">account::revoke_any_signer_capability</a>(<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>);
};
<b>if</b> (<a href="account.md#0x1_account_is_rotation_capability_offered">account::is_rotation_capability_offered</a>(multisig_address)) {
<a href="account.md#0x1_account_revoke_any_rotation_capability">account::revoke_any_rotation_capability</a>(<a href="multisig_account.md#0x1_multisig_account">multisig_account</a>);
};
}
</code></pre>



</details>

<a id="0x1_multisig_account_create_with_existing_account_and_revoke_auth_key"></a>
Expand Down
107 changes: 107 additions & 0 deletions aptos-move/framework/aptos-framework/sources/multisig_account.move
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,30 @@ module aptos_framework::multisig_account {

////////////////////////// Multisig account creation functions ///////////////////////////////

/// Private entry function that creates a new multisig account on top of an existing account.
///
/// This offers a migration path for an existing account with any type of auth key.
///
/// Note that this does not revoke auth key-based control over the account. Owners should separately rotate the auth
/// key after they are fully migrated to the new multisig account. Alternatively, they can call
/// create_with_existing_account_and_revoke_auth_key_call instead.
entry fun create_with_existing_account_call(
multisig_account: &signer,
owners: vector<address>,
num_signatures_required: u64,
metadata_keys: vector<String>,
metadata_values: vector<vector<u8>>,
) acquires MultisigAccount {
create_with_owners_internal(
multisig_account,
owners,
num_signatures_required,
option::none<SignerCapability>(),
metadata_keys,
metadata_values,
);
}

/// Creates a new multisig account on top of an existing account.
///
/// This offers a migration path for an existing account with a multi-ed25519 auth key (native multisig account).
Expand Down Expand Up @@ -547,6 +571,41 @@ module aptos_framework::multisig_account {
);
}

/// Private entry function that creates a new multisig account on top of an existing account and immediately rotate
/// the origin auth key to 0x0.
///
/// Note: If the original account is a resource account, this does not revoke all control over it as if any
/// SignerCapability of the resource account still exists, it can still be used to generate the signer for the
/// account.
entry fun create_with_existing_account_and_revoke_auth_key_call(
multisig_account: &signer,
owners: vector<address>,
num_signatures_required: u64,
metadata_keys: vector<String>,
metadata_values:vector<vector<u8>>,
) acquires MultisigAccount {
create_with_owners_internal(
multisig_account,
owners,
num_signatures_required,
option::none<SignerCapability>(),
metadata_keys,
metadata_values,
);

// Rotate the account's auth key to 0x0, which effectively revokes control via auth key.
let multisig_address = address_of(multisig_account);
account::rotate_authentication_key_internal(multisig_account, ZERO_AUTH_KEY);
// This also needs to revoke any signer capability or rotation capability that exists for the account to
// completely remove all access to the account.
if (account::is_signer_capability_offered(multisig_address)) {
account::revoke_any_signer_capability(multisig_account);
};
if (account::is_rotation_capability_offered(multisig_address)) {
account::revoke_any_rotation_capability(multisig_account);
};
}

/// Creates a new multisig account on top of an existing account and immediately rotate the origin auth key to 0x0.
///
/// Note: If the original account is a resource account, this does not revoke all control over it as if any
Expand Down Expand Up @@ -1688,6 +1747,26 @@ module aptos_framework::multisig_account {
);
}

#[test]
public entry fun test_create_multisig_account_on_top_of_existing_with_signer()
acquires MultisigAccount {
setup();

let multisig_address = @0xabc;
create_account(multisig_address);

let expected_owners = vector[@0x123, @0x124, @0x125];
create_with_existing_account_call(
&create_signer(multisig_address),
expected_owners,
2,
vector[],
vector[],
);
assert_multisig_account_exists(multisig_address);
assert!(owners(multisig_address) == expected_owners, 0);
}

#[test]
public entry fun test_create_multisig_account_on_top_of_existing_multi_ed25519_account()
acquires MultisigAccount {
Expand Down Expand Up @@ -1721,6 +1800,34 @@ module aptos_framework::multisig_account {
assert!(owners(multisig_address) == expected_owners, 0);
}

#[test]
public entry fun test_create_multisig_account_on_top_of_existing_and_revoke_auth_key_with_signer()
acquires MultisigAccount {
setup();

let multisig_address = @0xabc;
create_account(multisig_address);

// Create both a signer capability and rotation capability offers
account::set_rotation_capability_offer(multisig_address, @0x123);
account::set_signer_capability_offer(multisig_address, @0x123);

let expected_owners = vector[@0x123, @0x124, @0x125];
create_with_existing_account_and_revoke_auth_key_call(
&create_signer(multisig_address),
expected_owners,
2,
vector[],
vector[],
);
assert_multisig_account_exists(multisig_address);
assert!(owners(multisig_address) == expected_owners, 0);
assert!(account::get_authentication_key(multisig_address) == ZERO_AUTH_KEY, 1);
// Verify that all capability offers have been wiped.
assert!(!account::is_rotation_capability_offered(multisig_address), 2);
assert!(!account::is_signer_capability_offered(multisig_address), 3);
}

#[test]
public entry fun test_create_multisig_account_on_top_of_existing_multi_ed25519_account_and_revoke_auth_key()
acquires MultisigAccount {
Expand Down
Loading

0 comments on commit cb6f3a8

Please sign in to comment.