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 }