casper_contract/contract_api/
runtime.rsuse alloc::{collections::BTreeSet, vec, vec::Vec};
use core::mem::MaybeUninit;
use casper_types::{
account::AccountHash,
api_error,
bytesrepr::{self, FromBytes},
contracts::{ContractVersion, NamedKeys},
system::CallStackElement,
ApiError, BlockTime, CLTyped, CLValue, ContractHash, ContractPackageHash, Key, Phase,
RuntimeArgs, URef, BLAKE2B_DIGEST_LENGTH, BLOCKTIME_SERIALIZED_LENGTH, PHASE_SERIALIZED_LENGTH,
};
use crate::{contract_api, ext_ffi, unwrap_or_revert::UnwrapOrRevert};
const RANDOM_BYTES_COUNT: usize = 32;
pub fn ret(value: CLValue) -> ! {
let (ptr, size, _bytes) = contract_api::to_ptr(value);
unsafe {
ext_ffi::casper_ret(ptr, size);
}
}
pub fn revert<T: Into<ApiError>>(error: T) -> ! {
unsafe {
ext_ffi::casper_revert(error.into().into());
}
}
pub fn call_contract<T: CLTyped + FromBytes>(
contract_hash: ContractHash,
entry_point_name: &str,
runtime_args: RuntimeArgs,
) -> T {
let (contract_hash_ptr, contract_hash_size, _bytes1) = contract_api::to_ptr(contract_hash);
let (entry_point_name_ptr, entry_point_name_size, _bytes2) =
contract_api::to_ptr(entry_point_name);
let (runtime_args_ptr, runtime_args_size, _bytes2) = contract_api::to_ptr(runtime_args);
let bytes_written = {
let mut bytes_written = MaybeUninit::uninit();
let ret = unsafe {
ext_ffi::casper_call_contract(
contract_hash_ptr,
contract_hash_size,
entry_point_name_ptr,
entry_point_name_size,
runtime_args_ptr,
runtime_args_size,
bytes_written.as_mut_ptr(),
)
};
api_error::result_from(ret).unwrap_or_revert();
unsafe { bytes_written.assume_init() }
};
deserialize_contract_result(bytes_written)
}
pub fn call_versioned_contract<T: CLTyped + FromBytes>(
contract_package_hash: ContractPackageHash,
contract_version: Option<ContractVersion>,
entry_point_name: &str,
runtime_args: RuntimeArgs,
) -> T {
let (contract_package_hash_ptr, contract_package_hash_size, _bytes) =
contract_api::to_ptr(contract_package_hash);
let (contract_version_ptr, contract_version_size, _bytes) =
contract_api::to_ptr(contract_version);
let (entry_point_name_ptr, entry_point_name_size, _bytes) =
contract_api::to_ptr(entry_point_name);
let (runtime_args_ptr, runtime_args_size, _bytes) = contract_api::to_ptr(runtime_args);
let bytes_written = {
let mut bytes_written = MaybeUninit::uninit();
let ret = unsafe {
ext_ffi::casper_call_versioned_contract(
contract_package_hash_ptr,
contract_package_hash_size,
contract_version_ptr,
contract_version_size,
entry_point_name_ptr,
entry_point_name_size,
runtime_args_ptr,
runtime_args_size,
bytes_written.as_mut_ptr(),
)
};
api_error::result_from(ret).unwrap_or_revert();
unsafe { bytes_written.assume_init() }
};
deserialize_contract_result(bytes_written)
}
fn deserialize_contract_result<T: CLTyped + FromBytes>(bytes_written: usize) -> T {
let serialized_result = if bytes_written == 0 {
vec![]
} else {
let bytes_non_null_ptr = contract_api::alloc_bytes(bytes_written);
let mut dest: Vec<u8> = unsafe {
Vec::from_raw_parts(bytes_non_null_ptr.as_ptr(), bytes_written, bytes_written)
};
read_host_buffer_into(&mut dest).unwrap_or_revert();
dest
};
bytesrepr::deserialize(serialized_result).unwrap_or_revert()
}
fn get_named_arg_size(name: &str) -> Option<usize> {
let mut arg_size: usize = 0;
let ret = unsafe {
ext_ffi::casper_get_named_arg_size(
name.as_bytes().as_ptr(),
name.len(),
&mut arg_size as *mut usize,
)
};
match api_error::result_from(ret) {
Ok(_) => Some(arg_size),
Err(ApiError::MissingArgument) => None,
Err(e) => revert(e),
}
}
pub fn get_named_arg<T: FromBytes>(name: &str) -> T {
let arg_size = get_named_arg_size(name).unwrap_or_revert_with(ApiError::MissingArgument);
let arg_bytes = if arg_size > 0 {
let res = {
let data_non_null_ptr = contract_api::alloc_bytes(arg_size);
let ret = unsafe {
ext_ffi::casper_get_named_arg(
name.as_bytes().as_ptr(),
name.len(),
data_non_null_ptr.as_ptr(),
arg_size,
)
};
let data =
unsafe { Vec::from_raw_parts(data_non_null_ptr.as_ptr(), arg_size, arg_size) };
api_error::result_from(ret).map(|_| data)
};
res.unwrap_or_revert()
} else {
Vec::new()
};
bytesrepr::deserialize(arg_bytes).unwrap_or_revert_with(ApiError::InvalidArgument)
}
pub fn get_caller() -> AccountHash {
let output_size = {
let mut output_size = MaybeUninit::uninit();
let ret = unsafe { ext_ffi::casper_get_caller(output_size.as_mut_ptr()) };
api_error::result_from(ret).unwrap_or_revert();
unsafe { output_size.assume_init() }
};
let buf = read_host_buffer(output_size).unwrap_or_revert();
bytesrepr::deserialize(buf).unwrap_or_revert()
}
pub fn get_blocktime() -> BlockTime {
let dest_non_null_ptr = contract_api::alloc_bytes(BLOCKTIME_SERIALIZED_LENGTH);
let bytes = unsafe {
ext_ffi::casper_get_blocktime(dest_non_null_ptr.as_ptr());
Vec::from_raw_parts(
dest_non_null_ptr.as_ptr(),
BLOCKTIME_SERIALIZED_LENGTH,
BLOCKTIME_SERIALIZED_LENGTH,
)
};
bytesrepr::deserialize(bytes).unwrap_or_revert()
}
pub fn get_phase() -> Phase {
let dest_non_null_ptr = contract_api::alloc_bytes(PHASE_SERIALIZED_LENGTH);
unsafe { ext_ffi::casper_get_phase(dest_non_null_ptr.as_ptr()) };
let bytes = unsafe {
Vec::from_raw_parts(
dest_non_null_ptr.as_ptr(),
PHASE_SERIALIZED_LENGTH,
PHASE_SERIALIZED_LENGTH,
)
};
bytesrepr::deserialize(bytes).unwrap_or_revert()
}
pub fn get_key(name: &str) -> Option<Key> {
let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
let mut key_bytes = vec![0u8; Key::max_serialized_length()];
let mut total_bytes: usize = 0;
let ret = unsafe {
ext_ffi::casper_get_key(
name_ptr,
name_size,
key_bytes.as_mut_ptr(),
key_bytes.len(),
&mut total_bytes as *mut usize,
)
};
match api_error::result_from(ret) {
Ok(_) => {}
Err(ApiError::MissingKey) => return None,
Err(e) => revert(e),
}
key_bytes.truncate(total_bytes);
let key: Key = bytesrepr::deserialize(key_bytes).unwrap_or_revert();
Some(key)
}
pub fn has_key(name: &str) -> bool {
let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
let result = unsafe { ext_ffi::casper_has_key(name_ptr, name_size) };
result == 0
}
pub fn put_key(name: &str, key: Key) {
let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
let (key_ptr, key_size, _bytes2) = contract_api::to_ptr(key);
unsafe { ext_ffi::casper_put_key(name_ptr, name_size, key_ptr, key_size) };
}
pub fn remove_key(name: &str) {
let (name_ptr, name_size, _bytes) = contract_api::to_ptr(name);
unsafe { ext_ffi::casper_remove_key(name_ptr, name_size) }
}
pub fn list_authorization_keys() -> BTreeSet<AccountHash> {
let (total_authorization_keys, result_size) = {
let mut authorization_keys = MaybeUninit::uninit();
let mut result_size = MaybeUninit::uninit();
let ret = unsafe {
ext_ffi::casper_load_authorization_keys(
authorization_keys.as_mut_ptr(),
result_size.as_mut_ptr(),
)
};
api_error::result_from(ret).unwrap_or_revert();
let total_authorization_keys = unsafe { authorization_keys.assume_init() };
let result_size = unsafe { result_size.assume_init() };
(total_authorization_keys, result_size)
};
if total_authorization_keys == 0 {
return BTreeSet::new();
}
let bytes = read_host_buffer(result_size).unwrap_or_revert();
bytesrepr::deserialize(bytes).unwrap_or_revert()
}
pub fn list_named_keys() -> NamedKeys {
let (total_keys, result_size) = {
let mut total_keys = MaybeUninit::uninit();
let mut result_size = 0;
let ret = unsafe {
ext_ffi::casper_load_named_keys(total_keys.as_mut_ptr(), &mut result_size as *mut usize)
};
api_error::result_from(ret).unwrap_or_revert();
let total_keys = unsafe { total_keys.assume_init() };
(total_keys, result_size)
};
if total_keys == 0 {
return NamedKeys::new();
}
let bytes = read_host_buffer(result_size).unwrap_or_revert();
bytesrepr::deserialize(bytes).unwrap_or_revert()
}
pub fn is_valid_uref(uref: URef) -> bool {
let (uref_ptr, uref_size, _bytes) = contract_api::to_ptr(uref);
let result = unsafe { ext_ffi::casper_is_valid_uref(uref_ptr, uref_size) };
result != 0
}
pub fn blake2b<T: AsRef<[u8]>>(input: T) -> [u8; BLAKE2B_DIGEST_LENGTH] {
let mut ret = [0; BLAKE2B_DIGEST_LENGTH];
let result = unsafe {
ext_ffi::casper_blake2b(
input.as_ref().as_ptr(),
input.as_ref().len(),
ret.as_mut_ptr(),
BLAKE2B_DIGEST_LENGTH,
)
};
api_error::result_from(result).unwrap_or_revert();
ret
}
pub fn random_bytes() -> [u8; RANDOM_BYTES_COUNT] {
let mut ret = [0; RANDOM_BYTES_COUNT];
let result = unsafe { ext_ffi::casper_random_bytes(ret.as_mut_ptr(), RANDOM_BYTES_COUNT) };
api_error::result_from(result).unwrap_or_revert();
ret
}
fn read_host_buffer_into(dest: &mut [u8]) -> Result<usize, ApiError> {
let mut bytes_written = MaybeUninit::uninit();
let ret = unsafe {
ext_ffi::casper_read_host_buffer(dest.as_mut_ptr(), dest.len(), bytes_written.as_mut_ptr())
};
api_error::result_from(ret)?;
Ok(unsafe { bytes_written.assume_init() })
}
pub(crate) fn read_host_buffer(size: usize) -> Result<Vec<u8>, ApiError> {
let mut dest: Vec<u8> = if size == 0 {
Vec::new()
} else {
let bytes_non_null_ptr = contract_api::alloc_bytes(size);
unsafe { Vec::from_raw_parts(bytes_non_null_ptr.as_ptr(), size, size) }
};
read_host_buffer_into(&mut dest)?;
Ok(dest)
}
pub fn get_call_stack() -> Vec<CallStackElement> {
let (call_stack_len, result_size) = {
let mut call_stack_len: usize = 0;
let mut result_size: usize = 0;
let ret = unsafe {
ext_ffi::casper_load_call_stack(
&mut call_stack_len as *mut usize,
&mut result_size as *mut usize,
)
};
api_error::result_from(ret).unwrap_or_revert();
(call_stack_len, result_size)
};
if call_stack_len == 0 {
return Vec::new();
}
let bytes = read_host_buffer(result_size).unwrap_or_revert();
bytesrepr::deserialize(bytes).unwrap_or_revert()
}
#[cfg(feature = "test-support")]
pub fn print(text: &str) {
let (text_ptr, text_size, _bytes) = contract_api::to_ptr(text);
unsafe { ext_ffi::casper_print(text_ptr, text_size) }
}