use crate::{
db::{database::ZephyrDatabase, ledger::LedgerStateRead},
host::{FunctionInfo, Host, SorobanTempFunctionInfo},
};
use soroban_env_host::wasmi::{
self as soroban_wasmi,
core::{Trap, TrapCode::BadSignature},
Value,
};
use soroban_env_host::{
AddressObject, Bool, BytesObject, DurationObject, Error, HostError, I128Object, I256Object,
I256Val, I64Object, MapObject, StorageType, StringObject, Symbol, SymbolObject,
TimepointObject, U128Object, U256Object, U256Val, U32Val, U64Object, U64Val, Val, VecObject,
VmCaller, Void, WasmiMarshal,
};
use soroban_env_macros::generate_call_macro_with_all_host_functions;
use soroban_env_host::{
xdr::{Hash, ScErrorCode, ScErrorType},
CheckedEnvArg, Host as SorobanHost, VmCallerEnv,
};
use wasmi::{Func, Store};
pub(crate) fn build_u32val(host: &soroban_env_host::Host, int: i64) -> Result<U32Val, HostError> {
U32Val::check_env_arg(
U32Val::try_marshal_from_relative_value(soroban_wasmi::Value::I64(int), &host).map_err(
|_| {
Error::from_scerror(soroban_env_host::xdr::ScError::WasmVm(
ScErrorCode::InvalidInput,
))
},
)?,
host,
)
}
pub(crate) fn with_frame<G>(
host: soroban_env_host::Host,
result: Result<G, HostError>,
) -> Result<Val, HostError>
where
G: RelativeObjectConversion + CheckedEnvArg,
{
host.with_test_contract_frame(Hash([0; 32]), Symbol::from_small_str("test"), || {
let res = match result {
Ok(ok) => {
let ok = ok.check_env_arg(&host)?;
let val: soroban_wasmi::Value =
ok.marshal_relative_from_self(&host).map_err(|_| {
Error::from_scerror(soroban_env_host::xdr::ScError::WasmVm(
ScErrorCode::InvalidInput,
))
})?;
if let soroban_wasmi::Value::I64(v) = val {
Ok((v,))
} else {
Err(0)
}
}
Err(hosterr) => return Err(hosterr),
};
Ok(Val::from_payload(
res.map_err(|_| Error::from_contract_error(0))?.0 as u64,
))
})
}
pub(crate) trait RelativeObjectConversion: WasmiMarshal + Clone {
fn absolute_to_relative(self, _host: &SorobanHost) -> Result<Self, HostError> {
Ok(self)
}
fn relative_to_absolute(self, _host: &SorobanHost) -> Result<Self, HostError> {
Ok(self)
}
fn try_marshal_from_relative_value(
v: soroban_env_host::wasmi::Value,
host: &SorobanHost,
) -> Result<Self, Trap> {
let val = Self::try_marshal_from_value(v).ok_or_else(|| {
Trap::from(HostError::from(Error::from_type_and_code(
ScErrorType::Value,
ScErrorCode::InvalidInput,
)))
})?;
let backup = val.clone();
Ok(val.relative_to_absolute(host).unwrap_or(backup))
}
fn marshal_relative_from_self(
self,
host: &SorobanHost,
) -> Result<soroban_env_host::wasmi::Value, Trap> {
let backup = self.clone();
let rel = self.absolute_to_relative(host).unwrap_or(backup);
Ok(Self::marshal_from_self(rel))
}
}
macro_rules! impl_relative_object_conversion {
($T:ty) => {
impl RelativeObjectConversion for $T {
fn absolute_to_relative(self, host: &SorobanHost) -> Result<Self, HostError> {
Ok(Self::try_from(host.absolute_to_relative(self.into())?)?)
}
fn relative_to_absolute(self, host: &SorobanHost) -> Result<Self, HostError> {
Ok(Self::try_from(host.relative_to_absolute(self.into())?)?)
}
}
};
}
impl_relative_object_conversion!(Val);
impl_relative_object_conversion!(Symbol);
impl_relative_object_conversion!(AddressObject);
impl_relative_object_conversion!(BytesObject);
impl_relative_object_conversion!(DurationObject);
impl_relative_object_conversion!(TimepointObject);
impl_relative_object_conversion!(SymbolObject);
impl_relative_object_conversion!(StringObject);
impl_relative_object_conversion!(VecObject);
impl_relative_object_conversion!(MapObject);
impl_relative_object_conversion!(I64Object);
impl_relative_object_conversion!(I128Object);
impl_relative_object_conversion!(I256Object);
impl_relative_object_conversion!(U64Object);
impl_relative_object_conversion!(U128Object);
impl_relative_object_conversion!(U256Object);
impl_relative_object_conversion!(U64Val);
impl_relative_object_conversion!(U256Val);
impl_relative_object_conversion!(I256Val);
impl RelativeObjectConversion for i64 {}
impl RelativeObjectConversion for u64 {}
impl RelativeObjectConversion for Void {}
impl RelativeObjectConversion for Bool {}
impl RelativeObjectConversion for Error {}
impl RelativeObjectConversion for StorageType {}
impl RelativeObjectConversion for U32Val {}
macro_rules! generate_dispatch_functions {
{
$(
// This outer pattern matches a single 'mod' block of the token-tree
// passed from the x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all provided
// 'mod' blocks provided.
$(#[$mod_attr:meta])*
mod $mod_name:ident $mod_str:literal
{
$(
// This inner pattern matches a single function description
// inside a 'mod' block in the token-tree passed from the
// x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all such
// descriptions.
$(#[$fn_attr:meta])*
{ $fn_str:literal, $($min_proto:literal)?, $($max_proto:literal)?, fn $fn_id:ident ($($arg:ident:$type:ty),*) -> $ret:ty }
)*
}
)*
}
=>
{
$(
$(
$(#[$fn_attr])*
pub(crate) fn $fn_id<DB: ZephyrDatabase + Clone + 'static, L: LedgerStateRead + 'static>(caller: wasmi::Caller<Host<DB, L>>, $($arg:i64),*) ->
(i64,)
{
let host: soroban_env_host::Host = Host::<DB, L>::soroban_host(&caller);
let _ = host.enable_debug();
let effects = || -> Result<_, HostError> {
let mut vmcaller = VmCaller::none();
host.$fn_id(&mut vmcaller, $(<$type>::check_env_arg(<$type>::try_marshal_from_relative_value(Value::I64($arg), &host).map_err(|_| Error::from_contract_error(0))?, &host)?),*)
};
(host.with_test_contract_frame(Hash([0;32]), Symbol::from_small_str("test"), || {
let res = effects();
let res = match res {
Ok(ok) => {
let ok = ok.check_env_arg(&host)?;
let val: Value = ok.marshal_relative_from_self(&host).map_err(|_| Error::from_contract_error(0))?;
if let Value::I64(v) = val {
Ok((v,))
} else {
Err(BadSignature.into())
}
},
Err(hosterr) => {
let escalation: HostError =
host.error(hosterr.into(),
concat!("escalating error to VM trap from failed host function call: ",
stringify!($fn_id)), &[]);
let trap: Trap = escalation.into();
Err(trap)
}
};
Ok(Val::from_payload(res.unwrap_or((0, )).0 as u64))
}).unwrap_or(Val::from_payload(0 as u64)).get_payload() as i64, )
}
)*
)*
};
}
generate_call_macro_with_all_host_functions!("env.json");
call_macro_with_all_host_functions! { generate_dispatch_functions }
macro_rules! host_function_info_helper {
{$mod_str:literal, $fn_id:literal, $args:tt, $func_id:ident } => {
SorobanTempFunctionInfo {
module: $mod_str,
func: $fn_id,
wrapped: |store| Func::wrap(store, $func_id),
}
};
}
macro_rules! generate_host_function_infos {
{
$(
// This outer pattern matches a single 'mod' block of the token-tree
// passed from the x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all provided
// 'mod' blocks provided.
$(#[$mod_attr:meta])*
mod $mod_id:ident $mod_str:literal
{
$(
// This inner pattern matches a single function description
// inside a 'mod' block in the token-tree passed from the
// x-macro to this macro. It is embedded in a `$()*`
// pattern-repetition matcher so that it will match all such
// descriptions.
$(#[$fn_attr:meta])*
{ $fn_id:literal, $($min_proto:literal)?, $($max_proto:literal)?, fn $func_id:ident $args:tt -> $ret:ty }
)*
}
)*
}
=>
{
pub(crate) fn get_all_host_functions<DB, L>() -> Vec<SorobanTempFunctionInfo<DB, L>> where DB: ZephyrDatabase + Clone + 'static, L: LedgerStateRead + 'static {
let mut fns: Vec<SorobanTempFunctionInfo<DB, L>> = Vec::new();
for f in [
$(
$(
host_function_info_helper!{$mod_str, $fn_id, $args, $func_id},
)*
)*
] {
fns.push(f)
}
fns
}
};
}
call_macro_with_all_host_functions! { generate_host_function_infos }
pub fn generate_host_fn_infos<DB, L>(store: &mut Store<Host<DB, L>>) -> Vec<FunctionInfo>
where
DB: ZephyrDatabase + Clone + 'static,
L: LedgerStateRead + 'static,
{
let store = store;
let functions = get_all_host_functions::<DB, L>()
.iter()
.map(|temp| FunctionInfo {
module: temp.module,
func: temp.func,
wrapped: (temp.wrapped)(store),
})
.collect();
functions
}