Skip to content

Commit

Permalink
Merge pull request #146 from Entropy-Foundation/task/issue-1394
Browse files Browse the repository at this point in the history
[AN-Issue-1394] Added AptosVMViewer wrapper on AptosVM for continuous view function execution
  • Loading branch information
aregng authored Dec 31, 2024
2 parents 70f4027 + a0ff2c2 commit cc9e1e2
Showing 14 changed files with 793 additions and 14 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/aptos-framework-test.yaml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions aptos-move/aptos-vm/src/aptos_vm.rs
Original file line number Diff line number Diff line change
@@ -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,
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
@@ -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};
2 changes: 1 addition & 1 deletion aptos-move/aptos-vm/src/transaction_metadata.rs
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ impl TransactionMetadata {
.map(|res| !res.is_empty())
.unwrap_or(false),
payload_type_reference,
txn_app_hash: HashValue::sha3_256_of(
txn_app_hash: HashValue::keccak_256_of(
&bcs::to_bytes(&txn).expect("Unable to serialize SignedTransaction"),
)
.to_vec(),
1 change: 1 addition & 0 deletions aptos-move/e2e-testsuite/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -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}")
}
13 changes: 11 additions & 2 deletions crates/aptos-crypto/src/hash.rs
Original file line number Diff line number Diff line change
@@ -113,7 +113,7 @@ use std::{
fmt,
str::FromStr,
};
use tiny_keccak::{Hasher, Sha3};
use tiny_keccak::{Hasher, Sha3, Keccak};

/// A prefix used to begin the salt of every hashable structure. The salt
/// consists in this global prefix, concatenated with the specified
@@ -182,6 +182,15 @@ impl HashValue {
HashValue::from_keccak(sha3)
}

/// Convenience function that computes a `HashValue` internally equal to
/// the keccak_256 of a byte buffer. It will handle hasher creation, data
/// feeding and finalization.
pub fn keccak_256_of(buffer: &[u8]) -> Self {
let mut keccak = Keccak::v256();
keccak.update(buffer);
HashValue::from_keccak(keccak)
}

#[cfg(test)]
pub fn from_iter_sha3<'a, I>(buffers: I) -> Self
where
@@ -198,7 +207,7 @@ impl HashValue {
&mut self.hash[..]
}

fn from_keccak(state: Sha3) -> Self {
fn from_keccak(state: impl Hasher) -> Self {
let mut hash = Self::zero();
state.finalize(hash.as_ref_mut());
hash
Loading

0 comments on commit cc9e1e2

Please sign in to comment.