1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[allow(unused)]
7use core::fmt::{Result as FmtResult, Write};
8
9use paste::paste;
10
11#[cfg(all(target_family = "wasm", target_os = "unknown"))]
12mod impls {
13 #[link(wasm_import_module = "console")]
14 #[cfg(feature = "console")]
15 unsafe extern "C" {
16 pub(crate) fn log_txt(ptr: *const u8, len: usize);
17 }
18
19 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
20 #[link(wasm_import_module = "vm_hooks")]
21 #[allow(unused)]
22 unsafe extern "C" {
23 pub(crate) fn exit_early(code: i32) -> !;
24 pub(crate) fn write_result(d: *const u8, l: usize);
25 pub(crate) fn transient_store_bytes32(key: *const u8, value: *const u8);
26 pub(crate) fn transient_load_bytes32(key: *const u8, value: *const u8);
27 }
28}
29
30#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
31mod impls {
32 pub unsafe fn transient_store_bytes32(_: *const u8, _: *const u8) {}
33 pub unsafe fn transient_load_bytes32(_: *const u8, _: *const u8) {}
34}
35
36#[cfg(all(target_family = "wasm", target_os = "unknown"))]
37fn write_result_slice(s: &[u8]) {
38 unsafe { impls::write_result(s.as_ptr(), s.len()) }
39}
40
41pub const PANIC_PREAMBLE_WORD: [u8; 32 + 4] = match const_hex::const_decode_to_array::<{ 32 + 4 }>(
43 b"4e487b710000000000000000000000000000000000000000000000000000000000000000",
44) {
45 Ok(v) => v,
46 Err(_) => panic!(),
47};
48
49pub const ERROR_PREAMBLE_OFFSET: [u8; 4 + 32] = match const_hex::const_decode_to_array::<{ 4 + 32 }>(
51 b"08c379a00000000000000000000000000000000000000000000000000000000000000020",
52) {
53 Ok(v) => v,
54 Err(_) => panic!(),
55};
56
57#[derive(Clone, Debug, PartialEq)]
58#[repr(u8)]
59pub enum PanicCodes {
60 DecodingError = 0,
61 OverflowOrUnderflow = 0x11,
62 DivByZero = 0x12,
63}
64
65#[cfg(all(target_family = "wasm", target_os = "unknown"))]
66pub fn panic_with_code(x: PanicCodes) -> ! {
67 let mut b = PANIC_PREAMBLE_WORD;
68 b[4 + 32 - 1] = x as u8;
69 write_result_slice(&b);
70 unsafe { impls::exit_early(1) }
71}
72
73#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
74pub fn panic_with_code(x: PanicCodes) -> ! {
75 panic!("panicked with code: {x:?}");
76}
77
78#[macro_export]
79macro_rules! define_panic_macros {
80 (
81 $(($error_msg:expr, $panic_code:ident)),* $(,)?
82 ) => {
83 $(
84 paste! {
85 #[macro_export]
86 macro_rules! [<panic_on_err_ $error_msg>] {
87 ($e:expr, $msg:expr) => {{
88 match $e {
89 Some(v) => v,
90 None => {
91 #[cfg(feature = "msg-on-sdk-err")]
92 panic!("{}: {}", $error_msg, $msg);
93 #[cfg(not(feature = "msg-on-sdk-err"))]
94 $crate::panic_with_code($crate::PanicCodes::$panic_code);
95 }
96 }
97 }};
98 }
99 }
100 )*
101 };
102}
103
104define_panic_macros!(
105 ("overflow", OverflowOrUnderflow),
106 ("div_by_zero", DivByZero),
107);
108
109#[macro_export]
110macro_rules! panic_on_err_bad_decoding_bool {
111 ($msg:expr) => {{
112 #[cfg(feature = "msg-on-sdk-err")]
113 panic!("error decoding: {}", $msg);
114 #[cfg(not(feature = "msg-on-sdk-err"))]
115 $crate::panic_with_code($crate::PanicCodes::DecodingError);
116 }};
117 ($e:expr, $msg:expr) => {{
118 if !$e {
119 panic_on_err_bad_decoding_bool!($msg);
120 }
121 }};
122}
123
124#[allow(unused)]
125struct SliceWriter<'a>(&'a mut [u8], usize);
126
127impl<'a> Write for SliceWriter<'a> {
128 fn write_str(&mut self, s: &str) -> FmtResult {
129 let v = s.len().min(REVERT_BUF_SIZE.saturating_sub(self.1));
130 self.0[self.1..self.1 + v].copy_from_slice(&s.as_bytes()[..v]);
131 self.1 += v;
132 Ok(())
133 }
134}
135
136pub const SLOT_TRACING_COUNTER: [u8; 32] = [
138 0xad, 0x59, 0xcd, 0x5c, 0xcd, 0xcd, 0x00, 0x59, 0x2c, 0xd2, 0x06, 0xdc, 0x3b, 0xce, 0x83, 0xac,
139 0xe6, 0x1b, 0x8c, 0x80, 0xcb, 0xe9, 0xfd, 0x0d, 0x70, 0x09, 0x34, 0xba, 0x13, 0x78, 0x92, 0x22,
140];
141
142#[allow(unused)]
146const REVERT_BUF_SIZE: usize = 1024 * 2;
147
148#[cfg(all(feature = "panic-revert", feature = "panic-loc"))]
149compile_error!("panic-revert and panic-loc simultaneously enabled");
150
151#[cfg(all(feature = "panic-revert", feature = "panic-trace"))]
152compile_error!("panic-revert and panic-trace simultaneously enabled");
153
154#[cfg(all(feature = "panic-loc", feature = "panic-trace"))]
155compile_error!("panic-loc and panic-trace simultaneously enabled");
156
157#[derive(Debug, Clone, Copy, PartialEq)]
158#[repr(u8)]
159enum TracingDiscriminant {
160 Number = 0,
161 String = 1,
162}
163
164#[cfg(all(target_family = "wasm", target_os = "unknown"))]
165#[cfg_attr(all(feature = "panic", not(feature = "std")), panic_handler)]
166pub fn panic_handler(_msg: &core::panic::PanicInfo) -> ! {
167 #[cfg(feature = "console")]
168 {
169 let msg = alloc::format!("{_msg}");
170 unsafe { impls::log_txt(msg.as_ptr(), msg.len()) }
171 }
172 #[cfg(any(
173 feature = "panic-revert",
174 feature = "panic-loc",
175 feature = "panic-trace"
176 ))]
177 {
178 let mut buf = [0u8; REVERT_BUF_SIZE];
179 buf[..ERROR_PREAMBLE_OFFSET.len()].copy_from_slice(&ERROR_PREAMBLE_OFFSET);
180 let mut w = SliceWriter(&mut buf[ERROR_PREAMBLE_OFFSET.len() + 32..], 0);
181 #[cfg(feature = "panic-revert")]
182 {
183 write!(&mut w, "{_msg}").unwrap();
184 }
185 #[cfg(feature = "panic-loc")]
186 if let Some(loc) = _msg.location() {
187 write!(&mut w, "panic: {}:{}", loc.file(), loc.line()).unwrap();
188 } else {
189 write!(&mut w, "panic: unknown").unwrap();
190 }
191 #[cfg(feature = "panic-trace")]
192 {
193 let mut b = [0u8; 32];
194 unsafe { impls::transient_load_bytes32(SLOT_TRACING_COUNTER.as_ptr(), b.as_mut_ptr()) };
195 if b[0] == TracingDiscriminant::Number as u8 {
196 write!(
197 &mut w,
198 "trace no: {}",
199 u32::from_be_bytes(b[1..32 - size_of::<u32>()].try_into().unwrap())
200 )
201 } else {
202 write!(&mut w, "trace str: {}", trace_key_to_str(&b))
203 }
204 .unwrap()
205 }
206 let len_msg = w.1;
207 let len_offset = ERROR_PREAMBLE_OFFSET.len();
208 buf[len_offset + 28..len_offset + 32].copy_from_slice(&(len_msg as u32).to_be_bytes());
209 let len_full = ERROR_PREAMBLE_OFFSET.len() + 32 + len_msg;
210 let len_padded = len_full + (32 - (len_full % 32)) % 32;
211 write_result_slice(&buf[..len_padded]);
212 unsafe { impls::exit_early(1) }
213 }
214 #[allow(unreachable_code)]
218 core::arch::wasm32::unreachable()
219}
220
221#[cfg(all(target_arch = "riscv32", target_os = "unknown"))]
222#[cfg_attr(all(feature = "panic", not(feature = "std")), panic_handler)]
223pub fn panic_handler(_: &core::panic::PanicInfo) -> ! {
224 unsafe {
226 core::arch::asm!("ebreak");
227 }
228 loop {}
229}
230
231pub fn bump() {
232 let p = SLOT_TRACING_COUNTER.as_ptr();
233 let mut b = [0u8; 32];
236 unsafe { impls::transient_load_bytes32(p, b.as_mut_ptr()) };
237 let v = u32::from_be_bytes(b[32 - size_of::<u32>()..].try_into().unwrap()) + 1;
238 b[32 - size_of::<u32>()..].copy_from_slice(&v.to_be_bytes());
239 b[0] = TracingDiscriminant::Number as u8;
240 unsafe { impls::transient_store_bytes32(p, b.as_ptr()) }
241}
242
243pub const fn trace_key_of_str(s: &str) -> [u8; 32] {
244 let bytes = s.as_bytes();
245 let mut b = [0u8; 32];
246 b[0] = TracingDiscriminant::String as u8;
247 let mut i = 0;
248 while i < bytes.len() && i < 31 {
249 b[i + 1] = bytes[i];
250 i += 1;
251 }
252 b
253}
254
255#[allow(unused)]
256const fn trace_key_to_str(b: &[u8; 32]) -> &str {
257 let mut i = 1;
258 while i < 32 {
259 if b[i] == 0 {
260 break;
261 }
262 i += 1;
263 }
264 unsafe {
265 let slice = core::slice::from_raw_parts(b.as_ptr().add(1), i - 1);
266 core::str::from_utf8_unchecked(slice)
267 }
268}
269
270pub fn trace(k: &str) {
271 let v = trace_key_of_str(k);
272 unsafe { impls::transient_store_bytes32(SLOT_TRACING_COUNTER.as_ptr(), v.as_ptr()) }
273}
274
275#[macro_export]
276macro_rules! trace_guard {
277 ($($body:tt)*) => {{
278 trace(concat!(file!(), ":", line!()));
279 $($body)*
280 }};
281}
282
283#[cfg(all(test, feature = "std"))]
284mod test {
285 use proptest::prelude::*;
286
287 use super::*;
288
289 proptest! {
290 #[test]
291 fn test_key_back_and_forth(x in proptest::string::string_regex("[0-9a-zA-Z]{0,31}").unwrap()) {
292 assert_eq!(&x, trace_key_to_str(&trace_key_of_str(&x)));
293 }
294 }
295}