Skip to content
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

Aptos - Account Abstraction #15219

Merged
merged 10 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading