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 }