Skip to content

Commit

Permalink
[AN-Issue-1394] Added AptosVMViewer wrapper on AptosVM for continous …
Browse files Browse the repository at this point in the history
…view function execution

- AptosVMViewer provides means to continously execute view functions on
the same state-view without recreating VM over and over again compared
to AptosVM. It provides better about 7-8 times better preformance
compared to AptosVM. This will help to retrieve automation
task inforamtion in more optimal way.

- Added Rust variant of AutomationTaskMetaData and provided means to
build AutomatedTransactions based on AutomationTaskMetaData

- Enabled aptos-types unit-tests in CI flow. Ignored for the time being
the tests which are currently failing.

- Added tests for newly introduced APIs/fnctionalities
  • Loading branch information
Aregnaz Harutyunyan committed Dec 20, 2024
1 parent ace749e commit 467724b
Show file tree
Hide file tree
Showing 12 changed files with 768 additions and 10 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/aptos-framework-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v42


- name: Run aptos-types-unit-tests
run: |
${{ env.CARGO_BIN }} test --release -p aptos-types --lib unit_tests --no-fail-fast
- name: Run supra-framework
run: |
${{ env.CARGO_BIN }} test --release -p aptos-framework -- --skip prover
Expand Down
4 changes: 2 additions & 2 deletions aptos-move/aptos-vm/src/aptos_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2354,14 +2354,14 @@ impl AptosVM {
}
}

fn gas_used(max_gas_amount: Gas, gas_meter: &impl AptosGasMeter) -> u64 {
pub(crate) fn gas_used(max_gas_amount: Gas, gas_meter: &impl AptosGasMeter) -> u64 {
max_gas_amount
.checked_sub(gas_meter.balance())
.expect("Balance should always be less than or equal to max gas amount")
.into()
}

fn execute_view_function_in_vm(
pub(crate) fn execute_view_function_in_vm(
session: &mut SessionExt,
vm: &AptosVM,
module_id: ModuleId,
Expand Down
88 changes: 88 additions & 0 deletions aptos-move/aptos-vm/src/aptos_vm_viewer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) 2024 Supra.
// SPDX-License-Identifier: Apache-2.0

use aptos_types::state_store::StateView;
use aptos_types::transaction::{ViewFunction, ViewFunctionOutput};
use aptos_vm_logging::log_schema::AdapterLogSchema;
use crate::aptos_vm::get_or_vm_startup_failure;
use crate::AptosVM;
use crate::gas::{make_prod_gas_meter, ProdGasMeter};
use crate::move_vm_ext::SessionId::Void;

/// Move VM with only view function API.
/// Convenient to use when more than one view function needs to be executed on the same state-view,
/// as it avoids to set up AptosVM upon each function execution.
pub struct AptosVMViewer<'t, SV: StateView> {
vm: AptosVM,
state_view: &'t SV,
log_context: AdapterLogSchema,
}

impl <'t, SV: StateView> AptosVMViewer<'t, SV> {
/// Creates a new VM instance, initializing the runtime environment from the state.
pub fn new(state_view: &'t SV) -> Self {
let vm = AptosVM::new(state_view);
let log_context = AdapterLogSchema::new(state_view.id(), 0);
Self {
vm ,
state_view,
log_context
}
}

fn create_gas_meter(&self, max_gas_amount: u64) -> anyhow::Result<ProdGasMeter> {
let vm_gas_params = match get_or_vm_startup_failure(&self.vm.gas_params_internal(), &self.log_context) {
Ok(gas_params) => gas_params.vm.clone(),
Err(err) => {
return Err(anyhow::Error::msg(format!("{}", err)))
},
};
let storage_gas_params =
match get_or_vm_startup_failure(&self.vm.storage_gas_params, &self.log_context) {
Ok(gas_params) => gas_params.clone(),
Err(err) => {
return Err(anyhow::Error::msg(format!("{}", err)))
},
};

let gas_meter = make_prod_gas_meter(
self.vm.gas_feature_version,
vm_gas_params,
storage_gas_params,
/* is_approved_gov_script */ false,
max_gas_amount.into(),
);
Ok(gas_meter)
}

pub fn execute_view_function(
&self,
function: ViewFunction,
max_gas_amount: u64,
) -> ViewFunctionOutput {


let resolver = self.vm.as_move_resolver(self.state_view);
let mut session = self.vm.new_session(&resolver, Void, None);
let mut gas_meter = match self.create_gas_meter(max_gas_amount) {
Ok(meter) => meter,
Err(e) => return ViewFunctionOutput::new(Err(e), 0)
};
let (module_id, func_name, type_args, arguments) = function.into_inner();

let execution_result = AptosVM::execute_view_function_in_vm(
&mut session,
&self.vm,
module_id,
func_name,
type_args,
arguments,
&mut gas_meter,
);
let gas_used = AptosVM::gas_used(max_gas_amount.into(), &gas_meter);
match execution_result {
Ok(result) => ViewFunctionOutput::new(Ok(result), gas_used),
Err(e) => ViewFunctionOutput::new(Err(e), gas_used),
}
}
}
1 change: 1 addition & 0 deletions aptos-move/aptos-vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ mod transaction_validation;
pub mod validator_txns;
pub mod verifier;
mod automated_transaction_processor;
pub mod aptos_vm_viewer;

pub use crate::aptos_vm::{AptosSimulationVM, AptosVM};
use crate::sharded_block_executor::{executor_client::ExecutorClient, ShardedBlockExecutor};
Expand Down
1 change: 1 addition & 0 deletions aptos-move/e2e-testsuite/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ mod transaction_fuzzer;
mod verify_txn;
mod automation_registration;
mod automated_transactions;
mod vm_viewer;
108 changes: 108 additions & 0 deletions aptos-move/e2e-testsuite/src/tests/vm_viewer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) 2024 Supra.
// SPDX-License-Identifier: Apache-2.0

use aptos_language_e2e_tests::executor::FakeExecutor;
use aptos_types::move_utils::MemberId;
use aptos_types::transaction::{ViewFunction, ViewFunctionOutput};
use aptos_vm::aptos_vm_viewer::AptosVMViewer;
use move_core_types::language_storage::TypeTag;
use std::time::Instant;

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

fn to_view_function(fn_ref: MemberId, ty_args: Vec<TypeTag>, args: Vec<Vec<u8>>) -> ViewFunction {
ViewFunction::new(fn_ref.module_id, fn_ref.member_id, ty_args, args)
}

fn extract_view_output(output: ViewFunctionOutput) -> Vec<u8> {
output.values.unwrap().pop().unwrap()
}
#[test]
fn test_vm_viewer() {
let mut test_executor = FakeExecutor::from_head_genesis();
let timestamp_now_ref: MemberId = str::parse(TIMESTAMP_NOW_SECONDS).unwrap();
let account_seq_ref: MemberId = str::parse(ACCOUNT_SEQ_NUM).unwrap();
let account_balance_ref: MemberId = str::parse(ACCOUNT_BALANCE).unwrap();
let supra_coin_ty_tag: TypeTag = str::parse(SUPRA_COIN).unwrap();

// Prepare 5 accounts with different balance
let accounts = (1..5)
.map(|i| {
let account = test_executor.create_raw_account_data(100 * i, i);
test_executor.add_account_data(&account);
account
})
.collect::<Vec<_>>();
// Query account seq number and balance using direct AptosVM one-time interface
let one_time_ifc_time = Instant::now();
let expected_results = accounts
.iter()
.map(|account| {
let time = Instant::now();
let timestamp = extract_view_output(test_executor.execute_view_function(
timestamp_now_ref.clone(),
vec![],
vec![],
));
println!("AptosVM step: {}", time.elapsed().as_secs_f64());
let time = Instant::now();
let address_arg = account.address().to_vec();
let account_balance = extract_view_output(test_executor.execute_view_function(
account_balance_ref.clone(),
vec![supra_coin_ty_tag.clone()],
vec![address_arg.clone()],
));
println!("AptosVM step: {}", time.elapsed().as_secs_f64());
let time = Instant::now();
let account_seq_num = extract_view_output(test_executor.execute_view_function(
account_seq_ref.clone(),
vec![],
vec![address_arg],
));
println!("AptosVM step: {}", time.elapsed().as_secs_f64());
(timestamp, account_seq_num, account_balance)
})
.collect::<Vec<_>>();
let one_time_ifc_time = one_time_ifc_time.elapsed().as_secs_f64();

// Now do the same with AptosVMViewer interface
let viewer_ifc_time = Instant::now();
let time = Instant::now();
let vm_viewer = AptosVMViewer::new(test_executor.data_store());
println!("AptosVMViewer creation time: {}", time.elapsed().as_secs_f64());
let actual_results = accounts
.iter()
.map(|account| {
let time = Instant::now();
let timestamp = extract_view_output(vm_viewer.execute_view_function(
to_view_function(timestamp_now_ref.clone(), vec![], vec![]),
u64::MAX,
));
println!("AptosVMViewer step: {}", time.elapsed().as_secs_f64());
let time = Instant::now();
let address_arg = account.address().to_vec();
let account_balance = extract_view_output(vm_viewer.execute_view_function(
to_view_function(
account_balance_ref.clone(),
vec![supra_coin_ty_tag.clone()],
vec![address_arg.clone()],
),
u64::MAX,
));
println!("AptosVMViewer step: {}", time.elapsed().as_secs_f64());
let time = Instant::now();
let account_seq_num = extract_view_output(vm_viewer.execute_view_function(
to_view_function(account_seq_ref.clone(), vec![], vec![address_arg]),
u64::MAX,
));
println!("AptosVMViewer step: {}", time.elapsed().as_secs_f64());
(timestamp, account_seq_num, account_balance)
})
.collect::<Vec<_>>();
let viewer_ifc_time = viewer_ifc_time.elapsed().as_secs_f64();
assert_eq!(actual_results, expected_results);
println!("AptosVM: {one_time_ifc_time} - AptosVMViewer: {viewer_ifc_time}")
}
Loading

0 comments on commit 467724b

Please sign in to comment.