use crate::{
error::{Error, ExecutableError, Result},
exec::{Executable, LoadableFunction},
modules::Module,
runtime::payment_id::PaymentId,
};
use base58::ToBase58;
use jni::{objects::GlobalRef, JavaVM};
use log::debug;
use std::str::FromStr;
use wasmi::Value;
const MAX_FRAMES: usize = 64;
pub struct Frame {
contract_id: Vec<u8>,
bytecode: Vec<u8>,
nonce: u64,
}
impl Frame {
pub fn contract_id(&self) -> Vec<u8> {
self.contract_id.clone()
}
pub fn payment_id(&self) -> Vec<u8> {
PaymentId::new(self.contract_id.clone(), self.nonce).as_bytes()
}
}
pub struct Vm {
frames: Vec<Frame>,
first_frame: Frame,
memory: (u32, u32),
fuel_limit: u64,
modules: Vec<Module>,
pub jvm: Option<JavaVM>,
pub jvm_callback: Option<GlobalRef>,
nonce: u64,
}
impl Vm {
pub fn new(
contract_id: Vec<u8>,
bytecode: Vec<u8>,
memory: (u32, u32),
fuel_limit: u64,
modules: Vec<Module>,
jvm: Option<JavaVM>,
jvm_callback: Option<GlobalRef>,
) -> Result<Self> {
let first_frame = Frame {
contract_id,
bytecode,
nonce: 0,
};
debug!(
"The virtual machine is initialized to execute the contract: {}",
first_frame.contract_id.to_base58()
);
Ok(Self {
frames: Default::default(),
first_frame,
memory,
fuel_limit,
modules,
jvm,
jvm_callback,
nonce: 0,
})
}
pub fn call(
&mut self,
contract_id: Vec<u8>,
bytecode: Vec<u8>,
nonce: u64,
func_name: &str,
params: &[u8],
) -> Result<Vec<Value>> {
let frame = Frame {
contract_id,
bytecode,
nonce,
};
debug!(
"The contract with id: {} triggers the contract with id: {} to call the function: {}",
self.top_frame().contract_id().to_base58(),
frame.contract_id.to_base58(),
func_name
);
self.push_frame(frame)?;
self.run(func_name, params)
}
pub fn run(&mut self, func_name: &str, params: &[u8]) -> Result<Vec<Value>> {
let frame = self.top_frame();
let func_name = LoadableFunction::from_str(func_name)?;
let mut exec = Executable::new(self.memory.0, self.memory.1, self.fuel_limit);
exec.load_bytecode(&frame.bytecode)?;
debug!(
"Calling the function: {} contract: {}",
func_name.to_string(),
self.top_frame().contract_id().to_base58()
);
let result = exec.execute(&func_name, params, self.modules.clone(), self);
self.frames.pop();
result
}
pub fn top_frame(&self) -> &Frame {
self.frames.last().unwrap_or(&self.first_frame)
}
pub fn get_nonce(&mut self) -> u64 {
self.nonce += 1;
self.nonce
}
pub fn get_caller_current_frame(&self) -> Vec<u8> {
if self.frames.is_empty() {
vec![]
} else {
let len = self.frames.len();
match self.frames.get(len - 2) {
Some(frame) => frame.contract_id(),
None => self.first_frame.contract_id(),
}
}
}
fn push_frame(&mut self, frame: Frame) -> Result<()> {
if self.frames.len() == MAX_FRAMES {
return Err(Error::Executable(ExecutableError::StackOverflow));
}
self.frames.push(frame);
Ok(())
}
}