casper_contract/contract_api/
runtime.rs

1//! Functions for interacting with the current runtime.
2
3use alloc::{collections::BTreeSet, vec, vec::Vec};
4use core::mem::MaybeUninit;
5
6use casper_types::{
7    account::AccountHash,
8    api_error,
9    bytesrepr::{self, FromBytes, U64_SERIALIZED_LENGTH},
10    contract_messages::{MessagePayload, MessageTopicOperation},
11    contracts::{ContractHash, ContractPackageHash, ContractVersion, NamedKeys},
12    system::CallerInfo,
13    ApiError, BlockTime, CLTyped, CLValue, Digest, EntityVersion, HashAlgorithm, Key, Phase,
14    ProtocolVersion, RuntimeArgs, URef, BLAKE2B_DIGEST_LENGTH, BLOCKTIME_SERIALIZED_LENGTH,
15    PHASE_SERIALIZED_LENGTH,
16};
17
18use crate::{contract_api, ext_ffi, unwrap_or_revert::UnwrapOrRevert};
19
20/// Number of random bytes returned from the `random_bytes()` function.
21const RANDOM_BYTES_COUNT: usize = 32;
22
23const ACCOUNT: u8 = 0;
24
25#[repr(u8)]
26enum CallerIndex {
27    Initiator = 0,
28    Immediate = 1,
29    FullStack = 2,
30}
31
32/// Returns the given [`CLValue`] to the host, terminating the currently running module.
33///
34/// Note this function is only relevant to contracts stored on chain which are invoked via
35/// [`call_contract`] and can thus return a value to their caller.  The return value of a directly
36/// deployed contract is never used.
37pub fn ret(value: CLValue) -> ! {
38    let (ptr, size, _bytes) = contract_api::to_ptr(value);
39    unsafe {
40        ext_ffi::casper_ret(ptr, size);
41    }
42}
43
44/// Stops execution of a contract and reverts execution effects with a given [`ApiError`].
45///
46/// The provided `ApiError` is returned in the form of a numeric exit code to the caller via the
47/// deploy response.
48pub fn revert<T: Into<ApiError>>(error: T) -> ! {
49    unsafe {
50        ext_ffi::casper_revert(error.into().into());
51    }
52}
53
54/// Calls the given stored contract, passing the given arguments to it.
55///
56/// If the stored contract calls [`ret`], then that value is returned from `call_contract`.  If the
57/// stored contract calls [`revert`], then execution stops and `call_contract` doesn't return.
58/// Otherwise `call_contract` returns `()`.
59pub fn call_contract<T: CLTyped + FromBytes>(
60    contract_hash: ContractHash,
61    entry_point_name: &str,
62    runtime_args: RuntimeArgs,
63) -> T {
64    let (contract_hash_ptr, contract_hash_size, _bytes1) = contract_api::to_ptr(contract_hash);
65    let (entry_point_name_ptr, entry_point_name_size, _bytes2) =
66        contract_api::to_ptr(entry_point_name);
67    let (runtime_args_ptr, runtime_args_size, _bytes3) = contract_api::to_ptr(runtime_args);
68
69    let bytes_written = {
70        let mut bytes_written = MaybeUninit::uninit();
71        let ret = unsafe {
72            ext_ffi::casper_call_contract(
73                contract_hash_ptr,
74                contract_hash_size,
75                entry_point_name_ptr,
76                entry_point_name_size,
77                runtime_args_ptr,
78                runtime_args_size,
79                bytes_written.as_mut_ptr(),
80            )
81        };
82        api_error::result_from(ret).unwrap_or_revert();
83        unsafe { bytes_written.assume_init() }
84    };
85    deserialize_contract_result(bytes_written)
86}
87
88/// Invokes the specified `entry_point_name` of stored logic at a specific `contract_package_hash`
89/// address, for the most current version of a contract package by default or a specific
90/// `contract_version` if one is provided, and passing the provided `runtime_args` to it
91///
92/// If the stored contract calls [`ret`], then that value is returned from
93/// `call_versioned_contract`.  If the stored contract calls [`revert`], then execution stops and
94/// `call_versioned_contract` doesn't return. Otherwise `call_versioned_contract` returns `()`.
95pub fn call_versioned_contract<T: CLTyped + FromBytes>(
96    contract_package_hash: ContractPackageHash,
97    contract_version: Option<ContractVersion>,
98    entry_point_name: &str,
99    runtime_args: RuntimeArgs,
100) -> T {
101    let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
102        contract_api::to_ptr(contract_package_hash);
103    let (contract_version_ptr, contract_version_size, _bytes2) =
104        contract_api::to_ptr(contract_version);
105    let (entry_point_name_ptr, entry_point_name_size, _bytes3) =
106        contract_api::to_ptr(entry_point_name);
107    let (runtime_args_ptr, runtime_args_size, _bytes4) = contract_api::to_ptr(runtime_args);
108
109    let bytes_written = {
110        let mut bytes_written = MaybeUninit::uninit();
111        let ret = unsafe {
112            ext_ffi::casper_call_versioned_contract(
113                contract_package_hash_ptr,
114                contract_package_hash_size,
115                contract_version_ptr,
116                contract_version_size,
117                entry_point_name_ptr,
118                entry_point_name_size,
119                runtime_args_ptr,
120                runtime_args_size,
121                bytes_written.as_mut_ptr(),
122            )
123        };
124        api_error::result_from(ret).unwrap_or_revert();
125        unsafe { bytes_written.assume_init() }
126    };
127    deserialize_contract_result(bytes_written)
128}
129
130/// Invokes the specified `entry_point_name` of stored logic at a specific `contract_package_hash`
131/// address, for a specific pair of `major_version` and `contract_version`
132/// and passing the provided `runtime_args` to it
133///
134/// If the stored contract calls [`ret`], then that value is returned from
135/// `call_package_version`.  If the stored contract calls [`revert`], then execution stops and
136/// `call_package_version` doesn't return. Otherwise `call_package_version` returns `()`.
137pub fn call_package_version<T: CLTyped + FromBytes>(
138    contract_package_hash: ContractPackageHash,
139    major_version: Option<u32>,
140    contract_version: Option<EntityVersion>,
141    entry_point_name: &str,
142    runtime_args: RuntimeArgs,
143) -> T {
144    let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
145        contract_api::to_ptr(contract_package_hash);
146    let (major_version_ptr, major_version_size, _bytes_5) = contract_api::to_ptr(major_version);
147    let (contract_version_ptr, contract_version_size, _bytes2) =
148        contract_api::to_ptr(contract_version);
149    let (entry_point_name_ptr, entry_point_name_size, _bytes3) =
150        contract_api::to_ptr(entry_point_name);
151    let (runtime_args_ptr, runtime_args_size, _bytes4) = contract_api::to_ptr(runtime_args);
152
153    let bytes_written = {
154        let mut bytes_written = MaybeUninit::uninit();
155        let ret = unsafe {
156            ext_ffi::casper_call_package_version(
157                contract_package_hash_ptr,
158                contract_package_hash_size,
159                major_version_ptr,
160                major_version_size,
161                contract_version_ptr,
162                contract_version_size,
163                entry_point_name_ptr,
164                entry_point_name_size,
165                runtime_args_ptr,
166                runtime_args_size,
167                bytes_written.as_mut_ptr(),
168            )
169        };
170        api_error::result_from(ret).unwrap_or_revert();
171        unsafe { bytes_written.assume_init() }
172    };
173    deserialize_contract_result(bytes_written)
174}
175
176fn deserialize_contract_result<T: CLTyped + FromBytes>(bytes_written: usize) -> T {
177    let serialized_result = if bytes_written == 0 {
178        // If no bytes were written, the host buffer hasn't been set and hence shouldn't be read.
179        vec![]
180    } else {
181        // NOTE: this is a copy of the contents of `read_host_buffer()`.  Calling that directly from
182        // here causes several contracts to fail with a Wasmi `Unreachable` error.
183        let bytes_non_null_ptr = contract_api::alloc_bytes(bytes_written);
184        let mut dest: Vec<u8> = unsafe {
185            Vec::from_raw_parts(bytes_non_null_ptr.as_ptr(), bytes_written, bytes_written)
186        };
187        read_host_buffer_into(&mut dest).unwrap_or_revert();
188        dest
189    };
190
191    bytesrepr::deserialize(serialized_result).unwrap_or_revert()
192}
193
194/// Returns size in bytes of a given named argument passed to the host for the current module
195/// invocation.
196///
197/// This will return either Some with the size of argument if present, or None if given argument is
198/// not passed.
199fn get_named_arg_size(name: &str) -> Option<usize> {
200    let mut arg_size: usize = 0;
201    let ret = unsafe {
202        ext_ffi::casper_get_named_arg_size(
203            name.as_bytes().as_ptr(),
204            name.len(),
205            &mut arg_size as *mut usize,
206        )
207    };
208    match api_error::result_from(ret) {
209        Ok(_) => Some(arg_size),
210        Err(ApiError::MissingArgument) => None,
211        Err(e) => revert(e),
212    }
213}
214
215/// Returns given named argument passed to the host for the current module invocation.
216///
217/// Note that this is only relevant to contracts stored on-chain since a contract deployed directly
218/// is not invoked with any arguments.
219pub fn get_named_arg<T: FromBytes>(name: &str) -> T {
220    let arg_size = get_named_arg_size(name).unwrap_or_revert_with(ApiError::MissingArgument);
221    let arg_bytes = if arg_size > 0 {
222        let res = {
223            let data_non_null_ptr = contract_api::alloc_bytes(arg_size);
224            let ret = unsafe {
225                ext_ffi::casper_get_named_arg(
226                    name.as_bytes().as_ptr(),
227                    name.len(),
228                    data_non_null_ptr.as_ptr(),
229                    arg_size,
230                )
231            };
232            let data =
233                unsafe { Vec::from_raw_parts(data_non_null_ptr.as_ptr(), arg_size, arg_size) };
234            api_error::result_from(ret).map(|_| data)
235        };
236        // Assumed to be safe as `get_named_arg_size` checks the argument already
237        res.unwrap_or_revert()
238    } else {
239        // Avoids allocation with 0 bytes and a call to get_named_arg
240        Vec::new()
241    };
242    bytesrepr::deserialize(arg_bytes).unwrap_or_revert_with(ApiError::InvalidArgument)
243}
244
245/// Returns given named argument passed to the host for the current module invocation.
246/// If the argument is not found, returns `None`.
247///
248/// Note that this is only relevant to contracts stored on-chain since a contract deployed directly
249/// is not invoked with any arguments.
250pub fn try_get_named_arg<T: FromBytes>(name: &str) -> Option<T> {
251    let arg_size = get_named_arg_size(name)?;
252    let arg_bytes = if arg_size > 0 {
253        let res = {
254            let data_non_null_ptr = contract_api::alloc_bytes(arg_size);
255            let ret = unsafe {
256                ext_ffi::casper_get_named_arg(
257                    name.as_bytes().as_ptr(),
258                    name.len(),
259                    data_non_null_ptr.as_ptr(),
260                    arg_size,
261                )
262            };
263            let data =
264                unsafe { Vec::from_raw_parts(data_non_null_ptr.as_ptr(), arg_size, arg_size) };
265            api_error::result_from(ret).map(|_| data)
266        };
267        // Assumed to be safe as `get_named_arg_size` checks the argument already
268        res.unwrap_or_revert()
269    } else {
270        // Avoids allocation with 0 bytes and a call to get_named_arg
271        Vec::new()
272    };
273    bytesrepr::deserialize(arg_bytes).ok()
274}
275
276/// Returns the caller of the current context, i.e. the [`AccountHash`] of the account which made
277/// the deploy request.
278pub fn get_caller() -> AccountHash {
279    let output_size = {
280        let mut output_size = MaybeUninit::uninit();
281        let ret = unsafe { ext_ffi::casper_get_caller(output_size.as_mut_ptr()) };
282        api_error::result_from(ret).unwrap_or_revert();
283        unsafe { output_size.assume_init() }
284    };
285    let buf = read_host_buffer(output_size).unwrap_or_revert();
286    bytesrepr::deserialize(buf).unwrap_or_revert()
287}
288
289/// Returns the current [`BlockTime`].
290pub fn get_blocktime() -> BlockTime {
291    let dest_non_null_ptr = contract_api::alloc_bytes(BLOCKTIME_SERIALIZED_LENGTH);
292    let bytes = unsafe {
293        ext_ffi::casper_get_blocktime(dest_non_null_ptr.as_ptr());
294        Vec::from_raw_parts(
295            dest_non_null_ptr.as_ptr(),
296            BLOCKTIME_SERIALIZED_LENGTH,
297            BLOCKTIME_SERIALIZED_LENGTH,
298        )
299    };
300    bytesrepr::deserialize(bytes).unwrap_or_revert()
301}
302
303/// The default length of hashes such as account hash, state hash, hash addresses, etc.
304pub const DEFAULT_HASH_LENGTH: u8 = 32;
305/// The default size of ProtocolVersion. It's 3×u32 (major, minor, patch), so 12 bytes.
306pub const PROTOCOL_VERSION_LENGTH: u8 = 12;
307///The default size of the addressable entity flag.
308pub const ADDRESSABLE_ENTITY_LENGTH: u8 = 1;
309/// Index for the block time field of block info.
310pub const BLOCK_TIME_FIELD_IDX: u8 = 0;
311/// Index for the block height field of block info.
312pub const BLOCK_HEIGHT_FIELD_IDX: u8 = 1;
313/// Index for the parent block hash field of block info.
314pub const PARENT_BLOCK_HASH_FIELD_IDX: u8 = 2;
315/// Index for the state hash field of block info.
316pub const STATE_HASH_FIELD_IDX: u8 = 3;
317/// Index for the protocol version field of block info.
318pub const PROTOCOL_VERSION_FIELD_IDX: u8 = 4;
319/// Index for the addressable entity field of block info.
320pub const ADDRESSABLE_ENTITY_FIELD_IDX: u8 = 5;
321
322/// Returns the block height.
323pub fn get_block_height() -> u64 {
324    let dest_non_null_ptr = contract_api::alloc_bytes(U64_SERIALIZED_LENGTH);
325    let bytes = unsafe {
326        ext_ffi::casper_get_block_info(BLOCK_HEIGHT_FIELD_IDX, dest_non_null_ptr.as_ptr());
327        Vec::from_raw_parts(
328            dest_non_null_ptr.as_ptr(),
329            U64_SERIALIZED_LENGTH,
330            U64_SERIALIZED_LENGTH,
331        )
332    };
333    bytesrepr::deserialize(bytes).unwrap_or_revert()
334}
335
336/// Returns the parent block hash.
337pub fn get_parent_block_hash() -> Digest {
338    let dest_non_null_ptr = contract_api::alloc_bytes(DEFAULT_HASH_LENGTH as usize);
339    let bytes = unsafe {
340        ext_ffi::casper_get_block_info(PARENT_BLOCK_HASH_FIELD_IDX, dest_non_null_ptr.as_ptr());
341        Vec::from_raw_parts(
342            dest_non_null_ptr.as_ptr(),
343            DEFAULT_HASH_LENGTH as usize,
344            DEFAULT_HASH_LENGTH as usize,
345        )
346    };
347    bytesrepr::deserialize(bytes).unwrap_or_revert()
348}
349
350/// Returns the state root hash.
351pub fn get_state_hash() -> Digest {
352    let dest_non_null_ptr = contract_api::alloc_bytes(DEFAULT_HASH_LENGTH as usize);
353    let bytes = unsafe {
354        ext_ffi::casper_get_block_info(STATE_HASH_FIELD_IDX, dest_non_null_ptr.as_ptr());
355        Vec::from_raw_parts(
356            dest_non_null_ptr.as_ptr(),
357            DEFAULT_HASH_LENGTH as usize,
358            DEFAULT_HASH_LENGTH as usize,
359        )
360    };
361    bytesrepr::deserialize(bytes).unwrap_or_revert()
362}
363
364/// Returns the protocol version.
365pub fn get_protocol_version() -> ProtocolVersion {
366    let dest_non_null_ptr = contract_api::alloc_bytes(PROTOCOL_VERSION_LENGTH as usize);
367    let bytes = unsafe {
368        ext_ffi::casper_get_block_info(PROTOCOL_VERSION_FIELD_IDX, dest_non_null_ptr.as_ptr());
369        Vec::from_raw_parts(
370            dest_non_null_ptr.as_ptr(),
371            PROTOCOL_VERSION_LENGTH as usize,
372            PROTOCOL_VERSION_LENGTH as usize,
373        )
374    };
375    bytesrepr::deserialize(bytes).unwrap_or_revert()
376}
377
378/// Returns whether or not the addressable entity is turned on.
379pub fn get_addressable_entity() -> bool {
380    let dest_non_null_ptr = contract_api::alloc_bytes(ADDRESSABLE_ENTITY_LENGTH as usize);
381    let bytes = unsafe {
382        ext_ffi::casper_get_block_info(ADDRESSABLE_ENTITY_FIELD_IDX, dest_non_null_ptr.as_ptr());
383        Vec::from_raw_parts(
384            dest_non_null_ptr.as_ptr(),
385            ADDRESSABLE_ENTITY_LENGTH as usize,
386            ADDRESSABLE_ENTITY_LENGTH as usize,
387        )
388    };
389    bytesrepr::deserialize(bytes).unwrap_or_revert()
390}
391
392/// Returns the current [`Phase`].
393pub fn get_phase() -> Phase {
394    let dest_non_null_ptr = contract_api::alloc_bytes(PHASE_SERIALIZED_LENGTH);
395    unsafe { ext_ffi::casper_get_phase(dest_non_null_ptr.as_ptr()) };
396    let bytes = unsafe {
397        Vec::from_raw_parts(
398            dest_non_null_ptr.as_ptr(),
399            PHASE_SERIALIZED_LENGTH,
400            PHASE_SERIALIZED_LENGTH,
401        )
402    };
403    bytesrepr::deserialize(bytes).unwrap_or_revert()
404}
405
406/// Returns the requested named [`Key`] from the current context.
407///
408/// The current context is either the caller's account or a stored contract depending on whether the
409/// currently-executing module is a direct call or a sub-call respectively.
410pub fn get_key(name: &str) -> Option<Key> {
411    let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
412    let mut key_bytes = vec![0u8; Key::max_serialized_length()];
413    let mut total_bytes: usize = 0;
414    let ret = unsafe {
415        ext_ffi::casper_get_key(
416            name_ptr,
417            name_size,
418            key_bytes.as_mut_ptr(),
419            key_bytes.len(),
420            &mut total_bytes as *mut usize,
421        )
422    };
423    match api_error::result_from(ret) {
424        Ok(_) => {}
425        Err(ApiError::MissingKey) => return None,
426        Err(e) => revert(e),
427    }
428    key_bytes.truncate(total_bytes);
429    let key: Key = bytesrepr::deserialize(key_bytes).unwrap_or_revert();
430    Some(key)
431}
432
433/// Returns `true` if `name` exists in the current context's named keys.
434///
435/// The current context is either the caller's account or a stored contract depending on whether the
436/// currently-executing module is a direct call or a sub-call respectively.
437pub fn has_key(name: &str) -> bool {
438    let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
439    let result = unsafe { ext_ffi::casper_has_key(name_ptr, name_size) };
440    result == 0
441}
442
443/// Stores the given [`Key`] under `name` in the current context's named keys.
444///
445/// The current context is either the caller's account or a stored contract depending on whether the
446/// currently-executing module is a direct call or a sub-call respectively.
447pub fn put_key(name: &str, key: Key) {
448    let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
449    let (key_ptr, key_size, _bytes2) = contract_api::to_ptr(key);
450    unsafe { ext_ffi::casper_put_key(name_ptr, name_size, key_ptr, key_size) };
451}
452
453/// Removes the [`Key`] stored under `name` in the current context's named keys.
454///
455/// The current context is either the caller's account or a stored contract depending on whether the
456/// currently-executing module is a direct call or a sub-call respectively.
457pub fn remove_key(name: &str) {
458    let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
459    unsafe { ext_ffi::casper_remove_key(name_ptr, name_size) }
460}
461
462/// Returns the set of [`AccountHash`] from the calling account's context `authorization_keys`.
463pub fn list_authorization_keys() -> BTreeSet<AccountHash> {
464    let (total_authorization_keys, result_size) = {
465        let mut authorization_keys = MaybeUninit::uninit();
466        let mut result_size = MaybeUninit::uninit();
467        let ret = unsafe {
468            ext_ffi::casper_load_authorization_keys(
469                authorization_keys.as_mut_ptr(),
470                result_size.as_mut_ptr(),
471            )
472        };
473        api_error::result_from(ret).unwrap_or_revert();
474        let total_authorization_keys = unsafe { authorization_keys.assume_init() };
475        let result_size = unsafe { result_size.assume_init() };
476        (total_authorization_keys, result_size)
477    };
478
479    if total_authorization_keys == 0 {
480        return BTreeSet::new();
481    }
482
483    let bytes = read_host_buffer(result_size).unwrap_or_revert();
484    bytesrepr::deserialize(bytes).unwrap_or_revert()
485}
486
487/// Returns the named keys of the current context.
488///
489/// The current context is either the caller's account or a stored contract depending on whether the
490/// currently-executing module is a direct call or a sub-call respectively.
491pub fn list_named_keys() -> NamedKeys {
492    let (total_keys, result_size) = {
493        let mut total_keys = MaybeUninit::uninit();
494        let mut result_size = 0;
495        let ret = unsafe {
496            ext_ffi::casper_load_named_keys(total_keys.as_mut_ptr(), &mut result_size as *mut usize)
497        };
498        api_error::result_from(ret).unwrap_or_revert();
499        let total_keys = unsafe { total_keys.assume_init() };
500        (total_keys, result_size)
501    };
502    if total_keys == 0 {
503        return NamedKeys::new();
504    }
505    let bytes = read_host_buffer(result_size).unwrap_or_revert();
506    bytesrepr::deserialize(bytes).unwrap_or_revert()
507}
508
509/// Validates uref against named keys.
510pub fn is_valid_uref(uref: URef) -> bool {
511    let (uref_ptr, uref_size, _bytes) = contract_api::to_ptr(uref);
512    let result = unsafe { ext_ffi::casper_is_valid_uref(uref_ptr, uref_size) };
513    result != 0
514}
515
516/// Returns a 32-byte BLAKE2b digest
517pub fn blake2b<T: AsRef<[u8]>>(input: T) -> [u8; BLAKE2B_DIGEST_LENGTH] {
518    let mut ret = [0; BLAKE2B_DIGEST_LENGTH];
519    let result = unsafe {
520        ext_ffi::casper_generic_hash(
521            input.as_ref().as_ptr(),
522            input.as_ref().len(),
523            HashAlgorithm::Blake2b as u8,
524            ret.as_mut_ptr(),
525            BLAKE2B_DIGEST_LENGTH,
526        )
527    };
528    api_error::result_from(result).unwrap_or_revert();
529    ret
530}
531
532/// Returns 32 pseudo random bytes.
533pub fn random_bytes() -> [u8; RANDOM_BYTES_COUNT] {
534    let mut ret = [0; RANDOM_BYTES_COUNT];
535    let result = unsafe { ext_ffi::casper_random_bytes(ret.as_mut_ptr(), RANDOM_BYTES_COUNT) };
536    api_error::result_from(result).unwrap_or_revert();
537    ret
538}
539
540fn read_host_buffer_into(dest: &mut [u8]) -> Result<usize, ApiError> {
541    let mut bytes_written = MaybeUninit::uninit();
542    let ret = unsafe {
543        ext_ffi::casper_read_host_buffer(dest.as_mut_ptr(), dest.len(), bytes_written.as_mut_ptr())
544    };
545    // NOTE: When rewriting below expression as `result_from(ret).map(|_| unsafe { ... })`, and the
546    // caller ignores the return value, execution of the contract becomes unstable and ultimately
547    // leads to `Unreachable` error.
548    api_error::result_from(ret)?;
549    Ok(unsafe { bytes_written.assume_init() })
550}
551
552pub(crate) fn read_host_buffer(size: usize) -> Result<Vec<u8>, ApiError> {
553    let mut dest: Vec<u8> = if size == 0 {
554        Vec::new()
555    } else {
556        let bytes_non_null_ptr = contract_api::alloc_bytes(size);
557        unsafe { Vec::from_raw_parts(bytes_non_null_ptr.as_ptr(), size, size) }
558    };
559    read_host_buffer_into(&mut dest)?;
560    Ok(dest)
561}
562
563/// Returns the call stack.
564pub fn get_call_stack() -> Vec<CallerInfo> {
565    let (call_stack_len, result_size) = {
566        let mut call_stack_len: usize = 0;
567        let mut result_size: usize = 0;
568        let ret = unsafe {
569            ext_ffi::casper_load_caller_information(
570                CallerIndex::FullStack as u8,
571                &mut call_stack_len as *mut usize,
572                &mut result_size as *mut usize,
573            )
574        };
575        api_error::result_from(ret).unwrap_or_revert();
576        (call_stack_len, result_size)
577    };
578    if call_stack_len == 0 {
579        return Vec::new();
580    }
581    let bytes = read_host_buffer(result_size).unwrap_or_revert();
582    bytesrepr::deserialize(bytes).unwrap_or_revert()
583}
584
585fn get_initiator_or_immediate(action: u8) -> Result<CallerInfo, ApiError> {
586    let (call_stack_len, result_size) = {
587        let mut call_stack_len: usize = 0;
588        let mut result_size: usize = 0;
589        let ret = unsafe {
590            ext_ffi::casper_load_caller_information(
591                action,
592                &mut call_stack_len as *mut usize,
593                &mut result_size as *mut usize,
594            )
595        };
596        api_error::result_from(ret).unwrap_or_revert();
597        (call_stack_len, result_size)
598    };
599    if call_stack_len == 0 {
600        return Err(ApiError::InvalidCallerInfoRequest);
601    }
602    let bytes = read_host_buffer(result_size).unwrap_or_revert();
603    let caller: Vec<CallerInfo> = bytesrepr::deserialize(bytes).unwrap_or_revert();
604
605    if caller.len() != 1 {
606        return Err(ApiError::Unhandled);
607    };
608    let first = caller.first().unwrap_or_revert().clone();
609    Ok(first)
610}
611
612/// Returns the call stack initiator
613pub fn get_call_initiator() -> Result<AccountHash, ApiError> {
614    let caller = get_initiator_or_immediate(CallerIndex::Initiator as u8)?;
615    if caller.kind() != ACCOUNT {
616        return Err(ApiError::Unhandled);
617    };
618    if let Some(cl_value) = caller.get_field_by_index(ACCOUNT) {
619        let maybe_account_hash = cl_value
620            .to_t::<Option<AccountHash>>()
621            .map_err(|_| ApiError::CLTypeMismatch)?;
622        match maybe_account_hash {
623            Some(hash) => Ok(hash),
624            None => Err(ApiError::None),
625        }
626    } else {
627        Err(ApiError::PurseNotCreated)
628    }
629}
630
631/// Returns the immidiate caller within the call stack.
632pub fn get_immediate_caller() -> Result<CallerInfo, ApiError> {
633    get_initiator_or_immediate(CallerIndex::Immediate as u8)
634}
635
636/// Manages a message topic.
637pub fn manage_message_topic(
638    topic_name: &str,
639    operation: MessageTopicOperation,
640) -> Result<(), ApiError> {
641    if topic_name.is_empty() {
642        return Err(ApiError::InvalidArgument);
643    }
644
645    let (operation_ptr, operation_size, _bytes) = contract_api::to_ptr(operation);
646    let result = unsafe {
647        ext_ffi::casper_manage_message_topic(
648            topic_name.as_ptr(),
649            topic_name.len(),
650            operation_ptr,
651            operation_size,
652        )
653    };
654    api_error::result_from(result)
655}
656
657/// Emits a message on a topic.
658pub fn emit_message(topic_name: &str, message: &MessagePayload) -> Result<(), ApiError> {
659    if topic_name.is_empty() {
660        return Err(ApiError::InvalidArgument);
661    }
662
663    let (message_ptr, message_size, _bytes) = contract_api::to_ptr(message);
664
665    let result = unsafe {
666        ext_ffi::casper_emit_message(
667            topic_name.as_ptr(),
668            topic_name.len(),
669            message_ptr,
670            message_size,
671        )
672    };
673
674    api_error::result_from(result)
675}
676
677#[cfg(feature = "test-support")]
678/// Prints a debug message
679pub fn print(text: &str) {
680    let (text_ptr, text_size, _bytes) = contract_api::to_ptr(text);
681    unsafe { ext_ffi::casper_print(text_ptr, text_size) }
682}