use ixc_core_macros::message_selector;
use ixc_message_api::code::{ErrorCode};
use ixc_message_api::handler::{Allocator, HostBackend, RawHandler};
use ixc_message_api::packet::MessagePacket;
use ixc_message_api::AccountID;
use ixc_vm_api::{HandlerID, VM};
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::DerefMut;
use std::sync::Arc;
use ixc_message_api::code::ErrorCode::SystemCode;
use ixc_message_api::code::SystemCode::{AccountNotFound, FatalExecutionError, HandlerNotFound, InvalidHandler, MessageNotHandled, UnauthorizedCallerAccess};
pub struct Hypervisor<ST: StateHandler> {
vmdata: Arc<VMData>,
state_handler: ST,
}
impl<ST: StateHandler + Default> Default for Hypervisor<ST> {
fn default() -> Self {
Self::new(ST::default())
}
}
struct VMData {
vms: HashMap<String, Box<dyn VM>>,
default_vm: Option<String>,
}
impl<ST: StateHandler> Hypervisor<ST> {
pub fn new(state_handler: ST) -> Self {
Self {
vmdata: Arc::from(VMData {
vms: HashMap::new(),
default_vm: None,
}),
state_handler,
}
}
pub fn set_default_vm(&mut self, name: &str) -> Result<(), ()> {
let mut vmdata = Arc::get_mut(&mut self.vmdata).ok_or(())?;
vmdata.default_vm = Some(name.to_string());
Ok(())
}
pub fn register_vm(&mut self, name: &str, vm: Box<dyn VM>) -> Result<(), ()> {
let mut vmdata = Arc::get_mut(&mut self.vmdata).ok_or(())?;
vmdata.vms.insert(name.to_string(), vm);
Ok(())
}
pub fn invoke(&mut self, message_packet: &mut MessagePacket, allocator: &dyn Allocator) -> Result<(), ErrorCode> {
let tx = self.state_handler.new_transaction(message_packet.header().context_info.caller, true).
map_err(|_| SystemCode(FatalExecutionError))?;
let mut exec_context = ExecContext {
vmdata: self.vmdata.clone(),
tx: RefCell::new(tx),
};
let res = exec_context.invoke(message_packet, allocator);
let tx = exec_context.tx.into_inner();
if res.is_ok() {
self.state_handler.commit(tx)
.map_err(|_| SystemCode(FatalExecutionError))?;
}
res
}
}
pub trait StateHandler {
type Tx: Transaction;
fn new_transaction(&self, account_id: AccountID, volatile: bool) -> Result<Self::Tx, NewTxError>;
fn commit(&mut self, tx: Self::Tx) -> Result<(), CommitError>;
}
pub struct NewTxError;
pub enum CommitError {
UnfinishedCallStack,
}
pub trait Transaction {
fn init_account_storage(&mut self, account: AccountID) -> Result<(), PushFrameError>;
fn push_frame(&mut self, account: AccountID, volatile: bool) -> Result<(), PushFrameError>;
fn pop_frame(&mut self, commit: bool) -> Result<(), PopFrameError>;
fn active_account(&self) -> AccountID;
fn self_destruct_account(&mut self) -> Result<(), ()>;
fn raw_kv_get(&self, account_id: AccountID, key: &[u8]) -> Option<Vec<u8>>;
fn raw_kv_set(&self, account_id: AccountID, key: &[u8], value: &[u8]);
fn raw_kv_delete(&self, account_id: AccountID, key: &[u8]);
fn handle(&self, message_packet: &mut MessagePacket, allocator: &dyn Allocator) -> Result<(), ErrorCode>;
}
#[non_exhaustive]
pub enum PushFrameError {
VolatileAccessError,
}
#[non_exhaustive]
pub enum PopFrameError {
NoFrames,
}
struct ExecContext<TX: Transaction> {
vmdata: Arc<VMData>,
tx: RefCell<TX>,
}
impl<'a, TX: Transaction> ExecContext<TX> {
fn get_account_handler_id(&self, account_id: AccountID) -> Option<HandlerID> {
let id: u64 = account_id.into();
let key = format!("h:{}", id);
let value = self.tx.borrow().raw_kv_get(HYPERVISOR_ACCOUNT, key.as_bytes())?;
self.parse_handler_id(&value)
}
fn init_next_account(&self, handler_id: &HandlerID) -> Result<AccountID, PushFrameError> {
let mut tx = self.tx.borrow_mut();
let id = tx.raw_kv_get(HYPERVISOR_ACCOUNT, b"next_account_id").map_or(ACCOUNT_ID_NON_RESERVED_START, |v| {
u64::from_le_bytes(v.try_into().unwrap())
});
tx.init_account_storage(AccountID::new(id))?;
tx.raw_kv_set(HYPERVISOR_ACCOUNT, b"next_account_id", &(id + 1).to_le_bytes());
tx.raw_kv_set(HYPERVISOR_ACCOUNT, format!("h:{}", id).as_bytes(), format_handler_id(handler_id).as_bytes());
Ok(AccountID::new(id))
}
fn destroy_current_account_data(&self) -> Result<(), ()> {
let current_account = self.tx.borrow().active_account();
let id: u64 = current_account.into();
let key = format!("h:{}", id);
self.tx.borrow().raw_kv_delete(HYPERVISOR_ACCOUNT, key.as_bytes());
self.tx.borrow_mut().self_destruct_account()
}
fn parse_handler_id(&self, value: &[u8]) -> Option<HandlerID> {
parse_handler_id(value, &self.vmdata.default_vm)
}
}
const ACCOUNT_ID_NON_RESERVED_START: u64 = u16::MAX as u64 + 1;
const HYPERVISOR_ACCOUNT: AccountID = AccountID::new(1);
const STATE_ACCOUNT: AccountID = AccountID::new(2);
impl<TX: Transaction> HostBackend for ExecContext<TX> {
fn invoke(&self, message_packet: &mut MessagePacket, allocator: &dyn Allocator) -> Result<(), ErrorCode> {
let account = self.tx.borrow().active_account();
if message_packet.header().context_info.caller != account {
return Err(SystemCode(UnauthorizedCallerAccess));
}
let target_account = message_packet.header().context_info.account;
match target_account {
HYPERVISOR_ACCOUNT => return self.handle_system_message(message_packet, allocator),
STATE_ACCOUNT => return self.tx.borrow().handle(message_packet, allocator),
_ => {}
}
let handler_id = self.get_account_handler_id(target_account).
ok_or(SystemCode(AccountNotFound))?;
let vm = self.vmdata.vms.get(&handler_id.vm).
ok_or(SystemCode(HandlerNotFound))?;
self.tx.borrow_mut().push_frame(target_account, false). map_err(|_| SystemCode(InvalidHandler))?;
let res = vm.run_handler(&handler_id.vm_handler_id, message_packet, self, allocator);
self.tx.borrow_mut().pop_frame(res.is_ok()).
map_err(|_| SystemCode(InvalidHandler))?;
res
}
}
impl<TX: Transaction> ExecContext<TX> {
fn handle_system_message(&self, message_packet: &mut MessagePacket, allocator: &dyn Allocator) -> Result<(), ErrorCode> {
match message_packet.header().message_selector {
CREATE_SELECTOR => unsafe {
let create_header = message_packet.header_mut();
let handler_id = create_header.in_pointer1.get(message_packet);
let init_data = create_header.in_pointer2.get(message_packet);
let handler_id = self.parse_handler_id(handler_id).
ok_or(SystemCode(HandlerNotFound))?;
let vm = self.vmdata.vms.get(&handler_id.vm).
ok_or(SystemCode(HandlerNotFound))?;
let desc = vm.describe_handler(&handler_id.vm_handler_id).
ok_or(SystemCode(HandlerNotFound))?;
let id = self.init_next_account(&handler_id).
map_err(|_| SystemCode(InvalidHandler))?;
let mut on_create_packet = MessagePacket::allocate(allocator, 0).
map_err(|_| SystemCode(FatalExecutionError))?;
let mut on_create_header = on_create_packet.header_mut();
on_create_header.context_info.account = id;
on_create_header.context_info.caller = create_header.context_info.caller;
on_create_header.message_selector = ON_CREATE_SELECTOR;
on_create_header.in_pointer1.set_slice(init_data);
let res = vm.run_handler(&handler_id.vm_handler_id, &mut on_create_packet, self, allocator);
let is_ok = match res {
Ok(_) => true,
Err(SystemCode(ixc_message_api::code::SystemCode::MessageNotHandled)) => true,
_ => false,
};
self.tx.borrow_mut().pop_frame(is_ok).
map_err(|_| SystemCode(FatalExecutionError))?;
if is_ok {
create_header.in_pointer1.set_u64(id.into());
Ok(())
} else {
res
}
},
SELF_DESTRUCT_SELECTOR => {
self.destroy_current_account_data().
map_err(|_| SystemCode(FatalExecutionError))
}
_ => {
Err(SystemCode(MessageNotHandled))
}
}
}
}
const CREATE_SELECTOR: u64 = message_selector!("ixc.account.v1.create");
const ON_CREATE_SELECTOR: u64 = message_selector!("ixc.account.v1.on_create");
const SELF_DESTRUCT_SELECTOR: u64 = message_selector!("ixc.account.v1.self_destruct");
fn parse_handler_id(value: &[u8], default_vm: &Option<String>) -> Option<HandlerID> {
let str = String::from_utf8(value.to_vec()).ok()?;
let mut parts = str.split(':');
let mut vm = parts.next()?;
let vm_handler_id = if let Some(handler_id) = parts.next() {
handler_id
} else {
let handler_id = vm;
if let Some(dvm) = default_vm {
vm = dvm;
} else {
return None;
}
handler_id
};
Some(HandlerID {
vm: vm.to_string(),
vm_handler_id: vm_handler_id.to_string(),
})
}
fn format_handler_id(HandlerID { vm, vm_handler_id }: &HandlerID) -> String {
format!("{}:{}", vm, vm_handler_id)
}
#[cfg(test)]
mod tests {
#[test]
fn test_parse_handler_id() {
let value = b"vm1:handler1";
let handler_id = super::parse_handler_id(value, &None).unwrap();
assert_eq!(handler_id.vm, "vm1");
assert_eq!(handler_id.vm_handler_id, "handler1");
let value = b"handler1";
let opt_handler_id = super::parse_handler_id(value, &None);
assert!(opt_handler_id.is_none());
let value = b"handler1";
let handler_id = super::parse_handler_id(value, &Some("default".into())).unwrap();
assert_eq!(handler_id.vm, "default");
assert_eq!(handler_id.vm_handler_id, "handler1");
}
}