-
Notifications
You must be signed in to change notification settings - Fork 123
[AIP] Account Abstraction #512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| --- | ||
| aip: | ||
| title: Account Abstraction | ||
| author: lightmark | ||
| discussions-to (*optional): <a url pointing to the official discussion thread> | ||
| Status: Draft | ||
| last-call-end-date (*optional): <mm/dd/yyyy the last date to leave feedbacks and reviews> | ||
| type: Framework | ||
| created: 10/01/2024 | ||
| updated (*optional): <mm/dd/yyyy> | ||
| requires (*optional): <AIP number(s)> | ||
| --- | ||
|
|
||
| # AIP-X - Account Abstraction | ||
|
|
||
| ## Summary | ||
|
|
||
| Account Abstraction (AA) on Aptos allows any account to be authenticated through move code in addition to existing | ||
| authentication schemes supported by native code. This AIP will introduce a new authenticator called `Abstraction` that | ||
| delegates the authentication verification to AptosVM running move-based authentication logic. | ||
|
|
||
| ### Out of scope | ||
|
|
||
| How to properly design an authentication function/module is out of scope. | ||
| The `PayMaster`, aka, online gas payer, is not included in the design of AA in this AIP. | ||
|
|
||
| ## High-level Overview | ||
| The core concept is to enable the move function to authenticate account signers, moving beyond the limitations of native Rust authenticators, which only support a restricted set of cryptographic schemes. To accomplish this, we propose transferring the authentication process from native Rust code to the AptosVM layer for each abstracted account. The solution we propose involves the following steps: | ||
|
|
||
| 1. Store the `FunctionInfo` of the customized Move authentication function in a new resource at the AA (Abstract Account) address. | ||
| 2. When the account is configured to support an `AbstractionAuthenticator`, the Move function stored in step 1 will be executed (leveraging native dynamic dispatch) to perform the authentication check. | ||
|
|
||
| ### Why is this approach superior to other options? | ||
|
|
||
| - No major security concerns: Users who opt for AA are responsible for managing their account’s security. | ||
| - Scalable: This approach can easily scale as it decouples authentication from rigid native cryptographic checks and supports multiple authentication functions. | ||
| - Audit-proof and safe: The dispatchable function mechanism has already been audited and is widely used in the FA standard, ensuring its security. | ||
| - Rich transaction context: The Transaction Context already provides comprehensive information about the current transaction, which can be leveraged for robust authentication. | ||
|
|
||
| This solution enhances flexibility and scalability while maintaining a secure and efficient authentication process. | ||
|
|
||
| ## Impact | ||
|
|
||
| Account Abstraction(AA) aims to support dispatchable authentication **at the level of smart contract.** | ||
| The account can set up in such a way that the transaction can be authorized with complicated move smart contract instead of native account authenticators provided by Aptos blockchain core, enabling a series of possibilities: | ||
|
|
||
| - Multisig v2 account | ||
| - Sandbox or temporary account to isolate assets/risks, etc. | ||
| - Restricted permissions granted to trusted applications so users don’t need to sign every transaction. | ||
| - Delegation of asset management to a different account (semi-custodial model) | ||
|
Comment on lines
47
to
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it'd be nice if a bit more detail can be provided here for each item. i'm curious how this can be related to multisig v2 accounts
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. multisig v2 uses But this^ seems unrelated to AA design itself. It would be worth another AIP to explain this migration option. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enabling AA for an account means registering an account resource that contains a dispatchable auth function, right? If enabling AA can be done via say a private entry function call, I think there won't be any special treatment needed for multisig v2 accounts. Since multisig v2 accounts are just accounts that happen to have a MulsitigAccount resource, they can just call the AA enabling function like any other accounts.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. at the minimum, other authentication resources (like MulsitigAccount) need to be removed, in order for AA to be able to be registered, right @lightmark ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think they can coexist. Any problem for that. Once you get the master signer in any means, you can opt to remove any auth resources. |
||
|
|
||
| ## Alternative Solutions | ||
|
|
||
| For Ethereum’s ERC-4337 solution, Aptos would need to implement a user operation pool, support contract wallets, and include an external Paymaster for handling authentication and payments. This would require bundling signatures and transactions into user operations, then verifying them through independent modules on-chain, adding significant complexity to development and interaction. | ||
|
|
||
| ## Specification and Implementation Details | ||
|
|
||
| ### Account Abstraction Authenticator | ||
|
|
||
| First and foremost, we will introduce a new native Authenticator that represents the authentication scheme specified | ||
| by the current transaction is AA. | ||
| ```rust | ||
| pub enum AccountAuthenticator { | ||
| //... | ||
| Abstraction { | ||
| function_info: FunctionInfo, | ||
| auth_data: AbstractionAuthData, | ||
| }, | ||
| } | ||
| ``` | ||
| where `function_info` indicates the function registered on the `sender` account that will be called as authentication check | ||
| and `signature` is a byte array that includes all the necessary information as the input to the function. | ||
|
|
||
| If the `AccountAuthenticator` is not `Abstraction`, the `authentication_key` passed to the prologue will be `Some(auth_key)`; | ||
| while if it is `Abstraction` then `authentication_key == None` | ||
|
|
||
| ### Dispatchable Authenticator | ||
|
|
||
| At the framework level, we create a new resource, `DispatchableAuthenticator`. If an account has `DispatchableAuthenticator`, | ||
| it allows the account to be authenticated via the functions registered in the internal map. | ||
|
|
||
| ```rust | ||
| #[resource_group_member(group = aptos_framework::object::ObjectGroup)] | ||
| /// The dispatchable authenticator that defines how to authenticate this account in the specified module. | ||
| /// An integral part of Account Abstraction. | ||
| struct DispatchableAuthenticator has key, copy, drop { | ||
| auth_functions: SimpleMap<FunctionInfo, bool> | ||
| } | ||
|
|
||
| enum AbstractionAuthData has copy, drop { | ||
| V1 { digest: vector<u8>, authenticator: vector<u8> }, | ||
| } | ||
|
|
||
| fun authenticate(account: signer, func_info: FunctionInfo, auth_data: AbstractionAuthData): signer acquires DispatchableAuthenticator { | ||
| let func_infos = dispatchable_authenticator_internal(signer::address_of(&account)); | ||
| assert!(simple_map::contains_key(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); | ||
| function_info::load_module_from_function(&func_info); | ||
| dispatchable_authenticate(account, auth_data, &func_info) | ||
| } | ||
|
|
||
| /// The native function to dispatch customized move authentication function. | ||
| native fun dispatchable_authenticate(account: signer, auth_data: AbstractionAuthData, function: &FunctionInfo): signer; | ||
|
|
||
| ``` | ||
|
|
||
| Any authentication function must have the same interface as the following function: | ||
| ```rust | ||
| /// The native function to dispatch customized move authentication function. | ||
| public fun dispatchable_authenticate(account: signer, auth_data: AbstractionAuthData): signer; | ||
| ``` | ||
| The first parameter, `signer`, represents the current account that needs authentication, and the function's return value is also a `signer`. Typically, this function returns the input signer if the authentication check is successful. However, in some cases, a permissioned signer may be returned to control the resources the transaction can access. | ||
| `auth_data` is an enum that contains all the data required to authenticate the transaction via account abstraction. The initial version just has two fields, where `digest` is the sha3 digest of the corresponding transaction signing message and `authenticator` is the attached byte array which serves as the proof of the authentication verification. Usually it encodes the signature over the digest at least. | ||
| Best Practice: To prevent replay attacks (both cross-chain), the `authenticator` must depend on `digest`. | ||
|
|
||
| ### Dispatchable Authentication | ||
|
|
||
| In AptosVM, the following rust function `dispatchable_authenticate`, will call the `authenticate` function in the framework with `function_info` and `auth_data` specified in | ||
| the authenticator, who will dynamically call the function specified by `function_info` to perform the authentication check | ||
| if the function is registered in the map. The return value is the serialized signer data which will be the input to the | ||
| transaction body. | ||
|
|
||
| ```rust | ||
| fn dispatchable_authenticate( | ||
| session: &mut SessionExt, | ||
| gas_meter: &mut impl GasMeter, | ||
| account: AccountAddress, | ||
| function_info: FunctionInfo, | ||
| auth_data: &AbstractionAuthData, | ||
| traversal_context: &mut TraversalContext, | ||
| module_storage: &impl AptosModuleStorage, | ||
| ) -> VMResult<Vec<u8>> { | ||
| let auth_data = bcs::to_bytes(auth_data).expect("from rust succeeds"); | ||
| let mut params = | ||
| serialize_values(&vec![ | ||
| MoveValue::Signer(account), | ||
| function_info.as_move_value(), | ||
| ]); | ||
| params.push(auth_data); | ||
| session | ||
| .execute_function_bypass_visibility( | ||
| &LITE_ACCOUNT_MODULE, | ||
| AUTHENTICATE, | ||
| vec![], | ||
| params, | ||
| gas_meter, | ||
| traversal_context, | ||
| module_storage, | ||
| ) | ||
| .map(|mut return_vals| { | ||
| assert!( | ||
| return_vals.mutable_reference_outputs.is_empty() | ||
| && return_vals.return_values.len() == 1, | ||
| "Abstraction authentication function must only have 1 return value" | ||
| ); | ||
| let (signer_data, signer_layout) = return_vals.return_values.pop().expect("Must exist"); | ||
| assert_eq!( | ||
| signer_layout, | ||
| MoveTypeLayout::Signer, | ||
| "Abstraction authentication function returned non-signer." | ||
| ); | ||
| signer_data | ||
| }) | ||
| } | ||
| ``` | ||
|
|
||
| ### Authentication Flow | ||
| The AA authentication flow works as below: | ||
| 1. Transaction carries the Abstraction Authenticator. | ||
| 2. Rust authenticator will run and find it is Abstraction Authenticator so native authentication is noop. But `authentication_key` will be set to `None`. | ||
| 3. When running prologue, if the `authentication_key` is `None`, it will bypass the authentication key check. | ||
| 4. After the prologue, AptosVM will check each senders' whether their account authenticator is Abstraction Authenticator. If yes, AptosVM will run the move auth function specified in the `function_info` with `auth_data` and expect a `signer` to be returned. If any sender's auth function aborts, the authentication check for the whole transaction fails. It is noted that this function has a gas limit if the function call runs above the gas limit it will abort immediately. | ||
| 5. Run the user transaction body as usual with the `signer`s just returned. | ||
|
|
||
| ## Reference Implementation | ||
| https://github.com/aptos-labs/aptos-core/pull/15219 | ||
|
|
||
| The feature flag for this feature is `std::features::ACCOUNT_ABSTRACTION`. | ||
|
|
||
| ## Testing | ||
| The above PR has API e2e test the functionality of Account abstraction with various cases. | ||
|
|
||
| ## Risks and Drawbacks | ||
|
|
||
| - AA is hard to adopt w/o an auth function library. To make it easy for user to use, the framework should provide a bunch of commonly used | ||
| authentication modules/functions in the future work for users to use. Otherwise, it is really hard for beginner to implement customized | ||
| authentication logic in move. | ||
lightmark marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| - The gas limit of the auth function is fixed so AA cannot support too complicated move functions. | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| - The first parameter to the auth function is account `signer`. We believe it is not a security concern since if you choose to delegate your account access to this function, you already agree to give all the access to your account to this function. | ||
| - If the gas limit of the auth function is higher than necessary, it may become a dos attack vector. | ||
|
|
||
| ## Future Potential | ||
|
|
||
| We might want to find a way to cover the auth function gas in a way that does not have a fixed limit so abstracted auth | ||
| function can have more complex logic. | ||
|
|
||
|
|
||
| ## Timeline | ||
|
|
||
| ### Suggested implementation timeline | ||
|
|
||
| Oct 2024 | ||
|
|
||
| ### Suggested developer platform support timeline | ||
|
|
||
| By the end of 2024. | ||
|
|
||
| ### Suggested deployment timeline | ||
|
|
||
| By the end of 2024. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be more appropriate to include a provision for resource locks here? I made a similar suggestion in the LightAccount AIP, but I believe this feature could be integrated into this proposal as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be included in #510