veles_casper_ffi_shim/
lib.rs

1//! FFI shim for Casper host functions.
2//!
3//! This module provides a Foreign Function Interface (FFI) layer that allows
4//! smart contract code to run under any host target architecture. It defines
5//! the necessary set of functions to avoid `undefined symbol` when (for example)
6//! trying to run unit tests within your smart contract code.
7//!
8//! There is _some_ support for stubbing the host i.e. put_key/get_key/remove_key/has_key
9//! does implement a simple in-memory key-value store. However, most functions
10//! are just stubs that log their invocation and return default values.
11//!
12//! Importing this makes rust-analyzer happy.
13#![allow(unused_variables)]
14#![allow(clippy::missing_safety_doc)]
15
16/// Macro to handle unimplemented FFI functions without panicking
17macro_rules! unimplemented_ffi {
18    ($fn_name:expr) => {{
19        eprintln!("FFI function {} called but not implemented", $fn_name);
20        let error: u32 = ApiError::Unhandled.into();
21        error as i32
22    }};
23    ($fn_name:expr, void) => {{
24        eprintln!("FFI function {} called but not implemented", $fn_name);
25    }};
26}
27
28use std::{
29    cell::RefCell,
30    collections::{BTreeMap, VecDeque},
31    mem,
32    ptr::NonNull,
33    sync::{Arc, RwLock},
34};
35
36use casper_types::{
37    AccessRights, ApiError, CLTyped, CLValue, Key, StoredValue, U256, U512, URef, URefAddr,
38    api_error,
39    bytesrepr::{self, ToBytes},
40};
41
42// Custom error type for revert that can be handled without unwinding
43#[derive(Debug, Clone)]
44pub struct RevertError {
45    pub status: u32,
46    pub api_error: ApiError,
47}
48
49impl core::fmt::Display for RevertError {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        write!(f, "Casper revert: {:?} ({})", self.api_error, self.status)
52    }
53}
54
55impl core::error::Error for RevertError {}
56
57// Make RevertError compatible with panic_any
58unsafe impl Send for RevertError {}
59unsafe impl Sync for RevertError {}
60
61// Thread-local storage for revert errors
62thread_local! {
63    static REVERT_ERROR: RefCell<Option<RevertError>> = const { RefCell::new(None) };
64}
65
66/// Check if a revert occurred and return the error if it did
67pub fn check_revert() -> Option<RevertError> {
68    REVERT_ERROR.with(|r| r.borrow().clone())
69}
70
71/// Clear any pending revert error
72pub fn clear_revert() {
73    REVERT_ERROR.with(|r| *r.borrow_mut() = None);
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Hash)]
77pub enum HostFunction {
78    CasperReadValue,
79    CasperWrite,
80    CasperAdd,
81    CasperNewUref,
82    CasperLoadAuthorizationKeys,
83    CasperLoadNamedKeys,
84    CasperRet,
85    CasperGetKey(String),
86    CasperHasKey(String),
87    CasperPutKey(String, Key),
88    CasperRemoveKey(String),
89    CasperRevert,
90    CasperIsValidUref,
91    CasperAddAssociatedKey,
92    CasperRemoveAssociatedKey,
93    CasperUpdateAssociatedKey,
94    CasperSetActionThreshold,
95    CasperGetCaller,
96    CasperGetBlocktime,
97    CasperCreatePurse,
98    CasperTransferToAccount,
99    CasperTransferFromPurseToAccount,
100    CasperTransferFromPurseToPurse,
101    CasperGetBalance,
102    CasperGetPhase,
103    CasperGetSystemContract,
104    CasperGetMainPurse,
105    CasperReadHostBuffer,
106    CasperCreateContractPackageAtHash,
107    CasperCreateContractUserGroup,
108    CasperAddContractVersion,
109    CasperAddContractVersionWithMessageTopics,
110    CasperAddPackageVersionWithMessageTopics,
111    CasperDisableContractVersion,
112    CasperCallContract,
113    CasperCallVersionedContract,
114    CasperGetNamedArgSize,
115    CasperGetNamedArg,
116    CasperRemoveContractUserGroup,
117    CasperProvisionContractUserGroupUref,
118    CasperRemoveContractUserGroupUrefs,
119    CasperBlake2b,
120    CasperLoadCallStack,
121    CasperPrint,
122    CasperNewDictionary,
123    CasperDictionaryGet,
124    CasperDictionaryRead,
125    CasperDictionaryPut,
126    CasperRandomBytes,
127    CasperEnableContractVersion,
128    CasperManageMessageTopic,
129    CasperEmitMessage,
130    CasperLoadCallerInformation,
131    CasperGetBlockInfo,
132    CasperGenericHash,
133    CasperRecoverSecp256k1,
134    CasperVerifySignature,
135    CasperCallPackageVersion,
136}
137
138#[derive(Debug, Default)]
139pub struct EnvImpl {
140    /// Simplified, always creates deterministic addresses by counting up.
141    address_generator: U256,
142    database: BTreeMap<Key, StoredValue>,
143    args: BTreeMap<String, CLValue>,
144    named_keys: BTreeMap<String, Key>,
145    host_buffer: Option<CLValue>,
146    dictionaries: BTreeMap<URefAddr, BTreeMap<String, CLValue>>,
147    /// Very simple host function call trace for testing purposes.
148    trace: Vec<HostFunction>,
149}
150
151#[derive(Debug, Clone)]
152pub struct Env {
153    env_impl: Arc<RwLock<EnvImpl>>,
154}
155
156impl EnvImpl {
157    pub fn new() -> Self {
158        Self::default()
159    }
160    pub fn next_address(&mut self) -> [u8; 32] {
161        self.address_generator += U256::one();
162        let mut output = [0; 32];
163        self.address_generator.to_little_endian(&mut output);
164        output
165    }
166}
167
168impl Env {
169    pub fn named_keys(&self) -> BTreeMap<String, Key> {
170        self.env_impl.read().unwrap().named_keys.clone()
171    }
172
173    /// Returns and clears the current trace of host function calls.
174    ///
175    /// This is primarily intended for testing purposes.
176    pub fn trace(&self) -> Vec<HostFunction> {
177        mem::take(&mut self.env_impl.write().unwrap().trace)
178    }
179}
180
181#[derive(Debug)]
182pub struct EnvBuilder {
183    address_generator: U256,
184    database: BTreeMap<Key, StoredValue>,
185    args: BTreeMap<String, CLValue>,
186    named_keys: BTreeMap<String, Key>,
187    dictionaries: BTreeMap<URefAddr, BTreeMap<String, CLValue>>,
188}
189
190impl EnvBuilder {
191    pub fn new() -> Self {
192        Self {
193            address_generator: U256::zero(),
194            database: BTreeMap::new(),
195            args: BTreeMap::new(),
196            named_keys: BTreeMap::new(),
197            dictionaries: BTreeMap::new(),
198        }
199    }
200
201    pub fn with_address_generator(mut self, address_generator: U256) -> Self {
202        self.address_generator = address_generator;
203        self
204    }
205
206    pub fn with_database(mut self, database: BTreeMap<Key, StoredValue>) -> Self {
207        self.database = database;
208        self
209    }
210
211    pub fn with_args(mut self, args: BTreeMap<String, CLValue>) -> Self {
212        self.args = args;
213        self
214    }
215
216    pub fn with_arg<T: ToBytes + CLTyped>(mut self, name: impl Into<String>, value: T) -> Self {
217        let value = CLValue::from_t(value).expect("Failed to convert value to CLValue");
218        self.args.insert(name.into(), value);
219        self
220    }
221
222    pub fn with_storage(mut self, key: Key, value: StoredValue) -> Self {
223        self.database.insert(key, value);
224        self
225    }
226
227    pub fn with_named_keys(mut self, named_keys: BTreeMap<String, Key>) -> Self {
228        self.named_keys = named_keys;
229        self
230    }
231
232    pub fn with_named_key(mut self, name: impl Into<String>, key: Key) -> Self {
233        self.named_keys.insert(name.into(), key);
234        self
235    }
236
237    pub fn build(self) -> Env {
238        Env {
239            env_impl: Arc::new(RwLock::new(EnvImpl {
240                address_generator: self.address_generator,
241                database: self.database,
242                args: self.args,
243                named_keys: self.named_keys,
244                host_buffer: None,
245                dictionaries: self.dictionaries,
246                trace: Vec::new(),
247            })),
248        }
249    }
250}
251
252impl Default for EnvBuilder {
253    fn default() -> Self {
254        Self::new()
255    }
256}
257
258thread_local! {
259    static ENV: RefCell<RwLock<VecDeque<Env>>> = const { RefCell::new(RwLock::new(VecDeque::new())) };
260
261}
262
263pub fn dispatch_with<F>(new_env: Env, func: F)
264where
265    F: FnOnce(&Env),
266{
267    ENV.with(|stack| {
268        let env = stack.borrow();
269        env.write().unwrap().push_back(new_env.clone());
270
271        // Clear any previous revert error
272        clear_revert();
273
274        // Execute the function
275
276        func(&new_env);
277
278        env.write().unwrap().pop_back();
279    })
280}
281
282fn with_current_env<F, R>(func: F) -> R
283where
284    F: FnOnce(&mut EnvImpl) -> R,
285{
286    ENV.with(|stack| {
287        let env = stack.borrow();
288        let binding = env.read().unwrap();
289        let back_mut = binding.back().expect("Env should not be empty");
290        func(&mut back_mut.env_impl.write().unwrap())
291    })
292}
293
294#[unsafe(no_mangle)]
295pub unsafe extern "C" fn casper_read_value(
296    key_ptr: *const u8,
297    key_size: usize,
298    output_size: *mut usize,
299) -> i32 {
300    let key = unsafe { core::slice::from_raw_parts(key_ptr, key_size) };
301    let key: Key = bytesrepr::deserialize_from_slice(key).expect("Failed to deserialize key");
302    let mut output_size = NonNull::new(output_size).expect("output_size pointer must not be null");
303
304    with_current_env(|env| {
305        env.trace.push(HostFunction::CasperReadValue);
306        match env.database.get(&key) {
307            Some(value) => {
308                let cl_value: CLValue = value
309                    .clone()
310                    .try_into()
311                    .expect("Failed to convert to CLValue");
312
313                unsafe {
314                    *output_size.as_mut() = cl_value.inner_bytes().len();
315                }
316
317                let old_host_buffer = env.host_buffer.replace(cl_value);
318                if let Some(old_host_buffer) = &old_host_buffer {
319                    panic!("Host buffer should be empty before writing to it: {old_host_buffer:?}");
320                }
321
322                0 // Success
323            }
324            None => -1, // Not found
325        }
326    })
327}
328#[unsafe(no_mangle)]
329pub unsafe extern "C" fn casper_write(
330    key_ptr: *const u8,
331    key_size: usize,
332    value_ptr: *const u8,
333    value_size: usize,
334) {
335    let key = unsafe { core::slice::from_raw_parts(key_ptr, key_size) };
336    let key: Key = bytesrepr::deserialize_from_slice(key).expect("Failed to deserialize key");
337    let value = unsafe { core::slice::from_raw_parts(value_ptr, value_size) };
338    let value: CLValue =
339        bytesrepr::deserialize_from_slice(value).expect("Failed to deserialize value");
340
341    with_current_env(|env| {
342        env.trace.push(HostFunction::CasperWrite);
343        env.database.insert(key, StoredValue::CLValue(value));
344    })
345}
346#[unsafe(no_mangle)]
347pub unsafe extern "C" fn casper_add(
348    key_ptr: *const u8,
349    key_size: usize,
350    value_ptr: *const u8,
351    value_size: usize,
352) {
353    todo!()
354}
355#[unsafe(no_mangle)]
356pub unsafe extern "C" fn casper_new_uref(
357    uref_ptr: *mut u8,
358    value_ptr: *const u8,
359    value_size: usize,
360) {
361    let value = unsafe { core::slice::from_raw_parts(value_ptr, value_size) };
362    let value: CLValue =
363        bytesrepr::deserialize_from_slice(value).expect("Failed to deserialize value");
364
365    with_current_env(|env| {
366        env.trace.push(HostFunction::CasperNewUref);
367        let uref = URef::new(env.next_address(), AccessRights::READ_ADD_WRITE);
368        let key = Key::URef(uref);
369        env.database.insert(key, StoredValue::CLValue(value));
370
371        let key_bytes = uref.to_bytes().expect("Failed to serialize URef");
372        unsafe {
373            core::ptr::copy_nonoverlapping(key_bytes.as_ptr(), uref_ptr, key_bytes.len());
374        }
375    })
376}
377#[unsafe(no_mangle)]
378pub unsafe extern "C" fn casper_load_authorization_keys(
379    total_keys: *mut usize,
380    result_size: *mut usize,
381) -> i32 {
382    todo!()
383}
384#[unsafe(no_mangle)]
385pub unsafe extern "C" fn casper_load_named_keys(
386    total_keys: *mut usize,
387    result_size: *mut usize,
388) -> i32 {
389    todo!()
390}
391#[unsafe(no_mangle)]
392pub unsafe extern "C" fn casper_ret(value_ptr: *const u8, value_size: usize) -> ! {
393    todo!()
394}
395#[unsafe(no_mangle)]
396pub unsafe extern "C" fn casper_get_key(
397    name_ptr: *const u8,
398    name_size: usize,
399    output_ptr: *mut u8,
400    output_size: usize,
401    bytes_written_ptr: *mut usize,
402) -> i32 {
403    let result = with_current_env(|env| {
404        let name_bytes = unsafe { core::slice::from_raw_parts(name_ptr, name_size) };
405        let name: String =
406            bytesrepr::deserialize_from_slice(name_bytes).expect("Failed to deserialize name");
407        env.trace.push(HostFunction::CasperGetKey(name.clone()));
408
409        match env.named_keys.get(&name) {
410            Some(key) => {
411                let key_bytes = key.to_bytes().expect("Failed to serialize key");
412                if key_bytes.len() > output_size {
413                    return Err(ApiError::BufferTooSmall);
414                }
415                unsafe {
416                    core::ptr::copy_nonoverlapping(key_bytes.as_ptr(), output_ptr, key_bytes.len());
417                    *bytes_written_ptr = key_bytes.len();
418                }
419                Ok(()) // Success
420            }
421            None => Err(ApiError::MissingKey), // Key not found
422        }
423    });
424    api_error::i32_from(result)
425}
426#[unsafe(no_mangle)]
427pub unsafe extern "C" fn casper_has_key(name_ptr: *const u8, name_size: usize) -> i32 {
428    with_current_env(|env| {
429        let name_bytes = unsafe { core::slice::from_raw_parts(name_ptr, name_size) };
430        let name: String =
431            bytesrepr::deserialize_from_slice(name_bytes).expect("Failed to deserialize name");
432        env.trace.push(HostFunction::CasperHasKey(name.clone()));
433        if env.named_keys.contains_key(&name) {
434            0 // Key exists
435        } else {
436            1 // Key does not exist
437        }
438    })
439}
440#[unsafe(no_mangle)]
441pub unsafe extern "C" fn casper_put_key(
442    name_ptr: *const u8,
443    name_size: usize,
444    key_ptr: *const u8,
445    key_size: usize,
446) {
447    with_current_env(|env| {
448        let name_bytes = unsafe { core::slice::from_raw_parts(name_ptr, name_size) };
449        let name: String =
450            bytesrepr::deserialize_from_slice(name_bytes).expect("Failed to deserialize name");
451        let key_bytes = unsafe { core::slice::from_raw_parts(key_ptr, key_size) };
452        let key: Key =
453            bytesrepr::deserialize_from_slice(key_bytes).expect("Failed to deserialize key");
454        env.trace
455            .push(HostFunction::CasperPutKey(name.clone(), key));
456        env.named_keys.insert(name, key);
457    });
458}
459#[unsafe(no_mangle)]
460pub unsafe extern "C" fn casper_remove_key(name_ptr: *const u8, name_size: usize) {
461    with_current_env(|env| {
462        let name_bytes = unsafe { core::slice::from_raw_parts(name_ptr, name_size) };
463        let name: String =
464            bytesrepr::deserialize_from_slice(name_bytes).expect("Failed to deserialize name");
465        env.trace.push(HostFunction::CasperRemoveKey(name.clone()));
466        env.named_keys.remove(&name);
467    });
468}
469#[unsafe(no_mangle)]
470pub unsafe extern "C" fn casper_revert(status: u32) -> ! {
471    let api_error = ApiError::from(status);
472
473    // Store the revert error in thread-local storage for potential inspection
474    REVERT_ERROR.with(|r| *r.borrow_mut() = Some(RevertError { status, api_error }));
475
476    // Print comprehensive error information for debugging
477    eprintln!("=== CASPER REVERT ===");
478    eprintln!("Status: {}", status);
479    eprintln!("API Error: {:?}", api_error);
480    eprintln!("This indicates the smart contract execution was reverted.");
481    eprintln!("====================");
482
483    // Use abort for a clean termination without unwinding issues
484    std::process::abort();
485}
486#[unsafe(no_mangle)]
487pub unsafe extern "C" fn casper_is_valid_uref(uref_ptr: *const u8, uref_size: usize) -> i32 {
488    todo!()
489}
490#[unsafe(no_mangle)]
491pub unsafe extern "C" fn casper_add_associated_key(
492    account_hash_ptr: *const u8,
493    account_hash_size: usize,
494    weight: i32,
495) -> i32 {
496    todo!()
497}
498#[unsafe(no_mangle)]
499pub unsafe extern "C" fn casper_remove_associated_key(
500    account_hash_ptr: *const u8,
501    account_hash_size: usize,
502) -> i32 {
503    todo!()
504}
505#[unsafe(no_mangle)]
506pub unsafe extern "C" fn casper_update_associated_key(
507    account_hash_ptr: *const u8,
508    account_hash_size: usize,
509    weight: i32,
510) -> i32 {
511    todo!()
512}
513#[unsafe(no_mangle)]
514pub unsafe extern "C" fn casper_set_action_threshold(permission_level: u32, threshold: u32) -> i32 {
515    todo!()
516}
517#[unsafe(no_mangle)]
518pub unsafe extern "C" fn casper_get_caller(output_size_ptr: *mut usize) -> i32 {
519    todo!()
520}
521#[unsafe(no_mangle)]
522pub unsafe extern "C" fn casper_get_blocktime(dest_ptr: *const u8) {
523    todo!()
524}
525#[unsafe(no_mangle)]
526pub unsafe extern "C" fn casper_create_purse(purse_ptr: *mut u8, purse_size: usize) -> i32 {
527    with_current_env(|env| {
528        env.trace.push(HostFunction::CasperCreatePurse);
529        let uref = URef::new(env.next_address(), AccessRights::READ_ADD_WRITE);
530        let key_1 = Key::URef(uref);
531        let value_1 = StoredValue::CLValue(CLValue::unit());
532        env.database.insert(key_1, value_1);
533
534        let key_2 = Key::Balance(uref.addr());
535        let value_2 = StoredValue::CLValue(
536            CLValue::from_t(U512::zero()).expect("Failed to create CLValue for balance"),
537        );
538        env.database.insert(key_2, value_2);
539
540        let key_bytes = uref.to_bytes().expect("Failed to serialize URef");
541        unsafe {
542            core::ptr::copy_nonoverlapping(key_bytes.as_ptr(), purse_ptr, purse_size);
543        }
544    });
545
546    0 // Success
547}
548#[unsafe(no_mangle)]
549pub unsafe extern "C" fn casper_transfer_to_account(
550    target_ptr: *const u8,
551    target_size: usize,
552    amount_ptr: *const u8,
553    amount_size: usize,
554    id_ptr: *const u8,
555    id_size: usize,
556    result_ptr: *const i32,
557) -> i32 {
558    todo!()
559}
560#[unsafe(no_mangle)]
561pub unsafe extern "C" fn casper_transfer_from_purse_to_account(
562    source_ptr: *const u8,
563    source_size: usize,
564    target_ptr: *const u8,
565    target_size: usize,
566    amount_ptr: *const u8,
567    amount_size: usize,
568    id_ptr: *const u8,
569    id_size: usize,
570    result_ptr: *const i32,
571) -> i32 {
572    todo!()
573}
574#[unsafe(no_mangle)]
575pub unsafe extern "C" fn casper_transfer_from_purse_to_purse(
576    source_ptr: *const u8,
577    source_size: usize,
578    target_ptr: *const u8,
579    target_size: usize,
580    amount_ptr: *const u8,
581    amount_size: usize,
582    id_ptr: *const u8,
583    id_size: usize,
584) -> i32 {
585    todo!()
586}
587#[unsafe(no_mangle)]
588pub unsafe extern "C" fn casper_get_balance(
589    purse_ptr: *const u8,
590    purse_size: usize,
591    result_size: *mut usize,
592) -> i32 {
593    todo!()
594}
595#[unsafe(no_mangle)]
596pub unsafe extern "C" fn casper_get_phase(dest_ptr: *mut u8) {
597    todo!()
598}
599#[unsafe(no_mangle)]
600pub unsafe extern "C" fn casper_get_system_contract(
601    system_contract_index: u32,
602    dest_ptr: *mut u8,
603    dest_size: usize,
604) -> i32 {
605    todo!()
606}
607#[unsafe(no_mangle)]
608pub unsafe extern "C" fn casper_get_main_purse(dest_ptr: *mut u8) {
609    todo!()
610}
611#[unsafe(no_mangle)]
612pub unsafe extern "C" fn casper_read_host_buffer(
613    dest_ptr: *mut u8,
614    dest_size: usize,
615    bytes_written: *mut usize,
616) -> i32 {
617    let result = with_current_env(|env| match env.host_buffer.take() {
618        Some(host_buffer) => {
619            let bytes = host_buffer.inner_bytes();
620
621            unsafe {
622                *bytes_written = bytes.len();
623                assert_eq!(bytes.len(), dest_size, "Host buffer size mismatch");
624                core::ptr::copy_nonoverlapping(bytes.as_ptr(), dest_ptr, dest_size);
625            }
626            Ok(())
627        }
628        None => Err(ApiError::HostBufferEmpty),
629    });
630    api_error::i32_from(result)
631}
632#[unsafe(no_mangle)]
633pub unsafe extern "C" fn casper_create_contract_package_at_hash(
634    hash_addr_ptr: *mut u8,
635    access_addr_ptr: *mut u8,
636    is_locked: bool,
637) {
638    todo!();
639}
640#[unsafe(no_mangle)]
641pub unsafe extern "C" fn casper_create_contract_user_group(
642    contract_package_hash_ptr: *const u8,
643    contract_package_hash_size: usize,
644    label_ptr: *const u8,
645    label_size: usize,
646    num_new_urefs: u8,
647    existing_urefs_ptr: *const u8,
648    existing_urefs_size: usize,
649    output_size_ptr: *mut usize,
650) -> i32 {
651    todo!()
652}
653#[unsafe(no_mangle)]
654pub unsafe extern "C" fn casper_add_contract_version(
655    contract_package_hash_ptr: *const u8,
656    contract_package_hash_size: usize,
657    version_ptr: *const u32,
658    entry_points_ptr: *const u8,
659    entry_points_size: usize,
660    named_keys_ptr: *const u8,
661    named_keys_size: usize,
662    output_ptr: *mut u8,
663    output_size: usize,
664    bytes_written_ptr: *mut usize,
665) -> i32 {
666    todo!()
667}
668#[unsafe(no_mangle)]
669pub unsafe extern "C" fn casper_add_contract_version_with_message_topics(
670    contract_package_hash_ptr: *const u8,
671    contract_package_hash_size: usize,
672    version_ptr: *const u32,
673    entry_points_ptr: *const u8,
674    entry_points_size: usize,
675    named_keys_ptr: *const u8,
676    named_keys_size: usize,
677    message_topics_ptr: *const u8,
678    message_topics_size: usize,
679    output_ptr: *mut u8,
680    output_size: usize,
681) -> i32 {
682    0
683}
684#[unsafe(no_mangle)]
685pub unsafe extern "C" fn casper_add_package_version_with_message_topics(
686    package_hash_ptr: *const u8,
687    package_hash_size: usize,
688    version_ptr: *const u32,
689    entry_points_ptr: *const u8,
690    entry_points_size: usize,
691    named_keys_ptr: *const u8,
692    named_keys_size: usize,
693    message_topics_ptr: *const u8,
694    message_topics_size: usize,
695    output_ptr: *mut u8,
696    output_size: usize,
697) -> i32 {
698    todo!()
699}
700#[unsafe(no_mangle)]
701pub unsafe extern "C" fn casper_disable_contract_version(
702    contract_package_hash_ptr: *const u8,
703    contract_package_hash_size: usize,
704    contract_hash_ptr: *const u8,
705    contract_hash_size: usize,
706) -> i32 {
707    todo!()
708}
709#[unsafe(no_mangle)]
710pub unsafe extern "C" fn casper_call_contract(
711    contract_hash_ptr: *const u8,
712    contract_hash_size: usize,
713    entry_point_name_ptr: *const u8,
714    entry_point_name_size: usize,
715    runtime_args_ptr: *const u8,
716    runtime_args_size: usize,
717    result_size: *mut usize,
718) -> i32 {
719    todo!()
720}
721#[unsafe(no_mangle)]
722pub unsafe extern "C" fn casper_call_versioned_contract(
723    contract_package_hash_ptr: *const u8,
724    contract_package_hash_size: usize,
725    contract_version_ptr: *const u8,
726    contract_version_size: usize,
727    entry_point_name_ptr: *const u8,
728    entry_point_name_size: usize,
729    runtime_args_ptr: *const u8,
730    runtime_args_size: usize,
731    result_size: *mut usize,
732) -> i32 {
733    todo!()
734}
735
736#[unsafe(no_mangle)]
737pub unsafe extern "C" fn casper_get_named_arg_size(
738    name_ptr: *const u8,
739    name_size: usize,
740    dest_size: *mut usize,
741) -> i32 {
742    let name: &[u8] = unsafe { core::slice::from_raw_parts(name_ptr, name_size) };
743    let name: &str = core::str::from_utf8(name).expect("Failed to convert bytes to str");
744    with_current_env(|env| {
745        env.trace.push(HostFunction::CasperGetNamedArgSize);
746        match env.args.get(name) {
747            Some(value) => {
748                let size = value.inner_bytes().len();
749                unsafe {
750                    *dest_size = size;
751                }
752                0 // Success
753            }
754            None => {
755                unsafe {
756                    *dest_size = 0;
757                }
758                let i: u32 = ApiError::MissingArgument.into();
759                i as i32 // Missing argument
760            }
761        }
762    })
763}
764#[unsafe(no_mangle)]
765pub unsafe extern "C" fn casper_get_named_arg(
766    name_ptr: *const u8,
767    name_size: usize,
768    dest_ptr: *mut u8,
769    dest_size: usize,
770) -> i32 {
771    let name: &[u8] = unsafe { core::slice::from_raw_parts(name_ptr, name_size) };
772    let name: &str = core::str::from_utf8(name).expect("Failed to convert bytes to str");
773    let result = with_current_env(|env| {
774        env.trace.push(HostFunction::CasperGetNamedArg);
775        match env.args.get(name) {
776            Some(value) => {
777                let bytes = value.inner_bytes();
778                if bytes.len() > dest_size {
779                    return Err(ApiError::BufferTooSmall);
780                }
781                unsafe {
782                    core::ptr::copy_nonoverlapping(bytes.as_ptr(), dest_ptr, bytes.len());
783                }
784                Ok(()) // Success
785            }
786            None => Err(ApiError::MissingArgument),
787        }
788    });
789
790    api_error::i32_from(result)
791}
792#[unsafe(no_mangle)]
793pub unsafe extern "C" fn casper_remove_contract_user_group(
794    contract_package_hash_ptr: *const u8,
795    contract_package_hash_size: usize,
796    label_ptr: *const u8,
797    label_size: usize,
798) -> i32 {
799    todo!()
800}
801#[unsafe(no_mangle)]
802pub unsafe extern "C" fn casper_provision_contract_user_group_uref(
803    contract_package_hash_ptr: *const u8,
804    contract_package_hash_size: usize,
805    label_ptr: *const u8,
806    label_size: usize,
807    value_size_ptr: *const usize,
808) -> i32 {
809    todo!()
810}
811#[unsafe(no_mangle)]
812pub unsafe extern "C" fn casper_remove_contract_user_group_urefs(
813    contract_package_hash_ptr: *const u8,
814    contract_package_hash_size: usize,
815    label_ptr: *const u8,
816    label_size: usize,
817    urefs_ptr: *const u8,
818    urefs_size: usize,
819) -> i32 {
820    todo!()
821}
822#[deprecated(note = "Superseded by ext_ffi::casper_generic_hash")]
823#[unsafe(no_mangle)]
824pub unsafe extern "C" fn casper_blake2b(
825    in_ptr: *const u8,
826    in_size: usize,
827    out_ptr: *mut u8,
828    out_size: usize,
829) -> i32 {
830    todo!()
831}
832#[deprecated]
833#[unsafe(no_mangle)]
834pub unsafe extern "C" fn casper_load_call_stack(
835    call_stack_len_ptr: *mut usize,
836    result_size_ptr: *mut usize,
837) -> i32 {
838    todo!()
839}
840
841#[unsafe(no_mangle)]
842pub unsafe extern "C" fn casper_print(text_ptr: *const u8, text_size: usize) {
843    let text: &[u8] = unsafe { core::slice::from_raw_parts(text_ptr, text_size) };
844    let text: String =
845        bytesrepr::deserialize_from_slice(text).expect("Failed to deserialize text for printing");
846
847    eprintln!("Print: {text}");
848}
849
850/// Creates a new dictionary and returns its URef in the host buffer.
851///
852/// # Safety
853#[unsafe(no_mangle)]
854pub unsafe extern "C" fn casper_new_dictionary(output_size_ptr: *mut usize) -> i32 {
855    with_current_env(|env| {
856        let uref = URef::new(env.next_address(), AccessRights::READ_ADD_WRITE);
857        let key = Key::URef(uref);
858
859        let cl_value = CLValue::unit();
860
861        env.database
862            .insert(key, StoredValue::CLValue(cl_value.clone()));
863
864        env.dictionaries.entry(uref.addr()).or_default();
865
866        let old_host_buffer = env
867            .host_buffer
868            .replace(CLValue::from_t(uref).expect("Failed to create CLValue from URef"));
869        if let Some(old_value) = old_host_buffer {
870            panic!("Host buffer already contains a value, cannot overwrite it: {old_value:?}");
871        }
872        let key_bytes = uref.to_bytes().expect("Failed to serialize URef");
873        unsafe {
874            *output_size_ptr = key_bytes.len();
875        }
876        0 // Success
877    })
878}
879#[unsafe(no_mangle)]
880pub unsafe extern "C" fn casper_dictionary_get(
881    uref_ptr: *const u8,
882    uref_size: usize,
883    key_bytes_ptr: *const u8,
884    key_bytes_size: usize,
885    output_size: *mut usize,
886) -> i32 {
887    todo!()
888}
889#[unsafe(no_mangle)]
890pub unsafe extern "C" fn casper_dictionary_read(
891    key_ptr: *const u8,
892    key_size: usize,
893    output_size: *mut usize,
894) -> i32 {
895    todo!()
896}
897/// Inserts a key-value pair into the specified dictionary.
898///
899/// # Safety
900#[unsafe(no_mangle)]
901pub unsafe extern "C" fn casper_dictionary_put(
902    uref_ptr: *const u8,
903    uref_size: usize,
904    key_ptr: *const u8,
905    key_size: usize,
906    value_ptr: *const u8,
907    value_size: usize,
908) -> i32 {
909    with_current_env(|env| {
910        let uref_bytes = unsafe { core::slice::from_raw_parts(uref_ptr, uref_size) };
911        let uref: URef =
912            bytesrepr::deserialize_from_slice(uref_bytes).expect("Failed to deserialize URef");
913
914        let key_bytes = unsafe { core::slice::from_raw_parts(key_ptr, key_size) };
915        let key =
916            String::from_utf8(key_bytes.to_vec()).expect("Failed to convert key bytes to String");
917
918        let value_bytes = unsafe { core::slice::from_raw_parts(value_ptr, value_size) };
919        let value: CLValue =
920            bytesrepr::deserialize_from_slice(value_bytes).expect("Failed to deserialize value");
921
922        if let Some(dict) = env.dictionaries.get_mut(&uref.addr()) {
923            dict.insert(key, value);
924            0 // Success
925        } else {
926            -1 // Dictionary not found
927        }
928    })
929}
930#[unsafe(no_mangle)]
931pub unsafe extern "C" fn casper_random_bytes(out_ptr: *mut u8, out_size: usize) -> i32 {
932    todo!()
933}
934#[unsafe(no_mangle)]
935pub unsafe extern "C" fn casper_enable_contract_version(
936    contract_package_hash_ptr: *const u8,
937    contract_package_hash_size: usize,
938    contract_hash_ptr: *const u8,
939    contract_hash_size: usize,
940) -> i32 {
941    todo!()
942}
943#[unsafe(no_mangle)]
944pub unsafe extern "C" fn casper_manage_message_topic(
945    topic_name_ptr: *const u8,
946    topic_name_size: usize,
947    operation_ptr: *const u8,
948    operation_size: usize,
949) -> i32 {
950    todo!()
951}
952#[unsafe(no_mangle)]
953pub unsafe extern "C" fn casper_emit_message(
954    topic_name_ptr: *const u8,
955    topic_name_size: usize,
956    message_ptr: *const u8,
957    message_size: usize,
958) -> i32 {
959    todo!()
960}
961
962#[unsafe(no_mangle)]
963pub unsafe extern "C" fn casper_load_caller_information(
964    action: u8,
965    call_stack_len_ptr: *mut usize,
966    result_size_ptr: *mut usize,
967) -> i32 {
968    unimplemented_ffi!("casper_load_caller_information")
969}
970
971#[unsafe(no_mangle)]
972pub unsafe extern "C" fn casper_get_block_info(field_idx: u8, dest_ptr: *const u8) {
973    todo!("casper_get_block_info")
974}
975
976/// The 32-byte digest keccak256 hash function
977#[allow(dead_code)]
978fn keccak256<T: AsRef<[u8]>>(data: T) -> [u8; 32] {
979    use keccak_asm::Digest as KeccakDigest;
980    use keccak_asm::Keccak256;
981
982    let mut h = Keccak256::new();
983    KeccakDigest::update(&mut h, &data);
984    let mut out = [0u8; 32];
985    let result = KeccakDigest::finalize(h);
986    out.copy_from_slice(&result);
987    out
988}
989
990#[unsafe(no_mangle)]
991pub unsafe extern "C" fn casper_generic_hash(
992    in_ptr: *const u8,
993    in_size: usize,
994    hash_algo_type: u8,
995    out_ptr: *const u8,
996    out_size: usize,
997) -> i32 {
998    let result = {
999        // For allowing fallback in the code that uses this FFI function we'll report InvalidArgument as if given algorithm is not supported instead of failing.
1000        // This allows production code to fallback gracefully instead of panicking.
1001        Err(ApiError::InvalidArgument)
1002    };
1003
1004    api_error::i32_from(result)
1005}
1006
1007#[unsafe(no_mangle)]
1008pub unsafe extern "C" fn casper_recover_secp256k1(
1009    message_ptr: *const u8,
1010    message_size: usize,
1011    signature_ptr: *const u8,
1012    signature_size: usize,
1013    out_ptr: *const u8,
1014    recovery_id: u8,
1015) -> i32 {
1016    todo!()
1017}
1018
1019#[unsafe(no_mangle)]
1020pub unsafe extern "C" fn casper_verify_signature(
1021    message_ptr: *const u8,
1022    message_size: usize,
1023    signature_ptr: *const u8,
1024    signature_size: usize,
1025    public_key_ptr: *const u8,
1026    public_key_size: usize,
1027) -> i32 {
1028    todo!()
1029}
1030
1031#[unsafe(no_mangle)]
1032pub unsafe extern "C" fn casper_call_package_version(
1033    contract_package_hash_ptr: *const u8,
1034    contract_package_hash_size: usize,
1035    major_version_ptr: *const u8,
1036    major_version_size: usize,
1037    contract_version_ptr: *const u8,
1038    contract_version_size: usize,
1039    entry_point_name_ptr: *const u8,
1040    entry_point_name_size: usize,
1041    runtime_args_ptr: *const u8,
1042    runtime_args_size: usize,
1043    result_size: *mut usize,
1044) -> i32 {
1045    todo!()
1046}