diff --git a/soroban-env-common/Cargo.toml b/soroban-env-common/Cargo.toml index ba6cc567f..0678dfeaf 100644 --- a/soroban-env-common/Cargo.toml +++ b/soroban-env-common/Cargo.toml @@ -24,6 +24,7 @@ static_assertions = "1.1.0" std = ["stellar-xdr/std", "stellar-xdr/base64"] serde = ["dep:serde", "stellar-xdr/serde"] vm = ["wasmi"] +testutils = [] [package.metadata.docs.rs] all-features = true diff --git a/soroban-env-common/src/array.rs b/soroban-env-common/src/array.rs index ba1b2f8aa..513be73b1 100644 --- a/soroban-env-common/src/array.rs +++ b/soroban-env-common/src/array.rs @@ -14,12 +14,15 @@ impl TryFromVal for [u8; N] { return Err(ScHostValErrorCode::UnexpectedValType.into()); } let bytes = unsafe { Object::unchecked_from_val(val) }; - let len = unsafe { u32::unchecked_from_val(env.bytes_len(bytes)) } as usize; + let len = + unsafe { u32::unchecked_from_val(env.bytes_len(bytes).map_err(|_| ConversionError)?) } + as usize; if len != N { return Err(ConversionError.into()); } let mut arr = [0u8; N]; - env.bytes_copy_to_slice(bytes, RawVal::U32_ZERO, &mut arr)?; + env.bytes_copy_to_slice(bytes, RawVal::U32_ZERO, &mut arr) + .map_err(|_| ConversionError)?; Ok(arr) } } @@ -34,9 +37,12 @@ impl TryFromVal for Vec { return Err(ScHostValErrorCode::UnexpectedValType.into()); } let bytes = unsafe { Object::unchecked_from_val(val) }; - let len = unsafe { u32::unchecked_from_val(env.bytes_len(bytes)) } as usize; + let len = + unsafe { u32::unchecked_from_val(env.bytes_len(bytes).map_err(|_| ConversionError)?) } + as usize; let mut vec = vec![0u8; len]; - env.bytes_copy_to_slice(bytes, RawVal::U32_ZERO, &mut vec)?; + env.bytes_copy_to_slice(bytes, RawVal::U32_ZERO, &mut vec) + .map_err(|_| ConversionError)?; Ok(vec) } } @@ -45,7 +51,10 @@ impl TryFromVal for RawVal { type Error = Status; #[inline(always)] fn try_from_val(env: &E, v: &&[u8]) -> Result { - Ok(env.bytes_new_from_slice(v)?.to_raw()) + Ok(env + .bytes_new_from_slice(v) + .map_err(|_| ConversionError)? + .to_raw()) } } diff --git a/soroban-env-common/src/checked_env.rs b/soroban-env-common/src/checked_env.rs deleted file mode 100644 index 6aec861d2..000000000 --- a/soroban-env-common/src/checked_env.rs +++ /dev/null @@ -1,186 +0,0 @@ -use crate::call_macro_with_all_host_functions; -use crate::{EnvBase, Object, RawVal, Status, Symbol}; -use core::fmt::Debug; - -/// The CheckedEnv trait is similar to the Env trait -- it provides all the -/// same-named methods -- but they have a form that returns Result for the trait's associated `Error:Debug` type. -/// -/// There is a blanket `impl Env for T` so that any type -/// that implements `CheckedEnv` and `EnvBase` automatically also implements -/// `Env`, just by calling the corresponding `CheckedEnv` method and unwrapping -/// it. This allows the host crate to convert errors into WASM traps on the VM -/// causing the host call, while keeping the actual `Env` interface exposed to -/// users simple: any `Env` call that has any sort of error simply doesn't -/// return (panics when run locally, traps when running in a VM). - -/////////////////////////////////////////////////////////////////////////////// -/// X-macro use: defining trait CheckedEnv -/////////////////////////////////////////////////////////////////////////////// - -// This is a helper macro used only by generate_checked_env_trait below. It consumes -// a token-tree of the form: -// -// {fn $fn_id:ident $args:tt -> $ret:ty} -// -// and produces the the corresponding method declaration to be used in the Env -// trait. -macro_rules! host_function_helper { - { - $(#[$attr:meta])* - fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty - } - => - { - $(#[$attr])* - fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error>; - }; -} - -// This is a callback macro that pattern-matches the token-tree passed by the -// x-macro (call_macro_with_all_host_functions) and produces a suite of method -// declarations, which it places in the body of the declaration of the CheckedEnv -// trait. -macro_rules! generate_checked_env_trait { - { - $( - // 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_str:literal, fn $fn_id:ident $args:tt -> $ret:ty } - )* - } - )* - } - - => // The part of the macro above this line is a matcher; below is its expansion. - - { - // This macro expands to a single item: the CheckedEnv trait - - /// This trait is a variant of the [Env](crate::Env) trait used to define the - /// interface implemented by Host. The WASM VM dispatch functions call - /// methods on `CheckedEnv` and convert any `Result::Err(...)` return - /// value into a VM trap, halting VM execution. - /// - /// There is also a blanket `impl Env for T` that - /// implements the `Env` interface directly for `CheckedEnv` by - /// unwrapping all results, in other words "panicking on error". This is - /// used in local testing mode to adapt the `Host` to mimic the - /// (non-`Result`, halt-on-error) interface and behavior of `Guest` - /// when linking a contract to `Host` natively, for local testing. - pub trait CheckedEnv - { - type Error: Debug; - fn escalate_error_to_panic(&self,e:Self::Error) -> !; - $( - $( - // This invokes the host_function_helper! macro above - // passing only the relevant parts of the declaration - // matched by the inner pattern above. It is embedded in two - // nested `$()*` pattern-repetition expanders that - // correspond to the pattern-repetition matchers in the - // match section, but we ignore the structure of the 'mod' - // block repetition-level from the outer pattern in the - // expansion, flattening all functions from all 'mod' blocks - // into the CheckedEnv trait. - host_function_helper!{$(#[$fn_attr])* fn $fn_id $args -> $ret} - )* - )* - } - }; -} - -// Here we invoke the x-macro passing generate_env_trait as its callback macro. -call_macro_with_all_host_functions! { generate_checked_env_trait } - -/////////////////////////////////////////////////////////////////////////////// -/// X-macro use: impl Env for CheckedEnv -/////////////////////////////////////////////////////////////////////////////// - -// This is a helper macro used only by generate_impl_env_for_checked_env below. -// It consumes a token-tree of the form: -// -// {fn $fn_id:ident $args:tt -> $ret:ty} -// -// and produces the the corresponding method declaration to be used in the Env -// trait. -macro_rules! panic_function_helper { - {fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty} - => - { - fn $fn_id(&self, $($arg:$type),*) -> $ret { - match ::$fn_id(self, $($arg),*) { - Ok(x) => x, - Err(e) => self.escalate_error_to_panic(e) - } - } - }; -} - -// This is a callback macro that pattern-matches the token-tree passed by the -// x-macro (call_macro_with_all_host_functions) and produces a suite of method -// declarations, which it places in the body of the blanket impl of Env for -// T:CheckedEnv -macro_rules! impl_env_for_checked_env { - { - $( - // 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_str:literal, fn $fn_id:ident $args:tt -> $ret:ty } - )* - } - )* - } - - => // The part of the macro above this line is a matcher; below is its expansion. - - { - // This macro expands to a single item: a blanket impl that makes all - // `CheckedEnv+EnvBase` types automatically `Env` types, panicking with - // any non-Ok() result. - impl $crate::Env for T - { - $( - $( - // This invokes the panic_function_helper! macro above - // passing only the relevant parts of the declaration - // matched by the inner pattern above. It is embedded in two - // nested `$()*` pattern-repetition expanders that - // correspond to the pattern-repetition matchers in the - // match section, but we ignore the structure of the 'mod' - // block repetition-level from the outer pattern in the - // expansion, flattening all functions from all 'mod' blocks - // into the impl. - panic_function_helper!{fn $fn_id $args -> $ret} - )* - )* - } - }; -} - -// Here we invoke the x-macro passing impl_env_for_checked_env as its callback macro. -call_macro_with_all_host_functions! { impl_env_for_checked_env } diff --git a/soroban-env-common/src/compare.rs b/soroban-env-common/src/compare.rs index a69716b0a..93c89a0b3 100644 --- a/soroban-env-common/src/compare.rs +++ b/soroban-env-common/src/compare.rs @@ -1,7 +1,7 @@ #[cfg(feature = "std")] use std::rc::Rc; -use crate::{BitSet, CheckedEnv, Object, RawVal, RawValConvertible, Status, Symbol, Tag}; +use crate::{BitSet, Env, Object, RawVal, RawValConvertible, Status, Symbol, Tag}; use core::cmp::Ordering; /// General trait representing the ability to compare two values of some type. @@ -116,7 +116,7 @@ impl> Compare> for C { // better). But we can list out any concrete Ord instances we want to support // here. -impl Compare for E { +impl Compare for E { type Error = E::Error; fn compare(&self, a: &Object, b: &Object) -> Result { @@ -131,7 +131,7 @@ impl Compare for E { } } -impl Compare for E { +impl Compare for E { type Error = E::Error; fn compare(&self, a: &RawVal, b: &RawVal) -> Result { diff --git a/soroban-env-common/src/env.rs b/soroban-env-common/src/env.rs index 613bffe60..5c6d198d4 100644 --- a/soroban-env-common/src/env.rs +++ b/soroban-env-common/src/env.rs @@ -7,6 +7,36 @@ use core::any; /// Base trait extended by the [Env](crate::Env) trait, providing various special-case /// functions that do _not_ simply call across cross the guest/host interface. pub trait EnvBase: Sized + Clone { + /// The type of error returned from the environment when the environment + /// itself fails "unrecoverably", or at least in a way that the user is not + /// expected to be able to recover from, such as an internal logic error, + /// exceeding the execution budget, or being passed malformed input in a way + /// that the user-facing API does not anticipate or allow for. This type is + /// returned from _all_ environment-interface methods, and will only ever + /// take on two possible concrete types: either `Infallible` (in the + /// `Guest`) or `HostError` (in the `Host`). + /// + /// The `Guest` can treat all such errors as impossible-to-observe since + /// they will result in the `Host` _trapping_ the `Guest` before returning + /// an `Error` to it. Such errors still remain present in the `Env` API so + /// that we can use the same API in both scenarios, rather than having to + /// have separate "fallible" or "infallible" environments and separate + /// conversion routines for each (as was attempted in earlier iterations). + /// + /// This type is _not_ the same as an error intended to make it to the + /// user-facing API: user-facing errors should return `Ok(Status)` at the + /// environment-interface level, and then either directly handle or escalate + /// the contained `Status` code to the user as a `Status` or `Result<>` of + /// some other type, depending on the API. + type Error: core::fmt::Debug; + + /// Reject an error from the environment, turning it into a panic but on + /// terms that the environment controls (eg. transforming or logging it). + /// This should only ever be called by client-side / SDK local-testing code, + /// never in the `Host`. + #[cfg(feature = "testutils")] + fn escalate_error_to_panic(&self, e: Self::Error) -> !; + /// Used for recovering the concrete type of the Host. fn as_mut_any(&mut self) -> &mut dyn any::Any; @@ -32,16 +62,25 @@ pub trait EnvBase: Sized + Clone { /// Copy a slice of bytes from the caller's memory into an existing `Bytes` /// object the host, returning a new `Bytes`. - fn bytes_copy_from_slice(&self, b: Object, b_pos: RawVal, mem: &[u8]) - -> Result; + fn bytes_copy_from_slice( + &self, + b: Object, + b_pos: RawVal, + mem: &[u8], + ) -> Result; /// Copy a slice of bytes from a `Bytes` object in the host into the /// caller's memory. - fn bytes_copy_to_slice(&self, b: Object, b_pos: RawVal, mem: &mut [u8]) -> Result<(), Status>; + fn bytes_copy_to_slice( + &self, + b: Object, + b_pos: RawVal, + mem: &mut [u8], + ) -> Result<(), Self::Error>; /// Form a new `Bytes` object in the host from a slice of memory in the /// caller. - fn bytes_new_from_slice(&self, mem: &[u8]) -> Result; + fn bytes_new_from_slice(&self, mem: &[u8]) -> Result; // As with the bytes functions above, these take _slices_ with definite // lifetimes. The first slice is interpreted as a (very restricted) @@ -69,13 +108,17 @@ pub trait EnvBase: Sized + Clone { /// a simplified format string (supporting only positional `{}` markers) and /// a single [RawVal] argument that will be inserted at the marker in the /// format string. - fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), Status>; + fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), Self::Error>; /// Log a formatted debugging message to the debug log (if present), passing /// a simplified format string (supporting only positional `{}` markers) and /// a single string-slice argument that will be inserted at the marker in /// the format string. - fn log_static_fmt_static_str(&self, fmt: &'static str, s: &'static str) -> Result<(), Status>; + fn log_static_fmt_static_str( + &self, + fmt: &'static str, + s: &'static str, + ) -> Result<(), Self::Error>; /// Log a formatted debugging message to the debug log (if present), passing /// a simplified format string (supporting only positional `{}` markers) and @@ -86,7 +129,7 @@ pub trait EnvBase: Sized + Clone { fmt: &'static str, v: RawVal, s: &'static str, - ) -> Result<(), Status>; + ) -> Result<(), Self::Error>; /// Log a formatted debugging message to the debug log (if present), passing /// a simplified format string (supporting only positional `{}` markers) and @@ -97,7 +140,7 @@ pub trait EnvBase: Sized + Clone { fmt: &'static str, vals: &[RawVal], strs: &[&'static str], - ) -> Result<(), Status>; + ) -> Result<(), Self::Error>; } /////////////////////////////////////////////////////////////////////////////// @@ -149,7 +192,7 @@ macro_rules! host_function_helper { => { $(#[$attr])* - fn $fn_id(&self, $($arg:$type),*) -> $ret; + fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error>; }; } @@ -186,7 +229,7 @@ macro_rules! generate_env_trait { // This macro expands to a single item: the Env trait. /// This trait represents the interface between Host and Guest, used by - /// client contract code and implemented (via [CheckedEnv](crate::CheckedEnv)) by the host. + /// client contract code and implemented (via [Env](crate::Env)) by the host. /// It consists of functions that take or return only 64-bit values such /// as [RawVal] or [u64]. pub trait Env: EnvBase diff --git a/soroban-env-common/src/env_val.rs b/soroban-env-common/src/env_val.rs index 3458a7041..765ebf14c 100644 --- a/soroban-env-common/src/env_val.rs +++ b/soroban-env-common/src/env_val.rs @@ -57,7 +57,7 @@ impl TryFromVal for i64 { Ok(unsafe { val.unchecked_as_u63() }) } else if Object::val_is_obj_type(val, ScObjectType::I64) { let obj = unsafe { Object::unchecked_from_val(val) }; - Ok(env.obj_to_i64(obj)) + Ok(env.obj_to_i64(obj).map_err(|_| ConversionError)?) } else { log_err_convert::(env, &val); Err(ConversionError) @@ -73,7 +73,7 @@ impl TryFromVal for RawVal { if v >= 0 { Ok(unsafe { RawVal::unchecked_from_u63(v) }) } else { - Ok(env.obj_from_i64(v).to_raw()) + Ok(env.obj_from_i64(v).map_err(|_| ConversionError)?.to_raw()) } } } @@ -87,7 +87,7 @@ impl TryFromVal for u64 { let val = *val; if Object::val_is_obj_type(val, ScObjectType::U64) { let obj = unsafe { Object::unchecked_from_val(val) }; - Ok(env.obj_to_u64(obj)) + Ok(env.obj_to_u64(obj).map_err(|_| ConversionError)?) } else { log_err_convert::(env, &val); Err(ConversionError) @@ -99,7 +99,7 @@ impl TryFromVal for RawVal { type Error = ConversionError; fn try_from_val(env: &E, v: &u64) -> Result { - Ok(env.obj_from_u64(*v).to_raw()) + Ok(env.obj_from_u64(*v).map_err(|_| ConversionError)?.to_raw()) } } @@ -111,8 +111,8 @@ impl TryFromVal for i128 { fn try_from_val(env: &E, v: &RawVal) -> Result { let v = *v; let obj = v.try_into()?; - let lo = env.obj_to_i128_lo64(obj); - let hi = env.obj_to_i128_hi64(obj); + let lo = env.obj_to_i128_lo64(obj).map_err(|_| ConversionError)?; + let hi = env.obj_to_i128_hi64(obj).map_err(|_| ConversionError)?; let u: u128 = (lo as u128) | ((hi as u128) << 64); Ok(u as i128) } @@ -124,6 +124,7 @@ impl TryFromVal for RawVal { let v = *v; Ok(env .obj_from_i128_pieces(v as u64, (v as u128 >> 64) as u64) + .map_err(|_| ConversionError)? .into()) } } @@ -136,8 +137,8 @@ impl TryFromVal for u128 { fn try_from_val(env: &E, v: &RawVal) -> Result { let v = *v; let obj = v.try_into()?; - let lo = env.obj_to_u128_lo64(obj); - let hi = env.obj_to_u128_hi64(obj); + let lo = env.obj_to_u128_lo64(obj).map_err(|_| ConversionError)?; + let hi = env.obj_to_u128_hi64(obj).map_err(|_| ConversionError)?; let u: u128 = (lo as u128) | ((hi as u128) << 64); Ok(u) } @@ -147,7 +148,10 @@ impl TryFromVal for RawVal { fn try_from_val(env: &E, v: &u128) -> Result { let v = *v; - Ok(env.obj_from_u128_pieces(v as u64, (v >> 64) as u64).into()) + Ok(env + .obj_from_u128_pieces(v as u64, (v >> 64) as u64) + .map_err(|_| ConversionError)? + .into()) } } diff --git a/soroban-env-common/src/lib.rs b/soroban-env-common/src/lib.rs index d1cb37f68..1054cf354 100644 --- a/soroban-env-common/src/lib.rs +++ b/soroban-env-common/src/lib.rs @@ -38,7 +38,6 @@ mod val_wrapper; mod array; mod bitset; -mod checked_env; mod compare; mod convert; mod env; @@ -56,7 +55,7 @@ mod symbol; mod tuple; mod unimplemented_env; mod val; -mod vmcaller_checked_env; +mod vmcaller_env; // Re-export the XDR definitions pub use stellar_xdr as xdr; @@ -66,13 +65,12 @@ pub use raw_val::{ConversionError, RawVal, RawValConvertible, Tag}; pub use val::Val; // RawVal and EnvObj couple raw types to environments. -pub use checked_env::CheckedEnv; pub use compare::Compare; pub use convert::Convert; pub use env::{call_macro_with_all_host_functions, Env, EnvBase}; pub use env_val::{TryFromVal, TryIntoVal}; pub use unimplemented_env::UnimplementedEnv; -pub use vmcaller_checked_env::{VmCaller, VmCallerCheckedEnv}; +pub use vmcaller_env::{VmCaller, VmCallerEnv}; // BitSet, Status and Symbol wrap RawVals. // TODO: maybe these should wrap EnvVals? diff --git a/soroban-env-common/src/object.rs b/soroban-env-common/src/object.rs index ef5708afb..d964f458e 100644 --- a/soroban-env-common/src/object.rs +++ b/soroban-env-common/src/object.rs @@ -57,7 +57,7 @@ impl TryFromVal for ScObject where E: Env + Convert, { - type Error = E::Error; + type Error = >::Error; fn try_from_val(env: &E, val: &Object) -> Result { env.convert(*val) } diff --git a/soroban-env-common/src/status.rs b/soroban-env-common/src/status.rs index b95f38fcb..a98c009cc 100644 --- a/soroban-env-common/src/status.rs +++ b/soroban-env-common/src/status.rs @@ -299,6 +299,6 @@ impl Status { impl From for crate::Status { fn from(_: core::convert::Infallible) -> Self { - panic!() + unreachable!() } } diff --git a/soroban-env-common/src/str.rs b/soroban-env-common/src/str.rs index 310e9257c..f764c2e07 100644 --- a/soroban-env-common/src/str.rs +++ b/soroban-env-common/src/str.rs @@ -17,7 +17,11 @@ impl TryFromVal for String { return Err(ConversionError); } let obj: Object = val.try_into_val(env)?; - let len = unsafe { ::unchecked_from_val(env.bytes_len(obj)) }; + let len = unsafe { + ::unchecked_from_val( + env.bytes_len(obj).map_err(|_| ConversionError)?, + ) + }; let mut vec = std::vec![0; len as usize]; env.bytes_copy_to_slice(obj, RawVal::U32_ZERO, &mut vec) .map_err(|_| ConversionError)?; diff --git a/soroban-env-common/src/tuple.rs b/soroban-env-common/src/tuple.rs index e3f7ce856..3b31dc2b1 100644 --- a/soroban-env-common/src/tuple.rs +++ b/soroban-env-common/src/tuple.rs @@ -19,14 +19,14 @@ macro_rules! impl_for_tuple { return Err(ConversionError); } let vec = unsafe { Object::unchecked_from_val(val) }; - let len: u32 = env.vec_len(vec).try_into()?; + let len: u32 = env.vec_len(vec).map_err(|_| ConversionError)?.try_into()?; if len != $count { return Err(ConversionError); } Ok(( $({ let idx: u32 = $idx; - let val = env.vec_get(vec, idx.into()); + let val = env.vec_get(vec, idx.into()).map_err(|_| ConversionError)?; $typ::try_from_val(&env, &val).map_err(|_| ConversionError)? },)* )) @@ -39,8 +39,8 @@ macro_rules! impl_for_tuple { { type Error = ConversionError; fn try_from_val(env: &E, v: &($($typ,)*)) -> Result { - let vec = env.vec_new($count.into()); - $(let vec = env.vec_push_back(vec, v.$idx.try_into_val(&env).map_err(|_| ConversionError)?);)* + let vec = env.vec_new($count.into()).map_err(|_| ConversionError)?; + $(let vec = env.vec_push_back(vec, v.$idx.try_into_val(&env).map_err(|_| ConversionError)?).map_err(|_| ConversionError)?;)* Ok(vec.to_raw()) } } diff --git a/soroban-env-common/src/unimplemented_env.rs b/soroban-env-common/src/unimplemented_env.rs index 76c582216..8a4ec025f 100644 --- a/soroban-env-common/src/unimplemented_env.rs +++ b/soroban-env-common/src/unimplemented_env.rs @@ -1,5 +1,5 @@ use super::{call_macro_with_all_host_functions, Env, EnvBase, Object, RawVal, Status, Symbol}; -use core::any; +use core::{any, convert::Infallible}; /// A dummy implementation of the [Env] trait that fails with `unimplemented!()` in /// all functions. Useful for certain testing scenarios. @@ -7,6 +7,13 @@ use core::any; pub struct UnimplementedEnv; impl EnvBase for UnimplementedEnv { + type Error = Infallible; + + #[cfg(feature = "testutils")] + fn escalate_error_to_panic(&self, _e: Self::Error) -> ! { + unimplemented!() + } + fn as_mut_any(&mut self) -> &mut dyn any::Any { self } @@ -22,7 +29,7 @@ impl EnvBase for UnimplementedEnv { _b: Object, _b_pos: RawVal, _mem: &[u8], - ) -> Result { + ) -> Result { unimplemented!() } @@ -31,15 +38,15 @@ impl EnvBase for UnimplementedEnv { _b: Object, _b_pos: RawVal, _mem: &mut [u8], - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { unimplemented!() } - fn bytes_new_from_slice(&self, _mem: &[u8]) -> Result { + fn bytes_new_from_slice(&self, _mem: &[u8]) -> Result { unimplemented!() } - fn log_static_fmt_val(&self, _fmt: &'static str, _v: RawVal) -> Result<(), Status> { + fn log_static_fmt_val(&self, _fmt: &'static str, _v: RawVal) -> Result<(), Self::Error> { unimplemented!() } @@ -47,7 +54,7 @@ impl EnvBase for UnimplementedEnv { &self, _fmt: &'static str, _s: &'static str, - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { unimplemented!() } @@ -56,7 +63,7 @@ impl EnvBase for UnimplementedEnv { _fmt: &'static str, _v: RawVal, _s: &'static str, - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { unimplemented!() } @@ -65,7 +72,7 @@ impl EnvBase for UnimplementedEnv { _fmt: &'static str, _vals: &[RawVal], _strs: &[&'static str], - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { unimplemented!() } } @@ -81,7 +88,7 @@ macro_rules! host_function_helper { {fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty} => { - fn $fn_id(&self, $(_:$type),*) -> $ret { + fn $fn_id(&self, $(_:$type),*) -> Result<$ret, Self::Error> { unimplemented!() } }; diff --git a/soroban-env-common/src/vmcaller_checked_env.rs b/soroban-env-common/src/vmcaller_env.rs similarity index 83% rename from soroban-env-common/src/vmcaller_checked_env.rs rename to soroban-env-common/src/vmcaller_env.rs index 34b68fc8d..84dd4cf9a 100644 --- a/soroban-env-common/src/vmcaller_checked_env.rs +++ b/soroban-env-common/src/vmcaller_env.rs @@ -2,22 +2,21 @@ use crate::xdr::ScHostContextErrorCode; use crate::call_macro_with_all_host_functions; -use crate::{Object, RawVal, Status, Symbol}; -use core::fmt::Debug; +use crate::{EnvBase, Object, RawVal, Status, Symbol}; #[cfg(not(feature = "vm"))] use core::marker::PhantomData; -/// The VmCallerCheckedEnv trait is similar to the CheckedEnv trait -- it +/// The VmCallerEnv trait is similar to the Env trait -- it /// provides all the same-named methods -- but they have a form that takes an /// initial [`VmCaller`] argument by `&mut` that may or may-not wrap a /// `wasmi::Caller` structure, depending on whether it was invoked from a wasmi /// host-function wrapper. /// -/// There is a blanket `impl CheckedEnv for T` so that any -/// type (eg. `Host`) that implements `VmCallerCheckedEnv` automatically also -/// implements `CheckedEnv`, just by calling the corresponding -/// `VmCallerCheckedEnv` method with the [`VmCaller::none()`] argument. This -/// allows code to import and use `CheckedEnv` directly (such as the native +/// There is a blanket `impl Env for T` so that any +/// type (eg. `Host`) that implements `VmCallerEnv` automatically also +/// implements `Env`, just by calling the corresponding +/// `VmCallerEnv` method with the [`VmCaller::none()`] argument. This +/// allows code to import and use `Env` directly (such as the native /// contract) to call host methods without having to write `VmCaller::none()` /// everywhere. @@ -56,7 +55,7 @@ impl<'a, T> VmCaller<'a, T> { } /////////////////////////////////////////////////////////////////////////////// -/// X-macro use: defining trait VmCallerCheckedEnv +/// X-macro use: defining trait VmCallerEnv /////////////////////////////////////////////////////////////////////////////// // This is a helper macro used only by generate_vmcaller_checked_env_trait @@ -81,7 +80,7 @@ macro_rules! host_function_helper { // This is a callback macro that pattern-matches the token-tree passed by the // x-macro (call_macro_with_all_host_functions) and produces a suite of method // declarations, which it places in the body of the declaration of the -// VmCallerCheckedEnv trait. +// VmCallerEnv trait. macro_rules! generate_vmcaller_checked_env_trait { { $( @@ -108,25 +107,23 @@ macro_rules! generate_vmcaller_checked_env_trait { => // The part of the macro above this line is a matcher; below is its expansion. { - // This macro expands to a single item: the VmCallerCheckedEnv trait + // This macro expands to a single item: the VmCallerEnv trait /// This trait is a variant of the [Env](crate::Env) trait used to /// define the interface implemented by Host. The wasmi VM dispatch - /// functions call methods on `VmCallerCheckedEnv`, passing a + /// functions call methods on `VmCallerEnv`, passing a /// [`VmCaller`] that wraps the wasmi Caller context, and then convert /// any `Result::Err(...)` return value into a VM trap, halting VM /// execution. /// - /// There is also a blanket `impl CheckedEnv for - /// T` that implements the `CheckedEnv` for any `VmCallerCheckedEnv` by + /// There is also a blanket `impl Env for + /// T` that implements the `Env` for any `VmCallerEnv` by /// passing [`VmCaller::none()`] for the first argument, allowing user /// code such as the native contract to avoid writing `VmCaller::none()` /// everywhere. - pub trait VmCallerCheckedEnv + pub trait VmCallerEnv: EnvBase { type VmUserState; - type Error: Debug; - fn escalate_error_to_panic(&self,e:Self::Error) -> !; $( $( // This invokes the host_function_helper! macro above @@ -137,7 +134,7 @@ macro_rules! generate_vmcaller_checked_env_trait { // match section, but we ignore the structure of the 'mod' // block repetition-level from the outer pattern in the // expansion, flattening all functions from all 'mod' blocks - // into the VmCallerCheckedEnv trait. + // into the VmCallerEnv trait. host_function_helper!{$(#[$fn_attr])* fn $fn_id $args -> $ret} )* )* @@ -149,7 +146,7 @@ macro_rules! generate_vmcaller_checked_env_trait { call_macro_with_all_host_functions! { generate_vmcaller_checked_env_trait } /////////////////////////////////////////////////////////////////////////////// -/// X-macro use: impl CheckedEnv for VmCallerCheckedEnv +/// X-macro use: impl Env for VmCallerEnv /////////////////////////////////////////////////////////////////////////////// // This is a helper macro used only by @@ -165,7 +162,7 @@ macro_rules! vmcaller_none_function_helper { => { fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error> { - ::$fn_id(self, &mut VmCaller::none(), $($arg),*) + ::$fn_id(self, &mut VmCaller::none(), $($arg),*) } }; } @@ -173,7 +170,7 @@ macro_rules! vmcaller_none_function_helper { // This is a callback macro that pattern-matches the token-tree passed by the // x-macro (call_macro_with_all_host_functions) and produces a suite of method // declarations, which it places in the body of the blanket impl of Env for -// T:CheckedEnv +// T:Env macro_rules! impl_checked_env_for_vmcaller_checked_env { { $( @@ -201,14 +198,10 @@ macro_rules! impl_checked_env_for_vmcaller_checked_env { { // This macro expands to a single item: a blanket impl that makes all - // `VmCallerCheckedEnv` types automatically `CheckedEnv` types, just + // `VmCallerEnv` types automatically `Env` types, just // passing [`VmCaller::none()`] as their first argument. - impl $crate::CheckedEnv for T + impl $crate::Env for T { - type Error = ::Error; - fn escalate_error_to_panic(&self,e:Self::Error) -> ! { - ::escalate_error_to_panic(self,e) - } $( $( // This invokes the vmcaller_none_function_helper! macro above diff --git a/soroban-env-guest/Cargo.toml b/soroban-env-guest/Cargo.toml index fd9e16725..f2dec11e8 100644 --- a/soroban-env-guest/Cargo.toml +++ b/soroban-env-guest/Cargo.toml @@ -11,8 +11,11 @@ edition = "2021" rust-version = "1.66" [dependencies] -soroban-env-common = { workspace = true } +soroban-env-common = { workspace = true, default-features = false, features = [] } static_assertions = "1.1.0" [package.metadata.docs.rs] all-features = true + +[features] +testutils = ["soroban-env-common/testutils"] \ No newline at end of file diff --git a/soroban-env-guest/src/guest.rs b/soroban-env-guest/src/guest.rs index 062268cb1..97ad5f7c9 100644 --- a/soroban-env-guest/src/guest.rs +++ b/soroban-env-guest/src/guest.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![allow(unused_variables)] +use core::convert::Infallible; + use soroban_env_common::call_macro_with_all_host_functions; use super::{Env, EnvBase, Object, RawVal, Status, Symbol}; @@ -20,6 +22,13 @@ pub struct Guest; // Host for a non-WASM build. #[cfg(not(target_family = "wasm"))] impl EnvBase for Guest { + type Error = Infallible; + + #[cfg(feature = "testutils")] + fn escalate_error_to_panic(&self, e: Self::Error) -> ! { + unimplemented!() + } + fn as_mut_any(&mut self) -> &mut dyn core::any::Any { unimplemented!() } @@ -37,23 +46,32 @@ impl EnvBase for Guest { b: Object, b_pos: RawVal, mem: &[u8], - ) -> Result { + ) -> Result { unimplemented!() } - fn bytes_copy_to_slice(&self, b: Object, b_pos: RawVal, mem: &mut [u8]) -> Result<(), Status> { + fn bytes_copy_to_slice( + &self, + b: Object, + b_pos: RawVal, + mem: &mut [u8], + ) -> Result<(), Self::Error> { unimplemented!() } - fn bytes_new_from_slice(&self, mem: &[u8]) -> Result { + fn bytes_new_from_slice(&self, mem: &[u8]) -> Result { unimplemented!() } - fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), Status> { + fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), Self::Error> { unimplemented!() } - fn log_static_fmt_static_str(&self, fmt: &'static str, s: &'static str) -> Result<(), Status> { + fn log_static_fmt_static_str( + &self, + fmt: &'static str, + s: &'static str, + ) -> Result<(), Self::Error> { unimplemented!() } @@ -62,7 +80,7 @@ impl EnvBase for Guest { fmt: &'static str, v: RawVal, s: &'static str, - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { unimplemented!() } @@ -71,13 +89,20 @@ impl EnvBase for Guest { fmt: &'static str, vals: &[RawVal], strs: &[&'static str], - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { unimplemented!() } } #[cfg(target_family = "wasm")] impl EnvBase for Guest { + type Error = Infallible; + + #[cfg(feature = "testutils")] + fn escalate_error_to_panic(&self, e: Self::Error) -> ! { + core::arch::wasm32::unreachable() + } + fn as_mut_any(&mut self) -> &mut dyn core::any::Any { return self; } @@ -95,34 +120,39 @@ impl EnvBase for Guest { b: Object, b_pos: RawVal, mem: &[u8], - ) -> Result { + ) -> Result { sa::assert_eq_size!(u32, *const u8); sa::assert_eq_size!(u32, usize); let lm_pos: RawVal = RawVal::from_u32(mem.as_ptr() as u32); let len: RawVal = RawVal::from_u32(mem.len() as u32); // NB: any failure in the host function here will trap the guest, // not return, so we only have to code the happy path. - Ok(self.bytes_copy_from_linear_memory(b, b_pos, lm_pos, len)) + self.bytes_copy_from_linear_memory(b, b_pos, lm_pos, len) } - fn bytes_copy_to_slice(&self, b: Object, b_pos: RawVal, mem: &mut [u8]) -> Result<(), Status> { + fn bytes_copy_to_slice( + &self, + b: Object, + b_pos: RawVal, + mem: &mut [u8], + ) -> Result<(), Self::Error> { sa::assert_eq_size!(u32, *const u8); sa::assert_eq_size!(u32, usize); let lm_pos: RawVal = RawVal::from_u32(mem.as_ptr() as u32); let len: RawVal = RawVal::from_u32(mem.len() as u32); - self.bytes_copy_to_linear_memory(b, b_pos, lm_pos, len); + self.bytes_copy_to_linear_memory(b, b_pos, lm_pos, len)?; Ok(()) } - fn bytes_new_from_slice(&self, mem: &[u8]) -> Result { + fn bytes_new_from_slice(&self, mem: &[u8]) -> Result { sa::assert_eq_size!(u32, *const u8); sa::assert_eq_size!(u32, usize); let lm_pos: RawVal = RawVal::from_u32(mem.as_ptr() as u32); let len: RawVal = RawVal::from_u32(mem.len() as u32); - Ok(self.bytes_new_from_linear_memory(lm_pos, len)) + self.bytes_new_from_linear_memory(lm_pos, len) } - fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), Status> { + fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), Self::Error> { // TODO: It's possible we might want to do something in the wasm // case with static strings similar to the bytes functions above, // eg. decay the strings to u32 values and pass them to the host as linear @@ -137,7 +167,11 @@ impl EnvBase for Guest { Ok(()) } - fn log_static_fmt_static_str(&self, fmt: &'static str, s: &'static str) -> Result<(), Status> { + fn log_static_fmt_static_str( + &self, + fmt: &'static str, + s: &'static str, + ) -> Result<(), Self::Error> { // Intentionally a no-op in this cfg. See above. Ok(()) } @@ -147,7 +181,7 @@ impl EnvBase for Guest { fmt: &'static str, v: RawVal, s: &'static str, - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { // Intentionally a no-op in this cfg. See above. Ok(()) } @@ -157,7 +191,7 @@ impl EnvBase for Guest { fmt: &'static str, vals: &[RawVal], strs: &[&'static str], - ) -> Result<(), Status> { + ) -> Result<(), Self::Error> { // Intentionally a no-op in this cfg. See above. Ok(()) } @@ -179,9 +213,9 @@ macro_rules! guest_function_helper { {$mod_id:ident, fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty} => { - fn $fn_id(&self, $($arg:$type),*) -> $ret { + fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error>{ unsafe { - $mod_id::$fn_id($($arg),*) + Ok($mod_id::$fn_id($($arg),*)) } } }; diff --git a/soroban-env-host/Cargo.toml b/soroban-env-host/Cargo.toml index e88e23ba5..ed27c3246 100644 --- a/soroban-env-host/Cargo.toml +++ b/soroban-env-host/Cargo.toml @@ -47,7 +47,7 @@ wasmprinter = "0.2.41" vm = ["wasmi", "soroban-env-common/vm"] hostfn_log_fmt_values = [] serde = ["soroban-env-common/serde"] -testutils = [] +testutils = ["soroban-env-common/testutils"] [target.'cfg(target_os = "linux")'.dev-dependencies] perf-event = "0.4.7" diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index df5ad9010..e885e5736 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -16,7 +16,7 @@ use soroban_env_common::{ ScHostFnErrorCode, ScHostObjErrorCode, ScHostStorageErrorCode, ScHostValErrorCode, ScMap, ScMapEntry, ScObject, ScStatusType, ScVal, ScVec, ThresholdIndexes, }, - Convert, InvokerType, Status, TryFromVal, TryIntoVal, VmCaller, VmCallerCheckedEnv, + Convert, InvokerType, Status, TryFromVal, TryIntoVal, VmCaller, VmCallerEnv, }; use crate::budget::{AsBudget, Budget, CostType}; @@ -187,7 +187,7 @@ impl Convert for Host { impl Host { /// Constructs a new [`Host`] that will use the provided [`Storage`] for /// contract-data access functions such as - /// [`CheckedEnv::get_contract_data`]. + /// [`Env::get_contract_data`]. pub fn with_storage_and_budget(storage: Storage, budget: Budget) -> Self { Self(Rc::new(HostImpl { source_account: RefCell::new(None), @@ -858,8 +858,8 @@ impl Host { Err(panic_payload) => { // Return an error indicating the contract function // panicked. If if was a panic generated by a - // CheckedEnv-upgraded HostError, it had its status - // captured by VmCallerCheckedEnv::escalate_error_to_panic: + // Env-upgraded HostError, it had its status + // captured by VmCallerEnv::escalate_error_to_panic: // fish the Status stored in the frame back out and // propagate it. let func: RawVal = func.into(); @@ -1042,6 +1042,61 @@ impl Host { // Notes on metering: these are called from the guest and thus charged on the VM instructions. impl EnvBase for Host { + type Error = HostError; + + // This function is somewhat subtle. + // + // It exists to allow the client of the (VmCaller)Env interface(s) to + // essentially _reject_ an error returned by one of the Result-returning + // methods on the trait, choosing to panic instead. But doing so in some way + // that the trait defines, rather than calling panic in the client. + // + // The only client we expect to _do_ this is a non-builtin user contract + // compiled natively for local testing (and thus linked directly to `Host`). + // In a wasm build of a user contract, we already encourage users to think + // of `Env::Error` as infallible by literally defining `Guest::Error` as the + // `Infallible` type (which makes sense: we trap the user's VM on such + // errors, don't resume it at all). But in a non-wasm, native build of a + // user contract, `Env=Host` and `Env::Error=HostError`, a real type you can + // observe. So the user might actually have a code path returning from such + // an error that is suddenly non-dead and receiving an + // `Env::Error=HostError`, which (to maintain continuity with the VM case) + // they then _want_ to treat as impossible-to-have-occurred just like + // `Guest::Error`. They can panic, but that doesn't quite maintain the + // illusion properly. Instead they should call this method to "reject the + // error". + // + // When such a "rejected error" occurs, we do panic, but only after checking + // to see if we're in a `TestContract` invocation, and if so storing the + // error's Status value in that frame, such that `Host::call_n` above can + // recover the Status when it _catches_ the panic and converts it back to an + // error. + // + // It might seem like we ought to `std::panic::panic_any(e)` here, making + // the panic carry a `HostError` or `Status` and catching it by dynamic type + // inspection in the `call_n` catch logic. The reason we don't do so is that + // `panic_any` will not provide a nice printable value to the `PanicInfo`, + // it constructs, so when/if the panic makes it to a top-level printout it + // will display a relatively ugly message like "thread panicked at Box" to stderr, when it is much more useful to the user if we have it + // print the result of HostError::Debug, with its glorious status code, + // site-of-origin backtrace and debug log. + // + // To get it to do that, we have to call `panic!()`, not `panic_any`. + // Personally I think this is a glaring weakness of `panic_any` but we are + // not in a position to improve it. + #[cfg(feature = "testutils")] + fn escalate_error_to_panic(&self, e: Self::Error) -> ! { + let _ = self.with_current_frame_opt(|f| { + if let Some(Frame::TestContract(frame)) = f { + *frame.panic.borrow_mut() = Some(e.status); + } + Ok(()) + }); + let escalation = self.err_status_msg(e.status, "escalating error '{}' to panic"); + panic!("{:?}", escalation) + } + fn as_mut_any(&mut self) -> &mut dyn std::any::Any { todo!() } @@ -1059,7 +1114,7 @@ impl EnvBase for Host { b: Object, b_pos: RawVal, mem: &[u8], - ) -> Result { + ) -> Result { // This is only called from native contracts, either when testing or // when the contract is otherwise linked into the same address space as // us. We therefore access the memory we were passed directly. @@ -1080,12 +1135,15 @@ impl EnvBase for Host { .map_err(|he| he.status)?; let write_slice = &mut vnew[b_pos as usize..end_idx]; write_slice.copy_from_slice(mem); - self.add_host_object(vnew) - .map(|ev| ev.into()) - .map_err(|he| he.status) + self.add_host_object(vnew).map(|ev| ev.into()) } - fn bytes_copy_to_slice(&self, b: Object, b_pos: RawVal, mem: &mut [u8]) -> Result<(), Status> { + fn bytes_copy_to_slice( + &self, + b: Object, + b_pos: RawVal, + mem: &mut [u8], + ) -> Result<(), HostError> { let b_pos = u32::try_from(b_pos).map_err(|_| ScHostValErrorCode::UnexpectedValType)?; let len = u32::try_from(mem.len()).map_err(|_| ScHostValErrorCode::U32OutOfRange)?; self.visit_obj(b, move |hv: &Vec| { @@ -1097,22 +1155,22 @@ impl EnvBase for Host { mem.copy_from_slice(&hv.as_slice()[b_pos as usize..end_idx as usize]); Ok(()) }) - .map_err(|he| he.status) } - fn bytes_new_from_slice(&self, mem: &[u8]) -> Result { + fn bytes_new_from_slice(&self, mem: &[u8]) -> Result { self.add_host_object::>(mem.into()) - .map_err(|he| he.status) } - fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), Status> { + fn log_static_fmt_val(&self, fmt: &'static str, v: RawVal) -> Result<(), HostError> { self.record_debug_event(DebugEvent::new().msg(fmt).arg(v)) - .map_err(|he| he.status) } - fn log_static_fmt_static_str(&self, fmt: &'static str, s: &'static str) -> Result<(), Status> { + fn log_static_fmt_static_str( + &self, + fmt: &'static str, + s: &'static str, + ) -> Result<(), HostError> { self.record_debug_event(DebugEvent::new().msg(fmt).arg(s)) - .map_err(|he| he.status) } fn log_static_fmt_val_static_str( @@ -1120,9 +1178,8 @@ impl EnvBase for Host { fmt: &'static str, v: RawVal, s: &'static str, - ) -> Result<(), Status> { + ) -> Result<(), HostError> { self.record_debug_event(DebugEvent::new().msg(fmt).arg(v).arg(s)) - .map_err(|he| he.status) } fn log_static_fmt_general( @@ -1130,7 +1187,7 @@ impl EnvBase for Host { fmt: &'static str, vals: &[RawVal], strs: &[&'static str], - ) -> Result<(), Status> { + ) -> Result<(), HostError> { let mut evt = DebugEvent::new().msg(fmt); for v in vals { evt = evt.arg(*v) @@ -1138,56 +1195,12 @@ impl EnvBase for Host { for s in strs { evt = evt.arg(*s) } - self.record_debug_event(evt).map_err(|he| he.status) + self.record_debug_event(evt) } } -impl VmCallerCheckedEnv for Host { +impl VmCallerEnv for Host { type VmUserState = Host; - type Error = HostError; - - // This function is somewhat subtle. - // - // It exists to allow the client of the (VmCaller)CheckedEnv interface(s) to - // essentially _reject_ an error returned by one of the Result-returning - // methods on the trait, choosing to panic instead. But doing so in some way - // that the trait defines, rather than calling panic in the client. - // - // The only "client" we expect to _do_ this is the `impl Env for CheckedEnv` - // definition, in checked_env.rs, which itself is only used for testing - // native contracts, adapting `CheckedEnv` to the non-error-returning `Env` - // interface that contracts expect. - // - // When such a "rejected error" occurs, we do panic, but only after checking - // to see if we're in a `TestContract` invocation, and if so storing the - // error's Status value in that frame, such that `Host::call_n` above can - // recover the Status when it _catches_ the panic and converts it back to an - // error. - // - // It might seem like we ought to `std::panic::panic_any(e)` here, making - // the panic carry a `HostError` or `Status` and catching it by dynamic type - // inspection in the `call_n` catch logic. The reason we don't do so is that - // `panic_any` will not provide a nice printable value to the `PanicInfo`, - // it constructs, so when/if the panic makes it to a top-level printout it - // will display a relatively ugly message like "thread panicked at Box" to stderr, when it is much more useful to the user if we have it - // print the result of HostError::Debug, with its glorious status code, - // site-of-origin backtrace and debug log. - // - // To get it to do that, we have to call `panic!()`, not `panic_any`. - // Personally I think this is a glaring weakness of `panic_any` but we are - // not in a position to improve it. - fn escalate_error_to_panic(&self, e: Self::Error) -> ! { - #[cfg(any(test, feature = "testutils"))] - let _ = self.with_current_frame_opt(|f| { - if let Some(Frame::TestContract(frame)) = f { - *frame.panic.borrow_mut() = Some(e.status); - } - Ok(()) - }); - let escalation = self.err_status_msg(e.status, "escalating error '{}' to panic"); - panic!("{:?}", escalation) - } // Notes on metering: covered by the components fn log_value(&self, _vmcaller: &mut VmCaller, v: RawVal) -> Result { diff --git a/soroban-env-host/src/host/data_helper.rs b/soroban-env-host/src/host/data_helper.rs index a2dd82570..24d1a6c2e 100644 --- a/soroban-env-host/src/host/data_helper.rs +++ b/soroban-env-host/src/host/data_helper.rs @@ -1,6 +1,6 @@ use core::cmp::min; -use soroban_env_common::{CheckedEnv, InvokerType}; +use soroban_env_common::{Env, InvokerType}; use crate::budget::AsBudget; use crate::xdr::{ diff --git a/soroban-env-host/src/native_contract/base_types.rs b/soroban-env-host/src/native_contract/base_types.rs index 85747c421..99971f8f6 100644 --- a/soroban-env-host/src/native_contract/base_types.rs +++ b/soroban-env-host/src/native_contract/base_types.rs @@ -3,9 +3,7 @@ use crate::host::{Host, HostError}; use core::cmp::Ordering; use soroban_env_common::xdr::{AccountId, ScObjectType}; -use soroban_env_common::{ - CheckedEnv, Compare, ConversionError, EnvBase, Object, RawVal, TryFromVal, -}; +use soroban_env_common::{Compare, ConversionError, Env, EnvBase, Object, RawVal, TryFromVal}; #[derive(Clone)] pub struct Bytes { diff --git a/soroban-env-host/src/native_contract/invoker.rs b/soroban-env-host/src/native_contract/invoker.rs index d4f61c6d3..15599b6e9 100644 --- a/soroban-env-host/src/native_contract/invoker.rs +++ b/soroban-env-host/src/native_contract/invoker.rs @@ -1,4 +1,4 @@ -use soroban_env_common::{xdr::AccountId, CheckedEnv, InvokerType, TryFromVal, TryIntoVal}; +use soroban_env_common::{xdr::AccountId, Env, InvokerType, TryFromVal, TryIntoVal}; use soroban_native_sdk_macros::contracttype; use crate::{Host, HostError}; diff --git a/soroban-env-host/src/native_contract/testutils.rs b/soroban-env-host/src/native_contract/testutils.rs index dbbfa6e1b..f16702876 100644 --- a/soroban-env-host/src/native_contract/testutils.rs +++ b/soroban-env-host/src/native_contract/testutils.rs @@ -9,7 +9,7 @@ use ed25519_dalek::{Keypair, Signer}; use rand::thread_rng; use soroban_env_common::{ xdr::{AccountId, PublicKey, Uint256}, - CheckedEnv, + Env, }; use soroban_env_common::{EnvBase, RawVal, Symbol, TryFromVal, TryIntoVal}; diff --git a/soroban-env-host/src/native_contract/token/admin.rs b/soroban-env-host/src/native_contract/token/admin.rs index ed979ecea..deab351f7 100644 --- a/soroban-env-host/src/native_contract/token/admin.rs +++ b/soroban-env-host/src/native_contract/token/admin.rs @@ -2,7 +2,7 @@ use crate::host::Host; use crate::native_contract::token::public_types::{Identifier, Signature}; use crate::native_contract::token::storage_types::DataKey; use crate::{err, HostError}; -use soroban_env_common::{CheckedEnv, Compare, TryIntoVal}; +use soroban_env_common::{Compare, Env, TryIntoVal}; use super::error::ContractError; diff --git a/soroban-env-host/src/native_contract/token/allowance.rs b/soroban-env-host/src/native_contract/token/allowance.rs index 75df4e752..eb317e3e9 100644 --- a/soroban-env-host/src/native_contract/token/allowance.rs +++ b/soroban-env-host/src/native_contract/token/allowance.rs @@ -2,7 +2,7 @@ use crate::host::Host; use crate::native_contract::token::public_types::Identifier; use crate::native_contract::token::storage_types::{AllowanceDataKey, DataKey}; use crate::{err, HostError}; -use soroban_env_common::{CheckedEnv, TryIntoVal}; +use soroban_env_common::{Env, TryIntoVal}; use super::error::ContractError; diff --git a/soroban-env-host/src/native_contract/token/balance.rs b/soroban-env-host/src/native_contract/token/balance.rs index f7eba22ed..67787b486 100644 --- a/soroban-env-host/src/native_contract/token/balance.rs +++ b/soroban-env-host/src/native_contract/token/balance.rs @@ -8,7 +8,7 @@ use soroban_env_common::xdr::{ AccountEntry, AccountEntryExt, AccountEntryExtensionV1Ext, AccountFlags, AccountId, LedgerEntryData, TrustLineAsset, TrustLineEntry, TrustLineEntryExt, TrustLineFlags, }; -use soroban_env_common::{CheckedEnv, TryIntoVal}; +use soroban_env_common::{Env, TryIntoVal}; use super::error::ContractError; use super::storage_types::BalanceValue; diff --git a/soroban-env-host/src/native_contract/token/contract.rs b/soroban-env-host/src/native_contract/token/contract.rs index 7eee41bf0..283287faf 100644 --- a/soroban-env-host/src/native_contract/token/contract.rs +++ b/soroban-env-host/src/native_contract/token/contract.rs @@ -18,7 +18,7 @@ use crate::native_contract::token::public_types::{Identifier, Metadata, Signatur use crate::{err, HostError}; use soroban_env_common::xdr::Asset; -use soroban_env_common::{CheckedEnv, Compare, EnvBase, Symbol, TryFromVal, TryIntoVal}; +use soroban_env_common::{Compare, Env, EnvBase, Symbol, TryFromVal, TryIntoVal}; use soroban_native_sdk_macros::contractimpl; use super::balance::{ diff --git a/soroban-env-host/src/native_contract/token/cryptography.rs b/soroban-env-host/src/native_contract/token/cryptography.rs index e66f38486..af88eee60 100644 --- a/soroban-env-host/src/native_contract/token/cryptography.rs +++ b/soroban-env-host/src/native_contract/token/cryptography.rs @@ -8,7 +8,7 @@ use crate::native_contract::token::public_types::{ use crate::{err, HostError}; use core::cmp::Ordering; use soroban_env_common::xdr::{ThresholdIndexes, Uint256}; -use soroban_env_common::{CheckedEnv, InvokerType, Symbol, TryFromVal, TryIntoVal}; +use soroban_env_common::{Env, InvokerType, Symbol, TryFromVal, TryIntoVal}; use super::error::ContractError; diff --git a/soroban-env-host/src/native_contract/token/event.rs b/soroban-env-host/src/native_contract/token/event.rs index 77dbfaea5..c687b46d6 100644 --- a/soroban-env-host/src/native_contract/token/event.rs +++ b/soroban-env-host/src/native_contract/token/event.rs @@ -2,7 +2,7 @@ use crate::host::Host; use crate::native_contract::base_types::Vec; use crate::native_contract::token::public_types::Identifier; use crate::HostError; -use soroban_env_common::{CheckedEnv, Symbol, TryIntoVal}; +use soroban_env_common::{Env, Symbol, TryIntoVal}; pub(crate) fn incr_allow( e: &Host, diff --git a/soroban-env-host/src/native_contract/token/metadata.rs b/soroban-env-host/src/native_contract/token/metadata.rs index 91f3d647f..9ef1b89ee 100644 --- a/soroban-env-host/src/native_contract/token/metadata.rs +++ b/soroban-env-host/src/native_contract/token/metadata.rs @@ -2,7 +2,7 @@ use crate::native_contract::base_types::Bytes; use crate::native_contract::token::public_types::Metadata; use crate::native_contract::token::storage_types::DataKey; use crate::{host::Host, HostError}; -use soroban_env_common::{CheckedEnv, EnvBase, TryFromVal, TryIntoVal}; +use soroban_env_common::{Env, EnvBase, TryFromVal, TryIntoVal}; // Metering: *mostly* covered by components. pub fn write_metadata(e: &Host, metadata: Metadata) -> Result<(), HostError> { diff --git a/soroban-env-host/src/native_contract/token/nonce.rs b/soroban-env-host/src/native_contract/token/nonce.rs index 6248a506d..a851934ab 100644 --- a/soroban-env-host/src/native_contract/token/nonce.rs +++ b/soroban-env-host/src/native_contract/token/nonce.rs @@ -1,7 +1,7 @@ use crate::native_contract::token::public_types::Identifier; use crate::native_contract::token::storage_types::DataKey; use crate::{host::Host, HostError}; -use soroban_env_common::{CheckedEnv, RawVal, TryIntoVal}; +use soroban_env_common::{Env, RawVal, TryIntoVal}; use super::error::ContractError; diff --git a/soroban-env-host/src/native_contract/token/test_token.rs b/soroban-env-host/src/native_contract/token/test_token.rs index d19cc04a4..3314b1fa1 100644 --- a/soroban-env-host/src/native_contract/token/test_token.rs +++ b/soroban-env-host/src/native_contract/token/test_token.rs @@ -5,7 +5,7 @@ use crate::{ }; use soroban_env_common::{ xdr::{Asset, ContractId, CreateContractArgs, HostFunction, ScContractCode}, - CheckedEnv, RawVal, + Env, RawVal, }; use soroban_env_common::{Symbol, TryFromVal, TryIntoVal}; diff --git a/soroban-env-host/src/test/account.rs b/soroban-env-host/src/test/account.rs index 9549f0c51..508cc95ee 100644 --- a/soroban-env-host/src/test/account.rs +++ b/soroban-env-host/src/test/account.rs @@ -1,6 +1,6 @@ use std::rc::Rc; -use soroban_env_common::CheckedEnv; +use soroban_env_common::Env; use crate::{ budget::Budget, diff --git a/soroban-env-host/src/test/budget_metering.rs b/soroban-env-host/src/test/budget_metering.rs index 8a35499eb..bcc6e3e55 100644 --- a/soroban-env-host/src/test/budget_metering.rs +++ b/soroban-env-host/src/test/budget_metering.rs @@ -1,7 +1,7 @@ use crate::{ budget::CostType, xdr::{ScMap, ScMapEntry, ScObject, ScVal, ScVmErrorCode}, - CheckedEnv, Host, HostError, RawVal, Symbol, + Env, Host, HostError, RawVal, Symbol, }; use soroban_test_wasms::VEC; diff --git a/soroban-env-host/src/test/bytes.rs b/soroban-env-host/src/test/bytes.rs index 91527d236..f583801d5 100644 --- a/soroban-env-host/src/test/bytes.rs +++ b/soroban-env-host/src/test/bytes.rs @@ -1,7 +1,7 @@ use crate::xdr::ScHostFnErrorCode; use crate::{ xdr::{ScHostObjErrorCode, ScObject, ScStatic, ScStatus, ScVal}, - CheckedEnv, Host, HostError, RawVal, RawValConvertible, + Env, Host, HostError, RawVal, RawValConvertible, }; use soroban_env_common::EnvBase; diff --git a/soroban-env-host/src/test/contract_event.rs b/soroban-env-host/src/test/contract_event.rs index 46a27a895..54030c5cf 100644 --- a/soroban-env-host/src/test/contract_event.rs +++ b/soroban-env-host/src/test/contract_event.rs @@ -13,12 +13,12 @@ pub struct EmptyContractWithEvents; impl ContractFunctionSet for EmptyContractWithEvents { fn call(&self, _func: &Symbol, host: &Host, _args: &[RawVal]) -> Option { // Add a contract event - let mut data = host.map_new(); - data = host.map_put(data, 1_u32.into(), 2_u32.into()); - let mut topics = host.vec_new(().into()); - topics = host.vec_push_back(topics, 0u32.into()); - topics = host.vec_push_back(topics, 1u32.into()); - Some(host.contract_event(topics, data.to_raw())) + let mut data = host.map_new().unwrap(); + data = host.map_put(data, 1_u32.into(), 2_u32.into()).unwrap(); + let mut topics = host.vec_new(().into()).unwrap(); + topics = host.vec_push_back(topics, 0u32.into()).unwrap(); + topics = host.vec_push_back(topics, 1u32.into()).unwrap(); + Some(host.contract_event(topics, data.to_raw()).unwrap()) } } @@ -32,7 +32,7 @@ fn contract_event() -> Result<(), HostError> { let args = host.test_vec_obj::(&[1, 2])?; host.register_test_contract(id, test_contract)?; assert_eq!( - host.call(id, sym.into(), args.into()).get_payload(), + host.call(id, sym.into(), args.into())?.get_payload(), RawVal::from_void().get_payload() ); diff --git a/soroban-env-host/src/test/crypto.rs b/soroban-env-host/src/test/crypto.rs index 018bdaf28..c8cb28f3a 100644 --- a/soroban-env-host/src/test/crypto.rs +++ b/soroban-env-host/src/test/crypto.rs @@ -1,6 +1,6 @@ use crate::{ xdr::{ScObject, ScVal}, - CheckedEnv, Host, HostError, + Env, Host, HostError, }; use hex::FromHex; diff --git a/soroban-env-host/src/test/invocation.rs b/soroban-env-host/src/test/invocation.rs index ad265cbe9..de0b3e55d 100644 --- a/soroban-env-host/src/test/invocation.rs +++ b/soroban-env-host/src/test/invocation.rs @@ -7,7 +7,7 @@ use crate::{ events::{DebugArg, HostEvent}, vm::Vm, xdr::{Hash, ScHostObjErrorCode, ScStatusType, ScVal, ScVec}, - CheckedEnv, Host, HostError, Status, Symbol, Tag, + Env, Host, HostError, Status, Symbol, Tag, }; use soroban_test_wasms::{ADD_I32, INVOKE_CONTRACT, VEC}; diff --git a/soroban-env-host/src/test/ledger.rs b/soroban-env-host/src/test/ledger.rs index 19249ef07..bfbc68452 100644 --- a/soroban-env-host/src/test/ledger.rs +++ b/soroban-env-host/src/test/ledger.rs @@ -1,4 +1,4 @@ -use soroban_env_common::CheckedEnv; +use soroban_env_common::Env; use crate::{ budget::Budget, diff --git a/soroban-env-host/src/test/lifecycle.rs b/soroban-env-host/src/test/lifecycle.rs index 0cca4cf16..d8f4dd547 100644 --- a/soroban-env-host/src/test/lifecycle.rs +++ b/soroban-env-host/src/test/lifecycle.rs @@ -7,7 +7,7 @@ use crate::{ HashIdPreimageSourceAccountContractId, HostFunction, InstallContractCodeArgs, LedgerEntryData, ScContractCode, ScObject, ScVal, ScVec, Uint256, }, - CheckedEnv, Host, LedgerInfo, Symbol, + Env, Host, LedgerInfo, Symbol, }; use soroban_env_common::{RawVal, TryIntoVal}; use soroban_test_wasms::CREATE_CONTRACT; diff --git a/soroban-env-host/src/test/map.rs b/soroban-env-host/src/test/map.rs index cdd0cca4f..ffc708316 100644 --- a/soroban-env-host/src/test/map.rs +++ b/soroban-env-host/src/test/map.rs @@ -6,7 +6,7 @@ use soroban_env_common::xdr::{ use crate::{ xdr::{ScMap, ScMapEntry, ScObject, ScVal, ScVec}, - CheckedEnv, Host, HostError, RawVal, RawValConvertible, Status, Symbol, + Env, Host, HostError, RawVal, RawValConvertible, Status, Symbol, }; #[test] diff --git a/soroban-env-host/src/test/str.rs b/soroban-env-host/src/test/str.rs index e09cf9c41..39544d812 100644 --- a/soroban-env-host/src/test/str.rs +++ b/soroban-env-host/src/test/str.rs @@ -2,7 +2,7 @@ use std::convert::TryInto; use soroban_env_common::TryIntoVal; -use crate::{CheckedEnv, Host, HostError, Object, RawVal}; +use crate::{Env, Host, HostError, Object, RawVal}; #[test] fn str_conversions() -> Result<(), HostError> { diff --git a/soroban-env-host/src/test/token.rs b/soroban-env-host/src/test/token.rs index e156d0765..52b487575 100644 --- a/soroban-env-host/src/test/token.rs +++ b/soroban-env-host/src/test/token.rs @@ -29,7 +29,7 @@ use soroban_env_common::{ }, RawVal, }; -use soroban_env_common::{CheckedEnv, Symbol, TryFromVal, TryIntoVal}; +use soroban_env_common::{Env, Symbol, TryFromVal, TryIntoVal}; use crate::native_contract::base_types::BytesN; diff --git a/soroban-env-host/src/test/tuple.rs b/soroban-env-host/src/test/tuple.rs index a4f73c70d..e91435977 100644 --- a/soroban-env-host/src/test/tuple.rs +++ b/soroban-env-host/src/test/tuple.rs @@ -1,6 +1,6 @@ use soroban_env_common::TryIntoVal; -use crate::{CheckedEnv, Host, HostError, RawVal}; +use crate::{Env, Host, HostError, RawVal}; #[test] fn tuple_conversions() -> Result<(), HostError> { diff --git a/soroban-env-host/src/test/vec.rs b/soroban-env-host/src/test/vec.rs index eda91d8ad..b543bc136 100644 --- a/soroban-env-host/src/test/vec.rs +++ b/soroban-env-host/src/test/vec.rs @@ -4,7 +4,7 @@ use soroban_env_common::Compare; use crate::{ xdr::{ScHostFnErrorCode, ScHostObjErrorCode, ScObject, ScObjectType}, - CheckedEnv, Host, HostError, Object, RawVal, RawValConvertible, + Env, Host, HostError, Object, RawVal, RawValConvertible, }; #[test] diff --git a/soroban-env-host/src/vm/dispatch.rs b/soroban-env-host/src/vm/dispatch.rs index b293b26db..2e51151a4 100644 --- a/soroban-env-host/src/vm/dispatch.rs +++ b/soroban-env-host/src/vm/dispatch.rs @@ -1,5 +1,5 @@ use crate::{ - budget::CostType, Host, HostError, Object, RawVal, Status, Symbol, VmCaller, VmCallerCheckedEnv, + budget::CostType, Host, HostError, Object, RawVal, Status, Symbol, VmCaller, VmCallerEnv, }; use soroban_env_common::call_macro_with_all_host_functions; use wasmi::core::{FromValue, Trap, TrapCode::UnexpectedSignature, Value}; diff --git a/soroban-env-host/tests/array.rs b/soroban-env-host/tests/array.rs index f968104a5..23c36b31d 100644 --- a/soroban-env-host/tests/array.rs +++ b/soroban-env-host/tests/array.rs @@ -1,37 +1,39 @@ -use soroban_env_host::Host; +use soroban_env_host::{Host, HostError}; use soroban_env_common::{xdr::ScObjectType, Env, Object, RawVal, TryIntoVal}; #[test] -fn u8_array() { +fn u8_array() -> Result<(), HostError> { let host = Host::default(); let arr = [1u8, 2, 3]; let val: RawVal = arr.try_into_val(&host).unwrap(); let obj: Object = val.try_into().unwrap(); assert!(obj.is_obj_type(ScObjectType::Bytes)); - assert_eq!(3u32, host.bytes_len(obj).try_into().unwrap()); - assert_eq!(1u32, host.bytes_get(obj, 0u32.into()).try_into().unwrap()); - assert_eq!(2u32, host.bytes_get(obj, 1u32.into()).try_into().unwrap()); - assert_eq!(3u32, host.bytes_get(obj, 2u32.into()).try_into().unwrap()); + assert_eq!(3u32, host.bytes_len(obj)?.try_into()?); + assert_eq!(1u32, host.bytes_get(obj, 0u32.into())?.try_into()?); + assert_eq!(2u32, host.bytes_get(obj, 1u32.into())?.try_into()?); + assert_eq!(3u32, host.bytes_get(obj, 2u32.into())?.try_into()?); - let arr: [u8; 3] = val.try_into_val(&host).unwrap(); + let arr: [u8; 3] = val.try_into_val(&host)?; assert_eq!(arr, [1, 2, 3]); + Ok(()) } #[test] -fn u8_slice() { +fn u8_slice() -> Result<(), HostError> { let host = Host::default(); let slice: &[u8] = &[1u8, 2, 3]; - let val: RawVal = slice.try_into_val(&host).unwrap(); - let obj: Object = val.try_into().unwrap(); + let val: RawVal = slice.try_into_val(&host)?; + let obj: Object = val.try_into()?; assert!(obj.is_obj_type(ScObjectType::Bytes)); - assert_eq!(3u32, host.bytes_len(obj).try_into().unwrap()); - assert_eq!(1u32, host.bytes_get(obj, 0u32.into()).try_into().unwrap()); - assert_eq!(2u32, host.bytes_get(obj, 1u32.into()).try_into().unwrap()); - assert_eq!(3u32, host.bytes_get(obj, 2u32.into()).try_into().unwrap()); + assert_eq!(3u32, host.bytes_len(obj)?.try_into()?); + assert_eq!(1u32, host.bytes_get(obj, 0u32.into())?.try_into()?); + assert_eq!(2u32, host.bytes_get(obj, 1u32.into())?.try_into()?); + assert_eq!(3u32, host.bytes_get(obj, 2u32.into())?.try_into()?); - let arr: [u8; 3] = val.try_into_val(&host).unwrap(); + let arr: [u8; 3] = val.try_into_val(&host)?; assert_eq!(arr, [1, 2, 3]); + Ok(()) } diff --git a/soroban-env-host/tests/integration.rs b/soroban-env-host/tests/integration.rs index c65b31e7a..a8a7a5cce 100644 --- a/soroban-env-host/tests/integration.rs +++ b/soroban-env-host/tests/integration.rs @@ -1,19 +1,19 @@ use soroban_env_host::{ - events::HostEvent, xdr::ScObjectType, Compare, Env, EnvBase, Host, Object, RawVal, + events::HostEvent, xdr::ScObjectType, Compare, Env, EnvBase, Host, HostError, Object, RawVal, }; #[test] -fn vec_as_seen_by_user() -> Result<(), ()> { +fn vec_as_seen_by_user() -> Result<(), HostError> { let host = Host::default(); - let int1 = host.obj_from_i64(5); + let int1 = host.obj_from_i64(5)?; - let vec1a = host.vec_new(RawVal::from_void()); - let vec1b = host.vec_push_back(vec1a, *int1.as_ref()); + let vec1a = host.vec_new(RawVal::from_void())?; + let vec1b = host.vec_push_back(vec1a, *int1.as_ref())?; assert_ne!(vec1a.as_raw().get_payload(), vec1b.as_raw().get_payload()); - let vec2a = host.vec_new(RawVal::from_void()); - let vec2b = host.vec_push_back(vec2a, *int1.as_ref()); + let vec2a = host.vec_new(RawVal::from_void())?; + let vec2b = host.vec_push_back(vec2a, *int1.as_ref())?; assert_ne!(vec2a.as_raw().get_payload(), vec2b.as_raw().get_payload()); assert_ne!(vec1b.as_raw().get_payload(), vec2b.as_raw().get_payload()); @@ -29,10 +29,11 @@ fn vec_as_seen_by_user() -> Result<(), ()> { } #[test] -fn vec_host_fn() { +fn vec_host_fn() -> Result<(), HostError> { let host = Host::default(); - let m = host.map_new(); + let m = host.map_new()?; assert!(Object::val_is_obj_type(m.into(), ScObjectType::Map)); + Ok(()) } #[test]