Skip to content

Latest commit

 

History

History
210 lines (161 loc) · 10.7 KB

aip-104.md

File metadata and controls

210 lines (161 loc) · 10.7 KB
aip title author discussions-to (*optional) Status type created updated (*optional)
104
Account Abstraction
lightmark, igor-aptos, davidiw
Draft
Framework
10/01/2024
01/24/2025

AIP-104 - 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)

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.

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 auth_data is enum type that includes all the necessary information as the input to the function with upgradabilty.

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.

#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
/// The dispatchable authenticator that defines how to authenticates this account in the specified module.
/// An integral part of Account Abstraction.
enum DispatchableAuthenticator has key, copy, drop {
    V1 { auth_functions: OrderedMap<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!(ordered_map::contains(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:

/// 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.

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 ModuleStorage,
) -> 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(
            &ACCOUNT_ABSTRACTION_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 signers just returned.

Reference Implementation

aptos-labs/aptos-core#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.

  • 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.