diff --git a/soroban-env-common/src/arbitrary.rs b/soroban-env-common/src/arbitrary.rs index bda6877c5..1f15f4bc7 100644 --- a/soroban-env-common/src/arbitrary.rs +++ b/soroban-env-common/src/arbitrary.rs @@ -10,9 +10,8 @@ use arbitrary::{Arbitrary, Unstructured}; impl<'a> Arbitrary<'a> for Error { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - let scstatus = ScError::arbitrary(u)?; - let status = Error::from(scstatus); - - Ok(status) + let scerror = ScError::arbitrary(u)?; + let error = Error::from(scerror); + Ok(error) } } diff --git a/soroban-env-common/src/bytes.rs b/soroban-env-common/src/bytes.rs index 693c4f99b..1c4410186 100644 --- a/soroban-env-common/src/bytes.rs +++ b/soroban-env-common/src/bytes.rs @@ -1,5 +1,7 @@ use crate::{ - declare_tag_based_object_wrapper, ConversionError, Env, Error, TryFromVal, TryIntoVal, Val, + declare_tag_based_object_wrapper, + xdr::{ScErrorCode, ScErrorType}, + Env, Error, TryFromVal, TryIntoVal, Val, }; declare_tag_based_object_wrapper!(BytesObject); @@ -8,14 +10,17 @@ impl TryFromVal for [u8; N] { type Error = Error; fn try_from_val(env: &E, val: &BytesObject) -> Result { - let len: u32 = env.bytes_len(*val).map_err(|_| ConversionError)?.into(); + let len: u32 = env.bytes_len(*val).map_err(Into::into)?.into(); let len = len as usize; if len != N { - return Err(ConversionError.into()); + return Err(Error::from_type_and_code( + ScErrorType::Value, + ScErrorCode::UnexpectedSize, + )); } let mut arr = [0u8; N]; env.bytes_copy_to_slice(*val, Val::U32_ZERO, &mut arr) - .map_err(|_| ConversionError)?; + .map_err(Into::into)?; Ok(arr) } } @@ -34,11 +39,11 @@ impl TryFromVal for Vec { type Error = Error; fn try_from_val(env: &E, val: &BytesObject) -> Result { - let len: u32 = env.bytes_len(*val).map_err(|_| ConversionError)?.into(); + let len: u32 = env.bytes_len(*val).map_err(Into::into)?.into(); let len = len as usize; let mut vec = vec![0u8; len]; env.bytes_copy_to_slice(*val, Val::U32_ZERO, &mut vec) - .map_err(|_| ConversionError)?; + .map_err(Into::into)?; Ok(vec) } } @@ -57,7 +62,7 @@ impl TryFromVal for BytesObject { type Error = Error; #[inline(always)] fn try_from_val(env: &E, v: &&[u8]) -> Result { - Ok(env.bytes_new_from_slice(v).map_err(|_| ConversionError)?) + env.bytes_new_from_slice(v).map_err(Into::into) } } diff --git a/soroban-env-common/src/compare.rs b/soroban-env-common/src/compare.rs index d39d44595..542506c88 100644 --- a/soroban-env-common/src/compare.rs +++ b/soroban-env-common/src/compare.rs @@ -1,7 +1,11 @@ #[cfg(feature = "std")] use std::rc::Rc; -use crate::{val::ValConvert, Env, Tag, Val}; +use crate::{ + val::ValConvert, + xdr::{ScErrorCode, ScErrorType}, + Env, Error, Tag, Val, +}; use core::cmp::Ordering; /// General trait representing the ability to compare two values of some type. @@ -51,11 +55,9 @@ macro_rules! impl_compare_for_tuple { fn compare(&self, a: &($($T,)+), b: &($($T,)+)) -> Result { $( - if let Ordering::Less = >::compare(self, &a.$idx, &b.$idx)? { - return Ok(Ordering::Less) - } - if let Ordering::Greater = >::compare(self, &a.$idx, &b.$idx)? { - return Ok(Ordering::Greater) + match >::compare(self, &a.$idx, &b.$idx)? { + unequal @ (Ordering::Less | Ordering::Greater) => return Ok(unequal), + _ => () } )* Ok(Ordering::Equal) @@ -194,7 +196,11 @@ impl Compare for E { | Tag::SymbolObject | Tag::VecObject | Tag::MapObject - | Tag::AddressObject => unreachable!(), + | Tag::AddressObject => Err(Error::from_type_and_code( + ScErrorType::Context, + ScErrorCode::InternalError, + ) + .into()), Tag::ObjectCodeUpperBound => Ok(Ordering::Equal), Tag::Bad => Ok(Ordering::Equal), diff --git a/soroban-env-common/src/convert.rs b/soroban-env-common/src/convert.rs index b4bf0170e..588b8a9dd 100644 --- a/soroban-env-common/src/convert.rs +++ b/soroban-env-common/src/convert.rs @@ -1,15 +1,16 @@ use crate::{ num::{i256_from_pieces, i256_into_pieces, u256_from_pieces, u256_into_pieces}, - ConversionError, DurationSmall, DurationVal, Env, I128Small, I128Val, I256Small, I256Val, - I64Small, TimepointSmall, TimepointVal, U128Small, U128Val, U256Small, U256Val, U64Small, - U64Val, Val, I256, U256, + DurationSmall, DurationVal, Env, I128Small, I128Val, I256Small, I256Val, I64Small, + TimepointSmall, TimepointVal, U128Small, U128Val, U256Small, U256Val, U64Small, U64Val, Val, + I256, U256, }; use core::fmt::Debug; use stellar_xdr::int128_helpers; #[cfg(feature = "std")] use crate::{ - num, object::ScValObjRef, val::ValConvert, Error, Object, ScValObject, SymbolSmall, Tag, + num, object::ScValObjRef, val::ValConvert, ConversionError, Error, Object, ScValObject, + SymbolSmall, Tag, }; #[cfg(feature = "std")] use stellar_xdr::{ @@ -19,17 +20,27 @@ use stellar_xdr::{ /// General trait representing a the ability of some object to perform a /// (possibly unsuccessful) conversion between two other types. pub trait Convert { - type Error: Debug; + type Error: Debug + Into + From; fn convert(&self, f: F) -> Result; } +/// The opposite trait to [`TryFromVal`], analogous to the way that [`TryInto`] +/// exists as an opposite to [`TryFrom`]. Exists only for convenience of doing +/// conversions via `.try_into_val(e)` or specifying convertability with a bound +/// like `TryIntoVal`. pub trait TryIntoVal { - type Error: Debug; + type Error: Debug + Into + From; fn try_into_val(&self, env: &E) -> Result; } +/// Trait for types that can be fallibly converted to another type `V`, analogous +/// to the standard Rust type [`TryFrom`], but making use of the provided `Env` +/// implementation `E` in order to convert parts of the type that require it. +/// Mainly this exists because `Val` types that contain object handles need to +/// delegate to the environment to look up and extract the content of those +/// handles. pub trait TryFromVal: Sized { - type Error: Debug; + type Error: Debug + Into + From; fn try_from_val(env: &E, v: &V) -> Result; } @@ -49,7 +60,7 @@ where // i64 conversions impl TryFromVal for i64 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &Val) -> Result { let val = *val; @@ -57,20 +68,20 @@ impl TryFromVal for i64 { Ok(so.into()) } else { let obj = val.try_into()?; - Ok(env.obj_to_i64(obj).map_err(|_| ConversionError)?) + Ok(env.obj_to_i64(obj).map_err(Into::into)?) } } } impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &i64) -> Result { let v = *v; if let Ok(so) = I64Small::try_from(v) { Ok(so.into()) } else { - Ok(env.obj_from_i64(v).map_err(|_| ConversionError)?.to_val()) + Ok(env.obj_from_i64(v).map_err(Into::into)?.to_val()) } } } @@ -78,7 +89,7 @@ impl TryFromVal for Val { // u64 conversions impl TryFromVal for u64 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &Val) -> Result { let val = *val; @@ -86,13 +97,13 @@ impl TryFromVal for u64 { Ok(so.into()) } else { let obj = val.try_into()?; - Ok(env.obj_to_u64(obj).map_err(|_| ConversionError)?) + Ok(env.obj_to_u64(obj).map_err(Into::into)?) } } } impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &u64) -> Result { Ok(U64Val::try_from_val(env, v)?.to_val()) @@ -100,7 +111,7 @@ impl TryFromVal for Val { } impl TryFromVal for u64 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &U64Val) -> Result { let val = *val; @@ -108,20 +119,20 @@ impl TryFromVal for u64 { Ok(so.into()) } else { let obj = val.try_into()?; - Ok(env.obj_to_u64(obj).map_err(|_| ConversionError)?) + Ok(env.obj_to_u64(obj).map_err(Into::into)?) } } } impl TryFromVal for U64Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &u64) -> Result { let v = *v; if let Ok(so) = U64Small::try_from(v) { Ok(so.into()) } else { - Ok(env.obj_from_u64(v).map_err(|_| ConversionError)?.into()) + Ok(env.obj_from_u64(v).map_err(Into::into)?.into()) } } } @@ -129,7 +140,7 @@ impl TryFromVal for U64Val { // {Timepoint, Duration} <-> u64 conversions impl TryFromVal for u64 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &TimepointVal) -> Result { let val = *val; @@ -137,29 +148,26 @@ impl TryFromVal for u64 { Ok(so.into()) } else { let obj = val.try_into()?; - Ok(env.timepoint_obj_to_u64(obj).map_err(|_| ConversionError)?) + Ok(env.timepoint_obj_to_u64(obj).map_err(Into::into)?) } } } impl TryFromVal for TimepointVal { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &u64) -> Result { let v = *v; if let Ok(so) = TimepointSmall::try_from(v) { Ok(so.into()) } else { - Ok(env - .timepoint_obj_from_u64(v) - .map_err(|_| ConversionError)? - .into()) + Ok(env.timepoint_obj_from_u64(v).map_err(Into::into)?.into()) } } } impl TryFromVal for u64 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &DurationVal) -> Result { let val = *val; @@ -167,23 +175,20 @@ impl TryFromVal for u64 { Ok(so.into()) } else { let obj = val.try_into()?; - Ok(env.duration_obj_to_u64(obj).map_err(|_| ConversionError)?) + Ok(env.duration_obj_to_u64(obj).map_err(Into::into)?) } } } impl TryFromVal for DurationVal { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &u64) -> Result { let v = *v; if let Ok(so) = DurationSmall::try_from(v) { Ok(so.into()) } else { - Ok(env - .duration_obj_from_u64(v) - .map_err(|_| ConversionError)? - .into()) + Ok(env.duration_obj_from_u64(v).map_err(Into::into)?.into()) } } } @@ -191,7 +196,7 @@ impl TryFromVal for DurationVal { // i128 conversions impl TryFromVal for i128 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &Val) -> Result { let v = *v; @@ -199,15 +204,15 @@ impl TryFromVal for i128 { Ok(so.into()) } else { let obj = v.try_into()?; - let hi = env.obj_to_i128_hi64(obj).map_err(|_| ConversionError)?; - let lo = env.obj_to_i128_lo64(obj).map_err(|_| ConversionError)?; + let hi = env.obj_to_i128_hi64(obj).map_err(Into::into)?; + let lo = env.obj_to_i128_lo64(obj).map_err(Into::into)?; Ok(int128_helpers::i128_from_pieces(hi, lo)) } } } impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &i128) -> Result { Ok(I128Val::try_from_val(env, v)?.to_val()) @@ -215,7 +220,7 @@ impl TryFromVal for Val { } impl TryFromVal for I128Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &i128) -> Result { let v = *v; @@ -224,7 +229,7 @@ impl TryFromVal for I128Val { } else { Ok(env .obj_from_i128_pieces(int128_helpers::i128_hi(v), int128_helpers::i128_lo(v)) - .map_err(|_| ConversionError)? + .map_err(Into::into)? .into()) } } @@ -233,7 +238,7 @@ impl TryFromVal for I128Val { // u128 conversions impl TryFromVal for u128 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &Val) -> Result { let v = *v; @@ -241,15 +246,15 @@ impl TryFromVal for u128 { Ok(so.into()) } else { let obj = v.try_into()?; - let hi = env.obj_to_u128_hi64(obj).map_err(|_| ConversionError)?; - let lo = env.obj_to_u128_lo64(obj).map_err(|_| ConversionError)?; + let hi = env.obj_to_u128_hi64(obj).map_err(Into::into)?; + let lo = env.obj_to_u128_lo64(obj).map_err(Into::into)?; Ok(int128_helpers::u128_from_pieces(hi, lo)) } } } impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &u128) -> Result { Ok(U128Val::try_from_val(env, v)?.to_val()) @@ -257,7 +262,7 @@ impl TryFromVal for Val { } impl TryFromVal for U128Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &u128) -> Result { let v = *v; @@ -266,7 +271,7 @@ impl TryFromVal for U128Val { } else { Ok(env .obj_from_u128_pieces(int128_helpers::u128_hi(v), int128_helpers::u128_lo(v)) - .map_err(|_| ConversionError)? + .map_err(Into::into)? .into()) } } @@ -274,7 +279,7 @@ impl TryFromVal for U128Val { // i256 conversions impl TryFromVal for I256 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &Val) -> Result { let v = *v; @@ -282,17 +287,17 @@ impl TryFromVal for I256 { Ok(so.into()) } else { let obj = v.try_into()?; - let hi_hi = env.obj_to_i256_hi_hi(obj).map_err(|_| ConversionError)?; - let hi_lo = env.obj_to_i256_hi_lo(obj).map_err(|_| ConversionError)?; - let lo_hi = env.obj_to_i256_lo_hi(obj).map_err(|_| ConversionError)?; - let lo_lo = env.obj_to_i256_lo_lo(obj).map_err(|_| ConversionError)?; + let hi_hi = env.obj_to_i256_hi_hi(obj).map_err(Into::into)?; + let hi_lo = env.obj_to_i256_hi_lo(obj).map_err(Into::into)?; + let lo_hi = env.obj_to_i256_lo_hi(obj).map_err(Into::into)?; + let lo_lo = env.obj_to_i256_lo_lo(obj).map_err(Into::into)?; Ok(i256_from_pieces(hi_hi, hi_lo, lo_hi, lo_lo)) } } } impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &I256) -> Result { Ok(I256Val::try_from_val(env, v)?.to_val()) @@ -300,7 +305,7 @@ impl TryFromVal for Val { } impl TryFromVal for I256Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &I256) -> Result { let v = *v; @@ -310,7 +315,7 @@ impl TryFromVal for I256Val { let (hi_hi, hi_lo, lo_hi, lo_lo) = i256_into_pieces(v); Ok(env .obj_from_i256_pieces(hi_hi, hi_lo, lo_hi, lo_lo) - .map_err(|_| ConversionError)? + .map_err(Into::into)? .into()) } } @@ -318,7 +323,7 @@ impl TryFromVal for I256Val { // u256 conversions impl TryFromVal for U256 { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &Val) -> Result { let v = *v; @@ -326,17 +331,17 @@ impl TryFromVal for U256 { Ok(so.into()) } else { let obj = v.try_into()?; - let hi_hi = env.obj_to_u256_hi_hi(obj).map_err(|_| ConversionError)?; - let hi_lo = env.obj_to_u256_hi_lo(obj).map_err(|_| ConversionError)?; - let lo_hi = env.obj_to_u256_lo_hi(obj).map_err(|_| ConversionError)?; - let lo_lo = env.obj_to_u256_lo_lo(obj).map_err(|_| ConversionError)?; + let hi_hi = env.obj_to_u256_hi_hi(obj).map_err(Into::into)?; + let hi_lo = env.obj_to_u256_hi_lo(obj).map_err(Into::into)?; + let lo_hi = env.obj_to_u256_lo_hi(obj).map_err(Into::into)?; + let lo_lo = env.obj_to_u256_lo_lo(obj).map_err(Into::into)?; Ok(u256_from_pieces(hi_hi, hi_lo, lo_hi, lo_lo)) } } } impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &U256) -> Result { Ok(U256Val::try_from_val(env, v)?.to_val()) @@ -344,7 +349,7 @@ impl TryFromVal for Val { } impl TryFromVal for U256Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &U256) -> Result { let v = *v; @@ -354,7 +359,7 @@ impl TryFromVal for U256Val { let (hi_hi, hi_lo, lo_hi, lo_lo) = u256_into_pieces(v); Ok(env .obj_from_u256_pieces(hi_hi, hi_lo, lo_hi, lo_lo) - .map_err(|_| ConversionError)? + .map_err(Into::into)? .into()) } } diff --git a/soroban-env-common/src/env.rs b/soroban-env-common/src/env.rs index 4fb35b78f..a5acdaed4 100644 --- a/soroban-env-common/src/env.rs +++ b/soroban-env-common/src/env.rs @@ -32,7 +32,7 @@ pub trait EnvBase: Sized + Clone { /// environment-interface level, and then either directly handle or escalate /// the contained `Error` code to the user as a `Error` or `Result<>` of /// some other type, depending on the API. - type Error: core::fmt::Debug; + type Error: core::fmt::Debug + Into + From; /// Reject an error from the environment, turning it into a panic but on /// terms that the environment controls (eg. transforming or logging it). diff --git a/soroban-env-common/src/error.rs b/soroban-env-common/src/error.rs index 01d3a9b95..19b25bfc7 100644 --- a/soroban-env-common/src/error.rs +++ b/soroban-env-common/src/error.rs @@ -163,6 +163,14 @@ impl From for Error { } } +// This never happens, but it's needed for some impls of TryFromVal downstream +// in the SDK that use the xdr::Error type. +impl From for stellar_xdr::Error { + fn from(_value: Error) -> Self { + stellar_xdr::Error::Unsupported + } +} + #[cfg(feature = "wasmi")] impl From for Error { fn from(code: wasmi::core::TrapCode) -> Self { @@ -286,6 +294,12 @@ impl From for crate::Error { } } +impl From for core::convert::Infallible { + fn from(_value: crate::Error) -> Self { + unreachable!() + } +} + #[cfg(all(test, feature = "std"))] mod tests { use super::*; diff --git a/soroban-env-common/src/result.rs b/soroban-env-common/src/result.rs index 7bafc1ed7..60747057b 100644 --- a/soroban-env-common/src/result.rs +++ b/soroban-env-common/src/result.rs @@ -1,19 +1,23 @@ -use crate::{ConversionError, Env, Error, TryFromVal, TryIntoVal, Val}; +use crate::{Env, Error, TryFromVal, TryIntoVal, Val}; impl TryFromVal for Result where T: TryFromVal, R: TryFrom, + >::Error: Into, { - type Error = ConversionError; + type Error = crate::Error; #[inline(always)] fn try_from_val(env: &E, val: &Val) -> Result { let val = *val; - if let Ok(status) = Error::try_from_val(env, &val) { - Ok(Err(status.try_into().map_err(|_| ConversionError)?)) + if let Ok(error) = Error::try_from_val(env, &val) { + match R::try_from(error) { + Ok(err) => Ok(Err(err)), + Err(err) => Err(err.into()), + } } else { - let converted = T::try_from_val(env, &val).map_err(|_| ConversionError)?; + let converted = T::try_from_val(env, &val).map_err(Into::into)?; Ok(Ok(converted)) } } @@ -22,17 +26,18 @@ where impl TryFromVal> for Val where Val: TryFromVal, - Error: for<'a> TryFrom<&'a R>, + for<'a> &'a R: TryInto, + for<'a> <&'a R as TryInto>::Error: Into, { - type Error = ConversionError; + type Error = crate::Error; #[inline(always)] fn try_from_val(env: &E, v: &Result) -> Result { match v { - Ok(t) => t.try_into_val(env).map_err(|_| ConversionError), + Ok(t) => t.try_into_val(env).map_err(Into::into), Err(r) => { - let status: Error = Error::try_from(r).map_err(|_| ConversionError)?; - Ok(status.into()) + let error: Error = r.try_into().map_err(Into::into)?; + Ok(error.into()) } } } diff --git a/soroban-env-common/src/storage_type.rs b/soroban-env-common/src/storage_type.rs index 9f0628ec5..59e2db995 100644 --- a/soroban-env-common/src/storage_type.rs +++ b/soroban-env-common/src/storage_type.rs @@ -1,7 +1,8 @@ +use crate::{ + declare_wasmi_marshal_for_enum, + xdr::{ContractDataDurability, ScErrorCode, ScErrorType}, +}; use num_derive::FromPrimitive; -use stellar_xdr::ContractDataDurability; - -use crate::{declare_wasmi_marshal_for_enum, ConversionError}; /// This is just a distinct enum local to the env interface that is used as /// an argument to storage functions. It doesn't correspond to any [`Val`] types, @@ -15,13 +16,16 @@ pub enum StorageType { } impl TryFrom for ContractDataDurability { - type Error = ConversionError; + type Error = crate::Error; fn try_from(value: StorageType) -> Result { match value { StorageType::Temporary => Ok(ContractDataDurability::Temporary), StorageType::Persistent => Ok(ContractDataDurability::Persistent), - StorageType::Instance => Err(ConversionError {}), + StorageType::Instance => Err(crate::Error::from_type_and_code( + ScErrorType::Value, + ScErrorCode::InvalidInput, + )), } } } diff --git a/soroban-env-common/src/string.rs b/soroban-env-common/src/string.rs index ec39fa9ac..bc333b208 100644 --- a/soroban-env-common/src/string.rs +++ b/soroban-env-common/src/string.rs @@ -1,27 +1,32 @@ -use crate::{declare_tag_based_object_wrapper, ConversionError, Env, TryFromVal, Val}; +use crate::{declare_tag_based_object_wrapper, Env, TryFromVal, Val}; #[cfg(feature = "std")] -use crate::TryIntoVal; +use crate::{ + xdr::{ScErrorCode, ScErrorType}, + TryIntoVal, +}; declare_tag_based_object_wrapper!(StringObject); #[cfg(feature = "std")] impl TryFromVal for String { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &StringObject) -> Result { - let len: u32 = env.string_len(*v).map_err(|_| ConversionError)?.into(); + let len: u32 = env.string_len(*v).map_err(Into::into)?.into(); let len = len as usize; let mut vec = std::vec![0; len]; env.string_copy_to_slice(*v, Val::U32_ZERO, &mut vec) - .map_err(|_| ConversionError)?; - String::from_utf8(vec).map_err(|_| ConversionError) + .map_err(Into::into)?; + String::from_utf8(vec).map_err(|_| { + crate::Error::from_type_and_code(ScErrorType::Value, ScErrorCode::InvalidInput) + }) } } #[cfg(feature = "std")] impl TryFromVal for String { - type Error = ConversionError; + type Error = crate::Error; #[inline(always)] fn try_from_val(env: &E, val: &Val) -> Result { @@ -31,15 +36,15 @@ impl TryFromVal for String { } impl TryFromVal for StringObject { - type Error = ConversionError; + type Error = crate::Error; #[inline(always)] fn try_from_val(env: &E, val: &&str) -> Result { - env.string_new_from_slice(val).map_err(|_| ConversionError) + env.string_new_from_slice(val).map_err(Into::into) } } impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; #[inline(always)] fn try_from_val(env: &E, val: &&str) -> Result { Ok(StringObject::try_from_val(env, val)?.into()) @@ -48,7 +53,7 @@ impl TryFromVal for Val { #[cfg(feature = "std")] impl TryFromVal for StringObject { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &String) -> Result { v.as_str().try_into_val(env) @@ -57,7 +62,7 @@ impl TryFromVal for StringObject { #[cfg(feature = "std")] impl TryFromVal for Val { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &String) -> Result { v.as_str().try_into_val(env) diff --git a/soroban-env-common/src/symbol.rs b/soroban-env-common/src/symbol.rs index fb5e73a0b..6102ce53c 100644 --- a/soroban-env-common/src/symbol.rs +++ b/soroban-env-common/src/symbol.rs @@ -84,21 +84,20 @@ sa::const_assert!(CODE_MASK == 0x3f); sa::const_assert!(CODE_BITS * MAX_SMALL_CHARS + 2 == BODY_BITS); impl TryFromVal for Symbol { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &&str) -> Result { if let Ok(ss) = SymbolSmall::try_from_str(v) { Ok(Self(ss.0)) - } else if let Ok(so) = env.symbol_new_from_slice(v) { - Ok(Self(so.0)) } else { - Err(ConversionError) + let sobj = env.symbol_new_from_slice(v).map_err(Into::into)?; + Ok(Self(sobj.0)) } } } impl TryFromVal for Symbol { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &&[u8]) -> Result { // We don't know this byte-slice is actually utf-8 ... @@ -312,7 +311,7 @@ impl From for SymbolStr { } impl TryFromVal for SymbolStr { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &Symbol) -> Result { if let Ok(ss) = SymbolSmall::try_from(*v) { @@ -321,7 +320,7 @@ impl TryFromVal for SymbolStr { let obj: SymbolObject = unsafe { SymbolObject::unchecked_from_val(v.0) }; let mut arr = [0u8; SCSYMBOL_LIMIT as usize]; env.symbol_copy_to_slice(obj, Val::U32_ZERO, &mut arr) - .map_err(|_| ConversionError)?; + .map_err(Into::into)?; Ok(SymbolStr(arr)) } } @@ -414,26 +413,26 @@ use crate::xdr::{ScSymbol, ScVal}; #[cfg(feature = "std")] impl TryFrom for SymbolSmall { - type Error = ConversionError; + type Error = crate::Error; fn try_from(v: ScVal) -> Result { (&v).try_into() } } #[cfg(feature = "std")] impl TryFrom<&ScVal> for SymbolSmall { - type Error = ConversionError; + type Error = crate::Error; fn try_from(v: &ScVal) -> Result { if let ScVal::Symbol(crate::xdr::ScSymbol(vec)) = v { - vec.try_into().map_err(|_| ConversionError) + vec.try_into().map_err(Into::into) } else { - Err(ConversionError) + Err(ConversionError.into()) } } } #[cfg(feature = "std")] impl TryFromVal for Symbol { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &ScVal) -> Result { Symbol::try_from_val(env, &v) @@ -442,19 +441,19 @@ impl TryFromVal for Symbol { #[cfg(feature = "std")] impl TryFromVal for Symbol { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &&ScVal) -> Result { if let ScVal::Symbol(sym) = v { Symbol::try_from_val(env, &sym) } else { - Err(ConversionError) + Err(ConversionError.into()) } } } #[cfg(feature = "std")] impl TryFromVal for Symbol { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &ScSymbol) -> Result { Symbol::try_from_val(env, &v) @@ -463,7 +462,7 @@ impl TryFromVal for Symbol { #[cfg(feature = "std")] impl TryFromVal for Symbol { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &&ScSymbol) -> Result { Symbol::try_from_val(env, &v.0.as_slice()) } @@ -471,37 +470,39 @@ impl TryFromVal for Symbol { #[cfg(feature = "std")] impl TryFrom for ScVal { - type Error = ConversionError; - fn try_from(s: SymbolSmall) -> Result { + type Error = crate::Error; + fn try_from(s: SymbolSmall) -> Result { let res: Result, _> = s.into_iter().map(>::try_from).collect(); - Ok(ScVal::Symbol( - res.map_err(|_| ConversionError)? - .try_into() - .map_err(|_| ConversionError)?, - )) + let vec = res.map_err(|_| { + crate::Error::from_type_and_code( + crate::xdr::ScErrorType::Value, + crate::xdr::ScErrorCode::InvalidInput, + ) + })?; + Ok(ScVal::Symbol(vec.try_into()?)) } } #[cfg(feature = "std")] impl TryFrom<&SymbolSmall> for ScVal { - type Error = ConversionError; - fn try_from(s: &SymbolSmall) -> Result { + type Error = crate::Error; + fn try_from(s: &SymbolSmall) -> Result { (*s).try_into() } } #[cfg(feature = "std")] impl TryFromVal for ScVal { - type Error = ConversionError; - fn try_from_val(e: &E, s: &Symbol) -> Result { + type Error = crate::Error; + fn try_from_val(e: &E, s: &Symbol) -> Result { Ok(ScVal::Symbol(ScSymbol::try_from_val(e, s)?)) } } #[cfg(feature = "std")] impl TryFromVal for ScSymbol { - type Error = ConversionError; - fn try_from_val(e: &E, s: &Symbol) -> Result { + type Error = crate::Error; + fn try_from_val(e: &E, s: &Symbol) -> Result { let sstr = SymbolStr::try_from_val(e, s)?; Ok(ScSymbol(sstr.0.as_slice()[0..sstr.len()].try_into()?)) } diff --git a/soroban-env-common/src/tuple.rs b/soroban-env-common/src/tuple.rs index e9026dfc7..5f9f9339b 100644 --- a/soroban-env-common/src/tuple.rs +++ b/soroban-env-common/src/tuple.rs @@ -1,4 +1,4 @@ -use crate::{ConversionError, Env, TryFromVal, TryIntoVal, Val, VecObject}; +use crate::{Env, TryFromVal, TryIntoVal, Val, VecObject}; macro_rules! impl_for_tuple { ( $count:literal $count_usize:literal $($typ:ident $idx:tt)+ ) => { @@ -9,13 +9,13 @@ macro_rules! impl_for_tuple { where $($typ: TryFromVal),* { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &Val) -> Result { let vec: VecObject = val.try_into()?; let mut tmp: [Val; $count as usize] = [Val::VOID.to_val(); $count as usize]; - env.vec_unpack_to_slice(vec, &mut tmp).map_err(|_| ConversionError)?; - Ok(($($typ::try_from_val(env, &tmp[$idx]).map_err(|_| ConversionError)?,)*)) + env.vec_unpack_to_slice(vec, &mut tmp).map_err(Into::into)?; + Ok(($($typ::try_from_val(env, &tmp[$idx]).map_err(Into::into)?,)*)) } } @@ -23,12 +23,12 @@ macro_rules! impl_for_tuple { where $($typ: TryIntoVal),* { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, v: &($($typ,)*)) -> Result { let tmp: [Val; $count as usize] = [ - $(v.$idx.try_into_val(&env).map_err(|_| ConversionError)?,)* + $(v.$idx.try_into_val(&env).map_err(Into::into)?,)* ]; - let vec = env.vec_new_from_slice(&tmp).map_err(|_| ConversionError)?; + let vec = env.vec_new_from_slice(&tmp).map_err(Into::into)?; Ok(vec.to_val()) } } @@ -40,12 +40,12 @@ macro_rules! impl_for_tuple { where $($typ: TryFromVal),* { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &[Val; N]) -> Result { Ok(( $({ - $typ::try_from_val(&env, &val[$idx]).map_err(|_| ConversionError)? + $typ::try_from_val(&env, &val[$idx]).map_err(Into::into)? },)* )) } @@ -55,11 +55,11 @@ macro_rules! impl_for_tuple { where $(Val: TryFromVal),* { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(env: &E, val: &($($typ,)*)) -> Result { let mut arr: [Val; N] = [Val::VOID.into(); N]; - $(arr[$idx] = val.$idx.try_into_val(env).map_err(|_| ConversionError)?;)* + $(arr[$idx] = val.$idx.try_into_val(env).map_err(Into::into)?;)* Ok(arr) } } @@ -87,7 +87,7 @@ impl_for_tuple! { 13_u32 13_usize T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T // Val::VOID, see raw_val.rs for those conversions. impl TryFromVal for () { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(_env: &E, _val: &[Val; 0]) -> Result { Ok(()) @@ -95,7 +95,7 @@ impl TryFromVal for () { } impl TryFromVal for [Val; 0] { - type Error = ConversionError; + type Error = crate::Error; fn try_from_val(_env: &E, _v: &()) -> Result { Ok([Val::VOID.into(); 0]) diff --git a/soroban-env-common/src/val.rs b/soroban-env-common/src/val.rs index e3caf6d20..5ed701c40 100644 --- a/soroban-env-common/src/val.rs +++ b/soroban-env-common/src/val.rs @@ -1,3 +1,6 @@ +// This permits globals prouced by derive(num_enum::TryFromPrimitive) below. +#![cfg_attr(test, allow(non_upper_case_globals))] + use crate::{ declare_tag_based_object_wrapper, declare_tag_based_wrapper, impl_rawval_wrapper_base, impl_tryfroms_and_tryfromvals_delegating_to_rawvalconvertible, Compare, I32Val, SymbolSmall, @@ -317,8 +320,15 @@ declare_tag_based_object_wrapper!(AddressObject); // away, the same way `()` would, while remaining a separate type to allow // conversion to a more-structured error code at a higher level. -/// Error type indicating a failure to convert some type to another; details -/// of the failed conversion will typically be written to the debug log. +/// Error type indicating a failure to convert some type to another; details of +/// the failed conversion will typically be written to the debug log. +/// +/// This is intentionally minimal and uninformative to minimize impact of its +/// use on wasm codesize. It converts to `Error(ScErrorType::Value, +/// ScErrorCode::UnexpectedType)` when converted to a full `Error`, and ideally +/// it should only be used in ubiquitous cases that will occur in wasm, like +/// small-number or tag conversions, where code size is paramount and the +/// information-loss from using it is not too bad. #[derive(Debug, Eq, PartialEq)] pub struct ConversionError; @@ -334,6 +344,12 @@ impl From for ConversionError { } } +impl From for ConversionError { + fn from(_: crate::Error) -> Self { + ConversionError + } +} + /// Trait abstracting over types that can be converted into [Val], similar to /// [TryFrom] but with a different signature that enables generating slightly /// more efficient conversion code. An implementation of `TryFrom` is also diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 22a4a0033..e10087ca9 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -1226,6 +1226,8 @@ impl EnvBase for Host { impl VmCallerEnv for Host { type VmUserState = Host; + // region: "context" module functions + // Notes on metering: covered by the components fn log_from_linear_memory( &self, @@ -1341,6 +1343,79 @@ impl VmCallerEnv for Host { Ok(Val::VOID) } + fn get_ledger_version(&self, _vmcaller: &mut VmCaller) -> Result { + self.with_ledger_info(|li| Ok(li.protocol_version.into())) + } + + fn get_ledger_sequence(&self, _vmcaller: &mut VmCaller) -> Result { + self.with_ledger_info(|li| Ok(li.sequence_number.into())) + } + + fn get_ledger_timestamp(&self, _vmcaller: &mut VmCaller) -> Result { + self.with_ledger_info(|li| Ok(self.add_host_object(li.timestamp)?.into())) + } + + fn get_current_call_stack( + &self, + _vmcaller: &mut VmCaller, + ) -> Result { + let contexts = self.try_borrow_context()?; + + let get_host_val_tuple = |id: &Hash, function: &Symbol| -> Result<[Val; 2], HostError> { + let addr_val = self + .add_host_object(ScAddress::Contract(id.metered_clone(self)?))? + .into(); + let function_val = (*function).into(); + Ok([addr_val, function_val]) + }; + + let mut outer = Vec::with_capacity(contexts.len()); + for context in contexts.iter() { + let vals = match &context.frame { + Frame::ContractVM { vm, fn_name, .. } => { + get_host_val_tuple(&vm.contract_id, fn_name)? + } + Frame::HostFunction(_) => continue, + Frame::Token(id, function, ..) => get_host_val_tuple(id, function)?, + #[cfg(any(test, feature = "testutils"))] + Frame::TestContract(tc) => get_host_val_tuple(&tc.id, &tc.func)?, + }; + let inner = MeteredVector::from_array(&vals, self.as_budget())?; + outer.push(self.add_host_object(inner)?.into()); + } + self.add_host_object(HostVec::from_vec(outer)?) + } + + fn fail_with_error( + &self, + vmcaller: &mut VmCaller, + error: Error, + ) -> Result { + if error.is_type(ScErrorType::Contract) { + Err(self.error( + error, + "failing with contract error", + &[U32Val::from(error.get_code()).to_val()], + )) + } else { + Err(self.err( + ScErrorType::Context, + ScErrorCode::UnexpectedType, + "contract attempted to fail with non-ContractError status code", + &[error.to_val()], + )) + } + } + + fn get_ledger_network_id( + &self, + _vmcaller: &mut VmCaller, + ) -> Result { + self.with_ledger_info(|li| { + self.add_host_object(self.scbytes_from_slice(li.network_id.as_slice())?) + }) + } + // Notes on metering: covered by the components. fn get_current_contract_address( &self, @@ -1351,6 +1426,17 @@ impl VmCallerEnv for Host { )) } + fn get_max_expiration_ledger( + &self, + _vmcaller: &mut VmCaller, + ) -> Result { + Ok(self.max_expiration_ledger()?.into()) + } + + // endregion "context" module functions + + // region: "int" module functions + impl_wrapping_obj_from_num!(obj_from_u64, u64, u64); impl_wrapping_obj_to_num!(obj_to_u64, u64, u64); impl_wrapping_obj_from_num!(obj_from_i64, i64, i64); @@ -1600,6 +1686,9 @@ impl VmCallerEnv for Host { self.add_host_object(HostMap::new()) } + // endregion "int" module functions + // region: "map" module functions + fn map_put( &self, _vmcaller: &mut VmCaller, @@ -1822,6 +1911,9 @@ impl VmCallerEnv for Host { Ok(Val::VOID) } + // endregion "map" module functions + // region: "vec" module functions + fn vec_new(&self, _vmcaller: &mut VmCaller) -> Result { self.add_host_object(HostVec::new()) } @@ -2068,6 +2160,9 @@ impl VmCallerEnv for Host { Ok(Val::VOID) } + // endregion "vec" module functions + // region: "ledger" module functions + // Notes on metering: covered by components fn put_contract_data( &self, @@ -2381,6 +2476,9 @@ impl VmCallerEnv for Host { Ok(Val::VOID) } + // endregion "ledger" module functions + // region: "call" module functions + // Notes on metering: here covers the args unpacking. The actual VM work is changed at lower layers. fn call( &self, @@ -2455,6 +2553,9 @@ impl VmCallerEnv for Host { } } + // endregion "call" module functions + // region: "buf" module functions + // Notes on metering: covered by components fn serialize_to_bytes( &self, @@ -2843,6 +2944,9 @@ impl VmCallerEnv for Host { self.add_host_object(self.scbytes_from_vec(vnew)?) } + // endregion "buf" module functions + // region: "crypto" module functions + // Notes on metering: covered by components. fn compute_hash_sha256( &self, @@ -2892,90 +2996,16 @@ impl VmCallerEnv for Host { self.recover_key_ecdsa_secp256k1_internal(&hash, &sig, rid) } - fn get_ledger_version(&self, _vmcaller: &mut VmCaller) -> Result { - self.with_ledger_info(|li| Ok(li.protocol_version.into())) - } - - fn get_ledger_sequence(&self, _vmcaller: &mut VmCaller) -> Result { - self.with_ledger_info(|li| Ok(li.sequence_number.into())) - } - - fn get_ledger_timestamp(&self, _vmcaller: &mut VmCaller) -> Result { - self.with_ledger_info(|li| Ok(self.add_host_object(li.timestamp)?.into())) - } - - fn get_max_expiration_ledger( - &self, - _vmcaller: &mut VmCaller, - ) -> Result { - Ok(self.max_expiration_ledger()?.into()) - } - - fn get_ledger_network_id( - &self, - _vmcaller: &mut VmCaller, - ) -> Result { - self.with_ledger_info(|li| { - self.add_host_object(self.scbytes_from_slice(li.network_id.as_slice())?) - }) - } - - fn get_current_call_stack( - &self, - _vmcaller: &mut VmCaller, - ) -> Result { - let contexts = self.try_borrow_context()?; - - let get_host_val_tuple = |id: &Hash, function: &Symbol| -> Result<[Val; 2], HostError> { - let addr_val = self - .add_host_object(ScAddress::Contract(id.metered_clone(self)?))? - .into(); - let function_val = (*function).into(); - Ok([addr_val, function_val]) - }; - - let mut outer = Vec::with_capacity(contexts.len()); - for context in contexts.iter() { - let vals = match &context.frame { - Frame::ContractVM { vm, fn_name, .. } => { - get_host_val_tuple(&vm.contract_id, fn_name)? - } - Frame::HostFunction(_) => continue, - Frame::Token(id, function, ..) => get_host_val_tuple(id, function)?, - #[cfg(any(test, feature = "testutils"))] - Frame::TestContract(tc) => get_host_val_tuple(&tc.id, &tc.func)?, - }; - let inner = MeteredVector::from_array(&vals, self.as_budget())?; - outer.push(self.add_host_object(inner)?.into()); - } - self.add_host_object(HostVec::from_vec(outer)?) - } - - fn fail_with_error( - &self, - vmcaller: &mut VmCaller, - error: Error, - ) -> Result { - if error.is_type(ScErrorType::Contract) { - Err(self.error( - error, - "failing with contract error", - &[U32Val::from(error.get_code()).to_val()], - )) - } else { - Err(self.err( - ScErrorType::Context, - ScErrorCode::UnexpectedType, - "contract attempted to fail with non-ContractError status code", - &[error.to_val()], - )) - } - } + // endregion "crypto" module functions + // region: "test" module functions fn dummy0(&self, vmcaller: &mut VmCaller) -> Result { Ok(().into()) } + // endregion "test" module functions + // region: "address" module functions + fn require_auth_for_args( &self, vmcaller: &mut VmCaller, @@ -3075,6 +3105,9 @@ impl VmCallerEnv for Host { } } + // endregion "address" module functions + // region: "prng" module functions + fn prng_reseed( &self, vmcaller: &mut VmCaller, @@ -3136,6 +3169,7 @@ impl VmCallerEnv for Host { })?; self.add_host_object(vnew) } + // endregion "prng" module functions } #[cfg(any(test, feature = "testutils"))] diff --git a/soroban-env-host/src/host/error.rs b/soroban-env-host/src/host/error.rs index 4aa5d6394..5af9c735c 100644 --- a/soroban-env-host/src/host/error.rs +++ b/soroban-env-host/src/host/error.rs @@ -29,6 +29,12 @@ pub struct HostError { impl std::error::Error for HostError {} +impl Into for HostError { + fn into(self) -> Error { + self.error + } +} + impl Debug for HostError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // We do a little trimming here, skipping the first two frames (which @@ -346,7 +352,7 @@ where HostError: From<>::Error>, { fn debug_arg_maybe_expensive_or_fallible(host: &Host, arg: &Self) -> Result { - Val::try_from_val(host, arg).map_err(|e| e.into()) + Val::try_from_val(host, arg).map_err(|e| HostError::from(e)) } } diff --git a/soroban-native-sdk-macros/src/derive_type.rs b/soroban-native-sdk-macros/src/derive_type.rs index c65ceab2c..028a3f00a 100644 --- a/soroban-native-sdk-macros/src/derive_type.rs +++ b/soroban-native-sdk-macros/src/derive_type.rs @@ -153,7 +153,7 @@ pub fn derive_type_enum(ident: &Ident, data: &DataEnum) -> TokenStream2 { quote! { impl #ident { - fn discriminant_sym(&self, env: &crate::Host) -> Result { + fn discriminant_sym(&self, env: &crate::Host) -> Result { use soroban_env_common::TryFromVal; match self { #(#syms,)*