use crate::odra_vm_contract_env::OdraVmContractEnv;
use crate::OdraVm;
use odra_core::casper_types::{bytesrepr::Bytes, PublicKey, RuntimeArgs, U512};
use odra_core::entry_point_callback::EntryPointsCaller;
use odra_core::prelude::*;
use odra_core::{
host::{HostContext, HostEnv},
CallDef, ContractContext, ContractEnv
};
use odra_core::{EventError, GasReport, VmError};
pub struct OdraVmHost {
vm: Rc<OdraVm>,
contract_env: Rc<ContractEnv>
}
impl HostContext for OdraVmHost {
fn set_caller(&self, caller: Address) {
self.vm.set_caller(caller)
}
fn set_gas(&self, gas: u64) {
}
fn caller(&self) -> Address {
*self.vm.callstack_tip().address()
}
fn get_account(&self, index: usize) -> Address {
self.vm.get_account(index)
}
fn get_validator(&self, index: usize) -> PublicKey {
self.vm.get_validator(index)
}
fn remove_validator(&self, index: usize) {
self.vm.remove_validator(index);
}
fn balance_of(&self, address: &Address) -> U512 {
self.vm.balance_of(address)
}
fn advance_block_time(&self, time_diff: u64) {
self.vm.advance_block_time_by(time_diff)
}
fn advance_with_auctions(&self, time_diff: u64) {
self.vm.advance_with_auctions(time_diff)
}
fn auction_delay(&self) -> u64 {
self.vm.auction_delay()
}
fn unbonding_delay(&self) -> u64 {
self.vm.unbonding_delay()
}
fn delegated_amount(&self, delegator: Address, validator: PublicKey) -> U512 {
self.vm.delegated_amount(delegator, validator)
}
fn block_time(&self) -> u64 {
self.vm.get_block_time()
}
fn get_event(&self, contract_address: &Address, index: u32) -> Result<Bytes, EventError> {
self.vm.get_event(contract_address, index)
}
fn get_native_event(
&self,
contract_address: &Address,
index: u32
) -> Result<Bytes, EventError> {
self.vm.get_native_event(contract_address, index)
}
fn get_events_count(&self, contract_address: &Address) -> Result<u32, EventError> {
self.vm.get_events_count(contract_address)
}
fn get_native_events_count(&self, contract_address: &Address) -> Result<u32, EventError> {
self.vm.get_native_events_count(contract_address)
}
fn call_contract(
&self,
address: &Address,
call_def: CallDef,
_use_proxy: bool
) -> OdraResult<Bytes> {
crate::panic_hook::set_odra_panic_hook();
let mut opt_result: Option<Bytes> = None;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
opt_result = Some(self.vm.call_contract(*address, call_def));
}));
match opt_result {
Some(result) => Ok(result),
None => {
eprintln!("↳ Stack trace:\n{}", self.vm.read_stack_record());
let error = self.vm.error();
Err(error.unwrap_or(OdraError::VmError(VmError::Panic)))
}
}
}
fn new_contract(
&self,
name: &str,
init_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> OdraResult<Address> {
let address = self
.vm
.new_contract(name, init_args.clone(), entry_points_caller.clone());
if entry_points_caller
.entry_points()
.iter()
.any(|ep| ep.name == "init")
{
self.call_contract(
&address,
CallDef::new(String::from("init"), true, init_args),
false
)?;
self.vm.post_install(address);
}
Ok(address)
}
fn upgrade_contract(
&self,
name: &str,
contract_to_upgrade: Address,
upgrade_args: RuntimeArgs,
entry_points_caller: EntryPointsCaller
) -> OdraResult<Address> {
let address = self.vm.upgrade_contract(
name,
contract_to_upgrade,
upgrade_args.clone(),
entry_points_caller.clone()
);
if entry_points_caller
.entry_points()
.iter()
.any(|ep| ep.name == "upgrade")
{
self.call_contract(
&address,
CallDef::new(String::from("upgrade"), true, upgrade_args),
false
)?;
self.vm.post_install(address);
}
Ok(address)
}
fn register_contract(
&self,
address: Address,
contract_name: String,
entry_points_caller: EntryPointsCaller
) {
panic!("register_contract is not supported for OdraVM");
}
fn contract_env(&self) -> ContractEnv {
(*self.contract_env).clone()
}
fn gas_report(&self) -> GasReport {
GasReport::default()
}
fn last_call_gas_cost(&self) -> u64 {
0
}
fn sign_message(&self, message: &Bytes, address: &Address) -> Bytes {
self.vm.sign_message(message, address)
}
fn public_key(&self, address: &Address) -> PublicKey {
self.vm.public_key(address)
}
fn transfer(&self, to: Address, amount: U512) -> OdraResult<()> {
let caller = self.caller();
self.vm.checked_transfer_tokens(&caller, &to, &amount)
}
}
impl OdraVmHost {
pub fn new(vm: Rc<OdraVm>) -> Rc<Self> {
let contract_env = Rc::new(ContractEnv::new(OdraVmContractEnv::new(vm.clone())));
Rc::new(Self { vm, contract_env })
}
}