1use 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
20const 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
32pub 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
44pub fn revert<T: Into<ApiError>>(error: T) -> ! {
49 unsafe {
50 ext_ffi::casper_revert(error.into().into());
51 }
52}
53
54pub 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
88pub 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
130pub 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 vec![]
180 } else {
181 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
194fn 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
215pub 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 res.unwrap_or_revert()
238 } else {
239 Vec::new()
241 };
242 bytesrepr::deserialize(arg_bytes).unwrap_or_revert_with(ApiError::InvalidArgument)
243}
244
245pub 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 res.unwrap_or_revert()
269 } else {
270 Vec::new()
272 };
273 bytesrepr::deserialize(arg_bytes).ok()
274}
275
276pub 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
289pub 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
303pub const DEFAULT_HASH_LENGTH: u8 = 32;
305pub const PROTOCOL_VERSION_LENGTH: u8 = 12;
307pub const ADDRESSABLE_ENTITY_LENGTH: u8 = 1;
309pub const BLOCK_TIME_FIELD_IDX: u8 = 0;
311pub const BLOCK_HEIGHT_FIELD_IDX: u8 = 1;
313pub const PARENT_BLOCK_HASH_FIELD_IDX: u8 = 2;
315pub const STATE_HASH_FIELD_IDX: u8 = 3;
317pub const PROTOCOL_VERSION_FIELD_IDX: u8 = 4;
319pub const ADDRESSABLE_ENTITY_FIELD_IDX: u8 = 5;
321
322pub 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
336pub 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
350pub 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
364pub 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
378pub 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
392pub 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
406pub 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
433pub 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
443pub 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
453pub 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
462pub 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
487pub 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
509pub 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
516pub 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
532pub 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 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
563pub 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
612pub 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
631pub fn get_immediate_caller() -> Result<CallerInfo, ApiError> {
633 get_initiator_or_immediate(CallerIndex::Immediate as u8)
634}
635
636pub 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
657pub 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")]
678pub 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}