soroban_env_common/
env.rs

1use soroban_env_macros::generate_call_macro_with_all_host_functions;
2
3use crate::Object;
4
5use super::Symbol;
6use super::{
7    AddressObject, Bool, BytesObject, DurationObject, Error, I128Object, I256Object, I256Val,
8    I64Object, MapObject, StorageType, StringObject, SymbolObject, TimepointObject, U128Object,
9    U256Object, U256Val, U32Val, U64Object, U64Val, Val, VecObject, Void,
10};
11use crate::xdr::{ScErrorCode, ScErrorType};
12
13/// Base trait extended by the [Env](crate::Env) trait, providing various special-case
14/// functions that do _not_ simply call across cross the guest/host interface.
15pub trait EnvBase: Sized + Clone {
16    /// The type of error returned from the environment when the environment
17    /// itself fails "unrecoverably", or at least in a way that the user is not
18    /// expected to be able to recover from, such as an internal logic error,
19    /// exceeding the execution budget, or being passed malformed input in a way
20    /// that the user-facing API does not anticipate or allow for. This type is
21    /// returned from _all_ environment-interface methods, and will only ever
22    /// take on two possible concrete types: either `Infallible` (in the
23    /// `Guest`) or `HostError` (in the `Host`).
24    ///
25    /// The `Guest` can treat all such errors as impossible-to-observe since
26    /// they will result in the `Host` _trapping_ the `Guest` before returning
27    /// an `Error` to it. Such errors still remain present in the `Env` API so
28    /// that we can use the same API in both scenarios, rather than having to
29    /// have separate "fallible" or "infallible" environments and separate
30    /// conversion routines for each (as was attempted in earlier iterations).
31    ///
32    /// This type is _not_ the same as an error intended to make it to the
33    /// user-facing API: user-facing errors should return `Ok(Error)` at the
34    /// environment-interface level, and then either directly handle or escalate
35    /// the contained `Error` code to the user as a `Error` or `Result<>` of
36    /// some other type, depending on the API.
37    type Error: core::fmt::Debug + Into<crate::Error>;
38
39    /// Check that a [`Val`] is good according to the current Env. This is a
40    /// superset of calling `Val::good` as it also checks that if the `Val` is
41    /// an [`Object`], that the `Object` is good according to
42    /// [`Self::check_obj_integrity`].
43    fn check_val_integrity(&self, val: Val) -> Result<(), Self::Error> {
44        if !val.is_good() {
45            return Err(self.error_from_error_val(Error::from_type_and_code(
46                ScErrorType::Value,
47                ScErrorCode::InvalidInput,
48            )));
49        }
50        if let Ok(obj) = Object::try_from(val) {
51            self.check_obj_integrity(obj)
52        } else {
53            Ok(())
54        }
55    }
56
57    /// Check that an Object handle is good according to the current Env. For
58    /// general Val-validity checking one should use Val::good().
59    fn check_obj_integrity(&self, _obj: Object) -> Result<(), Self::Error> {
60        Ok(())
61    }
62
63    /// Convert a [`crate::Error`] into [`EnvBase::Error`]. This is similar to adding
64    /// `+ From<crate::Error>` to the associated type bound for `EnvBase::Error`
65    /// but it allows us to restrict that conversion in downstream crates, which
66    /// is desirable to keep "conversions that panic" (as the guest definition
67    /// of `EnvBase::Error` does) out of the common crate and avoid accidentally
68    /// triggering them in the host. It also gives the `Env` an opportunity to
69    /// log or enrich the error with context (both of which happen in `Host`).
70    fn error_from_error_val(&self, e: crate::Error) -> Self::Error;
71
72    /// Reject an error from the environment, turning it into a panic but on
73    /// terms that the environment controls (eg. enriching or logging it). This
74    /// should only ever be called by client-side / SDK local-testing code,
75    /// never in the `Host`.
76    #[cfg(feature = "testutils")]
77    fn escalate_error_to_panic(&self, e: Self::Error) -> !;
78
79    #[cfg(all(feature = "std", feature = "testutils"))]
80    #[deprecated(note = "replaced by trace_env_call")]
81    fn env_call_hook(&self, _fname: &'static str, _args: &[String]) -> Result<(), Self::Error> {
82        Ok(())
83    }
84
85    #[cfg(all(feature = "std", feature = "testutils"))]
86    #[deprecated(note = "replaced by trace_env_ret")]
87    fn env_ret_hook(
88        &self,
89        _fname: &'static str,
90        _res: &Result<String, &Self::Error>,
91    ) -> Result<(), Self::Error> {
92        Ok(())
93    }
94
95    /// Return true if the environment wants to receive trace calls and and
96    /// returns using [`Self::trace_env_call`] and [`Self::trace_env_ret`].
97    #[cfg(feature = "std")]
98    fn tracing_enabled(&self) -> bool {
99        false
100    }
101
102    /// A general interface for tracing all env-method calls, intended to
103    /// be called from macros that do dispatch on all such methods.
104    #[cfg(feature = "std")]
105    fn trace_env_call(
106        &self,
107        _fname: &'static str,
108        _args: &[&dyn core::fmt::Debug],
109    ) -> Result<(), Self::Error> {
110        Ok(())
111    }
112
113    /// A general interface for tracing all env-method returns, intended to
114    /// be called from macros that do dispatch on all such methods.
115    #[cfg(feature = "std")]
116    fn trace_env_ret(
117        &self,
118        _fname: &'static str,
119        _res: &Result<&dyn core::fmt::Debug, &Self::Error>,
120    ) -> Result<(), Self::Error> {
121        Ok(())
122    }
123
124    /// If `x` is `Err(...)`, ensure as much debug information as possible is
125    /// attached to that error; in any case return "essentially the same" `x` --
126    /// either `Ok(...)` or `Err(...)` -- just with extra error context.
127    ///
128    /// This is called on a best-effort basis while propagating errors in the
129    /// host, to attach context "as soon as possible", and is necessary because
130    /// some errors are generated in contexts that do not have access to a Host,
131    /// and so cannot attach error context at the site of error generation.
132    fn augment_err_result<T>(&self, x: Result<T, Self::Error>) -> Result<T, Self::Error> {
133        x
134    }
135
136    /// Used to check two environments are the same, returning Error if not.
137    fn check_same_env(&self, other: &Self) -> Result<(), Self::Error>;
138
139    // Helpers for methods that wish to pass Rust lifetime-qualified _slices_
140    // into the environment. These are _not_ done via Env trait methods to avoid
141    // the need to convert, and thus trust (or validate) "raw numbers" coming
142    // through that interface as "potentially pointers in the same address space
143    // as the host". This is a bit of a defense-in-depth approach as we _could_
144    // just accept "numbers as pointers in our address space" on a codepath that
145    // is sure its input is coming from a "trusted" contract, and arrange enough
146    // other static safety checks elsewhere in the calling path (eg. in the SDK)
147    // to ensure that "all callers are trusted" .. but we want to minimize the
148    // chance of future maintainers accidentally violating such an invariant,
149    // since getting it wrong would let guest code violate memory safety. So the
150    // _only_ interface to passing contract pointers to the host is going to be
151    // in EnvBase, not Env, and as a bonus we get lifetime checking for free.
152
153    /// Clone an existing `Bytes` object in the host, replacing the portion of
154    /// its memory with bytes supplied by `slice`, returning the new object. The
155    /// replaced portion of the original object's memory begins at `b_pos` and
156    /// extends for the same length as the new `slice`.
157    fn bytes_copy_from_slice(
158        &self,
159        b: BytesObject,
160        b_pos: U32Val,
161        slice: &[u8],
162    ) -> Result<BytesObject, Self::Error>;
163
164    /// Copy a slice of bytes from a `Bytes` object in the host into a slice in
165    /// the caller's memory.
166    fn bytes_copy_to_slice(
167        &self,
168        b: BytesObject,
169        b_pos: U32Val,
170        slice: &mut [u8],
171    ) -> Result<(), Self::Error>;
172
173    /// Copy a slice of bytes from a `String` object in the host into a slice in
174    /// the caller's memory.
175    fn string_copy_to_slice(
176        &self,
177        b: StringObject,
178        b_pos: U32Val,
179        slice: &mut [u8],
180    ) -> Result<(), Self::Error>;
181
182    /// Copy a slice of bytes from a `Symbol` object in the host into the
183    /// caller's memory.
184    fn symbol_copy_to_slice(
185        &self,
186        b: SymbolObject,
187        b_pos: U32Val,
188        mem: &mut [u8],
189    ) -> Result<(), Self::Error>;
190
191    /// Form a new `Bytes` host object from a slice of client memory.
192    fn bytes_new_from_slice(&self, slice: &[u8]) -> Result<BytesObject, Self::Error>;
193
194    /// Form a new `String` host object from a slice of client memory.
195    fn string_new_from_slice(&self, slice: &[u8]) -> Result<StringObject, Self::Error>;
196
197    /// Form a new `Symbol` host object from a slice of client memory.
198    fn symbol_new_from_slice(&self, slice: &[u8]) -> Result<SymbolObject, Self::Error>;
199
200    /// Form a new `Map` host object from a slice of symbol-names and a slice of values.
201    /// Keys must be in sorted order.
202    fn map_new_from_slices(&self, keys: &[&str], vals: &[Val]) -> Result<MapObject, Self::Error>;
203
204    /// Unpack a `Map` host object with a specified set of keys to a slice of
205    /// `Val`s. Keys must be in sorted order and must match the key set of
206    /// the unpacked object exactly.
207    fn map_unpack_to_slice(
208        &self,
209        map: MapObject,
210        keys: &[&str],
211        vals: &mut [Val],
212    ) -> Result<Void, Self::Error>;
213
214    /// Form a new `Vec` host object from a slice of values.
215    fn vec_new_from_slice(&self, vals: &[Val]) -> Result<VecObject, Self::Error>;
216
217    /// Form a new `Vec` host object from a slice of values. The values slice must
218    /// be the same length as the host object.
219    fn vec_unpack_to_slice(&self, vec: VecObject, vals: &mut [Val]) -> Result<Void, Self::Error>;
220
221    /// Return the index of a `Symbol` in an array of &strs, or error if not found.
222    fn symbol_index_in_strs(&self, key: Symbol, strs: &[&str]) -> Result<U32Val, Self::Error>;
223
224    /// Log a string and set of values as a diagnostic event, if diagnostic
225    /// events are enabled. When running on host, logs directly; when running on
226    /// guest, redirects through log_from_linear_memory.
227    fn log_from_slice(&self, msg: &str, vals: &[Val]) -> Result<Void, Self::Error>;
228
229    /// Check the current ledger protocol version against a provided lower
230    /// bound, error if protocol version is out-of-bound.
231    fn check_protocol_version_lower_bound(&self, lower_bound: u32) -> Result<(), Self::Error>;
232
233    /// Check the current ledger protocol version against a provided upper
234    /// bound, error if protocol version is out-of-bound.
235    fn check_protocol_version_upper_bound(&self, upper_bound: u32) -> Result<(), Self::Error>;
236}
237
238/// This trait is used by macro-generated dispatch and forwarding functions to
239/// check arguments being passed to the Env. The default implementations call
240/// through to the Env integrity-checking functions.
241pub trait CheckedEnvArg: Sized {
242    fn check_env_arg<E: crate::Env>(self, _e: &E) -> Result<Self, E::Error> {
243        Ok(self)
244    }
245}
246
247// If a new host function is added that uses argument types not yet listed
248// below, they will have to be added, otherwise this crate will not compile.
249
250impl CheckedEnvArg for i64 {}
251impl CheckedEnvArg for u64 {}
252impl CheckedEnvArg for StorageType {}
253
254macro_rules! impl_checkedenvarg_for_val_or_wrapper {
255    ($type:ty) => {
256        impl CheckedEnvArg for $type {
257            fn check_env_arg<E: crate::Env>(self, e: &E) -> Result<Self, E::Error> {
258                e.check_val_integrity(Val::from(self.clone()))?;
259                Ok(self)
260            }
261        }
262    };
263}
264impl_checkedenvarg_for_val_or_wrapper!(Val);
265impl_checkedenvarg_for_val_or_wrapper!(Symbol);
266
267impl_checkedenvarg_for_val_or_wrapper!(AddressObject);
268impl_checkedenvarg_for_val_or_wrapper!(BytesObject);
269impl_checkedenvarg_for_val_or_wrapper!(DurationObject);
270
271impl_checkedenvarg_for_val_or_wrapper!(TimepointObject);
272impl_checkedenvarg_for_val_or_wrapper!(SymbolObject);
273impl_checkedenvarg_for_val_or_wrapper!(StringObject);
274
275impl_checkedenvarg_for_val_or_wrapper!(VecObject);
276impl_checkedenvarg_for_val_or_wrapper!(MapObject);
277
278impl_checkedenvarg_for_val_or_wrapper!(I64Object);
279impl_checkedenvarg_for_val_or_wrapper!(I128Object);
280impl_checkedenvarg_for_val_or_wrapper!(I256Object);
281
282impl_checkedenvarg_for_val_or_wrapper!(U64Object);
283impl_checkedenvarg_for_val_or_wrapper!(U128Object);
284impl_checkedenvarg_for_val_or_wrapper!(U256Object);
285
286impl_checkedenvarg_for_val_or_wrapper!(U64Val);
287impl_checkedenvarg_for_val_or_wrapper!(U256Val);
288impl_checkedenvarg_for_val_or_wrapper!(I256Val);
289
290impl_checkedenvarg_for_val_or_wrapper!(Void);
291impl_checkedenvarg_for_val_or_wrapper!(Bool);
292impl_checkedenvarg_for_val_or_wrapper!(Error);
293impl_checkedenvarg_for_val_or_wrapper!(U32Val);
294
295///////////////////////////////////////////////////////////////////////////////
296// X-macro definition
297///////////////////////////////////////////////////////////////////////////////
298
299// The set of host functions need to be statically reflected-on in a variety of
300// contexts (both in this crate and elsewhere in the guest and host crates), so
301// we define them through an x-macro (a macro that calls a user-provided macro)
302// and call the x-macro from all such contexts.
303//
304// How this macro works:
305//  - It exports a higher-order "x-macro" called
306//    call_macro_with_all_host_functions
307//  - The x-macro takes the name of some callback macro to call
308//  - The x-macro invokes the callback macro once, passing a single large token
309//    tree, seen below in the body of the x-macro
310//
311// To use this macro:
312//  - Call sites define a callback macro that matches on the token-tree
313//  - Call sites invoke the x-macro passing their callback macro name
314//
315// The token-tree being passed is arbitrary, but is chosen to satisfy 3
316// criteria:
317//  - It's relatively easy to read, edit and understand its content
318//  - It's easy to decompose with pattern-matching in the callback macros
319//  - It contains everything any callback macro wants to match and use
320//
321// All callback macros have essentially the same token-tree matcher part,
322// only their expansion parts differ.
323
324generate_call_macro_with_all_host_functions!("env.json");
325
326///////////////////////////////////////////////////////////////////////////////
327/// X-macro use: defining trait Env
328///////////////////////////////////////////////////////////////////////////////
329//
330// This is a helper macro used only by generate_env_trait below. It consumes
331// a token-tree of the form:
332//
333//  {fn $fn_id:ident $args:tt -> $ret:ty}
334//
335// and produces the the corresponding method declaration to be used in the Env
336// trait.
337macro_rules! host_function_helper {
338    {
339        $($min_proto:literal)?, $($max_proto:literal)?,
340        $(#[$attr:meta])*
341        fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty}
342    =>
343    {
344        $(#[$attr])*
345        fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error>;
346    };
347}
348
349// This is a callback macro that pattern-matches the token-tree passed by the
350// x-macro (call_macro_with_all_host_functions) and produces a suite of method
351// declarations, which it places in the body of the declaration of the Env
352// trait.
353macro_rules! generate_env_trait {
354    {
355        $(
356            // This outer pattern matches a single 'mod' block of the token-tree
357            // passed from the x-macro to this macro. It is embedded in a `$()*`
358            // pattern-repetition matcher so that it will match all provided
359            // 'mod' blocks provided.
360            $(#[$mod_attr:meta])*
361            mod $mod_id:ident $mod_str:literal
362            {
363                $(
364                    // This inner pattern matches a single function description
365                    // inside a 'mod' block in the token-tree passed from the
366                    // x-macro to this macro. It is embedded in a `$()*`
367                    // pattern-repetition matcher so that it will match all such
368                    // descriptions.
369                    $(#[$fn_attr:meta])*
370                    { $fn_str:literal, $($min_proto:literal)?, $($max_proto:literal)?, fn $fn_id:ident $args:tt -> $ret:ty }
371                )*
372            }
373        )*
374    }
375
376    => // The part of the macro above this line is a matcher; below is its expansion.
377
378    {
379        // This macro expands to a single item: the Env trait.
380
381        /// This trait represents the interface between Host and Guest, used by
382        /// client contract code and implemented (via [Env](crate::Env)) by the host.
383        /// It consists of functions that take or return only 64-bit values such
384        /// as [Val] or [u64].
385        pub trait Env: EnvBase
386        {
387            $(
388                $(
389                    // This invokes the host_function_helper! macro above
390                    // passing only the relevant parts of the declaration
391                    // matched by the inner pattern above. It is embedded in two
392                    // nested `$()*` pattern-repetition expanders that
393                    // correspond to the pattern-repetition matchers in the
394                    // match section, but we ignore the structure of the 'mod'
395                    // block repetition-level from the outer pattern in the
396                    // expansion, flattening all functions from all 'mod' blocks
397                    // into the Env trait.
398                    host_function_helper!{$($min_proto)?, $($max_proto)?, $(#[$fn_attr])* fn $fn_id $args -> $ret}
399                )*
400            )*
401        }
402    };
403}
404
405// Here we invoke the x-macro passing generate_env_trait as its callback macro.
406call_macro_with_all_host_functions! { generate_env_trait }