1use crate::{
2 budget::AsBudget,
3 events::Events,
4 xdr::{self, LedgerKey, ScAddress, ScError, ScErrorCode, ScErrorType},
5 ConversionError, EnvBase, Error, Host, TryFromVal, U32Val, Val,
6};
7
8#[cfg(any(test, feature = "backtrace"))]
9use backtrace::{Backtrace, BacktraceFrame};
10use core::fmt::Debug;
11use std::{
12 cell::{Ref, RefCell, RefMut},
13 ops::DerefMut,
14};
15
16use super::metered_clone::MeteredClone;
17
18#[derive(Clone)]
19pub(crate) struct DebugInfo {
20 events: Events,
21 #[cfg(any(test, feature = "backtrace"))]
22 backtrace: Backtrace,
23}
24
25#[derive(Clone)]
26pub struct HostError {
27 pub error: Error,
28 pub(crate) info: Option<Box<DebugInfo>>,
29}
30
31impl std::error::Error for HostError {}
32
33impl Into<Error> for HostError {
34 fn into(self) -> Error {
35 self.error
36 }
37}
38
39impl DebugInfo {
40 fn write_events(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 const MAX_EVENTS: usize = 25;
44 let mut wrote_heading = false;
45 for (i, e) in self.events.0.iter().rev().take(MAX_EVENTS).enumerate() {
46 if !wrote_heading {
47 writeln!(f)?;
48 writeln!(f, "Event log (newest first):")?;
49 wrote_heading = true;
50 }
51 writeln!(f, " {}: {}", i, e)?;
52 }
53 if self.events.0.len() > MAX_EVENTS {
54 writeln!(
55 f,
56 " {}: ... {} events elided ...",
57 MAX_EVENTS,
58 self.events.0.len() - MAX_EVENTS
59 )?;
60 }
61 Ok(())
62 }
63
64 #[cfg(not(any(test, feature = "backtrace")))]
65 fn write_backtrace(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 Ok(())
67 }
68
69 #[cfg(any(test, feature = "backtrace"))]
70 fn write_backtrace(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 fn frame_name_matches(frame: &BacktraceFrame, pat: &str) -> bool {
76 for sym in frame.symbols() {
77 match sym.name() {
78 Some(sn) if format!("{:}", sn).contains(pat) => {
79 return true;
80 }
81 _ => (),
82 }
83 }
84 false
85 }
86
87 fn frame_is_short_backtrace_start(frame: &BacktraceFrame) -> bool {
88 frame_name_matches(frame, "__rust_begin_short_backtrace")
89 }
90
91 fn frame_is_initial_error_plumbing(frame: &BacktraceFrame) -> bool {
92 frame_name_matches(frame, "::from")
93 || frame_name_matches(frame, "::into")
94 || frame_name_matches(frame, "host::err")
95 || frame_name_matches(frame, "Host::err")
96 || frame_name_matches(frame, "Host>::err")
97 || frame_name_matches(frame, "::augment_err_result")
98 || frame_name_matches(frame, "::with_shadow_mode")
99 || frame_name_matches(frame, "::with_debug_mode")
100 || frame_name_matches(frame, "::maybe_get_debug_info")
101 || frame_name_matches(frame, "::map_err")
102 }
103 let mut bt = self.backtrace.clone();
104 bt.resolve();
105 let frames: Vec<BacktraceFrame> = bt
106 .frames()
107 .iter()
108 .skip_while(|f| frame_is_initial_error_plumbing(f))
109 .take_while(|f| !frame_is_short_backtrace_start(f))
110 .cloned()
111 .collect();
112 let bt: Backtrace = frames.into();
113 writeln!(f)?;
114 writeln!(f, "Backtrace (newest first):")?;
115 writeln!(f, "{:?}", bt)
116 }
117}
118
119impl Debug for HostError {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 writeln!(f, "HostError: {:?}", self.error)?;
122 if let Some(info) = &self.info {
123 info.write_events(f)?;
124 info.write_backtrace(f)
125 } else {
126 writeln!(f, "DebugInfo not available")
127 }
128 }
129}
130
131impl HostError {
132 #[cfg(any(test, feature = "testutils"))]
133 pub fn result_matches_err<T, C>(res: Result<T, HostError>, code: C) -> bool
134 where
135 Error: From<C>,
136 {
137 match res {
138 Ok(_) => {
139 eprintln!("result is not an error");
140 false
141 }
142 Err(he) => {
143 let error: Error = code.into();
144 if he.error != error {
145 eprintln!(
146 "expected error != actual error: {:?} != {:?}",
147 error, he.error
148 );
149 }
150 he.error == error
151 }
152 }
153 }
154
155 pub fn is_recoverable(&self) -> bool {
160 if !self.error.is_type(ScErrorType::Contract)
164 && self.error.is_code(ScErrorCode::InternalError)
165 {
166 return false;
167 }
168 if self.error.is_code(ScErrorCode::ExceededLimit)
172 && (self.error.is_type(ScErrorType::Storage) || self.error.is_type(ScErrorType::Budget))
173 {
174 return false;
175 }
176
177 true
178 }
179}
180
181impl<T> From<T> for HostError
182where
183 Error: From<T>,
184{
185 fn from(error: T) -> Self {
186 let error = error.into();
187 Self { error, info: None }
188 }
189}
190
191impl std::fmt::Display for HostError {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 <HostError as Debug>::fmt(self, f)
194 }
195}
196
197impl TryFrom<&HostError> for ScError {
198 type Error = xdr::Error;
199 fn try_from(err: &HostError) -> Result<Self, Self::Error> {
200 err.error.try_into()
201 }
202}
203
204impl From<HostError> for std::io::Error {
205 fn from(e: HostError) -> Self {
206 std::io::Error::new(std::io::ErrorKind::Other, e)
207 }
208}
209
210pub(crate) trait TryBorrowOrErr<T> {
211 fn try_borrow_or_err(&self) -> Result<Ref<'_, T>, Error>;
212 fn try_borrow_mut_or_err(&self) -> Result<RefMut<'_, T>, Error>;
213 fn try_borrow_or_err_with(&self, host: &Host, msg: &str) -> Result<Ref<'_, T>, HostError> {
214 self.try_borrow_or_err()
215 .map_err(|e| host.error(e, msg, &[]))
216 }
217 fn try_borrow_mut_or_err_with(
218 &self,
219 host: &Host,
220 msg: &str,
221 ) -> Result<RefMut<'_, T>, HostError> {
222 self.try_borrow_mut_or_err()
223 .map_err(|e| host.error(e, msg, &[]))
224 }
225}
226
227impl<T> TryBorrowOrErr<T> for RefCell<T> {
228 fn try_borrow_or_err(&self) -> Result<Ref<'_, T>, Error> {
229 self.try_borrow().map_err(|_| {
230 Error::from_type_and_code(ScErrorType::Context, ScErrorCode::InternalError)
231 })
232 }
233
234 fn try_borrow_mut_or_err(&self) -> Result<RefMut<'_, T>, Error> {
235 self.try_borrow_mut().map_err(|_| {
236 Error::from_type_and_code(ScErrorType::Context, ScErrorCode::InternalError)
237 })
238 }
239}
240
241impl Host {
242 pub(crate) fn err(
244 &self,
245 type_: ScErrorType,
246 code: ScErrorCode,
247 msg: &str,
248 args: &[Val],
249 ) -> HostError {
250 let error = Error::from_type_and_code(type_, code);
251 self.error(error, msg, args)
252 }
253
254 pub(crate) fn error(&self, error: Error, msg: &str, args: &[Val]) -> HostError {
260 let mut he = HostError::from(error);
261 self.with_debug_mode(|| {
262 if let Ok(mut events_refmut) = self.0.events.try_borrow_mut() {
270 self.record_err_diagnostics(events_refmut.deref_mut(), error, msg, args);
271 }
272 he = HostError {
273 error,
274 info: self.maybe_get_debug_info(),
275 };
276 Ok(())
277 });
278 he
279 }
280
281 pub(crate) fn maybe_get_debug_info(&self) -> Option<Box<DebugInfo>> {
282 #[allow(unused_mut)]
283 let mut res = None;
284 #[cfg(any(test, feature = "testutils"))]
289 {
290 self.with_debug_mode(|| {
291 if let Ok(events_ref) = self.0.events.try_borrow() {
292 let events = events_ref.externalize(self)?;
293 res = Some(Box::new(DebugInfo {
294 #[cfg(any(test, feature = "backtrace"))]
295 backtrace: Backtrace::new_unresolved(),
296 events,
297 }));
298 }
299 Ok(())
300 });
301 }
302 res
303 }
304
305 pub(crate) fn err_arith_overflow(&self) -> HostError {
308 self.err(
309 ScErrorType::Value,
310 ScErrorCode::ArithDomain,
311 "arithmetic overflow",
312 &[],
313 )
314 }
315
316 pub(crate) fn err_oob_linear_memory(&self) -> HostError {
317 self.err(
318 ScErrorType::WasmVm,
319 ScErrorCode::IndexBounds,
320 "out-of-bounds access to WASM linear memory",
321 &[],
322 )
323 }
324
325 pub(crate) fn err_oob_object_index(&self, index: Option<u32>) -> HostError {
326 let type_ = ScErrorType::Object;
327 let code = ScErrorCode::IndexBounds;
328 let msg = "object index out of bounds";
329 match index {
330 None => self.err(type_, code, msg, &[]),
331 Some(index) => self.err(type_, code, msg, &[U32Val::from(index).to_val()]),
332 }
333 }
334
335 pub(crate) fn map_err<T, E>(&self, res: Result<T, E>) -> Result<T, HostError>
353 where
354 Error: From<E>,
355 E: Debug,
356 {
357 res.map_err(|e| {
358 use std::borrow::Cow;
359 let mut msg: Cow<'_, str> = Cow::Borrowed(&"");
360 self.with_debug_mode(|| {
364 msg = Cow::Owned(format!("{:?}", e));
365 Ok(())
366 });
367 self.error(e.into(), &msg, &[])
368 })
369 }
370
371 pub(crate) fn account_address_from_key(&self, lk: &LedgerKey) -> Result<Val, HostError> {
375 let account_id = match lk {
376 LedgerKey::Account(e) => &e.account_id,
377 LedgerKey::Trustline(e) => &e.account_id,
378 _ => {
379 return Ok(Val::VOID.into());
380 }
381 };
382 self.add_host_object(ScAddress::Account(
383 account_id.metered_clone(self.as_budget())?,
384 ))
385 .map(|a| a.to_val())
386 }
387}
388
389pub(crate) trait DebugArg {
390 fn debug_arg(host: &Host, arg: &Self) -> Val {
391 let mut val: Option<Val> = None;
395 if let Ok(_guard) = host.0.events.try_borrow_mut() {
396 host.with_debug_mode(|| {
397 if let Ok(v) = Self::debug_arg_maybe_expensive_or_fallible(host, arg) {
398 val = Some(v);
399 }
400 Ok(())
401 });
402 val.unwrap_or_else(|| {
403 Error::from_type_and_code(ScErrorType::Events, ScErrorCode::InternalError).into()
404 })
405 } else {
406 Error::from_type_and_code(ScErrorType::Events, ScErrorCode::InternalError).into()
407 }
408 }
409 fn debug_arg_maybe_expensive_or_fallible(host: &Host, arg: &Self) -> Result<Val, HostError>;
410}
411
412impl<T> DebugArg for T
413where
414 Val: TryFromVal<Host, T>,
415 HostError: From<<Val as TryFromVal<Host, T>>::Error>,
416{
417 fn debug_arg_maybe_expensive_or_fallible(host: &Host, arg: &Self) -> Result<Val, HostError> {
418 Val::try_from_val(host, arg).map_err(|e| HostError::from(e))
419 }
420}
421
422impl DebugArg for xdr::Hash {
423 fn debug_arg_maybe_expensive_or_fallible(host: &Host, arg: &Self) -> Result<Val, HostError> {
424 host.bytes_new_from_slice(arg.as_slice()).map(|b| b.into())
425 }
426}
427
428impl DebugArg for str {
429 fn debug_arg_maybe_expensive_or_fallible(host: &Host, arg: &Self) -> Result<Val, HostError> {
430 host.string_new_from_slice(arg.as_bytes()).map(|s| s.into())
431 }
432}
433
434impl DebugArg for usize {
435 fn debug_arg_maybe_expensive_or_fallible(_host: &Host, arg: &Self) -> Result<Val, HostError> {
436 u32::try_from(*arg)
437 .map(|x| U32Val::from(x).into())
438 .map_err(|_| HostError::from(ConversionError))
439 }
440}
441
442#[macro_export]
452macro_rules! err {
453 ($host:expr, $error:expr, $msg:literal, $($args:expr),*) => {
454 {
455 const fn voidarg(_: &'static str) -> $crate::Val {
456 $crate::Val::VOID.to_val()
457 }
458 let mut buf = [$(voidarg(stringify!($args))),*];
464 let mut i = 0;
465 $host.with_debug_mode(||{
466 $(
467 buf[i] = <_ as $crate::host::error::DebugArg>::debug_arg($host, &$args);
469 i += 1;
471 )*
472 Ok(())
473 });
474 $host.error($error.into(), $msg, &buf[0..i])
475 }
476 };
477}