Skip to main content

casper_contract_sdk/casper/
native.rs

1use std::{
2    cell::RefCell,
3    collections::{BTreeMap, BTreeSet, VecDeque},
4    convert::Infallible,
5    fmt,
6    panic::{self, UnwindSafe},
7    ptr::{self, NonNull},
8    slice,
9    sync::{Arc, RwLock},
10};
11
12use crate::linkme::distributed_slice;
13use bytes::Bytes;
14use casper_executor_wasm_common::{
15    env_info::EnvInfo,
16    error::{
17        CALLEE_REVERTED, CALLEE_SUCCEEDED, CALLEE_TRAPPED, HOST_ERROR_INTERNAL,
18        HOST_ERROR_NOT_FOUND, HOST_ERROR_SUCCESS,
19    },
20    flags::ReturnFlags,
21};
22#[cfg(not(target_arch = "wasm32"))]
23use rand::Rng;
24
25use super::Entity;
26use crate::types::Address;
27
28/// The kind of export that is being registered.
29///
30/// This is used to identify the type of export and its name.
31///
32/// Depending on the location of given function it may be registered as a:
33///
34/// * `SmartContract` (if it's part of a `impl Contract` block),
35/// * `TraitImpl` (if it's part of a `impl Trait for Contract` block),
36/// * `Function` (if it's a standalone function).
37///
38/// This is used to dispatch exports under native code i.e. you want to write a test that calls
39/// "foobar" regardless of location.
40#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
41pub enum EntryPointKind {
42    /// Smart contract.
43    ///
44    /// This is used to identify the smart contract and its name.
45    ///
46    /// The `struct_name` is the name of the smart contract that is being registered.
47    /// The `name` is the name of the function that is being registered.
48    SmartContract {
49        struct_name: &'static str,
50        name: &'static str,
51    },
52    /// Trait implementation.
53    ///
54    /// This is used to identify the trait implementation and its name.
55    ///
56    /// The `trait_name` is the name of the trait that is being implemented.
57    /// The `impl_name` is the name of the implementation.
58    /// The `name` is the name of the function that is being implemented.
59    TraitImpl {
60        trait_name: &'static str,
61        impl_name: &'static str,
62        name: &'static str,
63    },
64    /// Function export.
65    ///
66    /// This is used to identify the function export and its name.
67    ///
68    /// The `name` is the name of the function that is being exported.
69    Function { name: &'static str },
70}
71
72impl EntryPointKind {
73    pub fn name(&self) -> &'static str {
74        match self {
75            EntryPointKind::SmartContract { name, .. }
76            | EntryPointKind::TraitImpl { name, .. }
77            | EntryPointKind::Function { name } => name,
78        }
79    }
80}
81
82/// Export is a structure that contains information about the exported function.
83///
84/// This is used to register the export and its name and physical location in the smart contract
85/// source code.
86pub struct EntryPoint {
87    /// The kind of entry point that is being registered.
88    pub kind: EntryPointKind,
89    pub fptr: fn() -> (),
90    pub module_path: &'static str,
91    pub file: &'static str,
92    pub line: u32,
93}
94
95#[distributed_slice]
96#[linkme(crate = crate::linkme)]
97pub static ENTRY_POINTS: [EntryPoint];
98
99impl fmt::Debug for EntryPoint {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        let Self {
102            kind,
103            fptr: _,
104            module_path,
105            file,
106            line,
107        } = self;
108
109        f.debug_struct("Export")
110            .field("kind", kind)
111            .field("fptr", &"<fptr>")
112            .field("module_path", module_path)
113            .field("file", file)
114            .field("line", line)
115            .finish()
116    }
117}
118
119/// Invokes an export by its name.
120///
121/// This function is used to invoke an export by its name regardless of its location in the smart
122/// contract.
123pub fn invoke_export_by_name(name: &str) {
124    let exports_by_name: Vec<_> = ENTRY_POINTS
125        .iter()
126        .filter(|export| export.kind.name() == name)
127        .collect();
128
129    assert_eq!(
130        exports_by_name.len(),
131        1,
132        "Expected exactly one export {name} found, but got {exports_by_name:?}"
133    );
134
135    (exports_by_name[0].fptr)();
136}
137
138#[derive(Debug)]
139pub enum NativeTrap {
140    Return(ReturnFlags, Bytes),
141    Panic(Box<dyn std::any::Any + Send + 'static>),
142}
143
144pub type Container = BTreeMap<u64, BTreeMap<Bytes, Bytes>>;
145
146#[derive(Clone, Debug)]
147#[allow(dead_code)]
148pub struct NativeParam(pub(crate) String);
149
150impl From<&casper_contract_sdk_sys::Param> for NativeParam {
151    fn from(val: &casper_contract_sdk_sys::Param) -> Self {
152        let name =
153            String::from_utf8_lossy(unsafe { slice::from_raw_parts(val.name_ptr, val.name_len) })
154                .into_owned();
155        NativeParam(name)
156    }
157}
158
159#[derive(Clone, Debug)]
160pub struct Environment {
161    pub db: Arc<RwLock<Container>>,
162    contracts: Arc<RwLock<BTreeSet<Address>>>,
163    // input_data: Arc<RwLock<Option<Bytes>>>,
164    input_data: Option<Bytes>,
165    caller: Entity,
166    callee: Entity,
167}
168
169impl Default for Environment {
170    fn default() -> Self {
171        Self {
172            db: Default::default(),
173            contracts: Default::default(),
174            input_data: Default::default(),
175            caller: DEFAULT_ADDRESS,
176            callee: DEFAULT_ADDRESS,
177        }
178    }
179}
180
181pub const DEFAULT_ADDRESS: Entity = Entity::Account([42; 32]);
182
183impl Environment {
184    #[must_use]
185    pub fn new(db: Container, caller: Entity) -> Self {
186        Self {
187            db: Arc::new(RwLock::new(db)),
188            contracts: Default::default(),
189            input_data: Default::default(),
190            caller,
191            callee: caller,
192        }
193    }
194
195    #[must_use]
196    pub fn with_caller(&self, caller: Entity) -> Self {
197        let mut env = self.clone();
198        env.caller = caller;
199        env
200    }
201
202    #[must_use]
203    pub fn smart_contract(&self, callee: Entity) -> Self {
204        let mut env = self.clone();
205        env.caller = self.callee;
206        env.callee = callee;
207        env
208    }
209
210    #[must_use]
211    pub fn session(&self, callee: Entity) -> Self {
212        let mut env = self.clone();
213        env.caller = callee;
214        env.callee = callee;
215        env
216    }
217
218    #[must_use]
219    pub fn with_callee(&self, callee: Entity) -> Self {
220        let mut env = self.clone();
221        env.callee = callee;
222        env
223    }
224
225    #[must_use]
226    pub fn with_input_data(&self, input_data: Vec<u8>) -> Self {
227        let mut env = self.clone();
228        env.input_data = Some(Bytes::from(input_data));
229        env
230    }
231}
232
233impl Environment {
234    fn key_prefix(&self, key: &[u8]) -> Vec<u8> {
235        let entity = self.callee;
236
237        let mut bytes = Vec::new();
238        bytes.extend(entity.tag().to_le_bytes());
239        bytes.extend(entity.address());
240        bytes.extend(key);
241
242        bytes
243    }
244
245    fn casper_read(
246        &self,
247        key_space: u64,
248        key_ptr: *const u8,
249        key_size: usize,
250        info: *mut casper_contract_sdk_sys::ReadInfo,
251        alloc: extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8,
252        alloc_ctx: *const core::ffi::c_void,
253    ) -> Result<u32, NativeTrap> {
254        let key_bytes = unsafe { slice::from_raw_parts(key_ptr, key_size) };
255        let key_bytes = self.key_prefix(key_bytes);
256
257        let Ok(db) = self.db.read() else {
258            return Ok(HOST_ERROR_INTERNAL);
259        };
260
261        let value = match db.get(&key_space) {
262            Some(values) => values.get(key_bytes.as_slice()).cloned(),
263            None => return Ok(HOST_ERROR_NOT_FOUND),
264        };
265        match value {
266            Some(tagged_value) => {
267                let ptr = NonNull::new(alloc(tagged_value.len(), alloc_ctx as _));
268
269                if let Some(ptr) = ptr {
270                    unsafe {
271                        (*info).data = ptr.as_ptr();
272                        (*info).size = tagged_value.len();
273                    }
274
275                    unsafe {
276                        ptr::copy_nonoverlapping(
277                            tagged_value.as_ptr(),
278                            ptr.as_ptr(),
279                            tagged_value.len(),
280                        );
281                    }
282                }
283
284                Ok(HOST_ERROR_SUCCESS)
285            }
286            None => Ok(HOST_ERROR_NOT_FOUND),
287        }
288    }
289
290    fn casper_write(
291        &self,
292        key_space: u64,
293        key_ptr: *const u8,
294        key_size: usize,
295        value_ptr: *const u8,
296        value_size: usize,
297    ) -> Result<u32, NativeTrap> {
298        assert!(!key_ptr.is_null());
299        assert!(!value_ptr.is_null());
300        // let key_bytes = unsafe { slice::from_raw_parts(key_ptr, key_size) };
301        let key_bytes = unsafe { slice::from_raw_parts(key_ptr, key_size) }.to_owned();
302        let key_bytes = self.key_prefix(&key_bytes);
303
304        let value_bytes = unsafe { slice::from_raw_parts(value_ptr, value_size) };
305
306        let mut db = self.db.write().unwrap();
307        db.entry(key_space).or_default().insert(
308            Bytes::from(key_bytes.to_vec()),
309            Bytes::from(value_bytes.to_vec()),
310        );
311        Ok(HOST_ERROR_SUCCESS)
312    }
313
314    fn casper_remove(
315        &self,
316        key_space: u64,
317        key_ptr: *const u8,
318        key_size: usize,
319    ) -> Result<u32, NativeTrap> {
320        assert!(!key_ptr.is_null());
321        let key_bytes = unsafe { slice::from_raw_parts(key_ptr, key_size) };
322        let key_bytes = self.key_prefix(key_bytes);
323
324        let mut db = self.db.write().unwrap();
325        if let Some(values) = db.get_mut(&key_space) {
326            values.remove(key_bytes.as_slice());
327            Ok(HOST_ERROR_SUCCESS)
328        } else {
329            Ok(HOST_ERROR_NOT_FOUND)
330        }
331    }
332
333    fn casper_print(&self, msg_ptr: *const u8, msg_size: usize) -> Result<(), NativeTrap> {
334        let msg_bytes = unsafe { slice::from_raw_parts(msg_ptr, msg_size) };
335        let msg = std::str::from_utf8(msg_bytes).expect("Valid UTF-8 string");
336        println!("💻 {msg}");
337        Ok(())
338    }
339
340    fn casper_return(
341        &self,
342        flags: u32,
343        data_ptr: *const u8,
344        data_len: usize,
345    ) -> Result<Infallible, NativeTrap> {
346        let return_flags = ReturnFlags::from_bits_truncate(flags);
347        let data = if data_ptr.is_null() {
348            Bytes::new()
349        } else {
350            Bytes::copy_from_slice(unsafe { slice::from_raw_parts(data_ptr, data_len) })
351        };
352        Err(NativeTrap::Return(return_flags, data))
353    }
354
355    fn casper_copy_input(
356        &self,
357        alloc: extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8,
358        alloc_ctx: *const core::ffi::c_void,
359    ) -> Result<*mut u8, NativeTrap> {
360        let input_data = self.input_data.clone();
361        let input_data = input_data.as_ref().cloned().unwrap_or_default();
362        let ptr = NonNull::new(alloc(input_data.len(), alloc_ctx as _));
363
364        match ptr {
365            Some(ptr) => {
366                if !input_data.is_empty() {
367                    unsafe {
368                        ptr::copy_nonoverlapping(
369                            input_data.as_ptr(),
370                            ptr.as_ptr(),
371                            input_data.len(),
372                        );
373                    }
374                }
375                Ok(unsafe { ptr.as_ptr().add(input_data.len()) })
376            }
377            None => Ok(ptr::null_mut()),
378        }
379    }
380
381    #[allow(clippy::too_many_arguments)]
382    fn casper_create(
383        &self,
384        code_ptr: *const u8,
385        code_size: usize,
386        transferred_value: u64,
387        constructor_ptr: *const u8,
388        constructor_size: usize,
389        input_ptr: *const u8,
390        input_size: usize,
391        seed_ptr: *const u8,
392        seed_size: usize,
393        result_ptr: *mut casper_contract_sdk_sys::CreateResult,
394    ) -> Result<u32, NativeTrap> {
395        // let manifest =
396        //     NonNull::new(manifest_ptr as *mut casper_contract_sdk_sys::Manifest).expect("Manifest
397        // instance");
398        let code = if code_ptr.is_null() {
399            None
400        } else {
401            Some(unsafe { slice::from_raw_parts(code_ptr, code_size) })
402        };
403
404        if code.is_some() {
405            panic!("Supplying code is not supported yet in native mode");
406        }
407
408        let constructor = if constructor_ptr.is_null() {
409            None
410        } else {
411            Some(unsafe { slice::from_raw_parts(constructor_ptr, constructor_size) })
412        };
413
414        let input_data = if input_ptr.is_null() {
415            None
416        } else {
417            Some(unsafe { slice::from_raw_parts(input_ptr, input_size) })
418        };
419
420        let _seed = if seed_ptr.is_null() {
421            None
422        } else {
423            Some(unsafe { slice::from_raw_parts(seed_ptr, seed_size) })
424        };
425
426        assert_eq!(
427            transferred_value, 0,
428            "Creating new contracts with transferred value is not supported in native mode"
429        );
430
431        let mut rng = rand::thread_rng();
432        let contract_address = rng.gen();
433        let package_address = rng.gen();
434
435        let mut result = NonNull::new(result_ptr).expect("Valid pointer");
436        unsafe {
437            result.as_mut().contract_address = package_address;
438        }
439
440        let mut contracts = self.contracts.write().unwrap();
441        contracts.insert(contract_address);
442
443        if let Some(entry_point) = constructor {
444            let entry_point = ENTRY_POINTS
445                .iter()
446                .find(|export| export.kind.name().as_bytes() == entry_point)
447                .expect("Entry point exists");
448
449            let mut stub = with_current_environment(|stub| stub);
450            stub.input_data = input_data.map(Bytes::copy_from_slice);
451
452            stub.caller = stub.callee;
453            stub.callee = Entity::Contract(package_address);
454
455            // stub.callee
456            // Call constructor, expect a trap
457            let result = dispatch_with(stub, || {
458                // TODO: Handle panic inside constructor
459                (entry_point.fptr)();
460            });
461
462            match result {
463                Ok(()) => {}
464                Err(NativeTrap::Return(flags, bytes)) => {
465                    if flags.contains(ReturnFlags::REVERT) {
466                        todo!("Constructor returned with a revert flag");
467                    }
468                    assert!(bytes.is_empty(), "When returning from the constructor it is expected that no bytes are passed in a return function");
469                }
470                Err(NativeTrap::Panic(_panic)) => {
471                    todo!();
472                }
473            }
474        }
475
476        Ok(HOST_ERROR_SUCCESS)
477    }
478
479    #[allow(clippy::too_many_arguments)]
480    fn casper_call(
481        &self,
482        address_ptr: *const u8,
483        address_size: usize,
484        transferred_value: u64,
485        entry_point_ptr: *const u8,
486        entry_point_size: usize,
487        input_ptr: *const u8,
488        input_size: usize,
489        alloc: extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8, /* For capturing output
490                                                                         * data */
491        alloc_ctx: *const core::ffi::c_void,
492    ) -> Result<u32, NativeTrap> {
493        let address = unsafe { slice::from_raw_parts(address_ptr, address_size) };
494        let input_data = unsafe { slice::from_raw_parts(input_ptr, input_size) };
495        let entry_point = {
496            let entry_point_ptr = NonNull::new(entry_point_ptr.cast_mut()).expect("Valid pointer");
497            let entry_point =
498                unsafe { slice::from_raw_parts(entry_point_ptr.as_ptr(), entry_point_size) };
499            let entry_point = std::str::from_utf8(entry_point).expect("Valid UTF-8 string");
500            entry_point.to_string()
501        };
502
503        assert_eq!(
504            transferred_value, 0,
505            "Transferred value is not supported in native mode"
506        );
507
508        let export = ENTRY_POINTS
509            .iter()
510            .find(|export|
511                matches!(export.kind, EntryPointKind::SmartContract { name, .. } | EntryPointKind::TraitImpl { name, .. }
512                    if name == entry_point)
513            )
514            .expect("Existing entry point");
515
516        let mut new_stub = with_current_environment(|stub| stub.clone());
517        new_stub.input_data = Some(Bytes::copy_from_slice(input_data));
518        new_stub.caller = new_stub.callee;
519        new_stub.callee = Entity::Contract(address.try_into().expect("Size to match"));
520
521        let ret = dispatch_with(new_stub, || {
522            // We need to convert any panic inside the entry point into a native trap. This probably
523            // should be done in a more configurable way.
524            dispatch_export_call(|| {
525                (export.fptr)();
526            })
527        });
528
529        let unfolded = match ret {
530            Ok(Ok(())) => Ok(()),
531            Ok(Err(error)) | Err(error) => Err(error),
532        };
533
534        match unfolded {
535            Ok(()) => Ok(CALLEE_SUCCEEDED),
536            Err(NativeTrap::Return(flags, bytes)) => {
537                let ptr = NonNull::new(alloc(bytes.len(), alloc_ctx.cast_mut()));
538                if let Some(output_ptr) = ptr {
539                    unsafe {
540                        ptr::copy_nonoverlapping(bytes.as_ptr(), output_ptr.as_ptr(), bytes.len());
541                    }
542                }
543
544                if flags.contains(ReturnFlags::REVERT) {
545                    Ok(CALLEE_REVERTED)
546                } else {
547                    Ok(CALLEE_SUCCEEDED)
548                }
549            }
550            Err(NativeTrap::Panic(panic)) => {
551                eprintln!("Panic {panic:?}");
552                Ok(CALLEE_TRAPPED)
553            }
554        }
555    }
556
557    #[doc = r"Obtain data from the blockchain environemnt of current wasm invocation.
558
559Example paths:
560
561* `env_read([CASPER_CALLER], 1, nullptr, &caller_addr)` -> read caller's address into
562  `caller_addr` memory.
563* `env_read([CASPER_CHAIN, BLOCK_HASH, 0], 3, nullptr, &block_hash)` -> read hash of the
564  current block into `block_hash` memory.
565* `env_read([CASPER_CHAIN, BLOCK_HASH, 5], 3, nullptr, &block_hash)` -> read hash of the 5th
566  block from the current one into `block_hash` memory.
567* `env_read([CASPER_AUTHORIZED_KEYS], 1, nullptr, &authorized_keys)` -> read list of
568  authorized keys into `authorized_keys` memory."]
569    fn casper_env_read(
570        &self,
571        _env_path: *const u64,
572        _env_path_size: usize,
573        _alloc: Option<extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8>,
574        _alloc_ctx: *const core::ffi::c_void,
575    ) -> Result<*mut u8, NativeTrap> {
576        todo!()
577    }
578
579    fn casper_env_info(&self, info_ptr: *const u8, info_size: u32) -> Result<u32, NativeTrap> {
580        assert_eq!(info_size as usize, size_of::<EnvInfo>());
581        let mut env_info = NonNull::new(info_ptr as *mut u8)
582            .expect("Valid ptr")
583            .cast::<EnvInfo>();
584        let env_info = unsafe { env_info.as_mut() };
585        *env_info = EnvInfo {
586            block_time: 0,
587            transferred_value: 0,
588            caller_addr: *self.caller.address(),
589            caller_kind: self.caller.tag(),
590            callee_addr: *self.callee.address(),
591            callee_kind: self.callee.tag(),
592        };
593        Ok(HOST_ERROR_SUCCESS)
594    }
595}
596
597thread_local! {
598    pub(crate) static LAST_TRAP: RefCell<Option<NativeTrap>> = const { RefCell::new(None) };
599    static ENV_STACK: RefCell<VecDeque<Environment>> = RefCell::new(VecDeque::from_iter([
600        // Stack of environments has a default element so unit tests do not require extra effort.
601        // Environment::default()
602    ]));
603}
604
605pub fn with_current_environment<T>(f: impl FnOnce(Environment) -> T) -> T {
606    ENV_STACK.with(|stack| {
607        let stub = {
608            let borrowed = stack.borrow();
609            let front = borrowed.front().expect("Stub exists").clone();
610            front
611        };
612        f(stub)
613    })
614}
615
616pub fn current_environment() -> Environment {
617    with_current_environment(|env| env)
618}
619
620fn handle_ret_with<T>(value: Result<T, NativeTrap>, ret: impl FnOnce() -> T) -> T {
621    match value {
622        Ok(result) => {
623            LAST_TRAP.with(|last_trap| last_trap.borrow_mut().take());
624            result
625        }
626        Err(trap) => {
627            let result = ret();
628            LAST_TRAP.with(|last_trap| last_trap.borrow_mut().replace(trap));
629            result
630        }
631    }
632}
633
634fn dispatch_export_call<F>(func: F) -> Result<(), NativeTrap>
635where
636    F: FnOnce() + Send + UnwindSafe,
637{
638    let call_result = panic::catch_unwind(|| {
639        func();
640    });
641    match call_result {
642        Ok(()) => {
643            let last_trap = LAST_TRAP.with(|last_trap| last_trap.borrow_mut().take());
644            match last_trap {
645                Some(last_trap) => Err(last_trap),
646                None => Ok(()),
647            }
648        }
649        Err(error) => Err(NativeTrap::Panic(error)),
650    }
651}
652
653fn handle_ret<T: Default>(value: Result<T, NativeTrap>) -> T {
654    handle_ret_with(value, || T::default())
655}
656
657/// Dispatches a function with a default environment.
658pub fn dispatch<T>(f: impl FnOnce() -> T) -> Result<T, NativeTrap> {
659    dispatch_with(Environment::default(), f)
660}
661
662/// Dispatches a function with a given environment.
663pub fn dispatch_with<T>(stub: Environment, f: impl FnOnce() -> T) -> Result<T, NativeTrap> {
664    ENV_STACK.with(|stack| {
665        let mut borrowed = stack.borrow_mut();
666        borrowed.push_front(stub);
667    });
668
669    // Clear previous trap (if present)
670    LAST_TRAP.with(|last_trap| last_trap.borrow_mut().take());
671
672    // Call a function
673    let result = f();
674
675    // Check if a trap was set and return it if so (otherwise return the result).
676    let last_trap = LAST_TRAP.with(|last_trap| last_trap.borrow_mut().take());
677
678    let result = if let Some(trap) = last_trap {
679        Err(trap)
680    } else {
681        Ok(result)
682    };
683
684    // Pop the stub from the stack
685    ENV_STACK.with(|stack| {
686        let mut borrowed = stack.borrow_mut();
687        borrowed.pop_front();
688    });
689
690    result
691}
692
693mod symbols {
694    // TODO: Figure out how to use for_each_host_function macro here and deal with never type in
695    // casper_return
696    #[no_mangle]
697    /// Read value from a storage available for caller's entity address.
698    pub extern "C" fn casper_read(
699        key_space: u64,
700        key_ptr: *const u8,
701        key_size: usize,
702        info: *mut ::casper_contract_sdk_sys::ReadInfo,
703        alloc: extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8,
704        alloc_ctx: *const core::ffi::c_void,
705    ) -> u32 {
706        let _name = "casper_read";
707        let _args = (&key_space, &key_ptr, &key_size, &info, &alloc, &alloc_ctx);
708        let _call_result = with_current_environment(|stub| {
709            stub.casper_read(key_space, key_ptr, key_size, info, alloc, alloc_ctx)
710        });
711        crate::casper::native::handle_ret(_call_result)
712    }
713
714    #[no_mangle]
715    pub extern "C" fn casper_write(
716        key_space: u64,
717        key_ptr: *const u8,
718        key_size: usize,
719        value_ptr: *const u8,
720        value_size: usize,
721    ) -> u32 {
722        let _name = "casper_write";
723        let _args = (&key_space, &key_ptr, &key_size, &value_ptr, &value_size);
724        let _call_result = with_current_environment(|stub| {
725            stub.casper_write(key_space, key_ptr, key_size, value_ptr, value_size)
726        });
727        crate::casper::native::handle_ret(_call_result)
728    }
729
730    #[no_mangle]
731    pub extern "C" fn casper_remove(key_space: u64, key_ptr: *const u8, key_size: usize) -> u32 {
732        let _name = "casper_remove";
733        let _args = (&key_space, &key_ptr, &key_size);
734        let _call_result =
735            with_current_environment(|stub| stub.casper_remove(key_space, key_ptr, key_size));
736        crate::casper::native::handle_ret(_call_result)
737    }
738
739    #[no_mangle]
740    pub extern "C" fn casper_print(msg_ptr: *const u8, msg_size: usize) {
741        let _name = "casper_print";
742        let _args = (&msg_ptr, &msg_size);
743        let _call_result = with_current_environment(|stub| stub.casper_print(msg_ptr, msg_size));
744        crate::casper::native::handle_ret(_call_result);
745    }
746
747    use casper_executor_wasm_common::error::HOST_ERROR_SUCCESS;
748
749    use crate::casper::native::LAST_TRAP;
750
751    #[no_mangle]
752    pub extern "C" fn casper_return(flags: u32, data_ptr: *const u8, data_len: usize) {
753        let _name = "casper_return";
754        let _args = (&flags, &data_ptr, &data_len);
755        let _call_result =
756            with_current_environment(|stub| stub.casper_return(flags, data_ptr, data_len));
757        let err = _call_result.unwrap_err(); // SAFE
758        LAST_TRAP.with(|last_trap| last_trap.borrow_mut().replace(err));
759    }
760
761    #[no_mangle]
762    pub extern "C" fn casper_copy_input(
763        alloc: extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8,
764        alloc_ctx: *const core::ffi::c_void,
765    ) -> *mut u8 {
766        let _name = "casper_copy_input";
767        let _args = (&alloc, &alloc_ctx);
768        let _call_result =
769            with_current_environment(|stub| stub.casper_copy_input(alloc, alloc_ctx));
770        crate::casper::native::handle_ret_with(_call_result, ptr::null_mut)
771    }
772
773    #[no_mangle]
774    pub extern "C" fn casper_create(
775        code_ptr: *const u8,
776        code_size: usize,
777        transferred_value: u64,
778        constructor_ptr: *const u8,
779        constructor_size: usize,
780        input_ptr: *const u8,
781        input_size: usize,
782        seed_ptr: *const u8,
783        seed_size: usize,
784        result_ptr: *mut casper_contract_sdk_sys::CreateResult,
785    ) -> u32 {
786        let _call_result = with_current_environment(|stub| {
787            stub.casper_create(
788                code_ptr,
789                code_size,
790                transferred_value,
791                constructor_ptr,
792                constructor_size,
793                input_ptr,
794                input_size,
795                seed_ptr,
796                seed_size,
797                result_ptr,
798            )
799        });
800        crate::casper::native::handle_ret(_call_result)
801    }
802
803    #[no_mangle]
804    pub extern "C" fn casper_call(
805        address_ptr: *const u8,
806        address_size: usize,
807        transferred_value: u64,
808        entry_point_ptr: *const u8,
809        entry_point_size: usize,
810        input_ptr: *const u8,
811        input_size: usize,
812        alloc: extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8, /* For capturing output
813                                                                         * data */
814        alloc_ctx: *const core::ffi::c_void,
815    ) -> u32 {
816        let _call_result = with_current_environment(|stub| {
817            stub.casper_call(
818                address_ptr,
819                address_size,
820                transferred_value,
821                entry_point_ptr,
822                entry_point_size,
823                input_ptr,
824                input_size,
825                alloc,
826                alloc_ctx,
827            )
828        });
829        crate::casper::native::handle_ret(_call_result)
830    }
831
832    #[no_mangle]
833    pub extern "C" fn casper_upgrade(
834        _code_ptr: *const u8,
835        _code_size: usize,
836        _entry_point_ptr: *const u8,
837        _entry_point_size: usize,
838        _input_ptr: *const u8,
839        _input_size: usize,
840    ) -> u32 {
841        todo!()
842    }
843
844    use core::slice;
845    use std::ptr;
846
847    use super::with_current_environment;
848
849    #[no_mangle]
850    pub extern "C" fn casper_env_read(
851        env_path: *const u64,
852        env_path_size: usize,
853        alloc: Option<extern "C" fn(usize, *mut core::ffi::c_void) -> *mut u8>,
854        alloc_ctx: *const core::ffi::c_void,
855    ) -> *mut u8 {
856        let _name = "casper_env_read";
857        let _args = (&env_path, &env_path_size, &alloc, &alloc_ctx);
858        let _call_result = with_current_environment(|stub| {
859            stub.casper_env_read(env_path, env_path_size, alloc, alloc_ctx)
860        });
861        crate::casper::native::handle_ret_with(_call_result, ptr::null_mut)
862    }
863    #[no_mangle]
864    pub extern "C" fn casper_env_balance(
865        _entity_kind: u32,
866        _entity_addr_ptr: *const u8,
867        _entity_addr_len: usize,
868    ) -> u64 {
869        todo!()
870    }
871    #[no_mangle]
872    pub extern "C" fn casper_transfer(
873        _entity_kind: u32,
874        _entity_addr_ptr: *const u8,
875        _entity_addr_len: usize,
876        _amount: u64,
877    ) -> u32 {
878        todo!()
879    }
880    #[no_mangle]
881    pub extern "C" fn casper_emit(
882        topic_ptr: *const u8,
883        topic_size: usize,
884        data_ptr: *const u8,
885        data_size: usize,
886    ) -> u32 {
887        let topic = unsafe { slice::from_raw_parts(topic_ptr, topic_size) };
888        let data = unsafe { slice::from_raw_parts(data_ptr, data_size) };
889        let topic = std::str::from_utf8(topic).expect("Valid UTF-8 string");
890        println!("Emitting event with topic: {topic:?} and data: {data:?}");
891        HOST_ERROR_SUCCESS
892    }
893
894    #[no_mangle]
895    pub extern "C" fn casper_env_info(info_ptr: *const u8, info_size: u32) -> u32 {
896        let ret = with_current_environment(|env| env.casper_env_info(info_ptr, info_size));
897        crate::casper::native::handle_ret(ret)
898    }
899}
900
901#[cfg(test)]
902mod tests {
903    use casper_executor_wasm_common::keyspace::Keyspace;
904
905    use crate::casper;
906
907    use super::*;
908
909    #[test]
910    fn foo() {
911        dispatch(|| {
912            casper::print("Hello");
913            casper::write(Keyspace::Context(b"test"), b"value 1").unwrap();
914
915            let change_context_1 =
916                with_current_environment(|stub| stub.smart_contract(Entity::Contract([1; 32])));
917
918            dispatch_with(change_context_1, || {
919                casper::write(Keyspace::Context(b"test"), b"value 2").unwrap();
920                casper::write(Keyspace::State, b"state").unwrap();
921            })
922            .unwrap();
923
924            let change_context_1 =
925                with_current_environment(|stub| stub.smart_contract(Entity::Contract([1; 32])));
926            dispatch_with(change_context_1, || {
927                assert_eq!(
928                    casper::read_into_vec(Keyspace::Context(b"test")),
929                    Ok(Some(b"value 2".to_vec()))
930                );
931                assert_eq!(
932                    casper::read_into_vec(Keyspace::State),
933                    Ok(Some(b"state".to_vec()))
934                );
935            })
936            .unwrap();
937
938            assert_eq!(casper::get_caller(), DEFAULT_ADDRESS);
939            assert_eq!(
940                casper::read_into_vec(Keyspace::Context(b"test")),
941                Ok(Some(b"value 1".to_vec()))
942            );
943        })
944        .unwrap();
945    }
946    #[test]
947    fn test() {
948        dispatch_with(Environment::default(), || {
949            let msg = "Hello";
950            let () = with_current_environment(|stub| stub.casper_print(msg.as_ptr(), msg.len()))
951                .expect("Ok");
952        })
953        .unwrap();
954    }
955
956    #[test]
957    fn test_returns() {
958        dispatch_with(Environment::default(), || {
959            let _ = with_current_environment(|stub| stub.casper_return(0, ptr::null(), 0));
960        })
961        .unwrap();
962    }
963}