Skip to content

Commit

Permalink
Merge pull request #1136 from multiversx/exec-exec
Browse files Browse the repository at this point in the history
Executor interface in VM
  • Loading branch information
andrei-marinica committed Jun 28, 2023
2 parents 3f1f16a + 0e44e27 commit 3d87e44
Show file tree
Hide file tree
Showing 32 changed files with 415 additions and 217 deletions.
3 changes: 3 additions & 0 deletions framework/scenario/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ path = "../meta"
version = "0.19.1"
path = "../../sdk/scenario-format"

[dependencies.multiversx-chain-vm-executor]
version = "0.1.0"

[dependencies.multiversx-chain-vm]
version = "=0.3.0"
path = "../../vm"
Expand Down
4 changes: 3 additions & 1 deletion framework/scenario/src/api/impl_vh/debug_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use std::rc::Rc;

use multiversx_chain_vm::{
executor::{BreakpointValue, VMHooks},
tx_mock::{StaticVarData, StaticVarStack, TxContext, TxContextRef, TxContextStack, TxPanic},
tx_mock::{TxContext, TxContextRef, TxContextStack, TxPanic},
vm_hooks::{TxContextWrapper, VMHooksDispatcher},
};
use multiversx_sc::err_msg;

use crate::debug_executor::{StaticVarData, StaticVarStack};

use super::{DebugHandle, VMHooksApi, VMHooksApiBackend};

#[derive(Clone)]
Expand Down
3 changes: 2 additions & 1 deletion framework/scenario/src/api/impl_vh/static_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use std::ops::Deref;

use multiversx_chain_vm::{
executor::VMHooks,
tx_mock::StaticVarData,
vm_hooks::{TxManagedTypesCell, VMHooksDispatcher, VMHooksHandler},
};
use multiversx_sc::api::RawHandle;

use crate::debug_executor::StaticVarData;

use super::{VMHooksApi, VMHooksApiBackend};

fn new_vh_dispatcher_managed_types_cell() -> Box<dyn VMHooks> {
Expand Down
7 changes: 3 additions & 4 deletions framework/scenario/src/api/impl_vh/vm_hooks_api.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::debug_executor::StaticVarData;

use super::VMHooksApiBackend;

use std::marker::PhantomData;

use multiversx_chain_vm::{
executor::{MemPtr, VMHooks},
tx_mock::StaticVarData,
};
use multiversx_chain_vm::executor::{MemPtr, VMHooks};
use multiversx_sc::api::{HandleTypeInfo, ManagedBufferApiImpl};

#[derive(Clone, Debug)]
Expand Down
4 changes: 3 additions & 1 deletion framework/scenario/src/api/impl_vh/vm_hooks_backend.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use multiversx_chain_vm::{executor::VMHooks, tx_mock::StaticVarData};
use multiversx_chain_vm::executor::VMHooks;
use multiversx_sc::api::HandleConstraints;

use crate::debug_executor::StaticVarData;

pub trait VMHooksApiBackend: Clone + 'static {
/// We use a single handle type for all handles.
type HandleType: HandleConstraints;
Expand Down
11 changes: 11 additions & 0 deletions framework/scenario/src/debug_executor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod catch_tx_panic;
mod contract_container;
mod contract_map;
mod static_var_stack;
mod tx_static_vars;

pub use catch_tx_panic::catch_tx_panic;
pub use contract_container::{ContractContainer, ContractContainerRef};
pub use contract_map::{ContractMap, ContractMapRef};
pub use static_var_stack::{StaticVarData, StaticVarStack};
pub use tx_static_vars::TxStaticVars;
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use multiversx_chain_vm::tx_mock::TxPanic;
use multiversx_chain_vm_executor::BreakpointValue;
use multiversx_sc::err_msg;

use crate::tx_mock::TxPanic;

/// Catches all thrown panics, as follows:
/// - BreakpointValue is considered expected abortion of execution and already handled, for them it returns Ok;
/// - TxPanic panics are considered VM failures with additional information. We are trying to get rid of them;
Expand Down
146 changes: 146 additions & 0 deletions framework/scenario/src/debug_executor/contract_container.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use std::rc::Rc;

use multiversx_chain_vm::tx_mock::{TxContextRef, TxFunctionName, TxPanic};
use multiversx_chain_vm_executor::{BreakpointValue, ExecutorError, Instance, MemLength, MemPtr};
use multiversx_sc::contract_base::CallableContract;

use super::{catch_tx_panic, StaticVarStack};

/// Contains a reference to a contract implementation.
///
/// It can optionally also contain an allowed endpoint whitelist, to simulate multi-contract.
pub struct ContractContainer {
callable: Box<dyn CallableContract>,
function_whitelist: Option<Vec<String>>,
pub panic_message: bool,
}

impl ContractContainer {
pub fn new(
callable: Box<dyn CallableContract>,
function_whitelist: Option<Vec<String>>,
panic_message: bool,
) -> Self {
ContractContainer {
callable,
function_whitelist,
panic_message,
}
}

fn validate_function_name(&self, function_name: &TxFunctionName) -> bool {
if let Some(function_whitelist) = &self.function_whitelist {
function_whitelist
.iter()
.any(|whitelisted_endpoint| whitelisted_endpoint.as_str() == function_name.as_str())
} else {
true
}
}

pub fn call(&self, function_name: &TxFunctionName) -> bool {
if self.validate_function_name(function_name) {
self.callable.call(function_name.as_str())
} else {
false
}
}
}

#[derive(Clone)]
pub struct ContractContainerRef(pub(crate) Rc<ContractContainer>);

impl ContractContainerRef {
pub fn new(contract_container: ContractContainer) -> Self {
ContractContainerRef(Rc::new(contract_container))
}
}

impl Instance for ContractContainerRef {
fn call(&self, func_name: &str) -> Result<(), String> {
let tx_func_name = TxFunctionName::from(func_name);
StaticVarStack::static_push();

let result = catch_tx_panic(self.0.panic_message, || {
let call_successful = self.0.call(&tx_func_name);
if call_successful {
Ok(())
} else {
Err(TxPanic::new(1, "invalid function (not found)"))
}
});

if let Err(tx_panic) = result {
TxContextRef::new_from_static().replace_tx_result_with_error(tx_panic);
}

StaticVarStack::static_pop();

Ok(())
}

fn check_signatures(&self) -> bool {
true
}

fn has_function(&self, func_name: &str) -> bool {
let tx_func_name = TxFunctionName::from(func_name);
self.0.validate_function_name(&tx_func_name)
}

fn get_exported_function_names(&self) -> Vec<String> {
panic!("ContractContainer get_exported_function_names not yet supported")
}

fn set_points_limit(&self, _limit: u64) -> Result<(), String> {
panic!("ContractContainerRef set_points_limit not supported")
}

fn set_points_used(&self, _points: u64) -> Result<(), String> {
panic!("ContractContainerRef set_points_used not supported")
}

fn get_points_used(&self) -> Result<u64, String> {
panic!("ContractContainerRef get_points_used not supported")
}

fn memory_length(&self) -> Result<u64, String> {
panic!("ContractContainerRef memory_length not supported")
}

fn memory_ptr(&self) -> Result<*mut u8, String> {
panic!("ContractContainerRef memory_ptr not supported")
}

fn memory_load(
&self,
_mem_ptr: MemPtr,
_mem_length: MemLength,
) -> Result<&[u8], ExecutorError> {
panic!("ContractContainerRef memory_load not supported")
}

fn memory_store(&self, _mem_ptr: MemPtr, _data: &[u8]) -> Result<(), ExecutorError> {
panic!("ContractContainerRef memory_store not supported")
}

fn memory_grow(&self, _by_num_pages: u32) -> Result<u32, ExecutorError> {
panic!("ContractContainerRef memory_grow not supported")
}

fn set_breakpoint_value(&self, _value: BreakpointValue) -> Result<(), String> {
panic!("ContractContainerRef set_breakpoint_value not supported")
}

fn get_breakpoint_value(&self) -> Result<BreakpointValue, String> {
panic!("ContractContainerRef get_breakpoint_value not supported")
}

fn reset(&self) -> Result<(), String> {
panic!("ContractContainerRef reset not supported")
}

fn cache(&self) -> Result<Vec<u8>, String> {
panic!("ContractContainerRef cache not supported")
}
}
116 changes: 116 additions & 0 deletions framework/scenario/src/debug_executor/contract_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use super::*;

use multiversx_chain_vm_executor::{
CompilationOptions, Executor, ExecutorError, Instance, OpcodeCost,
};
use std::{
cell::{Ref, RefCell, RefMut},
collections::HashMap,
fmt,
rc::Rc,
};

pub struct ContractMap {
contract_objs: HashMap<Vec<u8>, ContractContainerRef>,
}

impl fmt::Debug for ContractMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ContractMap").finish()
}
}

impl ContractMap {
pub fn new() -> Self {
ContractMap {
contract_objs: HashMap::new(),
}
}

pub fn get_contract(&self, contract_identifier: &[u8]) -> ContractContainerRef {
if let Some(contract_contatiner) = self.contract_objs.get(contract_identifier) {
contract_contatiner.clone()
} else {
unknown_contract_panic(contract_identifier)
}
}

pub fn register_contract(
&mut self,
contract_bytes: Vec<u8>,
contract_container: ContractContainer,
) {
let previous_entry = self.contract_objs.insert(
contract_bytes,
ContractContainerRef::new(contract_container),
);
assert!(previous_entry.is_none(), "contract inserted twice");
}

pub fn contains_contract(&self, contract_bytes: &[u8]) -> bool {
self.contract_objs.contains_key(contract_bytes)
}
}

fn unknown_contract_panic(contract_identifier: &[u8]) -> ! {
if let Ok(s) = std::str::from_utf8(contract_identifier) {
panic!("Unknown contract: {s}")
} else {
panic!(
"Unknown contract of length {} bytes",
contract_identifier.len()
)
}
}

impl Default for ContractMap {
fn default() -> Self {
Self::new()
}
}

#[derive(Default, Clone, Debug)]
pub struct ContractMapRef(Rc<RefCell<ContractMap>>);

impl ContractMapRef {
pub fn new() -> Self {
ContractMapRef(Rc::new(RefCell::new(ContractMap::new())))
}

pub fn borrow(&self) -> Ref<ContractMap> {
self.0.borrow()
}

pub fn borrow_mut(&self) -> RefMut<ContractMap> {
self.0.borrow_mut()
}
}

impl Executor for ContractMapRef {
fn set_vm_hooks_ptr(
&mut self,
_vm_hooks_ptr: *mut std::ffi::c_void,
) -> Result<(), ExecutorError> {
todo!()
}

fn set_opcode_cost(&mut self, _opcode_cost: &OpcodeCost) -> Result<(), ExecutorError> {
Ok(())
}

fn new_instance(
&self,
wasm_bytes: &[u8],
_compilation_options: &CompilationOptions,
) -> Result<Box<dyn Instance>, ExecutorError> {
Ok(Box::new(self.borrow().get_contract(wasm_bytes)))
}

fn new_instance_from_cache(
&self,
_cache_bytes: &[u8],
_compilation_options: &CompilationOptions,
) -> Result<Box<dyn Instance>, ExecutorError> {
panic!("ContractMap new_instance_from_cache not supported")
}
}
File renamed without changes.
File renamed without changes.
7 changes: 4 additions & 3 deletions framework/scenario/src/facade/scenario_world.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
api::DebugApi,
multiversx_chain_vm::world_mock::ContractContainer,
debug_executor::ContractContainer,
multiversx_sc::{
api,
contract_base::{CallableContractBuilder, ContractAbiProvider},
Expand Down Expand Up @@ -114,8 +114,9 @@ impl ScenarioWorld {
let contract_bytes = interpret_string(expression, &self.interpreter_context());
self.get_mut_contract_debugger_backend()
.vm_runner
.blockchain_mock
.register_contract_container(contract_bytes, contract_container);
.contract_map_ref
.borrow_mut()
.register_contract(contract_bytes, contract_container);
}

/// Links a contract path in a test to a contract implementation.
Expand Down
1 change: 1 addition & 0 deletions framework/scenario/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![feature(exhaustive_patterns)]

pub mod api;
pub mod debug_executor;
mod facade;
pub mod managed_test_util;
pub mod scenario;
Expand Down
Loading

0 comments on commit 3d87e44

Please sign in to comment.