Skip to content

Commit

Permalink
Account Abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
lightmark committed Nov 6, 2024
1 parent eae00b5 commit 502e9cb
Show file tree
Hide file tree
Showing 86 changed files with 6,394 additions and 3,548 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.

196 changes: 196 additions & 0 deletions api/src/tests/account_abstraction_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// 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;
use std::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();

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);

// case 2: successful AA txn.
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 a and c to aa
context
.api_execute_entry_function(
&mut a,
&format!("0x{}::single_key::update_public_key", a_addr.to_hex()),
json!([]),
json!([hex::encode(key_pair.public_key.to_bytes())]),
)
.await;
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 txn1 = context
.add_dispatchable_authentication_function(&a, func_info.clone())
.await;
let txn2 = context
.add_dispatchable_authentication_function(&c, func_info.clone())
.await;
context.commit_block(&vec![txn1, txn2]).await;

let sign_func = Arc::new(move |x: &[u8]| key_pair.private_key.sign_arbitrary_message(x).to_bytes().to_vec());
a.set_abstraction_auth(
func_info.clone(),
sign_func.clone(),
);
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
59 changes: 42 additions & 17 deletions api/test-context/src/test_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use aptos_types::{
block_metadata::BlockMetadata,
chain_id::ChainId,
indexer::indexer_db_reader::IndexerReader,
function_info::FunctionInfo,
ledger_info::{LedgerInfo, LedgerInfoWithSignatures},
transaction::{
signature_verified_transaction::into_signature_verified_block, Transaction,
Expand Down Expand Up @@ -410,6 +411,19 @@ impl TestContext {
)
}

pub async fn add_dispatchable_authentication_function(
&self,
account: &LocalAccount,
func: FunctionInfo,
) -> SignedTransaction {
let factory = self.transaction_factory();
account.sign_with_transaction_builder(
factory
.add_dispatchable_authentication_function(func)
.expiration_timestamp_secs(u64::MAX),
)
}

pub async fn execute_multisig_transaction(
&mut self,
owner: &mut LocalAccount,
Expand Down Expand Up @@ -675,7 +689,10 @@ impl TestContext {
}
}

pub async fn commit_block(&mut self, signed_txns: &[SignedTransaction]) {
pub async fn try_commit_block(
&mut self,
signed_txns: &[SignedTransaction],
) -> Vec<TransactionStatus> {
let metadata = self.new_block_metadata();
let timestamp = metadata.timestamp_usecs();
let txns: Vec<Transaction> = std::iter::once(Transaction::BlockMetadata(metadata.clone()))
Expand All @@ -699,28 +716,36 @@ impl TestContext {
.unwrap();
let compute_status = result.compute_status_for_input_txns().clone();
assert_eq!(compute_status.len(), txns.len(), "{:?}", result);
// But the rest of the txns must be Kept.
for st in compute_status {
if !compute_status
.iter()
.any(|s| !matches!(&s, TransactionStatus::Keep(_)))
{
self.executor
.commit_blocks(
vec![metadata.id()],
// StateCheckpoint/BlockEpilogue is added on top of the input transactions.
self.new_ledger_info(&metadata, result.root_hash(), txns.len() + 1),
)
.unwrap();

self.mempool
.mempool_notifier
.notify_new_commit(txns, timestamp)
.await
.unwrap();
}
compute_status
}

pub async fn commit_block(&mut self, signed_txns: &[SignedTransaction]) {
// The txns must be kept.
for st in self.try_commit_block(signed_txns).await {
match st {
TransactionStatus::Discard(st) => panic!("transaction is discarded: {:?}", st),
TransactionStatus::Retry => panic!("should not retry"),
TransactionStatus::Keep(_) => (),
}
}

self.executor
.commit_blocks(
vec![metadata.id()],
// StateCheckpoint/BlockEpilogue is added on top of the input transactions.
self.new_ledger_info(&metadata, result.root_hash(), txns.len() + 1),
)
.unwrap();

self.mempool
.mempool_notifier
.notify_new_commit(txns, timestamp)
.await
.unwrap();
}

pub async fn get_sequence_number(&self, account: AccountAddress) -> u64 {
Expand Down
22 changes: 11 additions & 11 deletions api/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ pub use state::RawStateValueRequest;
use std::str::FromStr;
pub use table::{RawTableItemRequest, TableItemRequest};
pub use transaction::{
AccountSignature, BlockMetadataTransaction, DeleteModule, DeleteResource, DeleteTableItem,
DirectWriteSet, Ed25519Signature, EncodeSubmissionRequest, EntryFunctionPayload, Event,
FeePayerSignature, GasEstimation, GasEstimationBcs, GenesisPayload, GenesisTransaction,
MultiAgentSignature, MultiEd25519Signature, MultiKeySignature, MultisigPayload,
MultisigTransactionPayload, NoAccountSignature, PendingTransaction, PublicKey, ScriptPayload,
ScriptWriteSet, Signature, SingleKeySignature, SubmitTransactionRequest, Transaction,
TransactionData, TransactionId, TransactionInfo, TransactionOnChainData, TransactionPayload,
TransactionSignature, TransactionSigningMessage, TransactionsBatchSingleSubmissionFailure,
TransactionsBatchSubmissionResult, UserCreateSigningMessageRequest, UserTransaction,
UserTransactionRequest, VersionedEvent, WriteModule, WriteResource, WriteSet, WriteSetChange,
WriteSetPayload, WriteTableItem,
AbstractionSignature, AccountSignature, BlockMetadataTransaction, DeleteModule, DeleteResource,
DeleteTableItem, DirectWriteSet, Ed25519Signature, EncodeSubmissionRequest,
EntryFunctionPayload, Event, FeePayerSignature, GasEstimation, GasEstimationBcs,
GenesisPayload, GenesisTransaction, MultiAgentSignature, MultiEd25519Signature,
MultiKeySignature, MultisigPayload, MultisigTransactionPayload, NoAccountSignature,
PendingTransaction, PublicKey, ScriptPayload, ScriptWriteSet, Signature, SingleKeySignature,
SubmitTransactionRequest, Transaction, TransactionData, TransactionId, TransactionInfo,
TransactionOnChainData, TransactionPayload, TransactionSignature, TransactionSigningMessage,
TransactionsBatchSingleSubmissionFailure, TransactionsBatchSubmissionResult,
UserCreateSigningMessageRequest, UserTransaction, UserTransactionRequest, VersionedEvent,
WriteModule, WriteResource, WriteSet, WriteSetChange, WriteSetPayload, WriteTableItem,
};
pub use view::{ViewFunction, ViewRequest};
pub use wrappers::{EventGuid, IdentifierWrapper, StateKeyWrapper};
Expand Down
Loading

0 comments on commit 502e9cb

Please sign in to comment.