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

[starcoin vm] Add vm_executor and vm_validator #3919

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion executor/src/block_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use starcoin_types::error::ExecutorResult;
use starcoin_types::transaction::TransactionStatus;
use starcoin_types::transaction::{Transaction, TransactionInfo};
use starcoin_vm_runtime::metrics::VMMetrics;
use starcoin_vm_runtime::starcoin_vm::StarcoinVM;
use starcoin_vm_runtime::VMExecutor;
use starcoin_vm_types::contract_event::ContractEvent;

#[derive(Clone, Debug, Eq, PartialEq)]
Expand All @@ -34,7 +36,7 @@ pub fn block_execute<S: ChainStateReader + ChainStateWriter>(
vm_metrics: Option<VMMetrics>,
) -> ExecutorResult<BlockExecutedData> {
let txn_outputs =
crate::execute_block_transactions(chain_state, txns.clone(), block_gas_limit, vm_metrics)
StarcoinVM::execute_block(chain_state, txns.clone(), block_gas_limit, vm_metrics)
.map_err(BlockExecutorError::BlockTransactionExecuteErr)?;

let mut executed_data = BlockExecutedData::default();
Expand Down
3 changes: 2 additions & 1 deletion executor/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use anyhow::Result;
use starcoin_types::transaction::{SignedUserTransaction, Transaction, TransactionOutput};
use starcoin_vm_runtime::metrics::VMMetrics;
use starcoin_vm_runtime::starcoin_vm::StarcoinVM;
use starcoin_vm_runtime::VMValidator;
use starcoin_vm_types::identifier::Identifier;
use starcoin_vm_types::language_storage::{ModuleId, TypeTag};
use starcoin_vm_types::{state_view::StateView, vm_status::VMStatus};
Expand Down Expand Up @@ -52,7 +53,7 @@ pub fn validate_transaction<S: StateView>(
metrics: Option<VMMetrics>,
) -> Option<VMStatus> {
let mut vm = StarcoinVM::new(metrics);
vm.verify_transaction(chain_state, txn)
vm.validate_transaction(txn, chain_state)
}

pub fn execute_readonly_function<S: StateView>(
Expand Down
8 changes: 4 additions & 4 deletions executor/tests/executor_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ fn test_txn_verify_err_case() -> Result<()> {
);

let signed_by_bob = bob.sign_txn(txn);
let verify_result = vm.verify_transaction(&NullStateView, signed_by_bob);
let verify_result = vm.verify_transaction(signed_by_bob, &NullStateView);
assert!(verify_result.is_some());
assert_eq!(
verify_result.unwrap().status_code(),
Expand Down Expand Up @@ -211,7 +211,7 @@ fn test_package_txn() -> Result<()> {
TransactionPayload::Package(package.clone()),
None,
));
let verify_result = vm.verify_transaction(&chain_state, txn);
let verify_result = vm.verify_transaction(txn, &chain_state);
assert!(verify_result.is_none());
// execute the package txn
account_execute_should_success(&alice, &chain_state, TransactionPayload::Package(package))
Expand All @@ -231,7 +231,7 @@ fn test_package_txn() -> Result<()> {
TransactionPayload::Package(package),
None,
));
let verify_result = vm.verify_transaction(&chain_state, txn);
let verify_result = vm.verify_transaction(txn, &chain_state);
assert!(verify_result.is_some());
assert_eq!(
verify_result.unwrap().status_code(),
Expand All @@ -252,7 +252,7 @@ fn test_package_txn() -> Result<()> {
TransactionPayload::Package(package.clone()),
None,
));
let verify_result = vm.verify_transaction(&chain_state, txn);
let verify_result = vm.verify_transaction(txn, &chain_state);
assert!(verify_result.is_none());
// execute the package txn
account_execute_should_success(&alice, &chain_state, TransactionPayload::Package(package))
Expand Down
48 changes: 48 additions & 0 deletions vm/types/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use crate::write_set::WriteOp;
pub use error::CallError;
pub use error::Error as TransactionError;
pub use module::Module;
use move_core_types::vm_status::StatusType;
pub use package::Package;
pub use pending_transaction::{Condition, PendingTransaction};
use schemars::{self, JsonSchema};
Expand Down Expand Up @@ -913,3 +914,50 @@ impl std::fmt::Display for TxStatus {
write!(f, "{}", s)
}
}

/// The result of running the transaction through the VM validator.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VMValidatorResult {
/// Result of the validation: `None` if the transaction was successfully validated
/// or `Some(DiscardedVMStatus)` if the transaction should be discarded.
status: Option<DiscardedVMStatus>,

/// Score for ranking the transaction priority (e.g., based on the gas price).
/// Only used when the status is `None`. Higher values indicate a higher priority.
score: u64,
}

impl VMValidatorResult {
pub fn new(vm_status: Option<DiscardedVMStatus>, score: u64) -> Self {
debug_assert!(
match vm_status {
None => true,
Some(status) =>
status.status_type() == StatusType::Unknown
|| status.status_type() == StatusType::Validation
|| status.status_type() == StatusType::InvariantViolation,
},
"Unexpected discarded status: {:?}",
vm_status
);
Self {
status: vm_status,
score,
}
}

pub fn error(vm_status: DiscardedVMStatus) -> Self {
Self {
status: Some(vm_status),
score: 0,
}
}

pub fn status(&self) -> Option<DiscardedVMStatus> {
self.status
}

pub fn score(&self) -> u64 {
self.score
}
}
38 changes: 38 additions & 0 deletions vm/vm-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,52 @@ pub mod data_cache;
pub mod metrics;
pub mod natives;
pub mod starcoin_vm;

use move_core_types::vm_status::VMStatus;
pub use move_vm_runtime::move_vm;
pub use move_vm_runtime::session;

mod access_path_cache;
mod errors;
pub mod move_vm_ext;

use crate::metrics::VMMetrics;
use anyhow::Result;
use starcoin_vm_types::access_path::AccessPath;
use starcoin_vm_types::account_address::AccountAddress;
use starcoin_vm_types::language_storage::StructTag;
use starcoin_vm_types::state_view::StateView;
use starcoin_vm_types::transaction::{SignedUserTransaction, Transaction, TransactionOutput};

/// This trait describes the VM's validation interfaces.
pub trait VMValidator {
/// Executes the prologue of the Aptos Account and verifies that the transaction is valid.
fn validate_transaction(
&mut self,
transaction: SignedUserTransaction,
state_view: &impl StateView,
) -> Option<VMStatus>;
}

/// This trait describes the VM's execution interface.
pub trait VMExecutor: Send + Sync {
// NOTE: At the moment there are no persistent caches that live past the end of a block (that's
// why execute_block doesn't take &self.)
// There are some cache invalidation issues around transactions publishing code that need to be
// sorted out before that's possible.

/// Executes a block of transactions and returns output for each one of them.
// fn execute_block(
// transactions: Vec<Transaction>,
// state_view: &impl StateView,
// ) -> Result<Vec<TransactionOutput>, VMStatus>;
fn execute_block<S: StateView>(
chain_state: &S,
txns: Vec<Transaction>,
block_gas_limit: u64,
metrics: Option<VMMetrics>,
) -> Result<Vec<TransactionOutput>>;
}

/// Get the AccessPath to a resource stored under `address` with type name `tag`
fn create_access_path(address: AccountAddress, tag: StructTag) -> AccessPath {
Expand Down
95 changes: 87 additions & 8 deletions vm/vm-runtime/src/starcoin_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ use std::sync::Arc;

#[cfg(feature = "metrics")]
use crate::metrics::VMMetrics;
use crate::{VMExecutor, VMValidator};

#[derive(Clone)]
#[allow(clippy::upper_case_acronyms)]
Expand Down Expand Up @@ -394,7 +395,7 @@ impl StarcoinVM {
}

fn verify_transaction_impl<S: StateView>(
&mut self,
&self,
transaction: &SignatureCheckedTransaction,
remote_cache: &StateViewCache<S>,
) -> Result<(), VMStatus> {
Expand Down Expand Up @@ -476,8 +477,8 @@ impl StarcoinVM {

pub fn verify_transaction<S: StateView>(
&mut self,
state_view: &S,
txn: SignedUserTransaction,
state_view: &S,
) -> Option<VMStatus> {
#[cfg(feature = "metrics")]
let _timer = self.metrics.as_ref().map(|metrics| {
Expand All @@ -491,10 +492,12 @@ impl StarcoinVM {
Ok(t) => t,
Err(_) => return Some(VMStatus::Error(StatusCode::INVALID_SIGNATURE)),
};

if let Err(err) = self.load_configs(state_view) {
warn!("Load config error at verify_transaction: {}", err);
return Some(VMStatus::Error(StatusCode::VM_STARTUP_FAILURE));
}

match self.verify_transaction_impl(&signature_verified_txn, &data_cache) {
Ok(_) => None,
Err(err) => {
Expand Down Expand Up @@ -604,7 +607,7 @@ impl StarcoinVM {
.map(|m| m.code().to_vec())
.collect(),
package.package_address(), // be careful with the sender.
gas_meter,
gas_meter,
PublishModuleBundleOption {
force_publish: enforced,
only_new_module,
Expand Down Expand Up @@ -705,11 +708,11 @@ impl StarcoinVM {
return Err(VMStatus::Error(StatusCode::UNREACHABLE));
}
}
.map_err(|e|
{
warn!("[VM] execute_script_function error, status_type: {:?}, status_code:{:?}, message:{:?}, location:{:?}", e.status_type(), e.major_status(), e.message(), e.location());
e.into_vm_status()
})?;
.map_err(|e|
{
warn!("[VM] execute_script_function error, status_type: {:?}, status_code:{:?}, message:{:?}, location:{:?}", e.status_type(), e.major_status(), e.message(), e.location());
e.into_vm_status()
})?;
charge_global_write_gas_usage(gas_meter, &session, &txn_data.sender())?;

self.success_transaction_cleanup(session, gas_meter, txn_data)
Expand Down Expand Up @@ -1493,3 +1496,79 @@ pub fn log_vm_status(
}
}
}

// Executor external API
impl VMExecutor for StarcoinVM {
/// Execute a block of `transactions`. The output vector will have the exact same length as the
/// input vector. The discarded transactions will be marked as `TransactionStatus::Discard` and
/// have an empty `WriteSet`. Also `state_view` is immutable, and does not have interior
/// mutability. Writes to be applied to the data view are encoded in the write set part of a
/// transaction output.
// fn execute_block(
// transactions: Vec<Transaction>,
// state_view: &impl StateView,
// ) -> Result<Vec<TransactionOutput>, VMStatus> {
// fail_point!("move_adapter::execute_block", |_| {
// Err(VMStatus::Error(
// StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
// ))
// });
//
// let concurrency_level = Self::get_concurrency_level();
// if concurrency_level > 1 {
// let (result, _) = crate::parallel_executor::ParallelAptosVM::execute_block(
// transactions,
// state_view,
// concurrency_level,
// )?;
// Ok(result)
// } else {
// let output = Self::execute_block_and_keep_vm_status(transactions, state_view)?;
// Ok(output
// .into_iter()
// .map(|(_vm_status, txn_output)| txn_output)
// .collect())
// }
// }

fn execute_block<S: StateView>(
chain_state: &S,
txns: Vec<Transaction>,
block_gas_limit: u64,
metrics: Option<VMMetrics>,
) -> Result<Vec<TransactionOutput>> {
let mut vm = StarcoinVM::new(metrics);
let result = vm
.execute_block_transactions(&chain_state, txns, Some(block_gas_limit))?
.into_iter()
.map(|(_, output)| {
debug! {"{:?}", output}
output
})
.collect();
Ok(result)
}
}

// VMValidator external API
impl VMValidator for StarcoinVM {
/// Determine if a transaction is valid. Will return `None` if the transaction is accepted,
/// `Some(Err)` if the VM rejects it, with `Err` as an error code. Verification performs the
/// following steps:
/// 1. The signature on the `SignedTransaction` matches the public key included in the
/// transaction
/// 2. The script to be executed is under given specific configuration.
/// 3. Invokes `Account.prologue`, which checks properties such as the transaction has the
/// right sequence number and the sender has enough balance to pay for the gas.
/// TBD:
/// 1. Transaction arguments matches the main function's type signature.
/// We don't check this item for now and would execute the check at execution time.
fn validate_transaction(
&mut self,
transaction: SignedUserTransaction,
state_view: &impl StateView,
) -> Option<VMStatus> {
//validate_signed_transaction(self, transaction, state_view)
self.verify_transaction(transaction, state_view)
}
}