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#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
41pub enum EntryPointKind {
42 SmartContract {
49 struct_name: &'static str,
50 name: &'static str,
51 },
52 TraitImpl {
60 trait_name: &'static str,
61 impl_name: &'static str,
62 name: &'static str,
63 },
64 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
82pub struct EntryPoint {
87 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
119pub 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: 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) }.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 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 let result = dispatch_with(stub, || {
458 (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, 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 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 ]));
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
657pub fn dispatch<T>(f: impl FnOnce() -> T) -> Result<T, NativeTrap> {
659 dispatch_with(Environment::default(), f)
660}
661
662pub 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 LAST_TRAP.with(|last_trap| last_trap.borrow_mut().take());
671
672 let result = f();
674
675 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 ENV_STACK.with(|stack| {
686 let mut borrowed = stack.borrow_mut();
687 borrowed.pop_front();
688 });
689
690 result
691}
692
693mod symbols {
694 #[no_mangle]
697 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(); 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, 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}