Abstracción de Cuenta Derivable
La Abstracción de Cuenta Derivable (DAA)
es un estándar para abstracción de cuenta que habilita esquemas de autenticación personalizados registrando una derivable_authentication_function
.
DAA difiere de la Abstracción de Cuenta (AA) vanilla en que, para una derivable_authentication_function
dada,
define cómo derivar determinísticamente la dirección de cuenta de una abstract_public_key
, lo que puede hacerse fuera de cadena.
En contraste, la AA vanilla se habilita para una cuenta pre-existente específica registrando explícitamente una authentication_function
en cadena
y enviando una transacción, lo que implica pasos adicionales y cuesta gas para cada cuenta.
Esto permite registrar esquemas de autenticación secundarios con experiencia de usuario idéntica a los nativos. Más específicamente, esto proporciona una forma flexible y segura de gestionar firmas cross-chain. (ver cuentas x-chain)
Conceptos Centrales
Sección titulada «Conceptos Centrales»Función de autenticación
Sección titulada «Función de autenticación»DAA funciona definiendo un esquema de autenticación personalizado y registrando una función de autenticación válida para realizar autenticación en cadena.
Cada cuenta abstracta debería tener una abstract_public_key
asociada y debería poder producir abstract_signature
s
cuyos formatos dependen del esquema de autenticación.
Simplemente dicho, la derivable_authentication_function
necesita verificar que:
- la
abstract_signature
es válida para laabstract_public_key
dada - la
abstract_signature
depende del digest de la transacción
// La función debería devolver un signer si la autenticación es exitosa, de lo contrario aborta la ejecuciónpublic fun authenticate(account: signer, auth_data: AbstractionAuthData): signer;
El framework DAA automáticamente verifica si la dirección derivada de abstract_public_key
coincide con la dirección del signer.
Datos de autenticación
Sección titulada «Datos de autenticación»AbstractionAuthData
es un enum que representa los datos de autenticación a ser pasados a funciones de autenticación personalizadas.
Se usa en todos los sabores de AA, pero la variante DerivableV1
define los siguientes campos:
digest
: El hash SHA3-256 del mensaje de firma.abstract_signature
: Bytes de firma abstracta que necesitan ser verificados contraabstract_public_key
.abstract_public_key
: Bytes de clave pública abstracta asociados a la cuenta abstracta
Así es como se ve el enum Move:
enum AbstractionAuthData has copy, drop { V1 { ... }, // Solo aplicable a AA vanilla DerivableV1 { digest: vector<u8>, // Hash SHA3-256 del mensaje de firma abstract_signature: vector<u8>, abstract_public_key: vector<u8>, }}
¿Por qué es importante el digest
?
El digest
es verificado por la MoveVM para asegurar que el mensaje de firma de la transacción siendo enviada es el mismo que el presentado en el AbstractionAuthData
. Esto
es importante porque permite a la función de autenticación verificar firmas con respecto a la transacción correcta.
Por ejemplo, si quieres permitir que una clave pública firme transacciones en nombre del usuario, puedes permitir que la clave pública firme una transacción con un payload específico.
Sin embargo, si un usuario malicioso envía una firma para la clave pública correcta pero un payload diferente del digest
, la firma no será válida.
Derivación de dirección de cuenta
Sección titulada «Derivación de dirección de cuenta»Con DAA, una derivable_authentication_function
dada define un espacio de direcciones de cuenta que pueden derivarse determinísticamente de su abstract_public_key
asociada.
La función en cadena se ve como la siguiente:
public fun derive_account_address(derivable_func_info: FunctionInfo, abstract_public_key: &vector<u8>): address { let bytes = bcs::to_bytes(&derivable_func_info); bytes.append(bcs::to_bytes(abstract_public_key)); bytes.push_back(DERIVABLE_ABSTRACTION_DERIVED_SCHEME); from_bcs::to_address(hash::sha3_256(bytes))}
donde FunctionInfo
es un identificador completamente calificado para una función en cadena:
struct FunctionInfo has copy, drop, store { module_address: address, module_name: String, function_name: String}
La derivación de dirección depende del identificador de la función de autenticación y de un separador de dominio específico de DAA. Debido a esto, cada espacio de direcciones está aislado de los otros y no es posible que la misma cuenta tenga múltiples funciones de autenticación.
Ejemplo (Move)
Este ejemplo demuestra abstracción de cuenta de dominio usando ed25519 hex para firmar.
module aptos_experimental::test_derivable_account_abstraction_ed25519_hex { use std::error; 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::AbstractionAuthData;
const EINVALID_SIGNATURE: u64 = 1;
/// Función de autorización para abstracción de cuenta derivable. 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.derivable_abstract_public_key()); let signature = new_signature_from_bytes(*aa_auth_data.derivable_abstract_signature()); assert!( ed25519::signature_verify_strict( &signature, &public_key, *hex_digest.bytes(), ), error::permission_denied(EINVALID_SIGNATURE) );
account }}
Ejemplo (Typescript)
const derivableAbstractedAccount = new DerivableAbstractedAccount({ /** * El resultado de la función signer estará disponible como el campo `abstract_signature` en la variante enum `AbstractionAuthData`. */ signer: (digest) => { const hexDigest = new TextEncoder().encode(Hex.fromHexInput(digest).toString()); return solanaAccount.sign(hexDigest).toUint8Array(); }, /** * La función de autenticación a ser invocada. */ authenticationFunction: `0x7::test_derivable_account_abstraction_ed25519_hex::authenticate`, /** * La clave pública abstracta (i.e la identidad de cuenta) */ abstractPublicKey: account.publicKey.toUint8Array(),});
Guía Paso a Paso Mínima
Sección titulada «Guía Paso a Paso Mínima»-
- Generar un par de claves ED25519
const ed25519Account = Account.generate(); -
- Crear una DAA
const daa = new DerivableAbstractedAccount({signer: (digest) => {const hexDigest = new TextEncoder().encode(Hex.fromHexInput(digest).toString());return ed25519Account.sign(hexDigest).toUint8Array();},authenticationFunction: `0x7::test_derivable_account_abstraction_ed25519_hex::authenticate`,abstractPublicKey: ed25519Account.publicKey.toUint8Array(),}); -
- Financiar la DAA para crearla en cadena
await aptos.fundAccount({ accountAddress: daa.accountAddress, amount: 1000000 }); -
- Crear una cuenta destinataria y transferir APT a ella
const recipient = Account.generate();const pendingTxn = await aptos.transaction.signAndSubmitTransaction({signer: daa,transaction: await aptos.transferCoinTransaction({sender: daa.accountAddress,recipient: recipient.accountAddress,amount: 100,}),});const response = await aptos.waitForTransaction({ transactionHash: pendingTxn.hash });