-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AccountAbstraction that works per domain #15899
base: main
Are you sure you want to change the base?
Conversation
@@ -45,6 +53,10 @@ module aptos_framework::account_abstraction { | |||
V1 { auth_functions: OrderedMap<FunctionInfo, bool> } | |||
} | |||
|
|||
enum DomainDispatchableAuthenticator has key { | |||
V1 { auth_functions: BigOrderedMap<FunctionInfo, String> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more intuitive to have domain -> function_info mapping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like the domain is the FunctionInfo itself and this isn't needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logically you are right. it's more like an UX question. If we do that, auth_data would carry this, so instead of evm
, users would attach sth like 0x1::aa::evm_auth
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or we create a module only for this, e.g., 0x1::domain_auth
. Then it has different auth functions as evm
, solana
, etc... Then user just use the domain name in the auth_data and we generate the function info according to this rule. It's a combination of static naming and dynamic dispatch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't actually want this in the framework, I think the idea of domain separated AA addresses makes sense, but I think how those are computed should be independent. We likely want Alin's blessing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
independent of?
Who should be responsible for this auth function implementation? Since it is critical to many people who use this feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we would want a view function that supports the following:
fun extract_authentication_address(aa_fi: FunctionInfo, user_data: vector<u8>): address
which internally calls
fun aa_authentication_address(aa_fi: FunctionInfo, metadata: vector<u8>): address
which effectively just does sha256(0xFX | aa_fi | metadata)
thus it would be up to the AA to suggest deterministic, verifiable metadata into the user_data... such as the Ethereum address.
@@ -45,6 +53,10 @@ module aptos_framework::account_abstraction { | |||
V1 { auth_functions: OrderedMap<FunctionInfo, bool> } | |||
} | |||
|
|||
enum DomainDispatchableAuthenticator has key { | |||
V1 { auth_functions: BigOrderedMap<FunctionInfo, String> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like the domain is the FunctionInfo itself and this isn't needed.
I have to see how Solana, evm, etc sign things, and what would be in the signing data. Probably to have a test for both, to confirm they would work. For domain name vs fully qualified function name - only reason to have it is if implementation is immutable and not in framework and needs security fix (so it needs to switch to a new function. If we name them in a chain agnostic way - like "ed25519_hex" instead of Solana, then impl could go into the framework as a general signing scheme, I think. |
I think it would be better to not add other chain signature verifications directly to Aptos and instead leave that for implementations to figure out. If we see something that makes sense, we could definitely add it, but signature verification also implies we have clarity on what we're verifying. I think this is something that is better left to evolve a bit more before injecting into the framework. At the same time, there is a real problem of deterministic name address lookup. My proposal is that we introduce a means of converting the AA authenticators into distinct domains:
Then we move this problem out of adding new permissioned elements and leave it to user space code for the time being. Note, Also note, for safety the format would probably be like: |
enum DomainAccount has copy, drop { | ||
V1 { | ||
domain_name: String, | ||
account_authentication_key: vector<u8>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this? @igor-aptos
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the identifier of the source account.
i.e. in what David wrote above - for the account address account will have - sha3_256(FI::data::5), that is the data.
It can be an address of the original chain for example, in the format implementation of AA function will use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it seems inappropriate to let user decide this field. Maybe it should be derived.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant - the implementation of AA function needs to verify that this matches with the signature.
I am not sure what "derived" means?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ignore my "derived" words.
the implementation of AA function needs to verify that this matches with the signature.
Then for a key pair, this should be the public key part which is part of theauthenticator
itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then for a key pair, this should be the public key part which is part of the authenticator itself.
yes, but aa dispatch cannot look where in authenticator is, right? so it seems like it needs to be here, and implementation of AA needs to check that it matches what is in the authenticator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah, the implemetation(AA function info) actually knows, and it can represent the domain itself.
I had a discussion with David and took some time to reflect on this. Indeed, auth_function: FunctionInfo itself determines the domain, making it a suitable identifier for a specific domain. |
3cff6bd
to
ed074aa
Compare
@@ -0,0 +1,56 @@ | |||
module aptos_framework::common_domain_aa_auths { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error constant definitions at the start of the module. Please add:
const EINVALID_ACCOUNT_IDENTITY: u64 = 1;
const EINVALID_SIGNATURE: u64 = 2;
Spotted by Graphite Reviewer
Is this helpful? React 👍 or 👎 to let us know.
fun bytes_to_hex(data: &vector<u8>): vector<u8> { | ||
// 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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bytes_to_hex
function is currently stubbed out but is a critical dependency for authenticate_ed25519_hex
. This function needs to be implemented to convert raw bytes to their hexadecimal string representation. Consider implementing it using a lookup table for efficiency, with each byte being converted to two hex characters. The current commented implementation shows the right approach but is missing the hex conversion logic.
Spotted by Graphite Reviewer
Is this helpful? React 👍 or 👎 to let us know.
let func_infos = &borrow_global<DomainDispatchableAuthenticator>(@aptos_framework).auth_functions; | ||
assert!(func_infos.contains(&func_info), error::not_found(EFUNCTION_INFO_EXISTENCE)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code needs to verify that the domain name in signing_data
matches the domain registered for this authentication function. Currently, it only checks that the function exists in the registry but not that it's associated with the correct domain. This could allow an attacker to use any registered authentication function with any registered domain. Consider adding:
assert!(func_infos.borrow(&func_info) == domain, error::not_found(EFUNCTION_INFO_EXISTENCE));
Spotted by Graphite Reviewer
Is this helpful? React 👍 or 👎 to let us know.
ed074aa
to
e7be2c8
Compare
updated to use FunctionInfo as domain itself. I cannot see how to extract
as long as namespace is separate, I don't see this being any different than creating object at AUID address?
what does this mean? |
/// 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<u8>): address { | ||
/// using bcs serialized structs here - this allows for no need for separators. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because the length encoding?
@@ -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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about normal aa? is this required?
The difference is A choose to use B by themselves registering B for their accounts. But here, if B is not written/reviewed by us and we mapped account in aptos framework for them, it is a risk since B can have shitty code. |
updated, and added an end-to-end smoke test showing the flow. |
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.
simplifying the implementation of common_domain_aa_auths::authenticate_ed25519_hex, as we don't need public_key in both account_identity and authenticator
7a23d84
to
8c611e0
Compare
let enabled = if let AbstractionAuthData::V1 { .. } = auth_data { | ||
self.features().is_account_abstraction_enabled() | ||
} else { | ||
self.features().is_domain_account_abstraction_enabled() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's specify domain V1 here instead.
@@ -23,6 +29,9 @@ 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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where does this 5 come from?
} | ||
|
||
#[view] | ||
public fun domain_aa_account_address_view( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we change the name? the module itself is account abstraction, so having aa
in the name is not very decent.
how about derive_domain_address()
@@ -45,6 +54,53 @@ module aptos_framework::account_abstraction { | |||
V1 { auth_functions: OrderedMap<FunctionInfo, bool> } | |||
} | |||
|
|||
/// The dispatchable domain-scoped authenticator, that defines how to authenticate | |||
enum DomainDispatchableAuthenticator has key { | |||
V1 { auth_functions: BigOrderedMap<FunctionInfo, bool> } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make the value an enum in case of upgrade.
); | ||
} | ||
|
||
entry fun register_domain_with_authentication_function_test_network_only( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
possibly move to a test file?
@@ -0,0 +1,32 @@ | |||
module aptos_framework::common_domain_aa_auths { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name is not decent imho...
could we make an account_abstraction folder and put both files inside?
txn_sender_public_key: Option<vector<u8>>, | ||
// None means no need to check, i.e. either AA (where it is already checked) or simulation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// 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)); | ||
} else { | ||
assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert!(using_dispatchable_authenticator(@aptos_framework), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); | |
assert!(using_dispatchable_authenticator(master_signer_addr), error::not_found(EDISPATCHABLE_AUTHENTICATOR_IS_NOT_USED)); |
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.
Description
How Has This Been Tested?
Key Areas to Review
Type of Change
Which Components or Systems Does This Change Impact?
Checklist