From 0ad2baa0b1d9ed2bdb020405febf7b5ee8eae134 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 5 Feb 2025 22:05:13 -0800 Subject: [PATCH 1/8] AccountAbstraction that works per domain Current AA requires registration for each account, and user has to have private key for an address to be able to do so. This change allows for AA to be per domain - for example for Solana we need "ed25519_hex", and domain-separated addresses to automatically accept it. --- .../doc/account_abstraction.md | 143 ++++++++++++++- .../aptos-framework/doc/auth_data.md | 170 +++++++++++++++++- .../sources/account/account_abstraction.move | 58 +++++- .../sources/account/auth_data.move | 30 +++- .../src/aptos_framework_sdk_builder.rs | 64 +++++++ types/src/transaction/authenticator.rs | 2 + 6 files changed, 450 insertions(+), 17 deletions(-) diff --git a/aptos-move/framework/aptos-framework/doc/account_abstraction.md b/aptos-move/framework/aptos-framework/doc/account_abstraction.md index 74ca592b443ef..3868d94904509 100644 --- a/aptos-move/framework/aptos-framework/doc/account_abstraction.md +++ b/aptos-move/framework/aptos-framework/doc/account_abstraction.md @@ -8,15 +8,18 @@ - [Struct `UpdateDispatchableAuthenticator`](#0x1_account_abstraction_UpdateDispatchableAuthenticator) - [Struct `RemoveDispatchableAuthenticator`](#0x1_account_abstraction_RemoveDispatchableAuthenticator) - [Enum Resource `DispatchableAuthenticator`](#0x1_account_abstraction_DispatchableAuthenticator) +- [Enum Resource `DomainDispatchableAuthenticator`](#0x1_account_abstraction_DomainDispatchableAuthenticator) - [Constants](#@Constants_0) - [Function `add_authentication_function`](#0x1_account_abstraction_add_authentication_function) - [Function `remove_authentication_function`](#0x1_account_abstraction_remove_authentication_function) - [Function `remove_authenticator`](#0x1_account_abstraction_remove_authenticator) +- [Function `register_domain_with_authentication_function`](#0x1_account_abstraction_register_domain_with_authentication_function) - [Function `resource_addr`](#0x1_account_abstraction_resource_addr) - [Function `update_dispatchable_authenticator_impl`](#0x1_account_abstraction_update_dispatchable_authenticator_impl) - [Function `using_dispatchable_authenticator`](#0x1_account_abstraction_using_dispatchable_authenticator) - [Function `dispatchable_authenticator`](#0x1_account_abstraction_dispatchable_authenticator) - [Function `dispatchable_authenticator_internal`](#0x1_account_abstraction_dispatchable_authenticator_internal) +- [Function `get_domain_address`](#0x1_account_abstraction_get_domain_address) - [Function `authenticate`](#0x1_account_abstraction_authenticate) - [Function `dispatchable_authenticate`](#0x1_account_abstraction_dispatchable_authenticate) - [Function `add_dispatchable_authentication_function`](#0x1_account_abstraction_add_dispatchable_authentication_function) @@ -27,16 +30,22 @@
use 0x1::auth_data;
+use 0x1::bcs;
+use 0x1::big_ordered_map;
 use 0x1::create_signer;
 use 0x1::error;
 use 0x1::event;
+use 0x1::from_bcs;
 use 0x1::function_info;
+use 0x1::hash;
 use 0x1::object;
 use 0x1::option;
 use 0x1::ordered_map;
 use 0x1::permissioned_signer;
 use 0x1::signer;
 use 0x1::string;
+use 0x1::system_addresses;
+use 0x1::vector;
 
@@ -145,6 +154,45 @@ enum + +## Enum Resource `DomainDispatchableAuthenticator` + + + +
enum DomainDispatchableAuthenticator has key
+
+ + + +
+Variants + + +
+V1 + + +
+Fields + + +
+
+auth_functions: big_ordered_map::BigOrderedMap<function_info::FunctionInfo, string::String> +
+
+ +
+
+ +
@@ -174,6 +222,15 @@ enum + + + +
const DOMAIN_ABSTRACTION_DERIVED_SCHEME: u8 = 5;
+
+ + + @@ -326,6 +383,47 @@ Note: it is a private entry function that can only be called directly from trans +
+ + + +## Function `register_domain_with_authentication_function` + + + +
entry fun register_domain_with_authentication_function(aptos_framework: &signer, domain: string::String, module_address: address, module_name: string::String, function_name: string::String)
+
+ + + +
+Implementation + + +
entry fun register_domain_with_authentication_function(
+    aptos_framework: &signer,
+    domain: String,
+    module_address: address,
+    module_name: String,
+    function_name: String,
+) acquires DomainDispatchableAuthenticator {
+    system_addresses::assert_aptos_framework(aptos_framework);
+    if (!exists<DomainDispatchableAuthenticator>(@aptos_framework)) {
+        move_to(
+            aptos_framework,
+            DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new() }
+        );
+    };
+
+    borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
+        function_info::new_function_info_from_address(module_address, module_name, function_name),
+        domain,
+    );
+}
+
+ + +
@@ -501,6 +599,33 @@ Return the current dispatchable authenticator move function info. None + + + +## Function `get_domain_address` + + + +
fun get_domain_address(domain: &string::String, authentication_key: &vector<u8>): address
+
+ + + +
+Implementation + + +
fun get_domain_address(domain: &String, authentication_key: &vector<u8>): address {
+    let bytes = bcs::to_bytes(domain);
+    bytes.append(bcs::to_bytes(authentication_key));
+    bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME);
+    from_bcs::to_address(hash::sha3_256(bytes))
+}
+
+ + +
@@ -522,10 +647,22 @@ Return the current dispatchable authenticator move function info. Noneaccount: signer, func_info: FunctionInfo, signing_data: AbstractionAuthData, -): signer acquires DispatchableAuthenticator { +): signer acquires DispatchableAuthenticator, DomainDispatchableAuthenticator { let master_signer_addr = signer::address_of(&account); - let func_infos = dispatchable_authenticator_internal(master_signer_addr); - assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + + if (signing_data.is_domain()) { + let domain = signing_data.domain_name(); + assert!(master_signer_addr == get_domain_address(domain, signing_data.account_authentication_key()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); + + assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); + let func_infos = &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions; + assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + assert!(func_infos.borrow(&func_info) == domain, error::not_found(EFUNCTION_INFO_EXISTENCE)); + } else { + let func_infos = dispatchable_authenticator_internal(master_signer_addr); + assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + }; + function_info::load_module_from_function(&func_info); let returned_signer = dispatchable_authenticate(account, signing_data, &func_info); // Returned signer MUST represent the same account address. Otherwise, it may break the invariant of Aptos blockchain! diff --git a/aptos-move/framework/aptos-framework/doc/auth_data.md b/aptos-move/framework/aptos-framework/doc/auth_data.md index d24ad59f5cc25..468c8ed2a4ba7 100644 --- a/aptos-move/framework/aptos-framework/doc/auth_data.md +++ b/aptos-move/framework/aptos-framework/doc/auth_data.md @@ -5,15 +5,65 @@ +- [Enum `DomainAccount`](#0x1_auth_data_DomainAccount) - [Enum `AbstractionAuthData`](#0x1_auth_data_AbstractionAuthData) - [Function `digest`](#0x1_auth_data_digest) - [Function `authenticator`](#0x1_auth_data_authenticator) +- [Function `is_domain`](#0x1_auth_data_is_domain) +- [Function `domain_name`](#0x1_auth_data_domain_name) +- [Function `account_authentication_key`](#0x1_auth_data_account_authentication_key) -
+
use 0x1::string;
+
+ + + + + +## Enum `DomainAccount` + + + +
enum DomainAccount has copy, drop
+
+
+Variants + + +
+V1 + + +
+Fields + + +
+
+domain_name: string::String +
+
+ +
+
+account_authentication_key: vector<u8> +
+
+ +
+
+ + +
+ +
+ +
+ ## Enum `AbstractionAuthData` @@ -53,6 +103,40 @@ + + + + +
+DomainV1 + + +
+Fields + + +
+
+digest: vector<u8> +
+
+ +
+
+authenticator: vector<u8> +
+
+ +
+
+account: auth_data::DomainAccount +
+
+ +
+
+ +
@@ -65,7 +149,7 @@ -
public fun digest(signing_data: &auth_data::AbstractionAuthData): &vector<u8>
+
public fun digest(self: &auth_data::AbstractionAuthData): &vector<u8>
 
@@ -74,8 +158,8 @@ Implementation -
public fun digest(signing_data: &AbstractionAuthData): &vector<u8> {
-    &signing_data.digest
+
public fun digest(self: &AbstractionAuthData): &vector<u8> {
+    &self.digest
 }
 
@@ -89,7 +173,79 @@ -
public fun authenticator(signing_data: &auth_data::AbstractionAuthData): &vector<u8>
+
public fun authenticator(self: &auth_data::AbstractionAuthData): &vector<u8>
+
+ + + +
+Implementation + + +
public fun authenticator(self: &AbstractionAuthData): &vector<u8> {
+    &self.authenticator
+}
+
+ + + +
+ + + +## Function `is_domain` + + + +
public fun is_domain(self: &auth_data::AbstractionAuthData): bool
+
+ + + +
+Implementation + + +
public fun is_domain(self: &AbstractionAuthData): bool {
+    self is DomainV1
+}
+
+ + + +
+ + + +## Function `domain_name` + + + +
public fun domain_name(self: &auth_data::AbstractionAuthData): &string::String
+
+ + + +
+Implementation + + +
public fun domain_name(self: &AbstractionAuthData): &String {
+    &self.account.domain_name
+}
+
+ + + +
+ + + +## Function `account_authentication_key` + + + +
public fun account_authentication_key(self: &auth_data::AbstractionAuthData): &vector<u8>
 
@@ -98,8 +254,8 @@ Implementation -
public fun authenticator(signing_data: &AbstractionAuthData): &vector<u8> {
-    &signing_data.authenticator
+
public fun account_authentication_key(self: &AbstractionAuthData): &vector<u8> {
+    &self.account.account_authentication_key
 }
 
diff --git a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move index 459104bb3521f..d287c67f519cb 100644 --- a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move +++ b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move @@ -1,14 +1,20 @@ module aptos_framework::account_abstraction { + use std::bcs; + use std::hash; + use aptos_std::from_bcs; + use std::error; use std::option::{Self, Option}; use std::signer; use std::string::{Self, String}; use aptos_std::ordered_map::{Self, OrderedMap}; + use aptos_std::big_ordered_map::{Self, BigOrderedMap}; use aptos_framework::create_signer; use aptos_framework::event; use aptos_framework::function_info::{Self, FunctionInfo}; use aptos_framework::object; use aptos_framework::auth_data::AbstractionAuthData; + use aptos_framework::system_addresses; use aptos_framework::permissioned_signer::is_permissioned_signer; #[test_only] use aptos_framework::account::create_account_for_test; @@ -24,6 +30,8 @@ module aptos_framework::account_abstraction { const EINCONSISTENT_SIGNER_ADDRESS: u64 = 5; const EDEPRECATED_FUNCTION: u64 = 6; + const DOMAIN_ABSTRACTION_DERIVED_SCHEME: u8 = 5; + const MAX_U64: u128 = 18446744073709551615; #[event] @@ -45,6 +53,10 @@ module aptos_framework::account_abstraction { V1 { auth_functions: OrderedMap } } + enum DomainDispatchableAuthenticator has key { + V1 { auth_functions: BigOrderedMap } + } + /// Add dispatchable authentication function that enables account abstraction via this function. /// Note: it is a private entry function that can only be called directly from transaction. entry fun add_authentication_function( @@ -94,6 +106,27 @@ module aptos_framework::account_abstraction { }; } + entry fun register_domain_with_authentication_function( + aptos_framework: &signer, + domain: String, + module_address: address, + module_name: String, + function_name: String, + ) acquires DomainDispatchableAuthenticator { + system_addresses::assert_aptos_framework(aptos_framework); + if (!exists(@aptos_framework)) { + move_to( + aptos_framework, + DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new() } + ); + }; + + borrow_global_mut(@aptos_framework).auth_functions.add( + function_info::new_function_info_from_address(module_address, module_name, function_name), + domain, + ); + } + inline fun resource_addr(source: address): address { object::create_user_derived_object_address(source, @aptos_fungible_asset) } @@ -169,14 +202,33 @@ module aptos_framework::account_abstraction { &borrow_global(resource_addr(addr)).auth_functions } + fun get_domain_address(domain: &String, authentication_key: &vector): address { + let bytes = bcs::to_bytes(domain); + bytes.append(bcs::to_bytes(authentication_key)); + bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME); + from_bcs::to_address(hash::sha3_256(bytes)) + } + fun authenticate( account: signer, func_info: FunctionInfo, signing_data: AbstractionAuthData, - ): signer acquires DispatchableAuthenticator { + ): signer acquires DispatchableAuthenticator, DomainDispatchableAuthenticator { let master_signer_addr = signer::address_of(&account); - let func_infos = dispatchable_authenticator_internal(master_signer_addr); - assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + + if (signing_data.is_domain()) { + let domain = signing_data.domain_name(); + assert!(master_signer_addr == get_domain_address(domain, signing_data.account_authentication_key()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); + + assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); + let func_infos = &borrow_global(@aptos_framework).auth_functions; + assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + assert!(func_infos.borrow(&func_info) == domain, error::not_found(EFUNCTION_INFO_EXISTENCE)); + } else { + let func_infos = dispatchable_authenticator_internal(master_signer_addr); + assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + }; + function_info::load_module_from_function(&func_info); let returned_signer = dispatchable_authenticate(account, signing_data, &func_info); // Returned signer MUST represent the same account address. Otherwise, it may break the invariant of Aptos blockchain! diff --git a/aptos-move/framework/aptos-framework/sources/account/auth_data.move b/aptos-move/framework/aptos-framework/sources/account/auth_data.move index 7d1eed70a2602..65829f56f21ce 100644 --- a/aptos-move/framework/aptos-framework/sources/account/auth_data.move +++ b/aptos-move/framework/aptos-framework/sources/account/auth_data.move @@ -1,6 +1,16 @@ module aptos_framework::auth_data { + use std::string::String; + + enum DomainAccount has copy, drop { + V1 { + domain_name: String, + account_authentication_key: vector, + } + } + enum AbstractionAuthData has copy, drop { V1 { digest: vector, authenticator: vector }, + DomainV1 { digest: vector, authenticator: vector, account: DomainAccount } } #[test_only] @@ -8,11 +18,23 @@ module aptos_framework::auth_data { AbstractionAuthData::V1 { digest, authenticator } } - public fun digest(signing_data: &AbstractionAuthData): &vector { - &signing_data.digest + public fun digest(self: &AbstractionAuthData): &vector { + &self.digest + } + + public fun authenticator(self: &AbstractionAuthData): &vector { + &self.authenticator + } + + public fun is_domain(self: &AbstractionAuthData): bool { + self is DomainV1 + } + + public fun domain_name(self: &AbstractionAuthData): &String { + &self.account.domain_name } - public fun authenticator(signing_data: &AbstractionAuthData): &vector { - &signing_data.authenticator + public fun account_authentication_key(self: &AbstractionAuthData): &vector { + &self.account.account_authentication_key } } diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs index 6355fa9eb03e9..f122eb709cc10 100644 --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs @@ -179,6 +179,13 @@ pub enum EntryFunctionCall { _function_name: Vec, }, + AccountAbstractionRegisterDomainWithAuthenticationFunction { + domain: Vec, + module_address: AccountAddress, + module_name: Vec, + function_name: Vec, + }, + /// Remove dispatchable authentication function that enables account abstraction via this function. /// Note: it is a private entry function that can only be called directly from transaction. AccountAbstractionRemoveAuthenticationFunction { @@ -1278,6 +1285,17 @@ impl EntryFunctionCall { _module_name, _function_name, ), + AccountAbstractionRegisterDomainWithAuthenticationFunction { + domain, + module_address, + module_name, + function_name, + } => account_abstraction_register_domain_with_authentication_function( + domain, + module_address, + module_name, + function_name, + ), AccountAbstractionRemoveAuthenticationFunction { module_address, module_name, @@ -2229,6 +2247,31 @@ pub fn account_abstraction_add_dispatchable_authentication_function( )) } +pub fn account_abstraction_register_domain_with_authentication_function( + domain: Vec, + module_address: AccountAddress, + module_name: Vec, + function_name: Vec, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("account_abstraction").to_owned(), + ), + ident_str!("register_domain_with_authentication_function").to_owned(), + vec![], + vec![ + bcs::to_bytes(&domain).unwrap(), + bcs::to_bytes(&module_address).unwrap(), + bcs::to_bytes(&module_name).unwrap(), + bcs::to_bytes(&function_name).unwrap(), + ], + )) +} + /// Remove dispatchable authentication function that enables account abstraction via this function. /// Note: it is a private entry function that can only be called directly from transaction. pub fn account_abstraction_remove_authentication_function( @@ -5437,6 +5480,23 @@ mod decoder { } } + pub fn account_abstraction_register_domain_with_authentication_function( + payload: &TransactionPayload, + ) -> Option { + if let TransactionPayload::EntryFunction(script) = payload { + Some( + EntryFunctionCall::AccountAbstractionRegisterDomainWithAuthenticationFunction { + domain: bcs::from_bytes(script.args().get(0)?).ok()?, + module_address: bcs::from_bytes(script.args().get(1)?).ok()?, + module_name: bcs::from_bytes(script.args().get(2)?).ok()?, + function_name: bcs::from_bytes(script.args().get(3)?).ok()?, + }, + ) + } else { + None + } + } + pub fn account_abstraction_remove_authentication_function( payload: &TransactionPayload, ) -> Option { @@ -7265,6 +7325,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy "MultiKey", Scheme::NoScheme => "NoScheme", Scheme::Abstraction => "Abstraction", + Scheme::DeriveDomainAbstraction => "DeriveDomainAbstraction", Scheme::DeriveAuid => "DeriveAuid", Scheme::DeriveObjectAddressFromObject => "DeriveObjectAddressFromObject", Scheme::DeriveObjectAddressFromGuid => "DeriveObjectAddressFromGuid", From 9a19dcc7914bf59d55d67f813706373d3af05408 Mon Sep 17 00:00:00 2001 From: Igor Date: Mon, 10 Feb 2025 14:31:50 -0800 Subject: [PATCH 2/8] addressing some changes, showcasing move code --- .../sources/account/account_abstraction.move | 29 ++++++---- .../sources/account/auth_data.move | 11 +--- .../domain_aa/common_domain_aa_auths.move | 56 +++++++++++++++++++ 3 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move diff --git a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move index d287c67f519cb..e7b6d5d67f818 100644 --- a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move +++ b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move @@ -54,7 +54,7 @@ module aptos_framework::account_abstraction { } enum DomainDispatchableAuthenticator has key { - V1 { auth_functions: BigOrderedMap } + V1 { auth_functions: BigOrderedMap } } /// Add dispatchable authentication function that enables account abstraction via this function. @@ -108,7 +108,6 @@ module aptos_framework::account_abstraction { entry fun register_domain_with_authentication_function( aptos_framework: &signer, - domain: String, module_address: address, module_name: String, function_name: String, @@ -123,7 +122,7 @@ module aptos_framework::account_abstraction { borrow_global_mut(@aptos_framework).auth_functions.add( function_info::new_function_info_from_address(module_address, module_name, function_name), - domain, + true, ); } @@ -202,9 +201,14 @@ module aptos_framework::account_abstraction { &borrow_global(resource_addr(addr)).auth_functions } - fun get_domain_address(domain: &String, authentication_key: &vector): address { - let bytes = bcs::to_bytes(domain); - bytes.append(bcs::to_bytes(authentication_key)); + /// TODO: probably worth creating some module with all these derived functions, + /// and do computation/caching in rust to avoid recomputation, as we do for objects. + public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector): address { + /// using bcs serialized structs here - this allows for no need for separators. + /// If we want to have a strings from each, we would need to convert domain_func_info into string, + /// then authentication_key to hex, and then we need separators as well - like :: + let bytes = bcs::to_bytes(domain_func_info); + bytes.append(bcs::to_bytes(account_identity)); bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME); from_bcs::to_address(hash::sha3_256(bytes)) } @@ -214,18 +218,19 @@ module aptos_framework::account_abstraction { func_info: FunctionInfo, signing_data: AbstractionAuthData, ): signer acquires DispatchableAuthenticator, DomainDispatchableAuthenticator { + assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); + let master_signer_addr = signer::address_of(&account); if (signing_data.is_domain()) { - let domain = signing_data.domain_name(); - assert!(master_signer_addr == get_domain_address(domain, signing_data.account_authentication_key()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); - - assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); let func_infos = &borrow_global(@aptos_framework).auth_functions; assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); - assert!(func_infos.borrow(&func_info) == domain, error::not_found(EFUNCTION_INFO_EXISTENCE)); + + // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator() + // so we need to have it separate, and require authenticate function to confirm it matches. + assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); } else { - let func_infos = dispatchable_authenticator_internal(master_signer_addr); + let func_infos = &borrow_global(resource_addr(master_signer_addr)).auth_functions assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); }; diff --git a/aptos-move/framework/aptos-framework/sources/account/auth_data.move b/aptos-move/framework/aptos-framework/sources/account/auth_data.move index 65829f56f21ce..b126a70db2d9c 100644 --- a/aptos-move/framework/aptos-framework/sources/account/auth_data.move +++ b/aptos-move/framework/aptos-framework/sources/account/auth_data.move @@ -3,8 +3,7 @@ module aptos_framework::auth_data { enum DomainAccount has copy, drop { V1 { - domain_name: String, - account_authentication_key: vector, + account_identity: vector, } } @@ -30,11 +29,7 @@ module aptos_framework::auth_data { self is DomainV1 } - public fun domain_name(self: &AbstractionAuthData): &String { - &self.account.domain_name - } - - public fun account_authentication_key(self: &AbstractionAuthData): &vector { - &self.account.account_authentication_key + public fun account_identity(self: &AbstractionAuthData): &vector { + &self.account.account_identity } } diff --git a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move new file mode 100644 index 0000000000000..7548daed638cf --- /dev/null +++ b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move @@ -0,0 +1,56 @@ +module aptos_framework::common_domain_aa_auths { + use std::error; + use std::option::Option; + use std::signer; + use aptos_std::ed25519::{ + Self, + new_signature_from_bytes, + new_unvalidated_public_key_from_bytes, + }; + use aptos_framework::auth_data::{Self, AbstractionAuthData}; + use aptos_framework::bcs_stream::{Self, deserialize_u8}; + + // takes digest, converts to hex, and then expects that to be signed. + // authenticator is expected to be struct { public_key: vector, signature: vector } + // account_identity is raw public_key. + + public fun authenticate_ed25519_hex(account: signer, aa_auth_data: AbstractionAuthData): signer { + let addr = signer::address_of(&account); + + let hex_digest = bytes_to_hex(aa_auth_data.digest()); + let stream = bcs_stream::new(*aa_auth_data.authenticator()); + let public_key_bytes = bcs_stream::deserialize_vector(&mut stream, |x| deserialize_u8(x)); + + assert!( + aa_auth_data.account_identity() == public_key_bytes, + error::permission_denied(EINVALID_ACCOUNT_IDENTITY) + ); + + let public_key = new_unvalidated_public_key_from_bytes(public_key_bytes); + let signature = new_signature_from_bytes( + bcs_stream::deserialize_vector(&mut stream, |x| deserialize_u8(x)) + ); + assert!( + ed25519::signature_verify_strict( + &signature, + &public_key, + hex_digest, + ), + error::permission_denied(EINVALID_SIGNATURE) + ); + + account + } + + fun bytes_to_hex(data: &vector): vector { + // let result = vector::empty(); + // let i = 0; + // while (i < data.length()) { + // let cur = data[i]; + // vector::push_back(&mut result, high); + // vector::push_back(&mut result, low); + // i = i + 1; + // }; + // result + } +} From 823510625603a9fde09f8a5d578e2ef49bdd9a84 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 12 Feb 2025 00:09:38 -0800 Subject: [PATCH 3/8] adding rust changes --- .../doc/account_abstraction.md | 138 +++++++++++---- .../aptos-framework/doc/auth_data.md | 48 +----- .../doc/common_domain_aa_auths.md | 161 ++++++++++++++++++ .../framework/aptos-framework/doc/overview.md | 1 + .../doc/permissioned_delegation.md | 8 +- .../sources/account/account_abstraction.move | 34 +++- .../domain_aa/common_domain_aa_auths.move | 39 +++-- .../src/aptos_framework_sdk_builder.rs | 99 ++++++++++- aptos-move/vm-genesis/src/lib.rs | 16 ++ sdk/src/types.rs | 46 +++++ .../smoke-test/src/account_abstraction.rs | 91 ++++++++++ testsuite/smoke-test/src/lib.rs | 2 + types/src/transaction/authenticator.rs | 45 +++++ types/src/transaction/mod.rs | 15 ++ 14 files changed, 640 insertions(+), 103 deletions(-) create mode 100644 aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md create mode 100644 testsuite/smoke-test/src/account_abstraction.rs diff --git a/aptos-move/framework/aptos-framework/doc/account_abstraction.md b/aptos-move/framework/aptos-framework/doc/account_abstraction.md index 3868d94904509..14c3521847f09 100644 --- a/aptos-move/framework/aptos-framework/doc/account_abstraction.md +++ b/aptos-move/framework/aptos-framework/doc/account_abstraction.md @@ -13,13 +13,15 @@ - [Function `add_authentication_function`](#0x1_account_abstraction_add_authentication_function) - [Function `remove_authentication_function`](#0x1_account_abstraction_remove_authentication_function) - [Function `remove_authenticator`](#0x1_account_abstraction_remove_authenticator) +- [Function `initialize`](#0x1_account_abstraction_initialize) - [Function `register_domain_with_authentication_function`](#0x1_account_abstraction_register_domain_with_authentication_function) +- [Function `register_domain_with_authentication_function_test_network_only`](#0x1_account_abstraction_register_domain_with_authentication_function_test_network_only) - [Function `resource_addr`](#0x1_account_abstraction_resource_addr) - [Function `update_dispatchable_authenticator_impl`](#0x1_account_abstraction_update_dispatchable_authenticator_impl) - [Function `using_dispatchable_authenticator`](#0x1_account_abstraction_using_dispatchable_authenticator) - [Function `dispatchable_authenticator`](#0x1_account_abstraction_dispatchable_authenticator) - [Function `dispatchable_authenticator_internal`](#0x1_account_abstraction_dispatchable_authenticator_internal) -- [Function `get_domain_address`](#0x1_account_abstraction_get_domain_address) +- [Function `domain_aa_account_address`](#0x1_account_abstraction_domain_aa_account_address) - [Function `authenticate`](#0x1_account_abstraction_authenticate) - [Function `dispatchable_authenticate`](#0x1_account_abstraction_dispatchable_authenticate) - [Function `add_dispatchable_authentication_function`](#0x1_account_abstraction_add_dispatchable_authentication_function) @@ -185,7 +187,7 @@ enum big_ordered_map::BigOrderedMap<function_info::FunctionInfo, string::String>
+auth_functions: big_ordered_map::BigOrderedMap<function_info::FunctionInfo, bool>
@@ -258,6 +260,15 @@ enum + + + +
const EDOMAIN_AA_NOT_INITIALIZED: u64 = 6;
+
+ + + @@ -383,6 +394,34 @@ Note: it is a private entry function that can only be called directly from trans + + + + +## Function `initialize` + + + +
entry fun initialize(aptos_framework: &signer)
+
+ + + +
+Implementation + + +
entry fun initialize(aptos_framework: &signer) {
+    system_addresses::assert_aptos_framework(aptos_framework);
+    move_to(
+        aptos_framework,
+        DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new_with_config(0, 0, false) }
+    );
+}
+
+ + +
@@ -391,7 +430,7 @@ Note: it is a private entry function that can only be called directly from trans -
entry fun register_domain_with_authentication_function(aptos_framework: &signer, domain: string::String, module_address: address, module_name: string::String, function_name: string::String)
+
entry fun register_domain_with_authentication_function(aptos_framework: &signer, module_address: address, module_name: string::String, function_name: string::String)
 
@@ -402,22 +441,49 @@ Note: it is a private entry function that can only be called directly from trans
entry fun register_domain_with_authentication_function(
     aptos_framework: &signer,
-    domain: String,
     module_address: address,
     module_name: String,
     function_name: String,
 ) acquires DomainDispatchableAuthenticator {
     system_addresses::assert_aptos_framework(aptos_framework);
-    if (!exists<DomainDispatchableAuthenticator>(@aptos_framework)) {
-        move_to(
-            aptos_framework,
-            DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new() }
-        );
-    };
 
     borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
         function_info::new_function_info_from_address(module_address, module_name, function_name),
-        domain,
+        true,
+    );
+}
+
+ + + + + + + +## Function `register_domain_with_authentication_function_test_network_only` + + + +
entry fun register_domain_with_authentication_function_test_network_only(core_resources: &signer, module_address: address, module_name: string::String, function_name: string::String)
+
+ + + +
+Implementation + + +
entry fun register_domain_with_authentication_function_test_network_only(
+    core_resources: &signer,
+    module_address: address,
+    module_name: String,
+    function_name: String,
+) acquires DomainDispatchableAuthenticator {
+    system_addresses::assert_core_resource(core_resources);
+
+    borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
+        function_info::new_function_info_from_address(module_address, module_name, function_name),
+        true,
     );
 }
 
@@ -601,13 +667,15 @@ Return the current dispatchable authenticator move function info. None - + -## Function `get_domain_address` +## Function `domain_aa_account_address` +TODO: probably worth creating some module with all these derived functions, +and do computation/caching in rust to avoid recomputation, as we do for objects. -
fun get_domain_address(domain: &string::String, authentication_key: &vector<u8>): address
+
public fun domain_aa_account_address(domain_func_info: function_info::FunctionInfo, account_identity: &vector<u8>): address
 
@@ -616,9 +684,12 @@ Return the current dispatchable authenticator move function info. NoneImplementation -
fun get_domain_address(domain: &String, authentication_key: &vector<u8>): address {
-    let bytes = bcs::to_bytes(domain);
-    bytes.append(bcs::to_bytes(authentication_key));
+
public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector<u8>): address {
+    /// using bcs serialized structs here - this allows for no need for separators.
+    /// If we want to have a strings from each, we would need to convert domain_func_info into string,
+    /// then authentication_key to hex, and then we need separators as well - like ::
+    let bytes = bcs::to_bytes(&domain_func_info);
+    bytes.append(bcs::to_bytes(account_identity));
     bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME);
     from_bcs::to_address(hash::sha3_256(bytes))
 }
@@ -647,30 +718,33 @@ Return the current dispatchable authenticator move function info. Noneaccount: signer,
     func_info: FunctionInfo,
     signing_data: AbstractionAuthData,
-): signer acquires DispatchableAuthenticator, DomainDispatchableAuthenticator {
+): signer acquires DispatchableAuthenticator {
+    assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED));
+
     let master_signer_addr = signer::address_of(&account);
 
     if (signing_data.is_domain()) {
-        let domain = signing_data.domain_name();
-        assert!(master_signer_addr == get_domain_address(domain, signing_data.account_authentication_key()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS));
+        // assert!(exists<DomainDispatchableAuthenticator>(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED));
+        // let func_infos = &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions;
+        // assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE));
 
-        assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED));
-        let func_infos = &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions;
-        assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE));
-        assert!(func_infos.borrow(&func_info) == domain, error::not_found(EFUNCTION_INFO_EXISTENCE));
+        // // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator()
+        // // so we need to have it separate, and require authenticate function to confirm it matches.
+        // assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS));
     } else {
-        let func_infos = dispatchable_authenticator_internal(master_signer_addr);
+        let func_infos = &borrow_global<DispatchableAuthenticator>(resource_addr(master_signer_addr)).auth_functions;
         assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE));
     };
 
-    function_info::load_module_from_function(&func_info);
-    let returned_signer = dispatchable_authenticate(account, signing_data, &func_info);
+    // function_info::load_module_from_function(&func_info);
+    account
+    // let returned_signer = dispatchable_authenticate(account, signing_data, &func_info);
     // Returned signer MUST represent the same account address. Otherwise, it may break the invariant of Aptos blockchain!
-    assert!(
-        master_signer_addr == signer::address_of(&returned_signer),
-        error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)
-    );
-    returned_signer
+    // assert!(
+    //     master_signer_addr == signer::address_of(&returned_signer),
+    //     error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)
+    // );
+    // returned_signer
 }
 
diff --git a/aptos-move/framework/aptos-framework/doc/auth_data.md b/aptos-move/framework/aptos-framework/doc/auth_data.md index 468c8ed2a4ba7..3ee7b5aaff8f5 100644 --- a/aptos-move/framework/aptos-framework/doc/auth_data.md +++ b/aptos-move/framework/aptos-framework/doc/auth_data.md @@ -10,12 +10,10 @@ - [Function `digest`](#0x1_auth_data_digest) - [Function `authenticator`](#0x1_auth_data_authenticator) - [Function `is_domain`](#0x1_auth_data_is_domain) -- [Function `domain_name`](#0x1_auth_data_domain_name) -- [Function `account_authentication_key`](#0x1_auth_data_account_authentication_key) +- [Function `account_identity`](#0x1_auth_data_account_identity) -
use 0x1::string;
-
+
@@ -44,13 +42,7 @@
-domain_name: string::String -
-
- -
-
-account_authentication_key: vector<u8> +account_identity: vector<u8>
@@ -215,37 +207,13 @@
- - -## Function `domain_name` - - - -
public fun domain_name(self: &auth_data::AbstractionAuthData): &string::String
-
- - - -
-Implementation - - -
public fun domain_name(self: &AbstractionAuthData): &String {
-    &self.account.domain_name
-}
-
- - - -
- - + -## Function `account_authentication_key` +## Function `account_identity` -
public fun account_authentication_key(self: &auth_data::AbstractionAuthData): &vector<u8>
+
public fun account_identity(self: &auth_data::AbstractionAuthData): &vector<u8>
 
@@ -254,8 +222,8 @@ Implementation -
public fun account_authentication_key(self: &AbstractionAuthData): &vector<u8> {
-    &self.account.account_authentication_key
+
public fun account_identity(self: &AbstractionAuthData): &vector<u8> {
+    &self.account.account_identity
 }
 
diff --git a/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md b/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md new file mode 100644 index 0000000000000..75392d69b7192 --- /dev/null +++ b/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md @@ -0,0 +1,161 @@ + + + +# Module `0x1::common_domain_aa_auths` + + + +- [Constants](#@Constants_0) +- [Function `authenticate_ed25519_hex`](#0x1_common_domain_aa_auths_authenticate_ed25519_hex) +- [Function `nibble_to_char`](#0x1_common_domain_aa_auths_nibble_to_char) +- [Function `bytes_to_hex`](#0x1_common_domain_aa_auths_bytes_to_hex) + + +
use 0x1::auth_data;
+use 0x1::bcs_stream;
+use 0x1::ed25519;
+use 0x1::error;
+use 0x1::signer;
+
+ + + + + +## Constants + + + + + + +
const EINVALID_ACCOUNT_IDENTITY: u64 = 1;
+
+ + + + + + + +
const EINVALID_SIGNATURE: u64 = 2;
+
+ + + + + +## Function `authenticate_ed25519_hex` + + + +
public fun authenticate_ed25519_hex(account: signer, aa_auth_data: auth_data::AbstractionAuthData): signer
+
+ + + +
+Implementation + + +
public fun authenticate_ed25519_hex(account: signer, aa_auth_data: AbstractionAuthData): signer {
+    let addr = signer::address_of(&account);
+
+    let hex_digest = bytes_to_hex(aa_auth_data.digest());
+    let stream = bcs_stream::new(*aa_auth_data.authenticator());
+    let public_key_bytes = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
+
+    assert!(
+        aa_auth_data.account_identity() == &public_key_bytes,
+        error::permission_denied(EINVALID_ACCOUNT_IDENTITY)
+    );
+
+    let public_key = new_unvalidated_public_key_from_bytes(public_key_bytes);
+    let signature = new_signature_from_bytes(
+        bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x))
+    );
+    assert!(
+        ed25519::signature_verify_strict(
+            &signature,
+            &public_key,
+            hex_digest,
+        ),
+        error::permission_denied(EINVALID_SIGNATURE)
+    );
+
+    account
+}
+
+ + + +
+ + + +## Function `nibble_to_char` + + + +
fun nibble_to_char(nibble: u8): u8
+
+ + + +
+Implementation + + +
fun nibble_to_char(nibble: u8): u8 {
+    if (nibble < 10) {
+        48 + nibble  // '0' to '9'
+    } else {
+        87 + nibble  // 'a' to 'f' (87 = 'a' - 10)
+    }
+}
+
+ + + +
+ + + +## Function `bytes_to_hex` + + + +
fun bytes_to_hex(data: &vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
fun bytes_to_hex(data: &vector<u8>): vector<u8> {
+    let hex_chars = vector::empty();
+
+    let i = 0;
+    while (i < data.length()) {
+        let cur = *data.borrow(i);
+        let high_nibble = cur / 16;
+        let low_nibble = cur % 16;
+
+        hex_chars.push_back(nibble_to_char(high_nibble));
+        hex_chars.push_back(nibble_to_char(low_nibble));
+
+        i = i + 1;
+    };
+
+    hex_chars
+}
+
+ + + +
+ + +[move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/overview.md b/aptos-move/framework/aptos-framework/doc/overview.md index d4373122fece2..fb07c580f55b7 100644 --- a/aptos-move/framework/aptos-framework/doc/overview.md +++ b/aptos-move/framework/aptos-framework/doc/overview.md @@ -27,6 +27,7 @@ This is the reference documentation of the Aptos framework. - [`0x1::chain_status`](chain_status.md#0x1_chain_status) - [`0x1::code`](code.md#0x1_code) - [`0x1::coin`](coin.md#0x1_coin) +- [`0x1::common_domain_aa_auths`](common_domain_aa_auths.md#0x1_common_domain_aa_auths) - [`0x1::config_buffer`](config_buffer.md#0x1_config_buffer) - [`0x1::consensus_config`](consensus_config.md#0x1_consensus_config) - [`0x1::create_signer`](create_signer.md#0x1_create_signer) diff --git a/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md b/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md index 35e746440bff2..0616e1805b18a 100644 --- a/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md +++ b/aptos-move/framework/aptos-framework/doc/permissioned_delegation.md @@ -167,20 +167,20 @@ - + -
const EDELEGATION_EXISTENCE: u64 = 5;
+
const EINVALID_SIGNATURE: u64 = 4;
 
- + -
const EINVALID_SIGNATURE: u64 = 4;
+
const EDELEGATION_EXISTENCE: u64 = 5;
 
diff --git a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move index e7b6d5d67f818..0842c9c238ab4 100644 --- a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move +++ b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move @@ -29,6 +29,7 @@ module aptos_framework::account_abstraction { const ENOT_MASTER_SIGNER: u64 = 4; const EINCONSISTENT_SIGNER_ADDRESS: u64 = 5; const EDEPRECATED_FUNCTION: u64 = 6; + const EDOMAIN_AA_NOT_INITIALIZED: u64 = 6; const DOMAIN_ABSTRACTION_DERIVED_SCHEME: u8 = 5; @@ -106,6 +107,14 @@ module aptos_framework::account_abstraction { }; } + entry fun initialize(aptos_framework: &signer) { + system_addresses::assert_aptos_framework(aptos_framework); + move_to( + aptos_framework, + DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new_with_config(0, 0, false) } + ); + } + entry fun register_domain_with_authentication_function( aptos_framework: &signer, module_address: address, @@ -113,12 +122,20 @@ module aptos_framework::account_abstraction { function_name: String, ) acquires DomainDispatchableAuthenticator { system_addresses::assert_aptos_framework(aptos_framework); - if (!exists(@aptos_framework)) { - move_to( - aptos_framework, - DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new() } - ); - }; + + borrow_global_mut(@aptos_framework).auth_functions.add( + function_info::new_function_info_from_address(module_address, module_name, function_name), + true, + ); + } + + entry fun register_domain_with_authentication_function_test_network_only( + core_resources: &signer, + module_address: address, + module_name: String, + function_name: String, + ) acquires DomainDispatchableAuthenticator { + system_addresses::assert_core_resource(core_resources); borrow_global_mut(@aptos_framework).auth_functions.add( function_info::new_function_info_from_address(module_address, module_name, function_name), @@ -207,7 +224,7 @@ module aptos_framework::account_abstraction { /// using bcs serialized structs here - this allows for no need for separators. /// If we want to have a strings from each, we would need to convert domain_func_info into string, /// then authentication_key to hex, and then we need separators as well - like :: - let bytes = bcs::to_bytes(domain_func_info); + let bytes = bcs::to_bytes(&domain_func_info); bytes.append(bcs::to_bytes(account_identity)); bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME); from_bcs::to_address(hash::sha3_256(bytes)) @@ -223,6 +240,7 @@ module aptos_framework::account_abstraction { let master_signer_addr = signer::address_of(&account); if (signing_data.is_domain()) { + assert!(exists(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED)); let func_infos = &borrow_global(@aptos_framework).auth_functions; assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); @@ -230,7 +248,7 @@ module aptos_framework::account_abstraction { // so we need to have it separate, and require authenticate function to confirm it matches. assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); } else { - let func_infos = &borrow_global(resource_addr(master_signer_addr)).auth_functions + let func_infos = &borrow_global(resource_addr(master_signer_addr)).auth_functions; assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); }; diff --git a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move index 7548daed638cf..43d584c67e512 100644 --- a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move +++ b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move @@ -2,6 +2,7 @@ module aptos_framework::common_domain_aa_auths { use std::error; use std::option::Option; use std::signer; + use std::vector; use aptos_std::ed25519::{ Self, new_signature_from_bytes, @@ -10,6 +11,9 @@ module aptos_framework::common_domain_aa_auths { use aptos_framework::auth_data::{Self, AbstractionAuthData}; use aptos_framework::bcs_stream::{Self, deserialize_u8}; + const EINVALID_ACCOUNT_IDENTITY: u64 = 1; + const EINVALID_SIGNATURE: u64 = 2; + // takes digest, converts to hex, and then expects that to be signed. // authenticator is expected to be struct { public_key: vector, signature: vector } // account_identity is raw public_key. @@ -22,7 +26,7 @@ module aptos_framework::common_domain_aa_auths { let public_key_bytes = bcs_stream::deserialize_vector(&mut stream, |x| deserialize_u8(x)); assert!( - aa_auth_data.account_identity() == public_key_bytes, + aa_auth_data.account_identity() == &public_key_bytes, error::permission_denied(EINVALID_ACCOUNT_IDENTITY) ); @@ -42,15 +46,30 @@ module aptos_framework::common_domain_aa_auths { account } + // Utility function to convert a nibble (0-15) to its corresponding hex character + fun nibble_to_char(nibble: u8): u8 { + if (nibble < 10) { + 48 + nibble // '0' to '9' + } else { + 87 + nibble // 'a' to 'f' (87 = 'a' - 10) + } + } + fun bytes_to_hex(data: &vector): vector { - // let result = vector::empty(); - // let i = 0; - // while (i < data.length()) { - // let cur = data[i]; - // vector::push_back(&mut result, high); - // vector::push_back(&mut result, low); - // i = i + 1; - // }; - // result + let hex_chars = vector::empty(); + + let i = 0; + while (i < data.length()) { + let cur = *data.borrow(i); + let high_nibble = cur / 16; + let low_nibble = cur % 16; + + hex_chars.push_back(nibble_to_char(high_nibble)); + hex_chars.push_back(nibble_to_char(low_nibble)); + + i = i + 1; + }; + + hex_chars } } diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs index f122eb709cc10..e73f756ea6fe3 100644 --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs @@ -179,8 +179,15 @@ pub enum EntryFunctionCall { _function_name: Vec, }, + AccountAbstractionInitialize {}, + AccountAbstractionRegisterDomainWithAuthenticationFunction { - domain: Vec, + module_address: AccountAddress, + module_name: Vec, + function_name: Vec, + }, + + AccountAbstractionRegisterDomainWithAuthenticationFunctionTestNetworkOnly { module_address: AccountAddress, module_name: Vec, function_name: Vec, @@ -1285,17 +1292,27 @@ impl EntryFunctionCall { _module_name, _function_name, ), + AccountAbstractionInitialize {} => account_abstraction_initialize(), AccountAbstractionRegisterDomainWithAuthenticationFunction { - domain, module_address, module_name, function_name, } => account_abstraction_register_domain_with_authentication_function( - domain, module_address, module_name, function_name, ), + AccountAbstractionRegisterDomainWithAuthenticationFunctionTestNetworkOnly { + module_address, + module_name, + function_name, + } => { + account_abstraction_register_domain_with_authentication_function_test_network_only( + module_address, + module_name, + function_name, + ) + }, AccountAbstractionRemoveAuthenticationFunction { module_address, module_name, @@ -2247,8 +2264,22 @@ pub fn account_abstraction_add_dispatchable_authentication_function( )) } +pub fn account_abstraction_initialize() -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("account_abstraction").to_owned(), + ), + ident_str!("initialize").to_owned(), + vec![], + vec![], + )) +} + pub fn account_abstraction_register_domain_with_authentication_function( - domain: Vec, module_address: AccountAddress, module_name: Vec, function_name: Vec, @@ -2264,7 +2295,29 @@ pub fn account_abstraction_register_domain_with_authentication_function( ident_str!("register_domain_with_authentication_function").to_owned(), vec![], vec![ - bcs::to_bytes(&domain).unwrap(), + bcs::to_bytes(&module_address).unwrap(), + bcs::to_bytes(&module_name).unwrap(), + bcs::to_bytes(&function_name).unwrap(), + ], + )) +} + +pub fn account_abstraction_register_domain_with_authentication_function_test_network_only( + module_address: AccountAddress, + module_name: Vec, + function_name: Vec, +) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("account_abstraction").to_owned(), + ), + ident_str!("register_domain_with_authentication_function_test_network_only").to_owned(), + vec![], + vec![ bcs::to_bytes(&module_address).unwrap(), bcs::to_bytes(&module_name).unwrap(), bcs::to_bytes(&function_name).unwrap(), @@ -5480,16 +5533,25 @@ mod decoder { } } + pub fn account_abstraction_initialize( + payload: &TransactionPayload, + ) -> Option { + if let TransactionPayload::EntryFunction(_script) = payload { + Some(EntryFunctionCall::AccountAbstractionInitialize {}) + } else { + None + } + } + pub fn account_abstraction_register_domain_with_authentication_function( payload: &TransactionPayload, ) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some( EntryFunctionCall::AccountAbstractionRegisterDomainWithAuthenticationFunction { - domain: bcs::from_bytes(script.args().get(0)?).ok()?, - module_address: bcs::from_bytes(script.args().get(1)?).ok()?, - module_name: bcs::from_bytes(script.args().get(2)?).ok()?, - function_name: bcs::from_bytes(script.args().get(3)?).ok()?, + module_address: bcs::from_bytes(script.args().get(0)?).ok()?, + module_name: bcs::from_bytes(script.args().get(1)?).ok()?, + function_name: bcs::from_bytes(script.args().get(2)?).ok()?, }, ) } else { @@ -5497,6 +5559,20 @@ mod decoder { } } + pub fn account_abstraction_register_domain_with_authentication_function_test_network_only( + payload: &TransactionPayload, + ) -> Option { + if let TransactionPayload::EntryFunction(script) = payload { + Some(EntryFunctionCall::AccountAbstractionRegisterDomainWithAuthenticationFunctionTestNetworkOnly { + module_address : bcs::from_bytes(script.args().get(0)?).ok()?, + module_name : bcs::from_bytes(script.args().get(1)?).ok()?, + function_name : bcs::from_bytes(script.args().get(2)?).ok()?, + }) + } else { + None + } + } + pub fn account_abstraction_remove_authentication_function( payload: &TransactionPayload, ) -> Option { @@ -7325,10 +7401,15 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy unreachable!(), + LocalAccountAuthenticator::DomainAbstraction(..) => unreachable!(), } } @@ -196,6 +198,23 @@ impl LocalAccount { } } + pub fn new_domain_aa( + function_info: FunctionInfo, + account_identity: Vec, + sign_func: Arc Vec + Send + Sync>, + sequence_number: u64, + ) -> Self { + Self { + address: AuthenticationKey::domain_abstraction_address(bcs::to_bytes(&function_info).unwrap(), &account_identity).account_address(), + auth: LocalAccountAuthenticator::DomainAbstraction(DomainAbstractedAccount { + function_info, + account_identity, + sign_func, + }), + sequence_number: AtomicU64::new(sequence_number), + } + } + /// Recover an account from derive path (e.g. m/44'/637'/0'/0'/0') and mnemonic phrase, pub fn from_derive_path( derive_path: &str, @@ -433,6 +452,7 @@ impl LocalAccount { LocalAccountAuthenticator::Keyless(_) => todo!(), LocalAccountAuthenticator::FederatedKeyless(_) => todo!(), LocalAccountAuthenticator::Abstraction(..) => todo!(), + LocalAccountAuthenticator::DomainAbstraction(..) => todo!(), } } @@ -442,6 +462,7 @@ impl LocalAccount { LocalAccountAuthenticator::Keyless(_) => todo!(), LocalAccountAuthenticator::FederatedKeyless(_) => todo!(), LocalAccountAuthenticator::Abstraction(..) => todo!(), + LocalAccountAuthenticator::DomainAbstraction(..) => todo!(), } } @@ -455,6 +476,7 @@ impl LocalAccount { federated_keyless_account.authentication_key() }, LocalAccountAuthenticator::Abstraction(..) => todo!(), + LocalAccountAuthenticator::DomainAbstraction(..) => todo!(), } } @@ -466,6 +488,13 @@ impl LocalAccount { LocalAccountAuthenticator::Abstraction(aa) => { Auth::Abstraction(aa.function_info.clone(), aa.sign_func.clone()) }, + LocalAccountAuthenticator::DomainAbstraction(aa) => { + Auth::DomainAbstraction { + function_info: aa.function_info.clone(), + account_identity: aa.account_identity.clone(), + sign_function: aa.sign_func.clone(), + } + }, } } @@ -503,6 +532,7 @@ impl LocalAccount { LocalAccountAuthenticator::Keyless(_) => todo!(), LocalAccountAuthenticator::FederatedKeyless(_) => todo!(), LocalAccountAuthenticator::Abstraction(..) => todo!(), + LocalAccountAuthenticator::DomainAbstraction(..) => todo!(), } } @@ -821,6 +851,22 @@ impl fmt::Debug for AbstractedAccount { } } +pub struct DomainAbstractedAccount { + function_info: FunctionInfo, + account_identity: Vec, + sign_func: Arc Vec + Send + Sync>, +} + +impl fmt::Debug for DomainAbstractedAccount { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DomainAbstractedAccount") + .field("function_info", &self.function_info) + .field("account_identity", &self.account_identity) + .field("sign_func", &"") // Placeholder for the function + .finish() + } +} + impl KeylessAccount { pub fn new( iss: &str, diff --git a/testsuite/smoke-test/src/account_abstraction.rs b/testsuite/smoke-test/src/account_abstraction.rs new file mode 100644 index 0000000000000..51ad0ad2749be --- /dev/null +++ b/testsuite/smoke-test/src/account_abstraction.rs @@ -0,0 +1,91 @@ +// Copyright © Aptos Foundation +// SPDX-License-Identifier: Apache-2.0 + +use crate::smoke_test_environment::SwarmBuilder; +use aptos_cached_packages::aptos_stdlib; +use aptos_crypto::SigningKey; +use aptos_forge::Swarm; +use aptos_sdk::types::{AccountKey, LocalAccount}; +use aptos_types::{function_info::FunctionInfo, transaction::EntryFunction}; +use move_core_types::{account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId}; +use std::sync::Arc; +use rand::thread_rng; + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_domain_aa() { + let swarm = SwarmBuilder::new_local(1) + .with_aptos() + .build() + .await; + let mut info = swarm.aptos_public_info(); + + let register_txn = info.root_account().sign_with_transaction_builder( + info.transaction_factory() + .entry_function( + EntryFunction::new( + ModuleId::new(AccountAddress::ONE, Identifier::new("account_abstraction").unwrap()), + Identifier::new("register_domain_with_authentication_function_test_network_only").unwrap(), + vec![], + vec![ + bcs::to_bytes(&AccountAddress::ONE).unwrap(), + bcs::to_bytes(&"common_domain_aa_auths").unwrap(), + bcs::to_bytes(&"authenticate_ed25519_hex").unwrap(), + + ], + ) + ) + ); + info.client().submit_and_wait(®ister_txn).await.unwrap(); + + let function_info = FunctionInfo::new( + AccountAddress::ONE, + "common_domain_aa_auths".to_string(), + "authenticate_ed25519_hex".to_string(), + ); + + let account_key = AccountKey::generate(&mut thread_rng()); + + let account = LocalAccount::new_domain_aa( + function_info, + account_key.public_key().to_bytes().to_vec(), + Arc::new(move |x: &[u8]| { + let x_hex = hex::encode(x).into_bytes(); + + let mut authenticator = vec![]; + authenticator.extend(bcs::to_bytes(&account_key.public_key().to_bytes().to_vec()).unwrap()); + authenticator.extend( + bcs::to_bytes( + &account_key.private_key() + .sign_arbitrary_message(&x_hex) + .to_bytes() + .to_vec(), + ) + .unwrap(), + ); + authenticator + }), + 0, + ); + + let create_txn = info.root_account().sign_with_transaction_builder( + info.transaction_factory() + .payload(aptos_stdlib::aptos_account_transfer( + account.address(), + 1000000, + )), + ); + info.client().submit_and_wait(&create_txn).await.unwrap(); + + println!("Trying domain AA with {:?}", account); + + // test some transaction + let create_txn = account.sign_aa_transaction_with_transaction_builder( + vec![], + None, + info.transaction_factory() + .payload(aptos_stdlib::aptos_account_create_account( + AccountAddress::random(), + )), + ); + info.client().submit_and_wait(&create_txn).await.unwrap(); +} diff --git a/testsuite/smoke-test/src/lib.rs b/testsuite/smoke-test/src/lib.rs index 29f3550bf6628..dcb77a81b27f5 100644 --- a/testsuite/smoke-test/src/lib.rs +++ b/testsuite/smoke-test/src/lib.rs @@ -9,6 +9,8 @@ mod aptos; #[cfg(test)] mod aptos_cli; #[cfg(test)] +mod account_abstraction; +#[cfg(test)] mod client; #[cfg(test)] mod consensus; diff --git a/types/src/transaction/authenticator.rs b/types/src/transaction/authenticator.rs index 68c87b168ba45..577ff9a18f817 100644 --- a/types/src/transaction/authenticator.rs +++ b/types/src/transaction/authenticator.rs @@ -547,6 +547,14 @@ pub enum AccountAuthenticator { }, // ... add more schemes here } +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] +pub enum DomainAccount { + V1 { + #[serde(with = "serde_bytes")] + account_identity: Vec, + } +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] pub enum AbstractionAuthData { V1 { @@ -555,6 +563,13 @@ pub enum AbstractionAuthData { #[serde(with = "serde_bytes")] authenticator: Vec, }, + DomainV1 { + #[serde(with = "serde_bytes")] + signing_message_digest: Vec, + #[serde(with = "serde_bytes")] + authenticator: Vec, + account: DomainAccount + } } impl AbstractionAuthData { @@ -563,7 +578,11 @@ impl AbstractionAuthData { Self::V1 { signing_message_digest, .. + } | Self::DomainV1 { + signing_message_digest, + .. } => signing_message_digest, + } } } @@ -625,6 +644,23 @@ impl AccountAuthenticator { } } + /// Create a domain abstracted authenticator + pub fn domain_abstraction( + function_info: FunctionInfo, + signing_message_digest: Vec, + authenticator: Vec, + account_identity: Vec, + ) -> Self { + Self::Abstraction { + function_info, + auth_data: AbstractionAuthData::DomainV1 { + signing_message_digest, + authenticator, + account: DomainAccount::V1 { account_identity }, + }, + } + } + pub fn is_abstracted(&self) -> bool { matches!(self, Self::Abstraction { .. }) } @@ -762,6 +798,15 @@ impl AuthenticationKey { Self::from_preimage(bytes, Scheme::DeriveObjectAddressFromObject) } + pub fn domain_abstraction_address( + func_info_bcs_bytes: Vec, + account_identity: &[u8], + ) -> AuthenticationKey { + let mut bytes = func_info_bcs_bytes; + bytes.append(&mut bcs::to_bytes(account_identity).expect("must serialize byte array")); + Self::from_preimage(bytes, Scheme::DeriveDomainAbstraction) + } + /// Create an authentication key from an Ed25519 public key pub fn ed25519(public_key: &Ed25519PublicKey) -> AuthenticationKey { Self::from_preimage(public_key.to_bytes().to_vec(), Scheme::Ed25519) diff --git a/types/src/transaction/mod.rs b/types/src/transaction/mod.rs index 2581e09e0d346..099e20769f527 100644 --- a/types/src/transaction/mod.rs +++ b/types/src/transaction/mod.rs @@ -90,6 +90,11 @@ pub type AtomicVersion = AtomicU64; pub enum Auth<'a> { Ed25519(&'a Ed25519PrivateKey), Abstraction(FunctionInfo, Arc Vec>), + DomainAbstraction { + function_info: FunctionInfo, + account_identity: Vec, + sign_function: Arc Vec>, + }, } /// RawTransaction is the portion of a transaction that a client signs. @@ -448,6 +453,16 @@ fn gen_auth( sign_function(digest.as_ref()), ) }, + Auth::DomainAbstraction { function_info, account_identity, sign_function } => { + let digest = + HashValue::sha3_256_of(signing_message(user_signed_message)?.as_slice()).to_vec(); + AccountAuthenticator::domain_abstraction( + function_info.clone(), + digest.clone(), + sign_function(digest.as_ref()), + account_identity.clone(), + ) + }, }) } From a3ba5f1ae17878e69fc766e6a4a0d57a8d455d64 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 12 Feb 2025 14:40:15 -0800 Subject: [PATCH 4/8] Making the full flow work. simplifying the implementation of common_domain_aa_auths::authenticate_ed25519_hex, as we don't need public_key in both account_identity and authenticator --- .../doc/account_abstraction.md | 316 ++++++++++-------- .../aptos-framework/doc/auth_data.md | 19 +- .../doc/common_domain_aa_auths.md | 102 +----- .../doc/transaction_validation.md | 18 +- .../sources/account/account_abstraction.move | 100 +++--- .../sources/account/auth_data.move | 5 +- .../domain_aa/common_domain_aa_auths.move | 59 +--- .../sources/transaction_validation.move | 18 +- .../src/aptos_framework_sdk_builder.rs | 18 + .../smoke-test/src/account_abstraction.rs | 21 +- 10 files changed, 315 insertions(+), 361 deletions(-) diff --git a/aptos-move/framework/aptos-framework/doc/account_abstraction.md b/aptos-move/framework/aptos-framework/doc/account_abstraction.md index 14c3521847f09..11d2f196a3977 100644 --- a/aptos-move/framework/aptos-framework/doc/account_abstraction.md +++ b/aptos-move/framework/aptos-framework/doc/account_abstraction.md @@ -10,18 +10,19 @@ - [Enum Resource `DispatchableAuthenticator`](#0x1_account_abstraction_DispatchableAuthenticator) - [Enum Resource `DomainDispatchableAuthenticator`](#0x1_account_abstraction_DomainDispatchableAuthenticator) - [Constants](#@Constants_0) +- [Function `using_dispatchable_authenticator`](#0x1_account_abstraction_using_dispatchable_authenticator) +- [Function `dispatchable_authenticator`](#0x1_account_abstraction_dispatchable_authenticator) +- [Function `domain_aa_account_address_view`](#0x1_account_abstraction_domain_aa_account_address_view) +- [Function `domain_aa_account_address`](#0x1_account_abstraction_domain_aa_account_address) - [Function `add_authentication_function`](#0x1_account_abstraction_add_authentication_function) - [Function `remove_authentication_function`](#0x1_account_abstraction_remove_authentication_function) - [Function `remove_authenticator`](#0x1_account_abstraction_remove_authenticator) -- [Function `initialize`](#0x1_account_abstraction_initialize) - [Function `register_domain_with_authentication_function`](#0x1_account_abstraction_register_domain_with_authentication_function) - [Function `register_domain_with_authentication_function_test_network_only`](#0x1_account_abstraction_register_domain_with_authentication_function_test_network_only) +- [Function `initialize`](#0x1_account_abstraction_initialize) - [Function `resource_addr`](#0x1_account_abstraction_resource_addr) - [Function `update_dispatchable_authenticator_impl`](#0x1_account_abstraction_update_dispatchable_authenticator_impl) -- [Function `using_dispatchable_authenticator`](#0x1_account_abstraction_using_dispatchable_authenticator) -- [Function `dispatchable_authenticator`](#0x1_account_abstraction_dispatchable_authenticator) - [Function `dispatchable_authenticator_internal`](#0x1_account_abstraction_dispatchable_authenticator_internal) -- [Function `domain_aa_account_address`](#0x1_account_abstraction_domain_aa_account_address) - [Function `authenticate`](#0x1_account_abstraction_authenticate) - [Function `dispatchable_authenticate`](#0x1_account_abstraction_dispatchable_authenticate) - [Function `add_dispatchable_authentication_function`](#0x1_account_abstraction_add_dispatchable_authentication_function) @@ -166,6 +167,7 @@ enum DomainDispatchableAuthenticator has key @@ -287,6 +289,128 @@ enum + +## Function `using_dispatchable_authenticator` + +Return true if the account is an abstracted account that can be authenticated with dispatchable move authenticator. + + +
#[view]
+public fun using_dispatchable_authenticator(addr: address): bool
+
+ + + +
+Implementation + + +
public fun using_dispatchable_authenticator(addr: address): bool {
+    exists<DispatchableAuthenticator>(resource_addr(addr))
+}
+
+ + + +
+ + + +## Function `dispatchable_authenticator` + +Return the current dispatchable authenticator move function info. None means this authentication scheme is disabled. + + +
#[view]
+public fun dispatchable_authenticator(addr: address): option::Option<vector<function_info::FunctionInfo>>
+
+ + + +
+Implementation + + +
public fun dispatchable_authenticator(addr: address): Option<vector<FunctionInfo>> acquires DispatchableAuthenticator {
+    let resource_addr = resource_addr(addr);
+    if (exists<DispatchableAuthenticator>(resource_addr)) {
+        option::some(
+            ordered_map::keys(&borrow_global<DispatchableAuthenticator>(resource_addr).auth_functions)
+        )
+    } else { option::none() }
+}
+
+ + + +
+ + + +## Function `domain_aa_account_address_view` + + + +
#[view]
+public fun domain_aa_account_address_view(module_address: address, module_name: string::String, function_name: string::String, account_identity: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun domain_aa_account_address_view(
+    module_address: address,
+    module_name: String,
+    function_name: String,
+    account_identity: vector<u8>
+): address {
+    domain_aa_account_address(
+        function_info::new_function_info_from_address(module_address, module_name, function_name),
+        &account_identity,
+    )
+}
+
+ + + +
+ + + +## Function `domain_aa_account_address` + +TODO: probably worth creating some module with all these derived functions, +and do computation/caching in rust to avoid recomputation, as we do for objects. + + +
public fun domain_aa_account_address(domain_func_info: function_info::FunctionInfo, account_identity: &vector<u8>): address
+
+ + + +
+Implementation + + +
public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector<u8>): address {
+    // using bcs serialized structs here - this allows for no need for separators.
+    // Alternative would've been to create unique string, we would need to convert domain_func_info into string,
+    // then authentication_key to hex, and then we need separators as well - like ::
+    let bytes = bcs::to_bytes(&domain_func_info);
+    bytes.append(bcs::to_bytes(account_identity));
+    bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME);
+    from_bcs::to_address(hash::sha3_256(bytes))
+}
+
+ + + +
+ ## Function `add_authentication_function` @@ -328,6 +452,7 @@ Note: it is a private entry function that can only be called directly from trans ## Function `remove_authentication_function` Remove dispatchable authentication function that enables account abstraction via this function. +dispatchable function needs to verify that signing_data.authenticator() is a valid signature of signing_data.digest(). Note: it is a private entry function that can only be called directly from transaction. @@ -396,13 +521,21 @@ Note: it is a private entry function that can only be called directly from trans - + -## Function `initialize` +## Function `register_domain_with_authentication_function` +Add dispatchable domain-scoped authentication function, that enables account abstraction via this function. +This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA). +dispatchable function needs to verify two things: +- that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA) +- that signing_data.account_identity() is correct identity representing the authenticator +(missing this step would allow impersonation) +Note: it is a private entry function that can only be called directly from transaction. -
entry fun initialize(aptos_framework: &signer)
+
+
entry fun register_domain_with_authentication_function(aptos_framework: &signer, module_address: address, module_name: string::String, function_name: string::String)
 
@@ -411,11 +544,17 @@ Note: it is a private entry function that can only be called directly from trans Implementation -
entry fun initialize(aptos_framework: &signer) {
+
entry fun register_domain_with_authentication_function(
+    aptos_framework: &signer,
+    module_address: address,
+    module_name: String,
+    function_name: String,
+) acquires DomainDispatchableAuthenticator {
     system_addresses::assert_aptos_framework(aptos_framework);
-    move_to(
-        aptos_framework,
-        DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new_with_config(0, 0, false) }
+
+    borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
+        function_info::new_function_info_from_address(module_address, module_name, function_name),
+        true,
     );
 }
 
@@ -424,13 +563,13 @@ Note: it is a private entry function that can only be called directly from trans - + -## Function `register_domain_with_authentication_function` +## Function `register_domain_with_authentication_function_test_network_only` -
entry fun register_domain_with_authentication_function(aptos_framework: &signer, module_address: address, module_name: string::String, function_name: string::String)
+
entry fun register_domain_with_authentication_function_test_network_only(core_resources: &signer, module_address: address, module_name: string::String, function_name: string::String)
 
@@ -439,13 +578,13 @@ Note: it is a private entry function that can only be called directly from trans Implementation -
entry fun register_domain_with_authentication_function(
-    aptos_framework: &signer,
+
entry fun register_domain_with_authentication_function_test_network_only(
+    core_resources: &signer,
     module_address: address,
     module_name: String,
     function_name: String,
 ) acquires DomainDispatchableAuthenticator {
-    system_addresses::assert_aptos_framework(aptos_framework);
+    system_addresses::assert_core_resource(core_resources);
 
     borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
         function_info::new_function_info_from_address(module_address, module_name, function_name),
@@ -458,13 +597,13 @@ Note: it is a private entry function that can only be called directly from trans
 
 
 
-
+
 
-## Function `register_domain_with_authentication_function_test_network_only`
+## Function `initialize`
 
 
 
-
entry fun register_domain_with_authentication_function_test_network_only(core_resources: &signer, module_address: address, module_name: string::String, function_name: string::String)
+
entry fun initialize(aptos_framework: &signer)
 
@@ -473,17 +612,11 @@ Note: it is a private entry function that can only be called directly from trans Implementation -
entry fun register_domain_with_authentication_function_test_network_only(
-    core_resources: &signer,
-    module_address: address,
-    module_name: String,
-    function_name: String,
-) acquires DomainDispatchableAuthenticator {
-    system_addresses::assert_core_resource(core_resources);
-
-    borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
-        function_info::new_function_info_from_address(module_address, module_name, function_name),
-        true,
+
entry fun initialize(aptos_framework: &signer) {
+    system_addresses::assert_aptos_framework(aptos_framework);
+    move_to(
+        aptos_framework,
+        DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new_with_config(0, 0, false) }
     );
 }
 
@@ -583,63 +716,6 @@ Note: it is a private entry function that can only be called directly from trans - - - - -## Function `using_dispatchable_authenticator` - -Return true if the account is an abstracted account that can be authenticated with dispatchable move authenticator. - - -
#[view]
-public fun using_dispatchable_authenticator(addr: address): bool
-
- - - -
-Implementation - - -
public fun using_dispatchable_authenticator(addr: address): bool {
-    exists<DispatchableAuthenticator>(resource_addr(addr))
-}
-
- - - -
- - - -## Function `dispatchable_authenticator` - -Return the current dispatchable authenticator move function info. None means this authentication scheme is disabled. - - -
#[view]
-public fun dispatchable_authenticator(addr: address): option::Option<vector<function_info::FunctionInfo>>
-
- - - -
-Implementation - - -
public fun dispatchable_authenticator(addr: address): Option<vector<FunctionInfo>> acquires DispatchableAuthenticator {
-    let resource_addr = resource_addr(addr);
-    if (exists<DispatchableAuthenticator>(resource_addr)) {
-        option::some(
-            ordered_map::keys(&borrow_global<DispatchableAuthenticator>(resource_addr).auth_functions)
-        )
-    } else { option::none() }
-}
-
- - -
@@ -665,38 +741,6 @@ Return the current dispatchable authenticator move function info. None - - - -## Function `domain_aa_account_address` - -TODO: probably worth creating some module with all these derived functions, -and do computation/caching in rust to avoid recomputation, as we do for objects. - - -
public fun domain_aa_account_address(domain_func_info: function_info::FunctionInfo, account_identity: &vector<u8>): address
-
- - - -
-Implementation - - -
public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector<u8>): address {
-    /// using bcs serialized structs here - this allows for no need for separators.
-    /// If we want to have a strings from each, we would need to convert domain_func_info into string,
-    /// then authentication_key to hex, and then we need separators as well - like ::
-    let bytes = bcs::to_bytes(&domain_func_info);
-    bytes.append(bcs::to_bytes(account_identity));
-    bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME);
-    from_bcs::to_address(hash::sha3_256(bytes))
-}
-
- - -
@@ -718,33 +762,31 @@ and do computation/caching in rust to avoid recomputation, as we do for objects. account: signer, func_info: FunctionInfo, signing_data: AbstractionAuthData, -): signer acquires DispatchableAuthenticator { - assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); - +): signer acquires DispatchableAuthenticator, DomainDispatchableAuthenticator { let master_signer_addr = signer::address_of(&account); if (signing_data.is_domain()) { - // assert!(exists<DomainDispatchableAuthenticator>(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED)); - // let func_infos = &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions; - // assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + assert!(exists<DomainDispatchableAuthenticator>(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED)); + let func_infos = &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions; + assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); - // // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator() - // // so we need to have it separate, and require authenticate function to confirm it matches. - // assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); + // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator() + // so we need to have it separate, and require authenticate function to confirm it matches. + assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); } else { + assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); let func_infos = &borrow_global<DispatchableAuthenticator>(resource_addr(master_signer_addr)).auth_functions; assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); }; - // function_info::load_module_from_function(&func_info); - account - // let returned_signer = dispatchable_authenticate(account, signing_data, &func_info); + function_info::load_module_from_function(&func_info); + let returned_signer = dispatchable_authenticate(account, signing_data, &func_info); // Returned signer MUST represent the same account address. Otherwise, it may break the invariant of Aptos blockchain! - // assert!( - // master_signer_addr == signer::address_of(&returned_signer), - // error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS) - // ); - // returned_signer + assert!( + master_signer_addr == signer::address_of(&returned_signer), + error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS) + ); + returned_signer }
diff --git a/aptos-move/framework/aptos-framework/doc/auth_data.md b/aptos-move/framework/aptos-framework/doc/auth_data.md index 3ee7b5aaff8f5..b339adb876b0f 100644 --- a/aptos-move/framework/aptos-framework/doc/auth_data.md +++ b/aptos-move/framework/aptos-framework/doc/auth_data.md @@ -7,13 +7,15 @@ - [Enum `DomainAccount`](#0x1_auth_data_DomainAccount) - [Enum `AbstractionAuthData`](#0x1_auth_data_AbstractionAuthData) +- [Constants](#@Constants_0) - [Function `digest`](#0x1_auth_data_digest) - [Function `authenticator`](#0x1_auth_data_authenticator) - [Function `is_domain`](#0x1_auth_data_is_domain) - [Function `account_identity`](#0x1_auth_data_account_identity) -
+
use 0x1::error;
+
@@ -135,6 +137,20 @@ + + +## Constants + + + + + + +
const ENOT_DOMAIN_AUTH_DATA: u64 = 1;
+
+ + + ## Function `digest` @@ -223,6 +239,7 @@
public fun account_identity(self: &AbstractionAuthData): &vector<u8> {
+    assert!(self is DomainV1, error::invalid_argument(ENOT_DOMAIN_AUTH_DATA));
     &self.account.account_identity
 }
 
diff --git a/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md b/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md index 75392d69b7192..456559910b95d 100644 --- a/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md +++ b/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md @@ -7,15 +7,13 @@ - [Constants](#@Constants_0) - [Function `authenticate_ed25519_hex`](#0x1_common_domain_aa_auths_authenticate_ed25519_hex) -- [Function `nibble_to_char`](#0x1_common_domain_aa_auths_nibble_to_char) -- [Function `bytes_to_hex`](#0x1_common_domain_aa_auths_bytes_to_hex)
use 0x1::auth_data;
-use 0x1::bcs_stream;
 use 0x1::ed25519;
 use 0x1::error;
-use 0x1::signer;
+use 0x1::string;
+use 0x1::string_utils;
 
@@ -25,20 +23,11 @@ ## Constants - - - - -
const EINVALID_ACCOUNT_IDENTITY: u64 = 1;
-
- - - -
const EINVALID_SIGNATURE: u64 = 2;
+
const EINVALID_SIGNATURE: u64 = 1;
 
@@ -59,26 +48,15 @@
public fun authenticate_ed25519_hex(account: signer, aa_auth_data: AbstractionAuthData): signer {
-    let addr = signer::address_of(&account);
-
-    let hex_digest = bytes_to_hex(aa_auth_data.digest());
-    let stream = bcs_stream::new(*aa_auth_data.authenticator());
-    let public_key_bytes = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
+    let hex_digest = string_utils::to_string(aa_auth_data.digest());
 
-    assert!(
-        aa_auth_data.account_identity() == &public_key_bytes,
-        error::permission_denied(EINVALID_ACCOUNT_IDENTITY)
-    );
-
-    let public_key = new_unvalidated_public_key_from_bytes(public_key_bytes);
-    let signature = new_signature_from_bytes(
-        bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x))
-    );
+    let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.account_identity());
+    let signature = new_signature_from_bytes(*aa_auth_data.authenticator());
     assert!(
         ed25519::signature_verify_strict(
             &signature,
             &public_key,
-            hex_digest,
+            *hex_digest.bytes(),
         ),
         error::permission_denied(EINVALID_SIGNATURE)
     );
@@ -89,72 +67,6 @@
 
 
 
-
-
-
-
-## Function `nibble_to_char`
-
-
-
-
fun nibble_to_char(nibble: u8): u8
-
- - - -
-Implementation - - -
fun nibble_to_char(nibble: u8): u8 {
-    if (nibble < 10) {
-        48 + nibble  // '0' to '9'
-    } else {
-        87 + nibble  // 'a' to 'f' (87 = 'a' - 10)
-    }
-}
-
- - - -
- - - -## Function `bytes_to_hex` - - - -
fun bytes_to_hex(data: &vector<u8>): vector<u8>
-
- - - -
-Implementation - - -
fun bytes_to_hex(data: &vector<u8>): vector<u8> {
-    let hex_chars = vector::empty();
-
-    let i = 0;
-    while (i < data.length()) {
-        let cur = *data.borrow(i);
-        let high_nibble = cur / 16;
-        let low_nibble = cur % 16;
-
-        hex_chars.push_back(nibble_to_char(high_nibble));
-        hex_chars.push_back(nibble_to_char(low_nibble));
-
-        i = i + 1;
-    };
-
-    hex_chars
-}
-
- - -
diff --git a/aptos-move/framework/aptos-framework/doc/transaction_validation.md b/aptos-move/framework/aptos-framework/doc/transaction_validation.md index 44c88dd519cee..7d9e1049a67bb 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_validation.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_validation.md @@ -52,7 +52,6 @@
use 0x1::account;
-use 0x1::account_abstraction;
 use 0x1::aptos_account;
 use 0x1::aptos_coin;
 use 0x1::bcs;
@@ -439,10 +438,7 @@ Only called during genesis to initialize system resources for this module.
                 );
             } else {
                 assert!(
-                    features::is_account_abstraction_enabled(
-                    ) && account_abstraction::using_dispatchable_authenticator(
-                        transaction_sender
-                    ),
+                    features::is_account_abstraction_enabled(),
                     error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
                 )
             };
@@ -767,10 +763,7 @@ Only called during genesis to initialize system resources for this module.
                 );
             } else {
                 assert!(
-                    features::is_account_abstraction_enabled(
-                    ) && account_abstraction::using_dispatchable_authenticator(
-                        secondary_address
-                    ),
+                    features::is_account_abstraction_enabled(),
                     error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
                 )
             };
@@ -1159,6 +1152,7 @@ new set of functions
 
 
fun unified_prologue(
     sender: signer,
+    // None means no need to check, i.e. either AA (where it is already checked) or simulation
     txn_sender_public_key: Option<vector<u8>>,
     txn_sequence_number: u64,
     secondary_signer_addresses: vector<address>,
@@ -1207,7 +1201,9 @@ If there is no fee_payer, fee_payer = sender
 
fun unified_prologue_fee_payer(
     sender: signer,
     fee_payer: signer,
+    // None means no need to check, i.e. either AA (where it is already checked) or simulation
     txn_sender_public_key: Option<vector<u8>>,
+    // None means no need to check, i.e. either AA (where it is already checked) or simulation
     fee_payer_public_key_hash: Option<vector<u8>>,
     txn_sequence_number: u64,
     secondary_signer_addresses: vector<address>,
@@ -1240,9 +1236,7 @@ If there is no fee_payer, fee_payer = sender
             );
         } else {
             assert!(
-                features::is_account_abstraction_enabled() && account_abstraction::using_dispatchable_authenticator(
-                    fee_payer_address
-                ),
+                features::is_account_abstraction_enabled(),
                 error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
             )
         };
diff --git a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move
index 0842c9c238ab4..a8528bffb4b29 100644
--- a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move
+++ b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move
@@ -54,10 +54,53 @@ module aptos_framework::account_abstraction {
         V1 { auth_functions: OrderedMap }
     }
 
+    /// The dispatchable domain-scoped authenticator, that defines how to authenticate
     enum DomainDispatchableAuthenticator has key {
         V1 { auth_functions: BigOrderedMap }
     }
 
+    #[view]
+    /// Return `true` if the account is an abstracted account that can be authenticated with dispatchable move authenticator.
+    public fun using_dispatchable_authenticator(addr: address): bool {
+        exists(resource_addr(addr))
+    }
+
+    #[view]
+    /// Return the current dispatchable authenticator move function info. `None` means this authentication scheme is disabled.
+    public fun dispatchable_authenticator(addr: address): Option> acquires DispatchableAuthenticator {
+        let resource_addr = resource_addr(addr);
+        if (exists(resource_addr)) {
+            option::some(
+                ordered_map::keys(&borrow_global(resource_addr).auth_functions)
+            )
+        } else { option::none() }
+    }
+
+    #[view]
+    public fun domain_aa_account_address_view(
+        module_address: address,
+        module_name: String,
+        function_name: String,
+        account_identity: vector
+    ): address {
+        domain_aa_account_address(
+            function_info::new_function_info_from_address(module_address, module_name, function_name),
+            &account_identity,
+        )
+    }
+
+    /// TODO: probably worth creating some module with all these derived functions,
+    /// and do computation/caching in rust to avoid recomputation, as we do for objects.
+    public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector): address {
+        // using bcs serialized structs here - this allows for no need for separators.
+        // Alternative would've been to create unique string, we would need to convert domain_func_info into string,
+        // then authentication_key to hex, and then we need separators as well - like ::
+        let bytes = bcs::to_bytes(&domain_func_info);
+        bytes.append(bcs::to_bytes(account_identity));
+        bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME);
+        from_bcs::to_address(hash::sha3_256(bytes))
+    }
+
     /// Add dispatchable authentication function that enables account abstraction via this function.
     /// Note: it is a private entry function that can only be called directly from transaction.
     entry fun add_authentication_function(
@@ -75,6 +118,7 @@ module aptos_framework::account_abstraction {
     }
 
     /// Remove dispatchable authentication function that enables account abstraction via this function.
+    /// dispatchable function needs to verify that signing_data.authenticator() is a valid signature of signing_data.digest().
     /// Note: it is a private entry function that can only be called directly from transaction.
     entry fun remove_authentication_function(
         account: &signer,
@@ -107,14 +151,14 @@ module aptos_framework::account_abstraction {
         };
     }
 
-    entry fun initialize(aptos_framework: &signer) {
-        system_addresses::assert_aptos_framework(aptos_framework);
-        move_to(
-            aptos_framework,
-            DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new_with_config(0, 0, false) }
-        );
-    }
-
+    /// Add dispatchable domain-scoped authentication function, that enables account abstraction via this function.
+    /// This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA).
+    /// dispatchable function needs to verify two things:
+    /// - that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA)
+    /// - that signing_data.account_identity() is correct identity representing the authenticator
+    ///   (missing this step would allow impersonation)
+    ///
+    /// Note: it is a private entry function that can only be called directly from transaction.
     entry fun register_domain_with_authentication_function(
         aptos_framework: &signer,
         module_address: address,
@@ -143,6 +187,14 @@ module aptos_framework::account_abstraction {
         );
     }
 
+    entry fun initialize(aptos_framework: &signer) {
+        system_addresses::assert_aptos_framework(aptos_framework);
+        move_to(
+            aptos_framework,
+            DomainDispatchableAuthenticator::V1 { auth_functions: big_ordered_map::new_with_config(0, 0, false) }
+        );
+    }
+
     inline fun resource_addr(source: address): address {
         object::create_user_derived_object_address(source, @aptos_fungible_asset)
     }
@@ -196,47 +248,16 @@ module aptos_framework::account_abstraction {
         }
     }
 
-    #[view]
-    /// Return `true` if the account is an abstracted account that can be authenticated with dispatchable move authenticator.
-    public fun using_dispatchable_authenticator(addr: address): bool {
-        exists(resource_addr(addr))
-    }
-
-    #[view]
-    /// Return the current dispatchable authenticator move function info. `None` means this authentication scheme is disabled.
-    public fun dispatchable_authenticator(addr: address): Option> acquires DispatchableAuthenticator {
-        let resource_addr = resource_addr(addr);
-        if (exists(resource_addr)) {
-            option::some(
-                ordered_map::keys(&borrow_global(resource_addr).auth_functions)
-            )
-        } else { option::none() }
-    }
-
     inline fun dispatchable_authenticator_internal(addr: address): &OrderedMap {
         assert!(using_dispatchable_authenticator(addr), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED));
         &borrow_global(resource_addr(addr)).auth_functions
     }
 
-    /// TODO: probably worth creating some module with all these derived functions,
-    /// and do computation/caching in rust to avoid recomputation, as we do for objects.
-    public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector): address {
-        /// using bcs serialized structs here - this allows for no need for separators.
-        /// If we want to have a strings from each, we would need to convert domain_func_info into string,
-        /// then authentication_key to hex, and then we need separators as well - like ::
-        let bytes = bcs::to_bytes(&domain_func_info);
-        bytes.append(bcs::to_bytes(account_identity));
-        bytes.push_back(DOMAIN_ABSTRACTION_DERIVED_SCHEME);
-        from_bcs::to_address(hash::sha3_256(bytes))
-    }
-
     fun authenticate(
         account: signer,
         func_info: FunctionInfo,
         signing_data: AbstractionAuthData,
     ): signer acquires DispatchableAuthenticator, DomainDispatchableAuthenticator {
-        assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED));
-
         let master_signer_addr = signer::address_of(&account);
 
         if (signing_data.is_domain()) {
@@ -248,6 +269,7 @@ module aptos_framework::account_abstraction {
             // so we need to have it separate, and require authenticate function to confirm it matches.
             assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS));
         } else {
+            assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED));
             let func_infos = &borrow_global(resource_addr(master_signer_addr)).auth_functions;
             assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE));
         };
diff --git a/aptos-move/framework/aptos-framework/sources/account/auth_data.move b/aptos-move/framework/aptos-framework/sources/account/auth_data.move
index b126a70db2d9c..abaf4da38801b 100644
--- a/aptos-move/framework/aptos-framework/sources/account/auth_data.move
+++ b/aptos-move/framework/aptos-framework/sources/account/auth_data.move
@@ -1,5 +1,7 @@
 module aptos_framework::auth_data {
-    use std::string::String;
+    use std::error;
+
+    const ENOT_DOMAIN_AUTH_DATA: u64 = 1;
 
     enum DomainAccount has copy, drop {
         V1 {
@@ -30,6 +32,7 @@ module aptos_framework::auth_data {
     }
 
     public fun account_identity(self: &AbstractionAuthData): &vector {
+        assert!(self is DomainV1, error::invalid_argument(ENOT_DOMAIN_AUTH_DATA));
         &self.account.account_identity
     }
 }
diff --git a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move
index 43d584c67e512..299342ebe15d8 100644
--- a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move
+++ b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move
@@ -3,73 +3,34 @@ module aptos_framework::common_domain_aa_auths {
     use std::option::Option;
     use std::signer;
     use std::vector;
+    use aptos_std::string_utils;
     use aptos_std::ed25519::{
         Self,
         new_signature_from_bytes,
         new_unvalidated_public_key_from_bytes,
     };
-    use aptos_framework::auth_data::{Self, AbstractionAuthData};
-    use aptos_framework::bcs_stream::{Self, deserialize_u8};
+    use aptos_framework::auth_data::AbstractionAuthData;
 
-    const EINVALID_ACCOUNT_IDENTITY: u64 = 1;
-    const EINVALID_SIGNATURE: u64 = 2;
+    const EINVALID_SIGNATURE: u64 = 1;
 
-    // takes digest, converts to hex, and then expects that to be signed.
-    // authenticator is expected to be struct { public_key: vector, signature: vector }
-    // account_identity is raw public_key.
 
+    // takes digest, converts to hex (prefixed with 0x, with lowercase letters), and then expects that to be signed.
+    // authenticator is expected to be signature: vector
+    // account_identity is raw public_key.
     public fun authenticate_ed25519_hex(account: signer, aa_auth_data: AbstractionAuthData): signer {
-        let addr = signer::address_of(&account);
-
-        let hex_digest = bytes_to_hex(aa_auth_data.digest());
-        let stream = bcs_stream::new(*aa_auth_data.authenticator());
-        let public_key_bytes = bcs_stream::deserialize_vector(&mut stream, |x| deserialize_u8(x));
-
-        assert!(
-            aa_auth_data.account_identity() == &public_key_bytes,
-            error::permission_denied(EINVALID_ACCOUNT_IDENTITY)
-        );
+        let hex_digest = string_utils::to_string(aa_auth_data.digest());
 
-        let public_key = new_unvalidated_public_key_from_bytes(public_key_bytes);
-        let signature = new_signature_from_bytes(
-            bcs_stream::deserialize_vector(&mut stream, |x| deserialize_u8(x))
-        );
+        let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.account_identity());
+        let signature = new_signature_from_bytes(*aa_auth_data.authenticator());
         assert!(
             ed25519::signature_verify_strict(
                 &signature,
                 &public_key,
-                hex_digest,
+                *hex_digest.bytes(),
             ),
             error::permission_denied(EINVALID_SIGNATURE)
         );
 
         account
     }
-
-    // Utility function to convert a nibble (0-15) to its corresponding hex character
-    fun nibble_to_char(nibble: u8): u8 {
-        if (nibble < 10) {
-            48 + nibble  // '0' to '9'
-        } else {
-            87 + nibble  // 'a' to 'f' (87 = 'a' - 10)
-        }
-    }
-
-    fun bytes_to_hex(data: &vector): vector {
-        let hex_chars = vector::empty();
-
-        let i = 0;
-        while (i < data.length()) {
-            let cur = *data.borrow(i);
-            let high_nibble = cur / 16;
-            let low_nibble = cur % 16;
-
-            hex_chars.push_back(nibble_to_char(high_nibble));
-            hex_chars.push_back(nibble_to_char(low_nibble));
-
-            i = i + 1;
-        };
-
-        hex_chars
-    }
 }
diff --git a/aptos-move/framework/aptos-framework/sources/transaction_validation.move b/aptos-move/framework/aptos-framework/sources/transaction_validation.move
index 4202a488b88f3..600d512aebde3 100644
--- a/aptos-move/framework/aptos-framework/sources/transaction_validation.move
+++ b/aptos-move/framework/aptos-framework/sources/transaction_validation.move
@@ -9,7 +9,6 @@ module aptos_framework::transaction_validation {
 
     use aptos_framework::account;
     use aptos_framework::aptos_account;
-    use aptos_framework::account_abstraction;
     use aptos_framework::aptos_coin::AptosCoin;
     use aptos_framework::chain_id;
     use aptos_framework::coin;
@@ -135,10 +134,7 @@ module aptos_framework::transaction_validation {
                     );
                 } else {
                     assert!(
-                        features::is_account_abstraction_enabled(
-                        ) && account_abstraction::using_dispatchable_authenticator(
-                            transaction_sender
-                        ),
+                        features::is_account_abstraction_enabled(),
                         error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
                     )
                 };
@@ -369,10 +365,7 @@ module aptos_framework::transaction_validation {
                     );
                 } else {
                     assert!(
-                        features::is_account_abstraction_enabled(
-                        ) && account_abstraction::using_dispatchable_authenticator(
-                            secondary_address
-                        ),
+                        features::is_account_abstraction_enabled(),
                         error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY)
                     )
                 };
@@ -593,6 +586,7 @@ module aptos_framework::transaction_validation {
 
     fun unified_prologue(
         sender: signer,
+        // None means no need to check, i.e. either AA (where it is already checked) or simulation
         txn_sender_public_key: Option>,
         txn_sequence_number: u64,
         secondary_signer_addresses: vector
, @@ -621,7 +615,9 @@ module aptos_framework::transaction_validation { fun unified_prologue_fee_payer( sender: signer, fee_payer: signer, + // None means no need to check, i.e. either AA (where it is already checked) or simulation txn_sender_public_key: Option>, + // None means no need to check, i.e. either AA (where it is already checked) or simulation fee_payer_public_key_hash: Option>, txn_sequence_number: u64, secondary_signer_addresses: vector
, @@ -654,9 +650,7 @@ module aptos_framework::transaction_validation { ); } else { assert!( - features::is_account_abstraction_enabled() && account_abstraction::using_dispatchable_authenticator( - fee_payer_address - ), + features::is_account_abstraction_enabled(), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ) }; diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs index e73f756ea6fe3..d4f8920a80c1b 100644 --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs @@ -181,6 +181,14 @@ pub enum EntryFunctionCall { AccountAbstractionInitialize {}, + /// Add dispatchable domain-scoped authentication function, that enables account abstraction via this function. + /// This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA). + /// dispatchable function needs to verify two things: + /// - that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA) + /// - that signing_data.account_identity() is correct identity representing the authenticator + /// (missing this step would allow impersonation) + /// + /// Note: it is a private entry function that can only be called directly from transaction. AccountAbstractionRegisterDomainWithAuthenticationFunction { module_address: AccountAddress, module_name: Vec, @@ -194,6 +202,7 @@ pub enum EntryFunctionCall { }, /// Remove dispatchable authentication function that enables account abstraction via this function. + /// dispatchable function needs to verify that signing_data.authenticator() is a valid signature of signing_data.digest(). /// Note: it is a private entry function that can only be called directly from transaction. AccountAbstractionRemoveAuthenticationFunction { module_address: AccountAddress, @@ -2279,6 +2288,14 @@ pub fn account_abstraction_initialize() -> TransactionPayload { )) } +/// Add dispatchable domain-scoped authentication function, that enables account abstraction via this function. +/// This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA). +/// dispatchable function needs to verify two things: +/// - that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA) +/// - that signing_data.account_identity() is correct identity representing the authenticator +/// (missing this step would allow impersonation) +/// +/// Note: it is a private entry function that can only be called directly from transaction. pub fn account_abstraction_register_domain_with_authentication_function( module_address: AccountAddress, module_name: Vec, @@ -2326,6 +2343,7 @@ pub fn account_abstraction_register_domain_with_authentication_function_test_net } /// Remove dispatchable authentication function that enables account abstraction via this function. +/// dispatchable function needs to verify that signing_data.authenticator() is a valid signature of signing_data.digest(). /// Note: it is a private entry function that can only be called directly from transaction. pub fn account_abstraction_remove_authentication_function( module_address: AccountAddress, diff --git a/testsuite/smoke-test/src/account_abstraction.rs b/testsuite/smoke-test/src/account_abstraction.rs index 51ad0ad2749be..911126972ddd1 100644 --- a/testsuite/smoke-test/src/account_abstraction.rs +++ b/testsuite/smoke-test/src/account_abstraction.rs @@ -49,20 +49,11 @@ async fn test_domain_aa() { function_info, account_key.public_key().to_bytes().to_vec(), Arc::new(move |x: &[u8]| { - let x_hex = hex::encode(x).into_bytes(); - - let mut authenticator = vec![]; - authenticator.extend(bcs::to_bytes(&account_key.public_key().to_bytes().to_vec()).unwrap()); - authenticator.extend( - bcs::to_bytes( - &account_key.private_key() - .sign_arbitrary_message(&x_hex) - .to_bytes() - .to_vec(), - ) - .unwrap(), - ); - authenticator + let x_hex = format!("0x{}", hex::encode(x)).into_bytes(); + account_key.private_key() + .sign_arbitrary_message(&x_hex) + .to_bytes() + .to_vec() }), 0, ); @@ -71,7 +62,7 @@ async fn test_domain_aa() { info.transaction_factory() .payload(aptos_stdlib::aptos_account_transfer( account.address(), - 1000000, + 10000000000, )), ); info.client().submit_and_wait(&create_txn).await.unwrap(); From bf3bdc57833bfdf84bc4619c9b494d04aa679206 Mon Sep 17 00:00:00 2001 From: Igor Date: Thu, 13 Feb 2025 00:29:34 -0800 Subject: [PATCH 5/8] fix test and allowing fee payer to create account for domain aa --- .../doc/transaction_validation.md | 16 ++++++++++++---- .../sources/transaction_validation.move | 16 ++++++++++++---- testsuite/smoke-test/src/account_abstraction.rs | 15 ++------------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/aptos-move/framework/aptos-framework/doc/transaction_validation.md b/aptos-move/framework/aptos-framework/doc/transaction_validation.md index 7d9e1049a67bb..81b890fb9ad3f 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_validation.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_validation.md @@ -468,10 +468,18 @@ Only called during genesis to initialize system resources for this module. if (!features::transaction_simulation_enhancement_enabled() || !skip_auth_key_check(is_simulation, &txn_authentication_key)) { - assert!( - txn_authentication_key == option::some(bcs::to_bytes(&transaction_sender)), - error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), - ); + if (option::is_some(&txn_authentication_key)) { + assert!( + txn_authentication_key == option::some(bcs::to_bytes(&transaction_sender)), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), + ); + } else { + // aa verifies authentication itself + assert!( + features::is_account_abstraction_enabled(), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) + ); + } } }; diff --git a/aptos-move/framework/aptos-framework/sources/transaction_validation.move b/aptos-move/framework/aptos-framework/sources/transaction_validation.move index 600d512aebde3..08c457e68221f 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_validation.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_validation.move @@ -164,10 +164,18 @@ module aptos_framework::transaction_validation { if (!features::transaction_simulation_enhancement_enabled() || !skip_auth_key_check(is_simulation, &txn_authentication_key)) { - assert!( - txn_authentication_key == option::some(bcs::to_bytes(&transaction_sender)), - error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), - ); + if (option::is_some(&txn_authentication_key)) { + assert!( + txn_authentication_key == option::some(bcs::to_bytes(&transaction_sender)), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), + ); + } else { + // aa verifies authentication itself + assert!( + features::is_account_abstraction_enabled(), + error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) + ); + } } }; diff --git a/testsuite/smoke-test/src/account_abstraction.rs b/testsuite/smoke-test/src/account_abstraction.rs index 911126972ddd1..ee24382ab3c0d 100644 --- a/testsuite/smoke-test/src/account_abstraction.rs +++ b/testsuite/smoke-test/src/account_abstraction.rs @@ -58,25 +58,14 @@ async fn test_domain_aa() { 0, ); - let create_txn = info.root_account().sign_with_transaction_builder( - info.transaction_factory() - .payload(aptos_stdlib::aptos_account_transfer( - account.address(), - 10000000000, - )), - ); - info.client().submit_and_wait(&create_txn).await.unwrap(); - - println!("Trying domain AA with {:?}", account); - // test some transaction let create_txn = account.sign_aa_transaction_with_transaction_builder( vec![], - None, + Some(&info.root_account()), info.transaction_factory() .payload(aptos_stdlib::aptos_account_create_account( AccountAddress::random(), )), ); - info.client().submit_and_wait(&create_txn).await.unwrap(); + info.client().submit_and_wait(&create_txn).await.expect(&format!("aa: {:?}", create_txn)); } From 45c89a472c5f6280021ee68face9b0e7b8b806cc Mon Sep 17 00:00:00 2001 From: Igor Date: Thu, 13 Feb 2025 14:58:20 -0800 Subject: [PATCH 6/8] lints --- .../doc/account_abstraction.md | 2 +- .../aptos-framework/doc/auth_data.md | 48 ++++++++++++++++--- .../doc/common_domain_aa_auths.md | 4 +- .../sources/account/account_abstraction.move | 2 +- .../sources/account/auth_data.move | 14 +++++- .../domain_aa/common_domain_aa_auths.move | 8 +--- sdk/src/types.rs | 16 ++++--- .../smoke-test/src/account_abstraction.rs | 46 ++++++++++-------- testsuite/smoke-test/src/lib.rs | 4 +- types/src/transaction/authenticator.rs | 10 ++-- types/src/transaction/mod.rs | 6 ++- 11 files changed, 106 insertions(+), 54 deletions(-) diff --git a/aptos-move/framework/aptos-framework/doc/account_abstraction.md b/aptos-move/framework/aptos-framework/doc/account_abstraction.md index 11d2f196a3977..7ba45b128fb9c 100644 --- a/aptos-move/framework/aptos-framework/doc/account_abstraction.md +++ b/aptos-move/framework/aptos-framework/doc/account_abstraction.md @@ -772,7 +772,7 @@ Note: it is a private entry function that can only be called directly from trans // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator() // so we need to have it separate, and require authenticate function to confirm it matches. - assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); + assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.domain_account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); } else { assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); let func_infos = &borrow_global<DispatchableAuthenticator>(resource_addr(master_signer_addr)).auth_functions; diff --git a/aptos-move/framework/aptos-framework/doc/auth_data.md b/aptos-move/framework/aptos-framework/doc/auth_data.md index b339adb876b0f..13e8bc23d776d 100644 --- a/aptos-move/framework/aptos-framework/doc/auth_data.md +++ b/aptos-move/framework/aptos-framework/doc/auth_data.md @@ -11,7 +11,8 @@ - [Function `digest`](#0x1_auth_data_digest) - [Function `authenticator`](#0x1_auth_data_authenticator) - [Function `is_domain`](#0x1_auth_data_is_domain) -- [Function `account_identity`](#0x1_auth_data_account_identity) +- [Function `domain_authenticator`](#0x1_auth_data_domain_authenticator) +- [Function `domain_account_identity`](#0x1_auth_data_domain_account_identity)
use 0x1::error;
@@ -146,7 +147,16 @@
 
 
 
-
const ENOT_DOMAIN_AUTH_DATA: u64 = 1;
+
const ENOT_DOMAIN_AUTH_DATA: u64 = 2;
+
+ + + + + + + +
const ENOT_REGULAR_AUTH_DATA: u64 = 1;
 
@@ -191,6 +201,7 @@
public fun authenticator(self: &AbstractionAuthData): &vector<u8> {
+    assert!(self is V1, error::invalid_argument(ENOT_REGULAR_AUTH_DATA));
     &self.authenticator
 }
 
@@ -223,13 +234,38 @@ - + + +## Function `domain_authenticator` + + + +
public fun domain_authenticator(self: &auth_data::AbstractionAuthData): &vector<u8>
+
+ + + +
+Implementation + + +
public fun domain_authenticator(self: &AbstractionAuthData): &vector<u8> {
+    assert!(self is DomainV1, error::invalid_argument(ENOT_REGULAR_AUTH_DATA));
+    &self.authenticator
+}
+
+ + + +
+ + -## Function `account_identity` +## Function `domain_account_identity` -
public fun account_identity(self: &auth_data::AbstractionAuthData): &vector<u8>
+
public fun domain_account_identity(self: &auth_data::AbstractionAuthData): &vector<u8>
 
@@ -238,7 +274,7 @@ Implementation -
public fun account_identity(self: &AbstractionAuthData): &vector<u8> {
+
public fun domain_account_identity(self: &AbstractionAuthData): &vector<u8> {
     assert!(self is DomainV1, error::invalid_argument(ENOT_DOMAIN_AUTH_DATA));
     &self.account.account_identity
 }
diff --git a/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md b/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md
index 456559910b95d..93b9d688b0284 100644
--- a/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md
+++ b/aptos-move/framework/aptos-framework/doc/common_domain_aa_auths.md
@@ -50,8 +50,8 @@
 
public fun authenticate_ed25519_hex(account: signer, aa_auth_data: AbstractionAuthData): signer {
     let hex_digest = string_utils::to_string(aa_auth_data.digest());
 
-    let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.account_identity());
-    let signature = new_signature_from_bytes(*aa_auth_data.authenticator());
+    let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.domain_account_identity());
+    let signature = new_signature_from_bytes(*aa_auth_data.domain_authenticator());
     assert!(
         ed25519::signature_verify_strict(
             &signature,
diff --git a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move
index a8528bffb4b29..a041f9899548c 100644
--- a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move
+++ b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move
@@ -267,7 +267,7 @@ module aptos_framework::account_abstraction {
 
             // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator()
             // so we need to have it separate, and require authenticate function to confirm it matches.
-            assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS));
+            assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.domain_account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS));
         } else {
             assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED));
             let func_infos = &borrow_global(resource_addr(master_signer_addr)).auth_functions;
diff --git a/aptos-move/framework/aptos-framework/sources/account/auth_data.move b/aptos-move/framework/aptos-framework/sources/account/auth_data.move
index abaf4da38801b..03ff3255e5bf7 100644
--- a/aptos-move/framework/aptos-framework/sources/account/auth_data.move
+++ b/aptos-move/framework/aptos-framework/sources/account/auth_data.move
@@ -1,7 +1,8 @@
 module aptos_framework::auth_data {
     use std::error;
 
-    const ENOT_DOMAIN_AUTH_DATA: u64 = 1;
+    const ENOT_REGULAR_AUTH_DATA: u64 = 1;
+    const ENOT_DOMAIN_AUTH_DATA: u64 = 2;
 
     enum DomainAccount has copy, drop {
         V1 {
@@ -23,7 +24,11 @@ module aptos_framework::auth_data {
         &self.digest
     }
 
+    // separate authenticator and domain_authenticator - to not allow accidental mixing
+    // in user authentication code
+
     public fun authenticator(self: &AbstractionAuthData): &vector {
+        assert!(self is V1, error::invalid_argument(ENOT_REGULAR_AUTH_DATA));
         &self.authenticator
     }
 
@@ -31,7 +36,12 @@ module aptos_framework::auth_data {
         self is DomainV1
     }
 
-    public fun account_identity(self: &AbstractionAuthData): &vector {
+    public fun domain_authenticator(self: &AbstractionAuthData): &vector {
+        assert!(self is DomainV1, error::invalid_argument(ENOT_REGULAR_AUTH_DATA));
+        &self.authenticator
+    }
+
+    public fun domain_account_identity(self: &AbstractionAuthData): &vector {
         assert!(self is DomainV1, error::invalid_argument(ENOT_DOMAIN_AUTH_DATA));
         &self.account.account_identity
     }
diff --git a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move
index 299342ebe15d8..f1b843b42b846 100644
--- a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move
+++ b/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move
@@ -1,8 +1,5 @@
 module aptos_framework::common_domain_aa_auths {
     use std::error;
-    use std::option::Option;
-    use std::signer;
-    use std::vector;
     use aptos_std::string_utils;
     use aptos_std::ed25519::{
         Self,
@@ -13,15 +10,14 @@ module aptos_framework::common_domain_aa_auths {
 
     const EINVALID_SIGNATURE: u64 = 1;
 
-
     // takes digest, converts to hex (prefixed with 0x, with lowercase letters), and then expects that to be signed.
     // authenticator is expected to be signature: vector
     // account_identity is raw public_key.
     public fun authenticate_ed25519_hex(account: signer, aa_auth_data: AbstractionAuthData): signer {
         let hex_digest = string_utils::to_string(aa_auth_data.digest());
 
-        let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.account_identity());
-        let signature = new_signature_from_bytes(*aa_auth_data.authenticator());
+        let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.domain_account_identity());
+        let signature = new_signature_from_bytes(*aa_auth_data.domain_authenticator());
         assert!(
             ed25519::signature_verify_strict(
                 &signature,
diff --git a/sdk/src/types.rs b/sdk/src/types.rs
index 37d56dd79b437..2e738f268fc6a 100644
--- a/sdk/src/types.rs
+++ b/sdk/src/types.rs
@@ -205,7 +205,11 @@ impl LocalAccount {
         sequence_number: u64,
     ) -> Self {
         Self {
-            address: AuthenticationKey::domain_abstraction_address(bcs::to_bytes(&function_info).unwrap(), &account_identity).account_address(),
+            address: AuthenticationKey::domain_abstraction_address(
+                bcs::to_bytes(&function_info).unwrap(),
+                &account_identity,
+            )
+            .account_address(),
             auth: LocalAccountAuthenticator::DomainAbstraction(DomainAbstractedAccount {
                 function_info,
                 account_identity,
@@ -488,12 +492,10 @@ impl LocalAccount {
             LocalAccountAuthenticator::Abstraction(aa) => {
                 Auth::Abstraction(aa.function_info.clone(), aa.sign_func.clone())
             },
-            LocalAccountAuthenticator::DomainAbstraction(aa) => {
-                Auth::DomainAbstraction {
-                    function_info: aa.function_info.clone(),
-                    account_identity: aa.account_identity.clone(),
-                    sign_function: aa.sign_func.clone(),
-                }
+            LocalAccountAuthenticator::DomainAbstraction(aa) => Auth::DomainAbstraction {
+                function_info: aa.function_info.clone(),
+                account_identity: aa.account_identity.clone(),
+                sign_function: aa.sign_func.clone(),
             },
         }
     }
diff --git a/testsuite/smoke-test/src/account_abstraction.rs b/testsuite/smoke-test/src/account_abstraction.rs
index ee24382ab3c0d..7c759715c63eb 100644
--- a/testsuite/smoke-test/src/account_abstraction.rs
+++ b/testsuite/smoke-test/src/account_abstraction.rs
@@ -7,33 +7,33 @@ use aptos_crypto::SigningKey;
 use aptos_forge::Swarm;
 use aptos_sdk::types::{AccountKey, LocalAccount};
 use aptos_types::{function_info::FunctionInfo, transaction::EntryFunction};
-use move_core_types::{account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId};
-use std::sync::Arc;
+use move_core_types::{
+    account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId,
+};
 use rand::thread_rng;
+use std::sync::Arc;
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
 async fn test_domain_aa() {
-    let swarm = SwarmBuilder::new_local(1)
-        .with_aptos()
-        .build()
-        .await;
+    let swarm = SwarmBuilder::new_local(1).with_aptos().build().await;
     let mut info = swarm.aptos_public_info();
 
     let register_txn = info.root_account().sign_with_transaction_builder(
         info.transaction_factory()
-            .entry_function(
-                EntryFunction::new(
-                    ModuleId::new(AccountAddress::ONE, Identifier::new("account_abstraction").unwrap()),
-                    Identifier::new("register_domain_with_authentication_function_test_network_only").unwrap(),
-                    vec![],
-                    vec![
-                        bcs::to_bytes(&AccountAddress::ONE).unwrap(),
-                        bcs::to_bytes(&"common_domain_aa_auths").unwrap(),
-                        bcs::to_bytes(&"authenticate_ed25519_hex").unwrap(),
-
-                    ],
-                )
-            )
+            .entry_function(EntryFunction::new(
+                ModuleId::new(
+                    AccountAddress::ONE,
+                    Identifier::new("account_abstraction").unwrap(),
+                ),
+                Identifier::new("register_domain_with_authentication_function_test_network_only")
+                    .unwrap(),
+                vec![],
+                vec![
+                    bcs::to_bytes(&AccountAddress::ONE).unwrap(),
+                    bcs::to_bytes(&"common_domain_aa_auths").unwrap(),
+                    bcs::to_bytes(&"authenticate_ed25519_hex").unwrap(),
+                ],
+            )),
     );
     info.client().submit_and_wait(®ister_txn).await.unwrap();
 
@@ -50,7 +50,8 @@ async fn test_domain_aa() {
         account_key.public_key().to_bytes().to_vec(),
         Arc::new(move |x: &[u8]| {
             let x_hex = format!("0x{}", hex::encode(x)).into_bytes();
-            account_key.private_key()
+            account_key
+                .private_key()
                 .sign_arbitrary_message(&x_hex)
                 .to_bytes()
                 .to_vec()
@@ -67,5 +68,8 @@ async fn test_domain_aa() {
                 AccountAddress::random(),
             )),
     );
-    info.client().submit_and_wait(&create_txn).await.expect(&format!("aa: {:?}", create_txn));
+    info.client()
+        .submit_and_wait(&create_txn)
+        .await
+        .unwrap_or_else(|_| panic!("aa: {:?}", create_txn));
 }
diff --git a/testsuite/smoke-test/src/lib.rs b/testsuite/smoke-test/src/lib.rs
index dcb77a81b27f5..bb1c7b7677f01 100644
--- a/testsuite/smoke-test/src/lib.rs
+++ b/testsuite/smoke-test/src/lib.rs
@@ -4,13 +4,13 @@
 
 extern crate core;
 
+#[cfg(test)]
+mod account_abstraction;
 #[cfg(test)]
 mod aptos;
 #[cfg(test)]
 mod aptos_cli;
 #[cfg(test)]
-mod account_abstraction;
-#[cfg(test)]
 mod client;
 #[cfg(test)]
 mod consensus;
diff --git a/types/src/transaction/authenticator.rs b/types/src/transaction/authenticator.rs
index 577ff9a18f817..baa478d9552c1 100644
--- a/types/src/transaction/authenticator.rs
+++ b/types/src/transaction/authenticator.rs
@@ -552,7 +552,7 @@ pub enum DomainAccount {
     V1 {
         #[serde(with = "serde_bytes")]
         account_identity: Vec,
-    }
+    },
 }
 
 #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
@@ -568,8 +568,8 @@ pub enum AbstractionAuthData {
         signing_message_digest: Vec,
         #[serde(with = "serde_bytes")]
         authenticator: Vec,
-        account: DomainAccount
-    }
+        account: DomainAccount,
+    },
 }
 
 impl AbstractionAuthData {
@@ -578,11 +578,11 @@ impl AbstractionAuthData {
             Self::V1 {
                 signing_message_digest,
                 ..
-            } | Self::DomainV1 {
+            }
+            | Self::DomainV1 {
                 signing_message_digest,
                 ..
             } => signing_message_digest,
-
         }
     }
 }
diff --git a/types/src/transaction/mod.rs b/types/src/transaction/mod.rs
index 099e20769f527..2702ebee4ca65 100644
--- a/types/src/transaction/mod.rs
+++ b/types/src/transaction/mod.rs
@@ -453,7 +453,11 @@ fn gen_auth(
                 sign_function(digest.as_ref()),
             )
         },
-        Auth::DomainAbstraction { function_info, account_identity, sign_function } => {
+        Auth::DomainAbstraction {
+            function_info,
+            account_identity,
+            sign_function,
+        } => {
             let digest =
                 HashValue::sha3_256_of(signing_message(user_signed_message)?.as_slice()).to_vec();
             AccountAuthenticator::domain_abstraction(

From 8c611e00d8f5cff2bdcc70d04a21cbf0372745c8 Mon Sep 17 00:00:00 2001
From: Igor 
Date: Fri, 14 Feb 2025 00:12:03 -0800
Subject: [PATCH 7/8] feature flag

---
 .../src/components/feature_flags.rs                |  3 +++
 aptos-move/aptos-vm/src/aptos_vm.rs                | 14 ++++++++++++--
 types/src/on_chain_config/aptos_features.rs        |  6 ++++++
 3 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/aptos-move/aptos-release-builder/src/components/feature_flags.rs b/aptos-move/aptos-release-builder/src/components/feature_flags.rs
index d03a4e4f3ba0b..e766ac44d1a77 100644
--- a/aptos-move/aptos-release-builder/src/components/feature_flags.rs
+++ b/aptos-move/aptos-release-builder/src/components/feature_flags.rs
@@ -139,6 +139,7 @@ pub enum FeatureFlag {
     AccountAbstraction,
     VMBinaryFormatV8,
     BulletproofsBatchNatives,
+    DomainAccountAbstraction,
 }
 
 fn generate_features_blob(writer: &CodeWriter, data: &[u64]) {
@@ -369,6 +370,7 @@ impl From for AptosFeatureFlag {
             FeatureFlag::PermissionedSigner => AptosFeatureFlag::PERMISSIONED_SIGNER,
             FeatureFlag::AccountAbstraction => AptosFeatureFlag::ACCOUNT_ABSTRACTION,
             FeatureFlag::BulletproofsBatchNatives => AptosFeatureFlag::BULLETPROOFS_BATCH_NATIVES,
+            FeatureFlag::DomainAccountAbstraction => AptosFeatureFlag::DOMAIN_ACCOUNT_ABSTRACTION,
         }
     }
 }
@@ -526,6 +528,7 @@ impl From for FeatureFlag {
             AptosFeatureFlag::PERMISSIONED_SIGNER => FeatureFlag::PermissionedSigner,
             AptosFeatureFlag::ACCOUNT_ABSTRACTION => FeatureFlag::AccountAbstraction,
             AptosFeatureFlag::BULLETPROOFS_BATCH_NATIVES => FeatureFlag::BulletproofsBatchNatives,
+            AptosFeatureFlag::DOMAIN_ACCOUNT_ABSTRACTION => FeatureFlag::DomainAccountAbstraction,
         }
     }
 }
diff --git a/aptos-move/aptos-vm/src/aptos_vm.rs b/aptos-move/aptos-vm/src/aptos_vm.rs
index f4fedbe0417aa..ce4caf52c1015 100644
--- a/aptos-move/aptos-vm/src/aptos_vm.rs
+++ b/aptos-move/aptos-vm/src/aptos_vm.rs
@@ -1930,7 +1930,12 @@ impl AptosVM {
                     function_info,
                     auth_data,
                 }) => {
-                    if self.features().is_account_abstraction_enabled() {
+                    let enabled = if let AbstractionAuthData::V1 { .. } = auth_data {
+                        self.features().is_account_abstraction_enabled()
+                    } else {
+                        self.features().is_domain_account_abstraction_enabled()
+                    };
+                    if enabled {
                         dispatchable_authenticate(
                             session,
                             gas_meter,
@@ -1962,7 +1967,12 @@ impl AptosVM {
                     function_info,
                     auth_data,
                 } => {
-                    if self.features().is_account_abstraction_enabled() {
+                    let enabled = if let AbstractionAuthData::V1 { .. } = auth_data {
+                        self.features().is_account_abstraction_enabled()
+                    } else {
+                        self.features().is_domain_account_abstraction_enabled()
+                    };
+                    if enabled {
                         dispatchable_authenticate(
                             session,
                             gas_meter,
diff --git a/types/src/on_chain_config/aptos_features.rs b/types/src/on_chain_config/aptos_features.rs
index 9865fab81e291..85168b3523e39 100644
--- a/types/src/on_chain_config/aptos_features.rs
+++ b/types/src/on_chain_config/aptos_features.rs
@@ -121,6 +121,7 @@ pub enum FeatureFlag {
     /// Enables bytecode version v8
     VM_BINARY_FORMAT_V8 = 86,
     BULLETPROOFS_BATCH_NATIVES = 87,
+    DOMAIN_ACCOUNT_ABSTRACTION = 88,
 }
 
 impl FeatureFlag {
@@ -207,6 +208,7 @@ impl FeatureFlag {
             FeatureFlag::ENABLE_CALL_TREE_AND_INSTRUCTION_VM_CACHE,
             FeatureFlag::ACCOUNT_ABSTRACTION,
             FeatureFlag::BULLETPROOFS_BATCH_NATIVES,
+            FeatureFlag::DOMAIN_ACCOUNT_ABSTRACTION,
         ]
     }
 }
@@ -286,6 +288,10 @@ impl Features {
         self.is_enabled(FeatureFlag::ACCOUNT_ABSTRACTION)
     }
 
+    pub fn is_domain_account_abstraction_enabled(&self) -> bool {
+        self.is_enabled(FeatureFlag::DOMAIN_ACCOUNT_ABSTRACTION)
+    }
+
     pub fn is_module_event_enabled(&self) -> bool {
         self.is_enabled(FeatureFlag::MODULE_EVENT)
     }

From 8ecb74710d975c13ed6125455eba6179ed05921e Mon Sep 17 00:00:00 2001
From: Igor 
Date: Fri, 14 Feb 2025 14:25:39 -0800
Subject: [PATCH 8/8] renaming files

---
 aptos-move/aptos-vm/src/aptos_vm.rs           |  11 +-
 .../doc/account_abstraction.md                | 146 ++++++++++--------
 .../domain_account_abstraction_ed25519_hex.md |  80 ++++++++++
 .../framework/aptos-framework/doc/overview.md |   2 +-
 .../doc/transaction_validation.md             |  38 ++++-
 .../sources/account/account_abstraction.move  |  56 +++----
 ...main_account_abstraction_ed25519_hex.move} |  14 +-
 .../sources/transaction_validation.move       |  18 ++-
 .../src/aptos_framework_sdk_builder.rs        |  69 +--------
 .../framework/move-stdlib/doc/features.md     |  37 +++++
 .../move-stdlib/sources/configs/features.move |   9 ++
 aptos-move/vm-genesis/src/lib.rs              |  16 ++
 .../smoke-test/src/account_abstraction.rs     |  48 +++---
 13 files changed, 347 insertions(+), 197 deletions(-)
 create mode 100644 aptos-move/framework/aptos-framework/doc/domain_account_abstraction_ed25519_hex.md
 rename aptos-move/framework/aptos-framework/sources/account/{domain_aa/common_domain_aa_auths.move => common_account_abstractions/domain_account_abstraction_ed25519_hex.move} (61%)

diff --git a/aptos-move/aptos-vm/src/aptos_vm.rs b/aptos-move/aptos-vm/src/aptos_vm.rs
index ce4caf52c1015..ce065846734c7 100644
--- a/aptos-move/aptos-vm/src/aptos_vm.rs
+++ b/aptos-move/aptos-vm/src/aptos_vm.rs
@@ -1967,10 +1967,13 @@ impl AptosVM {
                     function_info,
                     auth_data,
                 } => {
-                    let enabled = if let AbstractionAuthData::V1 { .. } = auth_data {
-                        self.features().is_account_abstraction_enabled()
-                    } else {
-                        self.features().is_domain_account_abstraction_enabled()
+                    let enabled = match auth_data {
+                        AbstractionAuthData::V1 { .. } => {
+                            self.features().is_account_abstraction_enabled()
+                        },
+                        AbstractionAuthData::DomainV1 { .. } => {
+                            self.features().is_domain_account_abstraction_enabled()
+                        },
                     };
                     if enabled {
                         dispatchable_authenticate(
diff --git a/aptos-move/framework/aptos-framework/doc/account_abstraction.md b/aptos-move/framework/aptos-framework/doc/account_abstraction.md
index 7ba45b128fb9c..772bfbd5ea53d 100644
--- a/aptos-move/framework/aptos-framework/doc/account_abstraction.md
+++ b/aptos-move/framework/aptos-framework/doc/account_abstraction.md
@@ -8,21 +8,22 @@
 -  [Struct `UpdateDispatchableAuthenticator`](#0x1_account_abstraction_UpdateDispatchableAuthenticator)
 -  [Struct `RemoveDispatchableAuthenticator`](#0x1_account_abstraction_RemoveDispatchableAuthenticator)
 -  [Enum Resource `DispatchableAuthenticator`](#0x1_account_abstraction_DispatchableAuthenticator)
+-  [Enum `DomainRegisterValue`](#0x1_account_abstraction_DomainRegisterValue)
 -  [Enum Resource `DomainDispatchableAuthenticator`](#0x1_account_abstraction_DomainDispatchableAuthenticator)
 -  [Constants](#@Constants_0)
 -  [Function `using_dispatchable_authenticator`](#0x1_account_abstraction_using_dispatchable_authenticator)
 -  [Function `dispatchable_authenticator`](#0x1_account_abstraction_dispatchable_authenticator)
--  [Function `domain_aa_account_address_view`](#0x1_account_abstraction_domain_aa_account_address_view)
--  [Function `domain_aa_account_address`](#0x1_account_abstraction_domain_aa_account_address)
+-  [Function `derive_domain_account_address_view`](#0x1_account_abstraction_derive_domain_account_address_view)
+-  [Function `derive_domain_account_address`](#0x1_account_abstraction_derive_domain_account_address)
 -  [Function `add_authentication_function`](#0x1_account_abstraction_add_authentication_function)
 -  [Function `remove_authentication_function`](#0x1_account_abstraction_remove_authentication_function)
 -  [Function `remove_authenticator`](#0x1_account_abstraction_remove_authenticator)
 -  [Function `register_domain_with_authentication_function`](#0x1_account_abstraction_register_domain_with_authentication_function)
--  [Function `register_domain_with_authentication_function_test_network_only`](#0x1_account_abstraction_register_domain_with_authentication_function_test_network_only)
 -  [Function `initialize`](#0x1_account_abstraction_initialize)
 -  [Function `resource_addr`](#0x1_account_abstraction_resource_addr)
 -  [Function `update_dispatchable_authenticator_impl`](#0x1_account_abstraction_update_dispatchable_authenticator_impl)
 -  [Function `dispatchable_authenticator_internal`](#0x1_account_abstraction_dispatchable_authenticator_internal)
+-  [Function `dispatchable_domain_authenticator_internal`](#0x1_account_abstraction_dispatchable_domain_authenticator_internal)
 -  [Function `authenticate`](#0x1_account_abstraction_authenticate)
 -  [Function `dispatchable_authenticate`](#0x1_account_abstraction_dispatchable_authenticate)
 -  [Function `add_dispatchable_authentication_function`](#0x1_account_abstraction_add_dispatchable_authentication_function)
@@ -157,6 +158,39 @@ enum 
+
+## Enum `DomainRegisterValue`
+
+
+
+
enum DomainRegisterValue has store
+
+ + + +
+Variants + + +
+Empty + + +
+Fields + + +
+
+ +
@@ -189,7 +223,7 @@ The dispatchable domain-scoped authenticator, that defines how to authenticate
-auth_functions: big_ordered_map::BigOrderedMap<function_info::FunctionInfo, bool> +auth_functions: big_ordered_map::BigOrderedMap<function_info::FunctionInfo, account_abstraction::DomainRegisterValue>
@@ -228,6 +262,8 @@ The dispatchable domain-scoped authenticator, that defines how to authenticate +domain_aa_account_address uses this for domain separation within its native implementation +source is defined in Scheme enum in types/src/transaction/authenticator.rs
const DOMAIN_ABSTRACTION_DERIVED_SCHEME: u8 = 5;
@@ -346,14 +382,14 @@ Return the current dispatchable authenticator move function info. None
 
-
+
 
-## Function `domain_aa_account_address_view`
+## Function `derive_domain_account_address_view`
 
 
 
 
#[view]
-public fun domain_aa_account_address_view(module_address: address, module_name: string::String, function_name: string::String, account_identity: vector<u8>): address
+public fun derive_domain_account_address_view(module_address: address, module_name: string::String, function_name: string::String, account_identity: vector<u8>): address
 
@@ -362,13 +398,13 @@ Return the current dispatchable authenticator move function info. NoneImplementation -
public fun domain_aa_account_address_view(
+
public fun derive_domain_account_address_view(
     module_address: address,
     module_name: String,
     function_name: String,
     account_identity: vector<u8>
 ): address {
-    domain_aa_account_address(
+    derive_domain_account_address(
         function_info::new_function_info_from_address(module_address, module_name, function_name),
         &account_identity,
     )
@@ -379,15 +415,15 @@ Return the current dispatchable authenticator move function info. None
 
-
+
 
-## Function `domain_aa_account_address`
+## Function `derive_domain_account_address`
 
 TODO: probably worth creating some module with all these derived functions,
 and do computation/caching in rust to avoid recomputation, as we do for objects.
 
 
-
public fun domain_aa_account_address(domain_func_info: function_info::FunctionInfo, account_identity: &vector<u8>): address
+
public fun derive_domain_account_address(domain_func_info: function_info::FunctionInfo, account_identity: &vector<u8>): address
 
@@ -396,7 +432,7 @@ and do computation/caching in rust to avoid recomputation, as we do for objects. Implementation -
public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector<u8>): address {
+
public fun derive_domain_account_address(domain_func_info: FunctionInfo, account_identity: &vector<u8>): address {
     // using bcs serialized structs here - this allows for no need for separators.
     // Alternative would've been to create unique string, we would need to convert domain_func_info into string,
     // then authentication_key to hex, and then we need separators as well - like ::
@@ -528,14 +564,15 @@ Note: it is a private entry function that can only be called directly from trans
 Add dispatchable domain-scoped authentication function, that enables account abstraction via this function.
 This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA).
 dispatchable function needs to verify two things:
-- that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA)
-- that signing_data.account_identity() is correct identity representing the authenticator
+- that signing_data.domain_authenticator() is a valid signature of signing_data.digest() (just like regular AA)
+- that signing_data.domain_account_identity() is correct identity representing the authenticator
 (missing this step would allow impersonation)
 
-Note: it is a private entry function that can only be called directly from transaction.
+Note: This is  public entry function, as it requires framework signer, and that can
+only be obtained as a part of the governance script.
 
 
-
entry fun register_domain_with_authentication_function(aptos_framework: &signer, module_address: address, module_name: string::String, function_name: string::String)
+
public entry fun register_domain_with_authentication_function(aptos_framework: &signer, module_address: address, module_name: string::String, function_name: string::String)
 
@@ -544,7 +581,7 @@ Note: it is a private entry function that can only be called directly from trans Implementation -
entry fun register_domain_with_authentication_function(
+
public entry fun register_domain_with_authentication_function(
     aptos_framework: &signer,
     module_address: address,
     module_name: String,
@@ -554,41 +591,7 @@ Note: it is a private entry function that can only be called directly from trans
 
     borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
         function_info::new_function_info_from_address(module_address, module_name, function_name),
-        true,
-    );
-}
-
- - - -
- - - -## Function `register_domain_with_authentication_function_test_network_only` - - - -
entry fun register_domain_with_authentication_function_test_network_only(core_resources: &signer, module_address: address, module_name: string::String, function_name: string::String)
-
- - - -
-Implementation - - -
entry fun register_domain_with_authentication_function_test_network_only(
-    core_resources: &signer,
-    module_address: address,
-    module_name: String,
-    function_name: String,
-) acquires DomainDispatchableAuthenticator {
-    system_addresses::assert_core_resource(core_resources);
-
-    borrow_global_mut<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions.add(
-        function_info::new_function_info_from_address(module_address, module_name, function_name),
-        true,
+        DomainRegisterValue::Empty,
     );
 }
 
@@ -741,6 +744,31 @@ Note: it is a private entry function that can only be called directly from trans +
+ + + +## Function `dispatchable_domain_authenticator_internal` + + + +
fun dispatchable_domain_authenticator_internal(): &big_ordered_map::BigOrderedMap<function_info::FunctionInfo, account_abstraction::DomainRegisterValue>
+
+ + + +
+Implementation + + +
inline fun dispatchable_domain_authenticator_internal(): &BigOrderedMap<FunctionInfo, DomainRegisterValue> {
+    assert!(exists<DomainDispatchableAuthenticator>(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED));
+    &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions
+}
+
+ + +
@@ -766,16 +794,12 @@ Note: it is a private entry function that can only be called directly from trans let master_signer_addr = signer::address_of(&account); if (signing_data.is_domain()) { - assert!(exists<DomainDispatchableAuthenticator>(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED)); - let func_infos = &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions; - assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + assert!(master_signer_addr == derive_domain_account_address(func_info, signing_data.domain_account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); - // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator() - // so we need to have it separate, and require authenticate function to confirm it matches. - assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.domain_account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); + let func_infos = dispatchable_domain_authenticator_internal(); + assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); } else { - assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); - let func_infos = &borrow_global<DispatchableAuthenticator>(resource_addr(master_signer_addr)).auth_functions; + let func_infos = dispatchable_authenticator_internal(master_signer_addr); assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); }; diff --git a/aptos-move/framework/aptos-framework/doc/domain_account_abstraction_ed25519_hex.md b/aptos-move/framework/aptos-framework/doc/domain_account_abstraction_ed25519_hex.md new file mode 100644 index 0000000000000..5ac1610bae71c --- /dev/null +++ b/aptos-move/framework/aptos-framework/doc/domain_account_abstraction_ed25519_hex.md @@ -0,0 +1,80 @@ + + + +# Module `0x1::domain_account_abstraction_ed25519_hex` + +Domain account abstraction using ed25519 hex for signing. + +Authentication takes digest, converts to hex (prefixed with 0x, with lowercase letters), +and then expects that to be signed. +authenticator is expected to be signature: vector +account_identity is raw public_key. + + +- [Constants](#@Constants_0) +- [Function `authenticate`](#0x1_domain_account_abstraction_ed25519_hex_authenticate) + + +
use 0x1::auth_data;
+use 0x1::ed25519;
+use 0x1::error;
+use 0x1::string;
+use 0x1::string_utils;
+
+ + + + + +## Constants + + + + + + +
const EINVALID_SIGNATURE: u64 = 1;
+
+ + + + + +## Function `authenticate` + +Authorization function for domain account abstraction. + + +
public fun authenticate(account: signer, aa_auth_data: auth_data::AbstractionAuthData): signer
+
+ + + +
+Implementation + + +
public fun authenticate(account: signer, aa_auth_data: AbstractionAuthData): signer {
+    let hex_digest = string_utils::to_string(aa_auth_data.digest());
+
+    let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.domain_account_identity());
+    let signature = new_signature_from_bytes(*aa_auth_data.domain_authenticator());
+    assert!(
+        ed25519::signature_verify_strict(
+            &signature,
+            &public_key,
+            *hex_digest.bytes(),
+        ),
+        error::permission_denied(EINVALID_SIGNATURE)
+    );
+
+    account
+}
+
+ + + +
+ + +[move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/overview.md b/aptos-move/framework/aptos-framework/doc/overview.md index fb07c580f55b7..6783b560bbf1f 100644 --- a/aptos-move/framework/aptos-framework/doc/overview.md +++ b/aptos-move/framework/aptos-framework/doc/overview.md @@ -27,13 +27,13 @@ This is the reference documentation of the Aptos framework. - [`0x1::chain_status`](chain_status.md#0x1_chain_status) - [`0x1::code`](code.md#0x1_code) - [`0x1::coin`](coin.md#0x1_coin) -- [`0x1::common_domain_aa_auths`](common_domain_aa_auths.md#0x1_common_domain_aa_auths) - [`0x1::config_buffer`](config_buffer.md#0x1_config_buffer) - [`0x1::consensus_config`](consensus_config.md#0x1_consensus_config) - [`0x1::create_signer`](create_signer.md#0x1_create_signer) - [`0x1::delegation_pool`](delegation_pool.md#0x1_delegation_pool) - [`0x1::dispatchable_fungible_asset`](dispatchable_fungible_asset.md#0x1_dispatchable_fungible_asset) - [`0x1::dkg`](dkg.md#0x1_dkg) +- [`0x1::domain_account_abstraction_ed25519_hex`](domain_account_abstraction_ed25519_hex.md#0x1_domain_account_abstraction_ed25519_hex) - [`0x1::event`](event.md#0x1_event) - [`0x1::execution_config`](execution_config.md#0x1_execution_config) - [`0x1::function_info`](function_info.md#0x1_function_info) diff --git a/aptos-move/framework/aptos-framework/doc/transaction_validation.md b/aptos-move/framework/aptos-framework/doc/transaction_validation.md index 81b890fb9ad3f..c88e551da5d1f 100644 --- a/aptos-move/framework/aptos-framework/doc/transaction_validation.md +++ b/aptos-move/framework/aptos-framework/doc/transaction_validation.md @@ -11,6 +11,7 @@ - [Function `grant_gas_permission`](#0x1_transaction_validation_grant_gas_permission) - [Function `revoke_gas_permission`](#0x1_transaction_validation_revoke_gas_permission) - [Function `initialize`](#0x1_transaction_validation_initialize) +- [Function `allow_missing_txn_authentication_key`](#0x1_transaction_validation_allow_missing_txn_authentication_key) - [Function `prologue_common`](#0x1_transaction_validation_prologue_common) - [Function `script_prologue`](#0x1_transaction_validation_script_prologue) - [Function `script_prologue_extended`](#0x1_transaction_validation_script_prologue_extended) @@ -52,6 +53,7 @@
use 0x1::account;
+use 0x1::account_abstraction;
 use 0x1::aptos_account;
 use 0x1::aptos_coin;
 use 0x1::bcs;
@@ -385,6 +387,33 @@ Only called during genesis to initialize system resources for this module.
 
 
 
+
+
+
+
+## Function `allow_missing_txn_authentication_key`
+
+
+
+
fun allow_missing_txn_authentication_key(transaction_sender: address): bool
+
+ + + +
+Implementation + + +
inline fun allow_missing_txn_authentication_key(transaction_sender: address): bool {
+    // aa verifies authentication itself
+    features::is_account_abstraction_enabled() &&
+    (features::is_domain_account_abstraction_enabled()
+        || account_abstraction::using_dispatchable_authenticator(transaction_sender))
+}
+
+ + +
@@ -438,7 +467,7 @@ Only called during genesis to initialize system resources for this module. ); } else { assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(transaction_sender), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ) }; @@ -474,9 +503,8 @@ Only called during genesis to initialize system resources for this module. error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), ); } else { - // aa verifies authentication itself assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(transaction_sender), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ); } @@ -771,7 +799,7 @@ Only called during genesis to initialize system resources for this module. ); } else { assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(secondary_address), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ) }; @@ -1244,7 +1272,7 @@ If there is no fee_payer, fee_payer = sender ); } else { assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(fee_payer_address), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ) }; diff --git a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move index a041f9899548c..b53552c347e70 100644 --- a/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move +++ b/aptos-move/framework/aptos-framework/sources/account/account_abstraction.move @@ -31,6 +31,8 @@ module aptos_framework::account_abstraction { const EDEPRECATED_FUNCTION: u64 = 6; const EDOMAIN_AA_NOT_INITIALIZED: u64 = 6; + /// domain_aa_account_address uses this for domain separation within its native implementation + /// source is defined in Scheme enum in types/src/transaction/authenticator.rs const DOMAIN_ABSTRACTION_DERIVED_SCHEME: u8 = 5; const MAX_U64: u128 = 18446744073709551615; @@ -54,9 +56,13 @@ module aptos_framework::account_abstraction { V1 { auth_functions: OrderedMap } } + enum DomainRegisterValue has store { + Empty, + } + /// The dispatchable domain-scoped authenticator, that defines how to authenticate enum DomainDispatchableAuthenticator has key { - V1 { auth_functions: BigOrderedMap } + V1 { auth_functions: BigOrderedMap } } #[view] @@ -77,13 +83,13 @@ module aptos_framework::account_abstraction { } #[view] - public fun domain_aa_account_address_view( + public fun derive_domain_account_address_view( module_address: address, module_name: String, function_name: String, account_identity: vector ): address { - domain_aa_account_address( + derive_domain_account_address( function_info::new_function_info_from_address(module_address, module_name, function_name), &account_identity, ) @@ -91,7 +97,7 @@ module aptos_framework::account_abstraction { /// TODO: probably worth creating some module with all these derived functions, /// and do computation/caching in rust to avoid recomputation, as we do for objects. - public fun domain_aa_account_address(domain_func_info: FunctionInfo, account_identity: &vector): address { + public fun derive_domain_account_address(domain_func_info: FunctionInfo, account_identity: &vector): address { // using bcs serialized structs here - this allows for no need for separators. // Alternative would've been to create unique string, we would need to convert domain_func_info into string, // then authentication_key to hex, and then we need separators as well - like :: @@ -154,12 +160,13 @@ module aptos_framework::account_abstraction { /// Add dispatchable domain-scoped authentication function, that enables account abstraction via this function. /// This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA). /// dispatchable function needs to verify two things: - /// - that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA) - /// - that signing_data.account_identity() is correct identity representing the authenticator + /// - that signing_data.domain_authenticator() is a valid signature of signing_data.digest() (just like regular AA) + /// - that signing_data.domain_account_identity() is correct identity representing the authenticator /// (missing this step would allow impersonation) /// - /// Note: it is a private entry function that can only be called directly from transaction. - entry fun register_domain_with_authentication_function( + /// Note: This is public entry function, as it requires framework signer, and that can + /// only be obtained as a part of the governance script. + public entry fun register_domain_with_authentication_function( aptos_framework: &signer, module_address: address, module_name: String, @@ -169,21 +176,7 @@ module aptos_framework::account_abstraction { borrow_global_mut(@aptos_framework).auth_functions.add( function_info::new_function_info_from_address(module_address, module_name, function_name), - true, - ); - } - - entry fun register_domain_with_authentication_function_test_network_only( - core_resources: &signer, - module_address: address, - module_name: String, - function_name: String, - ) acquires DomainDispatchableAuthenticator { - system_addresses::assert_core_resource(core_resources); - - borrow_global_mut(@aptos_framework).auth_functions.add( - function_info::new_function_info_from_address(module_address, module_name, function_name), - true, + DomainRegisterValue::Empty, ); } @@ -253,6 +246,11 @@ module aptos_framework::account_abstraction { &borrow_global(resource_addr(addr)).auth_functions } + inline fun dispatchable_domain_authenticator_internal(): &BigOrderedMap { + assert!(exists(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED)); + &borrow_global(@aptos_framework).auth_functions + } + fun authenticate( account: signer, func_info: FunctionInfo, @@ -261,16 +259,12 @@ module aptos_framework::account_abstraction { let master_signer_addr = signer::address_of(&account); if (signing_data.is_domain()) { - assert!(exists(@aptos_framework), error::not_found(EDOMAIN_AA_NOT_INITIALIZED)); - let func_infos = &borrow_global(@aptos_framework).auth_functions; - assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); + assert!(master_signer_addr == derive_domain_account_address(func_info, signing_data.domain_account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); - // I don't think we can figure out a generic way to extract account_identity from signing_data.authenticator() - // so we need to have it separate, and require authenticate function to confirm it matches. - assert!(master_signer_addr == domain_aa_account_address(func_info, signing_data.domain_account_identity()), error::invalid_state(EINCONSISTENT_SIGNER_ADDRESS)); + let func_infos = dispatchable_domain_authenticator_internal(); + assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); } else { - assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); - let func_infos = &borrow_global(resource_addr(master_signer_addr)).auth_functions; + let func_infos = dispatchable_authenticator_internal(master_signer_addr); assert!(ordered_map::contains(func_infos, &func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); }; diff --git a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move b/aptos-move/framework/aptos-framework/sources/account/common_account_abstractions/domain_account_abstraction_ed25519_hex.move similarity index 61% rename from aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move rename to aptos-move/framework/aptos-framework/sources/account/common_account_abstractions/domain_account_abstraction_ed25519_hex.move index f1b843b42b846..c957a9f04a002 100644 --- a/aptos-move/framework/aptos-framework/sources/account/domain_aa/common_domain_aa_auths.move +++ b/aptos-move/framework/aptos-framework/sources/account/common_account_abstractions/domain_account_abstraction_ed25519_hex.move @@ -1,4 +1,10 @@ -module aptos_framework::common_domain_aa_auths { +/// Domain account abstraction using ed25519 hex for signing. +/// +/// Authentication takes digest, converts to hex (prefixed with 0x, with lowercase letters), +/// and then expects that to be signed. +/// authenticator is expected to be signature: vector +/// account_identity is raw public_key. +module aptos_framework::domain_account_abstraction_ed25519_hex { use std::error; use aptos_std::string_utils; use aptos_std::ed25519::{ @@ -10,10 +16,8 @@ module aptos_framework::common_domain_aa_auths { const EINVALID_SIGNATURE: u64 = 1; - // takes digest, converts to hex (prefixed with 0x, with lowercase letters), and then expects that to be signed. - // authenticator is expected to be signature: vector - // account_identity is raw public_key. - public fun authenticate_ed25519_hex(account: signer, aa_auth_data: AbstractionAuthData): signer { + /// Authorization function for domain account abstraction. + public fun authenticate(account: signer, aa_auth_data: AbstractionAuthData): signer { let hex_digest = string_utils::to_string(aa_auth_data.digest()); let public_key = new_unvalidated_public_key_from_bytes(*aa_auth_data.domain_account_identity()); diff --git a/aptos-move/framework/aptos-framework/sources/transaction_validation.move b/aptos-move/framework/aptos-framework/sources/transaction_validation.move index 08c457e68221f..aa604e6669d2c 100644 --- a/aptos-move/framework/aptos-framework/sources/transaction_validation.move +++ b/aptos-move/framework/aptos-framework/sources/transaction_validation.move @@ -9,6 +9,7 @@ module aptos_framework::transaction_validation { use aptos_framework::account; use aptos_framework::aptos_account; + use aptos_framework::account_abstraction; use aptos_framework::aptos_coin::AptosCoin; use aptos_framework::chain_id; use aptos_framework::coin; @@ -98,6 +99,14 @@ module aptos_framework::transaction_validation { }); } + // TODO: can be removed after features have been rolled out. + inline fun allow_missing_txn_authentication_key(transaction_sender: address): bool { + // aa verifies authentication itself + features::is_account_abstraction_enabled() && + (features::is_domain_account_abstraction_enabled() + || account_abstraction::using_dispatchable_authenticator(transaction_sender)) + } + fun prologue_common( sender: &signer, gas_payer: &signer, @@ -134,7 +143,7 @@ module aptos_framework::transaction_validation { ); } else { assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(transaction_sender), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ) }; @@ -170,9 +179,8 @@ module aptos_framework::transaction_validation { error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY), ); } else { - // aa verifies authentication itself assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(transaction_sender), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ); } @@ -373,7 +381,7 @@ module aptos_framework::transaction_validation { ); } else { assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(secondary_address), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ) }; @@ -658,7 +666,7 @@ module aptos_framework::transaction_validation { ); } else { assert!( - features::is_account_abstraction_enabled(), + allow_missing_txn_authentication_key(fee_payer_address), error::invalid_argument(PROLOGUE_EINVALID_ACCOUNT_AUTH_KEY) ) }; diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs index d4f8920a80c1b..10ed11cec882b 100644 --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs @@ -184,23 +184,18 @@ pub enum EntryFunctionCall { /// Add dispatchable domain-scoped authentication function, that enables account abstraction via this function. /// This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA). /// dispatchable function needs to verify two things: - /// - that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA) - /// - that signing_data.account_identity() is correct identity representing the authenticator + /// - that signing_data.domain_authenticator() is a valid signature of signing_data.digest() (just like regular AA) + /// - that signing_data.domain_account_identity() is correct identity representing the authenticator /// (missing this step would allow impersonation) /// - /// Note: it is a private entry function that can only be called directly from transaction. + /// Note: This is public entry function, as it requires framework signer, and that can + /// only be obtained as a part of the governance script. AccountAbstractionRegisterDomainWithAuthenticationFunction { module_address: AccountAddress, module_name: Vec, function_name: Vec, }, - AccountAbstractionRegisterDomainWithAuthenticationFunctionTestNetworkOnly { - module_address: AccountAddress, - module_name: Vec, - function_name: Vec, - }, - /// Remove dispatchable authentication function that enables account abstraction via this function. /// dispatchable function needs to verify that signing_data.authenticator() is a valid signature of signing_data.digest(). /// Note: it is a private entry function that can only be called directly from transaction. @@ -1311,17 +1306,6 @@ impl EntryFunctionCall { module_name, function_name, ), - AccountAbstractionRegisterDomainWithAuthenticationFunctionTestNetworkOnly { - module_address, - module_name, - function_name, - } => { - account_abstraction_register_domain_with_authentication_function_test_network_only( - module_address, - module_name, - function_name, - ) - }, AccountAbstractionRemoveAuthenticationFunction { module_address, module_name, @@ -2291,11 +2275,12 @@ pub fn account_abstraction_initialize() -> TransactionPayload { /// Add dispatchable domain-scoped authentication function, that enables account abstraction via this function. /// This means all accounts within the domain can use it to authenticate, without needing an initialization (unlike non-domain AA). /// dispatchable function needs to verify two things: -/// - that signing_data.authenticator() is a valid signature of signing_data.digest() (just like regular AA) -/// - that signing_data.account_identity() is correct identity representing the authenticator +/// - that signing_data.domain_authenticator() is a valid signature of signing_data.digest() (just like regular AA) +/// - that signing_data.domain_account_identity() is correct identity representing the authenticator /// (missing this step would allow impersonation) /// -/// Note: it is a private entry function that can only be called directly from transaction. +/// Note: This is public entry function, as it requires framework signer, and that can +/// only be obtained as a part of the governance script. pub fn account_abstraction_register_domain_with_authentication_function( module_address: AccountAddress, module_name: Vec, @@ -2319,29 +2304,6 @@ pub fn account_abstraction_register_domain_with_authentication_function( )) } -pub fn account_abstraction_register_domain_with_authentication_function_test_network_only( - module_address: AccountAddress, - module_name: Vec, - function_name: Vec, -) -> TransactionPayload { - TransactionPayload::EntryFunction(EntryFunction::new( - ModuleId::new( - AccountAddress::new([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, - ]), - ident_str!("account_abstraction").to_owned(), - ), - ident_str!("register_domain_with_authentication_function_test_network_only").to_owned(), - vec![], - vec![ - bcs::to_bytes(&module_address).unwrap(), - bcs::to_bytes(&module_name).unwrap(), - bcs::to_bytes(&function_name).unwrap(), - ], - )) -} - /// Remove dispatchable authentication function that enables account abstraction via this function. /// dispatchable function needs to verify that signing_data.authenticator() is a valid signature of signing_data.digest(). /// Note: it is a private entry function that can only be called directly from transaction. @@ -5577,20 +5539,6 @@ mod decoder { } } - pub fn account_abstraction_register_domain_with_authentication_function_test_network_only( - payload: &TransactionPayload, - ) -> Option { - if let TransactionPayload::EntryFunction(script) = payload { - Some(EntryFunctionCall::AccountAbstractionRegisterDomainWithAuthenticationFunctionTestNetworkOnly { - module_address : bcs::from_bytes(script.args().get(0)?).ok()?, - module_name : bcs::from_bytes(script.args().get(1)?).ok()?, - function_name : bcs::from_bytes(script.args().get(2)?).ok()?, - }) - } else { - None - } - } - pub fn account_abstraction_remove_authentication_function( payload: &TransactionPayload, ) -> Option { @@ -7427,7 +7375,6 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy + +Whether the account abstraction is enabled. + +Lifetime: transient + + +
const DOMAIN_ACCOUNT_ABSTRACTION: u64 = 88;
+
+ + + @@ -3506,6 +3519,30 @@ Deprecated feature + + + + +## Function `is_domain_account_abstraction_enabled` + + + +
public fun is_domain_account_abstraction_enabled(): bool
+
+ + + +
+Implementation + + +
public fun is_domain_account_abstraction_enabled(): bool acquires Features {
+    is_enabled(DOMAIN_ACCOUNT_ABSTRACTION)
+}
+
+ + +
diff --git a/aptos-move/framework/move-stdlib/sources/configs/features.move b/aptos-move/framework/move-stdlib/sources/configs/features.move index eb032b61f12ad..8b629df322922 100644 --- a/aptos-move/framework/move-stdlib/sources/configs/features.move +++ b/aptos-move/framework/move-stdlib/sources/configs/features.move @@ -644,6 +644,15 @@ module std::features { is_enabled(BULLETPROOFS_BATCH_NATIVES) } + /// Whether the account abstraction is enabled. + /// + /// Lifetime: transient + const DOMAIN_ACCOUNT_ABSTRACTION: u64 = 88; + + public fun is_domain_account_abstraction_enabled(): bool acquires Features { + is_enabled(DOMAIN_ACCOUNT_ABSTRACTION) + } + // ============================================================================================ // Feature Flag Implementation diff --git a/aptos-move/vm-genesis/src/lib.rs b/aptos-move/vm-genesis/src/lib.rs index 147320552944e..19dd43e08cd1b 100644 --- a/aptos-move/vm-genesis/src/lib.rs +++ b/aptos-move/vm-genesis/src/lib.rs @@ -600,6 +600,22 @@ fn initialize_account_abstraction( vec![], serialize_values(&vec![MoveValue::Signer(CORE_CODE_ADDRESS)]), ); + + exec_function( + session, + module_storage, + ACCOUNT_ABSTRACTION_MODULE_NAME, + "register_domain_with_authentication_function", + vec![], + serialize_values(&vec![ + MoveValue::Signer(CORE_CODE_ADDRESS), + MoveValue::Address(AccountAddress::ONE), + "domain_account_abstraction_ed25519_hex" + .to_string() + .as_move_value(), + "authenticate".to_string().as_move_value(), + ]), + ); } fn initialize_reconfiguration_state( diff --git a/testsuite/smoke-test/src/account_abstraction.rs b/testsuite/smoke-test/src/account_abstraction.rs index 7c759715c63eb..1b459a0d7ecbd 100644 --- a/testsuite/smoke-test/src/account_abstraction.rs +++ b/testsuite/smoke-test/src/account_abstraction.rs @@ -6,10 +6,8 @@ use aptos_cached_packages::aptos_stdlib; use aptos_crypto::SigningKey; use aptos_forge::Swarm; use aptos_sdk::types::{AccountKey, LocalAccount}; -use aptos_types::{function_info::FunctionInfo, transaction::EntryFunction}; -use move_core_types::{ - account_address::AccountAddress, identifier::Identifier, language_storage::ModuleId, -}; +use aptos_types::function_info::FunctionInfo; +use move_core_types::account_address::AccountAddress; use rand::thread_rng; use std::sync::Arc; @@ -18,29 +16,31 @@ async fn test_domain_aa() { let swarm = SwarmBuilder::new_local(1).with_aptos().build().await; let mut info = swarm.aptos_public_info(); - let register_txn = info.root_account().sign_with_transaction_builder( - info.transaction_factory() - .entry_function(EntryFunction::new( - ModuleId::new( - AccountAddress::ONE, - Identifier::new("account_abstraction").unwrap(), - ), - Identifier::new("register_domain_with_authentication_function_test_network_only") - .unwrap(), - vec![], - vec![ - bcs::to_bytes(&AccountAddress::ONE).unwrap(), - bcs::to_bytes(&"common_domain_aa_auths").unwrap(), - bcs::to_bytes(&"authenticate_ed25519_hex").unwrap(), - ], - )), - ); - info.client().submit_and_wait(®ister_txn).await.unwrap(); + // This method is in genesis now: + // + // let register_txn = info.root_account().sign_with_transaction_builder( + // info.transaction_factory() + // .entry_function(EntryFunction::new( + // ModuleId::new( + // AccountAddress::ONE, + // Identifier::new("account_abstraction").unwrap(), + // ), + // Identifier::new("register_domain_with_authentication_function_test_network_only") + // .unwrap(), + // vec![], + // vec![ + // bcs::to_bytes(&AccountAddress::ONE).unwrap(), + // bcs::to_bytes(&"domain_account_abstraction_ed25519_hex").unwrap(), + // bcs::to_bytes(&"authenticate").unwrap(), + // ], + // )), + // ); + // info.client().submit_and_wait(®ister_txn).await.unwrap(); let function_info = FunctionInfo::new( AccountAddress::ONE, - "common_domain_aa_auths".to_string(), - "authenticate_ed25519_hex".to_string(), + "domain_account_abstraction_ed25519_hex".to_string(), + "authenticate".to_string(), ); let account_key = AccountKey::generate(&mut thread_rng());