Skip to content

Commit

Permalink
[AN-Issue-1432] Updated AutomatedTransactionBuilder with expiration t…
Browse files Browse the repository at this point in the history
…ime threshold value (#162)

* [AN-Issue-1432] Updated AutomatedTransactionBuilder with expiration time threshold value

* Introduced getter interfaces for AutomationTaskMetaData properties

* Updated autoamted txn builder and automation task meta data

- Added getter interface for AutomatedTransactionBuilder properties
- Updated AutomationTaskMetaData rust struct to match the one in Move
  - Updated e2e-tests to detect incorrespondance in the same types in move and rust layer

* Fixed test failure

---------

Co-authored-by: Aregnaz Harutyunyan <>
  • Loading branch information
aregng authored Jan 23, 2025
1 parent 76f21c7 commit aaf2118
Show file tree
Hide file tree
Showing 10 changed files with 1,370 additions and 309 deletions.
83 changes: 43 additions & 40 deletions aptos-move/e2e-testsuite/src/tests/automated_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
// SPDX-License-Identifier: Apache-2.0

use crate::tests::automation_registration::AutomationRegistrationTestContext;
use aptos_cached_packages::{aptos_framework_sdk_builder};
use aptos_cached_packages::aptos_framework_sdk_builder;
use aptos_crypto::HashValue;
use aptos_types::transaction::automated_transaction::AutomatedTransaction;
use aptos_types::chain_id::ChainId;
use aptos_types::transaction::automated_transaction::{
AutomatedTransaction, AutomatedTransactionBuilder, BuilderResult,
};
use aptos_types::transaction::{ExecutionStatus, Transaction, TransactionStatus};
use aptos_vm::transaction_metadata::TransactionMetadata;
use move_core_types::vm_status::StatusCode;

#[test]
Expand Down Expand Up @@ -65,7 +67,8 @@ fn check_expired_automated_transaction() {
fn check_automated_transaction_with_insufficient_balance() {
let mut test_context = AutomationRegistrationTestContext::new();
let dest_account = test_context.new_account_data(0, 0);
let payload = aptos_framework_sdk_builder::supra_account_transfer(dest_account.address().clone(), 100);
let payload =
aptos_framework_sdk_builder::supra_account_transfer(dest_account.address().clone(), 100);
let sequence_number = 0;

let raw_transaction = test_context
Expand Down Expand Up @@ -93,7 +96,8 @@ fn check_automated_transaction_with_insufficient_balance() {
fn check_automated_transaction_successful_execution() {
let mut test_context = AutomationRegistrationTestContext::new();
let dest_account = test_context.new_account_data(1_000_000, 0);
let payload = aptos_framework_sdk_builder::supra_account_transfer(dest_account.address().clone(), 100);
let payload =
aptos_framework_sdk_builder::supra_account_transfer(dest_account.address().clone(), 100);
let gas_price = 100;
let max_gas_amount = 100;

Expand All @@ -114,24 +118,23 @@ fn check_automated_transaction_successful_execution() {
&TransactionStatus::Keep(ExecutionStatus::Success),
"{output:?}"
);

// For now no active transaction available, so this task execution will fail on prologue.
let sequence_number = 0;
let raw_transaction = test_context
.sender_account_data()
.account()
.transaction()
.payload(payload.clone())
.sequence_number(sequence_number)
.gas_unit_price(gas_price)
.max_gas_amount(max_gas_amount)
.ttl(expiration_time)
.raw();

let parent_hash = HashValue::new([42; HashValue::LENGTH]);
let automated_txn = AutomatedTransaction::new(raw_transaction.clone(), parent_hash, 1);
let result =
test_context.execute_tagged_transaction(Transaction::AutomatedTransaction(automated_txn.clone()));
let next_task_index = test_context.get_next_task_index_from_registry();
assert_eq!(next_task_index, 1);
let automated_task_details = test_context.get_task_details(next_task_index - 1);
let automated_txn_builder = AutomatedTransactionBuilder::try_from(automated_task_details)
.expect("Successful builder creation");
let maybe_automated_txn = automated_txn_builder
.clone()
.with_chain_id(ChainId::test())
.with_block_height(1)
.with_gas_unit_price(gas_price)
.build();
let BuilderResult::Success(automated_txn) = maybe_automated_txn else {
panic!("Automated transaction should successfully build")
};

let result = test_context
.execute_tagged_transaction(Transaction::AutomatedTransaction(automated_txn.clone()));
AutomationRegistrationTestContext::check_discarded_output(
result,
StatusCode::NO_ACTIVE_AUTOMATED_TASK,
Expand All @@ -143,8 +146,8 @@ fn check_automated_transaction_successful_execution() {
// Execute automated transaction one more time which should be success, as task is already become active after epoch change
let sender_address = test_context.sender_account_address();
let sender_seq_num = test_context.account_sequence_number(sender_address);
let output =
test_context.execute_and_apply_transaction(Transaction::AutomatedTransaction(automated_txn.clone()));
let output = test_context
.execute_and_apply_transaction(Transaction::AutomatedTransaction(automated_txn.clone()));
assert_eq!(
output.status(),
&TransactionStatus::Keep(ExecutionStatus::Success),
Expand All @@ -153,23 +156,23 @@ fn check_automated_transaction_successful_execution() {
let dest_account_balance = test_context.account_balance(dest_account.address().clone());
assert_eq!(dest_account_balance, 1_000_100);
// check that sequence number is not updated.
assert_eq!(sender_seq_num, test_context.account_sequence_number(sender_address));
assert_eq!(
sender_seq_num,
test_context.account_sequence_number(sender_address)
);

// try to submit automated transaction with incorrect sender
let raw_transaction = dest_account
.account()
.transaction()
.payload(payload)
.sequence_number(sequence_number)
.gas_unit_price(gas_price)
.max_gas_amount(max_gas_amount)
.ttl(expiration_time)
.raw();

let parent_hash = HashValue::new([42; HashValue::LENGTH]);
let automated_txn = AutomatedTransaction::new(raw_transaction, parent_hash, 1);
let result =
test_context.execute_tagged_transaction(Transaction::AutomatedTransaction(automated_txn.clone()));
let maybe_automated_txn = automated_txn_builder
.with_sender(*dest_account.address())
.with_chain_id(ChainId::test())
.with_block_height(1)
.with_gas_unit_price(gas_price)
.build();
let BuilderResult::Success(automated_txn) = maybe_automated_txn else {
panic!("Automated transaction should successfully build")
};
let result = test_context
.execute_tagged_transaction(Transaction::AutomatedTransaction(automated_txn.clone()));
AutomationRegistrationTestContext::check_discarded_output(
result,
StatusCode::NO_ACTIVE_AUTOMATED_TASK,
Expand Down
70 changes: 44 additions & 26 deletions aptos-move/e2e-testsuite/src/tests/automation_registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
use aptos_cached_packages::aptos_framework_sdk_builder;
use aptos_language_e2e_tests::account::{Account, AccountData};
use aptos_language_e2e_tests::executor::FakeExecutor;
use aptos_types::transaction::automation::RegistrationParams;
use aptos_types::transaction::automation::{AutomationTaskMetaData, RegistrationParams};
use aptos_types::transaction::{
EntryFunction, ExecutionStatus, SignedTransaction, TransactionOutput, TransactionPayload,
TransactionStatus,
};
use move_core_types::account_address::AccountAddress;
use move_core_types::value::MoveValue;
use move_core_types::vm_status::StatusCode;
use std::ops::{Deref, DerefMut};
use move_core_types::account_address::AccountAddress;

const TIMESTAMP_NOW_SECONDS: &str = "0x1::timestamp::now_seconds";
const ACCOUNT_BALANCE: &str = "0x1::coin::balance";
const SUPRA_COIN: &str = "0x1::supra_coin::SupraCoin";
const ACCOUNT_SEQ_NUM: &str = "0x1::account::get_sequence_number";
const AUTOMATION_NEXT_TASK_ID: &str = "0x1::automation_registry::get_next_task_index";
const AUTOMATION_TASK_DETAILS: &str = "0x1::automation_registry::get_task_details";

pub(crate) struct AutomationRegistrationTestContext {
executor: FakeExecutor,
Expand Down Expand Up @@ -75,7 +77,10 @@ impl AutomationRegistrationTestContext {
.sign()
}

pub(crate) fn check_miscellaneous_output(output: TransactionOutput, expected_status_code: StatusCode) {
pub(crate) fn check_miscellaneous_output(
output: TransactionOutput,
expected_status_code: StatusCode,
) {
match output.status() {
TransactionStatus::Keep(ExecutionStatus::MiscellaneousError(maybe_status_code)) => {
assert_eq!(
Expand All @@ -87,31 +92,27 @@ impl AutomationRegistrationTestContext {
_ => panic!("Unexpected transaction status: {output:?}"),
}
}
pub(crate) fn check_discarded_output(output: TransactionOutput, expected_status_code: StatusCode) {
pub(crate) fn check_discarded_output(
output: TransactionOutput,
expected_status_code: StatusCode,
) {
match output.status() {
TransactionStatus::Discard(status_code ) => {
assert_eq!(
status_code,
&expected_status_code,
"{output:?}"
);
TransactionStatus::Discard(status_code) => {
assert_eq!(status_code, &expected_status_code, "{output:?}");
},
_ => panic!("Unexpected transaction status: {output:?}"),
}
}

pub (crate) fn chain_time_now(&mut self) -> u64 {
let view_output = self.execute_view_function(
str::parse(TIMESTAMP_NOW_SECONDS).unwrap(),
vec![],
vec![],
);
pub(crate) fn chain_time_now(&mut self) -> u64 {
let view_output =
self.execute_view_function(str::parse(TIMESTAMP_NOW_SECONDS).unwrap(), vec![], vec![]);
let result = view_output.values.expect("Valid result");
assert_eq!(result.len(), 1);
bcs::from_bytes::<u64>(&result[0]).unwrap()
}

pub(crate) fn advance_chain_time_in_secs(&mut self, secs: u64) {
pub(crate) fn advance_chain_time_in_secs(&mut self, secs: u64) {
self.set_block_time(secs * 1_000_000);
self.new_block()
}
Expand All @@ -125,8 +126,8 @@ impl AutomationRegistrationTestContext {
let result = view_output.values.expect("Valid result");
assert_eq!(result.len(), 1);
bcs::from_bytes::<u64>(&result[0]).unwrap()

}

pub(crate) fn account_sequence_number(&mut self, account_address: AccountAddress) -> u64 {
let view_output = self.execute_view_function(
str::parse(ACCOUNT_SEQ_NUM).unwrap(),
Expand All @@ -136,7 +137,31 @@ impl AutomationRegistrationTestContext {
let result = view_output.values.expect("Valid result");
assert_eq!(result.len(), 1);
bcs::from_bytes::<u64>(&result[0]).unwrap()
}

pub(crate) fn get_next_task_index_from_registry(&mut self) -> u64 {
let view_output = self.execute_view_function(
str::parse(AUTOMATION_NEXT_TASK_ID).unwrap(),
vec![],
vec![],
);
let result = view_output.values.expect("Valid result");
assert_eq!(result.len(), 1);
bcs::from_bytes::<u64>(&result[0]).unwrap()
}

pub(crate) fn get_task_details(&mut self, index: u64) -> AutomationTaskMetaData {
let view_output = self.execute_view_function(
str::parse(AUTOMATION_TASK_DETAILS).unwrap(),
vec![],
vec![MoveValue::U64(index)
.simple_serialize()
.expect("Successful serialization")],
);
let result = view_output.values.expect("Valid result");
assert!(!result.is_empty());
bcs::from_bytes::<AutomationTaskMetaData>(&result[0])
.expect("Successful deserialization of AutomationTaskMetaData")
}
}

Expand Down Expand Up @@ -182,14 +207,7 @@ fn check_successful_registration() {
);

// Check automation registry state.
let view_output = test_context.execute_view_function(
str::parse(AUTOMATION_NEXT_TASK_ID).unwrap(),
vec![],
vec![],
);
let result = view_output.values.expect("Valid result");
assert_eq!(result.len(), 1);
let next_task_id = bcs::from_bytes::<u64>(&result[0]).unwrap();
let next_task_id = test_context.get_next_task_index_from_registry();
assert_eq!(next_task_id, 1);
let sender_seq_num = test_context.account_sequence_number(sender_address);
assert_eq!(sender_seq_num, sender_seq_num_old + 1);
Expand Down
31 changes: 31 additions & 0 deletions aptos-move/framework/aptos-stdlib/doc/fixed_point64.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ a 64-bit fractional part.
- [Function `sub`](#0x1_fixed_point64_sub)
- [Function `add`](#0x1_fixed_point64_add)
- [Function `multiply_u128`](#0x1_fixed_point64_multiply_u128)
- [Function `multiply_u128_return_fixpoint64`](#0x1_fixed_point64_multiply_u128_return_fixpoint64)
- [Function `divide_u128`](#0x1_fixed_point64_divide_u128)
- [Function `create_from_rational`](#0x1_fixed_point64_create_from_rational)
- [Function `create_from_raw_value`](#0x1_fixed_point64_create_from_raw_value)
Expand Down Expand Up @@ -255,6 +256,36 @@ overflows.



</details>

<a id="0x1_fixed_point64_multiply_u128_return_fixpoint64"></a>

## Function `multiply_u128_return_fixpoint64`



<pre><code><b>public</b> <b>fun</b> <a href="fixed_point64.md#0x1_fixed_point64_multiply_u128_return_fixpoint64">multiply_u128_return_fixpoint64</a>(val: u128, multiplier: <a href="fixed_point64.md#0x1_fixed_point64_FixedPoint64">fixed_point64::FixedPoint64</a>): <a href="fixed_point64.md#0x1_fixed_point64_FixedPoint64">fixed_point64::FixedPoint64</a>
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="fixed_point64.md#0x1_fixed_point64_multiply_u128_return_fixpoint64">multiply_u128_return_fixpoint64</a>(val: u128, multiplier: <a href="fixed_point64.md#0x1_fixed_point64_FixedPoint64">FixedPoint64</a>): <a href="fixed_point64.md#0x1_fixed_point64_FixedPoint64">FixedPoint64</a> {
// The product of two 128 bit values <b>has</b> 256 bits, so perform the
// multiplication <b>with</b> u256 types and keep the full 256 bit product
// <b>to</b> avoid losing accuracy.
<b>let</b> unscaled_product = (val <b>as</b> u256) * (multiplier.value <b>as</b> u256);
// Check whether the value is too large.
<b>assert</b>!(unscaled_product &lt;= <a href="fixed_point64.md#0x1_fixed_point64_MAX_U128">MAX_U128</a>, <a href="fixed_point64.md#0x1_fixed_point64_EMULTIPLICATION">EMULTIPLICATION</a>);
<a href="fixed_point64.md#0x1_fixed_point64_create_from_raw_value">create_from_raw_value</a>((unscaled_product <b>as</b> u128))
}
</code></pre>



</details>

<a id="0x1_fixed_point64_divide_u128"></a>
Expand Down
Loading

0 comments on commit aaf2118

Please sign in to comment.