Skip to content

Commit

Permalink
Aptos - Account Abstraction (#15219)
Browse files Browse the repository at this point in the history
## Description
```
module aptos_framework::signing_data {
    enum SigningData has copy, drop {
        V1 { digest: vector<u8>, authenticator: vector<u8> },
    }

    #[test_only]
    public fun create_signing_data(digest: vector<u8>): SigningData {
        SigningData::V1 { digest }
    }

    public fun digest(signing_data: &SigningData): &vector<u8> {
        &signing_data.digest
    }

    public fun authenticator(signing_data: &SigningData): &vector<u8> {
        &signing_data.authenticator
    }
}
```
a function with the following signature can be used to authorize account..
```
/// The native function to dispatch customized move authentication function.
 native fun dispatchable_authenticate(
        account: signer,
        signing_data: SigningData,
        function: &FunctionInfo
    ): signer;
```
  • Loading branch information
lightmark authored Jan 17, 2025
1 parent 0ba0bf6 commit 1d8460a
Show file tree
Hide file tree
Showing 88 changed files with 10,878 additions and 2,551 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 42 additions & 1 deletion api/doc/spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -13959,6 +13959,21 @@
},
"components": {
"schemas": {
"AbstractionSignature": {
"type": "object",
"required": [
"function_info",
"auth_data"
],
"properties": {
"function_info": {
"type": "string"
},
"auth_data": {
"$ref": "#/components/schemas/HexEncodedBytes"
}
}
},
"AccountData": {
"type": "object",
"description": "Account data\n\nA simplified version of the onchain Account resource",
Expand Down Expand Up @@ -13993,6 +14008,9 @@
},
{
"$ref": "#/components/schemas/AccountSignature_NoAccountSignature"
},
{
"$ref": "#/components/schemas/AccountSignature_AbstractionSignature"
}
],
"discriminator": {
Expand All @@ -14002,10 +14020,33 @@
"multi_ed25519_signature": "#/components/schemas/AccountSignature_MultiEd25519Signature",
"single_key_signature": "#/components/schemas/AccountSignature_SingleKeySignature",
"multi_key_signature": "#/components/schemas/AccountSignature_MultiKeySignature",
"no_account_signature": "#/components/schemas/AccountSignature_NoAccountSignature"
"no_account_signature": "#/components/schemas/AccountSignature_NoAccountSignature",
"abstraction_signature": "#/components/schemas/AccountSignature_AbstractionSignature"
}
}
},
"AccountSignature_AbstractionSignature": {
"allOf": [
{
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"abstraction_signature"
],
"example": "abstraction_signature"
}
}
},
{
"$ref": "#/components/schemas/AbstractionSignature"
}
]
},
"AccountSignature_Ed25519Signature": {
"allOf": [
{
Expand Down
24 changes: 24 additions & 0 deletions api/doc/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10438,6 +10438,16 @@ paths:
operationId: view
components:
schemas:
AbstractionSignature:
type: object
required:
- function_info
- auth_data
properties:
function_info:
type: string
auth_data:
$ref: '#/components/schemas/HexEncodedBytes'
AccountData:
type: object
description: |-
Expand Down Expand Up @@ -10468,6 +10478,7 @@ components:
- $ref: '#/components/schemas/AccountSignature_SingleKeySignature'
- $ref: '#/components/schemas/AccountSignature_MultiKeySignature'
- $ref: '#/components/schemas/AccountSignature_NoAccountSignature'
- $ref: '#/components/schemas/AccountSignature_AbstractionSignature'
discriminator:
propertyName: type
mapping:
Expand All @@ -10476,6 +10487,19 @@ components:
single_key_signature: '#/components/schemas/AccountSignature_SingleKeySignature'
multi_key_signature: '#/components/schemas/AccountSignature_MultiKeySignature'
no_account_signature: '#/components/schemas/AccountSignature_NoAccountSignature'
abstraction_signature: '#/components/schemas/AccountSignature_AbstractionSignature'
AccountSignature_AbstractionSignature:
allOf:
- type: object
required:
- type
properties:
type:
type: string
enum:
- abstraction_signature
example: abstraction_signature
- $ref: '#/components/schemas/AbstractionSignature'
AccountSignature_Ed25519Signature:
allOf:
- type: object
Expand Down
188 changes: 188 additions & 0 deletions api/src/tests/account_abstraction_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright © Aptos Foundation
// Parts of the project are originally copyright © Meta Platforms, Inc.
// SPDX-License-Identifier: Apache-2.0

use super::new_test_context;
use aptos_api_test_context::{current_function_name, TestContext};
use aptos_crypto::{
bls12381::{PrivateKey, PublicKey},
test_utils::KeyPair,
SigningKey, Uniform,
};
use aptos_types::{
function_info::FunctionInfo,
transaction::{EntryFunction, TransactionStatus},
};
use move_core_types::{identifier::Identifier, language_storage::ModuleId, vm_status::StatusCode};
use rand::rngs::OsRng;
use serde_json::json;
use std::{path::PathBuf, sync::Arc};

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_account_abstraction_single_signer() {
let key_pair = Arc::new(KeyPair::<PrivateKey, PublicKey>::generate(&mut OsRng));

let mut context = new_test_context(current_function_name!());
let mut account = context.create_account().await;
let user_addr = account.address();
let other = context.create_account().await;

// Publish packages
let named_addresses = vec![("aa".to_string(), user_addr)];
let txn = futures::executor::block_on(async move {
let path = PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
.join("../aptos-move/move-examples/account_abstraction/bls12381_single_key");
TestContext::build_package(path, named_addresses)
});
context.publish_package(&mut account, txn).await;

let txn0 = context.mint_user_account(&account).await;
context.commit_block(&vec![txn0]).await;

context
.api_execute_entry_function(
&mut account,
&format!("0x{}::single_key::update_public_key", user_addr.to_hex()),
json!([]),
json!([hex::encode(key_pair.public_key.to_bytes())]),
)
.await;

let func_info = FunctionInfo::new(
user_addr,
"single_key".to_string(),
"authenticate".to_string(),
);
let txn3 = context
.add_dispatchable_authentication_function(&account, func_info.clone())
.await;
context.commit_block(&vec![txn3]).await;

let factory = context.transaction_factory();

let fake_sign = Arc::new(|_: &[u8]| b"invalid_signature".to_vec());
// case 1: invalid aa signature
account.set_abstraction_auth(func_info.clone(), fake_sign);
let aa_txn = account.sign_aa_transaction_with_transaction_builder(
vec![],
None,
factory
.account_transfer(other.address(), 1)
.expiration_timestamp_secs(u64::MAX),
);

let txn_status = context.try_commit_block(&vec![aa_txn]).await;
assert!(matches!(
txn_status.last(),
Some(TransactionStatus::Discard(StatusCode::ABORTED))
));
// decrement seq num for aborted txn.
account.decrement_sequence_number();

// case 2: successful AA txn.
let sign_func = Arc::new(move |x: &[u8]| {
key_pair
.private_key
.sign_arbitrary_message(x)
.to_bytes()
.to_vec()
});
account.set_abstraction_auth(func_info.clone(), sign_func);
let balance_start = context.get_apt_balance(other.address()).await;
let aa_txn = account.sign_aa_transaction_with_transaction_builder(
vec![],
None,
factory
.account_transfer(other.address(), 4)
.expiration_timestamp_secs(u64::MAX),
);
context
.expect_status_code(202)
.post_bcs_txn("/transactions", bcs::to_bytes(&aa_txn).unwrap())
.await;
context.commit_mempool_txns(1).await;
assert_eq!(
balance_start + 4,
context.get_apt_balance(other.address()).await
);
}

/// This tests a function with params (signer_a, signer_b, signer_c, d) works for the AA authentication flow.
/// a, c are AA; b, d are normal ed25519 accounts.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_account_abstraction_multi_agent_with_abstracted_sender() {
let key_pair = Arc::new(KeyPair::<PrivateKey, PublicKey>::generate(&mut OsRng));
let mut context = new_test_context(current_function_name!());
let mut a = context.create_account().await;
let b = context.create_account().await;
let mut c = context.create_account().await;
let d = context.create_account().await;
let a_addr = a.address();

// Publish packages
let named_addresses = vec![("aa".to_string(), a_addr)];
let txn = futures::executor::block_on(async move {
let path = PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
.join("../aptos-move/move-examples/account_abstraction/bls12381_single_key");
TestContext::build_package(path, named_addresses)
});
context.publish_package(&mut a, txn).await;

context
.commit_block(&vec![context.mint_user_account(&a).await])
.await;
context
.commit_block(&vec![context.mint_user_account(&b).await])
.await;
context
.commit_block(&vec![context.mint_user_account(&c).await])
.await;

// Convert c to aa
context
.api_execute_entry_function(
&mut c,
&format!("0x{}::single_key::update_public_key", a_addr.to_hex()),
json!([]),
json!([hex::encode(key_pair.public_key.to_bytes())]),
)
.await;
let func_info = FunctionInfo::new(a_addr, "single_key".to_string(), "authenticate".to_string());
let txn = context
.add_dispatchable_authentication_function(&c, func_info.clone())
.await;
context.commit_block(&vec![txn]).await;

let sign_func = Arc::new(move |x: &[u8]| {
key_pair
.private_key
.sign_arbitrary_message(x)
.to_bytes()
.to_vec()
});
c.set_abstraction_auth(func_info, sign_func);

let factory = context.transaction_factory();
let balance_start = context.get_apt_balance(d.address()).await;
let aa_txn = a.sign_aa_transaction_with_transaction_builder(
vec![&b, &c],
None,
factory
.entry_function(EntryFunction::new(
ModuleId::new(a_addr, Identifier::new("test_functions").unwrap()),
Identifier::new("transfer_to_the_last").unwrap(),
vec![],
vec![bcs::to_bytes(&d.address()).unwrap()],
))
.expiration_timestamp_secs(u64::MAX),
);
context
.expect_status_code(202)
.post_bcs_txn("/transactions", bcs::to_bytes(&aa_txn).unwrap())
.await;
context.commit_mempool_txns(1).await;
assert_eq!(
balance_start + 3,
context.get_apt_balance(d.address()).await
);
}
1 change: 1 addition & 0 deletions api/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Parts of the project are originally copyright © Meta Platforms, Inc.
// SPDX-License-Identifier: Apache-2.0

mod account_abstraction_test;
mod accounts_test;
mod blocks_test;
mod converter_test;
Expand Down
Loading

0 comments on commit 1d8460a

Please sign in to comment.