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