1use alloc::{
4 collections::{BTreeMap, BTreeSet},
5 format,
6 string::String,
7 vec,
8 vec::Vec,
9};
10use core::{convert::From, mem::MaybeUninit};
11
12use casper_types::{
13 addressable_entity::EntryPoints,
14 api_error,
15 bytesrepr::{self, FromBytes, ToBytes},
16 contract_messages::MessageTopicOperation,
17 contracts::{ContractHash, ContractPackageHash, ContractVersion, NamedKeys},
18 AccessRights, ApiError, CLTyped, CLValue, EntityVersion, HashAddr, Key, URef,
19 DICTIONARY_ITEM_KEY_MAX_LENGTH, UREF_SERIALIZED_LENGTH,
20};
21
22use crate::{
23 contract_api::{self, runtime, runtime::revert},
24 ext_ffi,
25 unwrap_or_revert::UnwrapOrRevert,
26};
27
28pub fn read<T: CLTyped + FromBytes>(uref: URef) -> Result<Option<T>, bytesrepr::Error> {
30 let key: Key = uref.into();
31 read_from_key(key)
32}
33
34pub fn read_from_key<T: CLTyped + FromBytes>(key: Key) -> Result<Option<T>, bytesrepr::Error> {
36 let (key_ptr, key_size, _bytes) = contract_api::to_ptr(key);
37
38 let value_size = {
39 let mut value_size = MaybeUninit::uninit();
40 let ret = unsafe { ext_ffi::casper_read_value(key_ptr, key_size, value_size.as_mut_ptr()) };
41 match api_error::result_from(ret) {
42 Ok(_) => unsafe { value_size.assume_init() },
43 Err(ApiError::ValueNotFound) => return Ok(None),
44 Err(e) => runtime::revert(e),
45 }
46 };
47
48 let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
49 Ok(Some(bytesrepr::deserialize(value_bytes)?))
50}
51
52pub fn read_or_revert<T: CLTyped + FromBytes>(uref: URef) -> T {
54 read(uref)
55 .unwrap_or_revert_with(ApiError::Read)
56 .unwrap_or_revert_with(ApiError::ValueNotFound)
57}
58
59pub fn write<T: CLTyped + ToBytes>(uref: URef, value: T) {
61 let key = Key::from(uref);
62 let (key_ptr, key_size, _bytes1) = contract_api::to_ptr(key);
63
64 let cl_value = CLValue::from_t(value).unwrap_or_revert();
65 let (cl_value_ptr, cl_value_size, _bytes2) = contract_api::to_ptr(cl_value);
66
67 unsafe {
68 ext_ffi::casper_write(key_ptr, key_size, cl_value_ptr, cl_value_size);
69 }
70}
71
72pub fn add<T: CLTyped + ToBytes>(uref: URef, value: T) {
74 let key = Key::from(uref);
75 let (key_ptr, key_size, _bytes1) = contract_api::to_ptr(key);
76
77 let cl_value = CLValue::from_t(value).unwrap_or_revert();
78 let (cl_value_ptr, cl_value_size, _bytes2) = contract_api::to_ptr(cl_value);
79
80 unsafe {
81 ext_ffi::casper_add(key_ptr, key_size, cl_value_ptr, cl_value_size);
83 }
84}
85
86pub fn new_uref<T: CLTyped + ToBytes>(init: T) -> URef {
88 let uref_non_null_ptr = contract_api::alloc_bytes(UREF_SERIALIZED_LENGTH);
89 let cl_value = CLValue::from_t(init).unwrap_or_revert();
90 let (cl_value_ptr, cl_value_size, _cl_value_bytes) = contract_api::to_ptr(cl_value);
91 let bytes = unsafe {
92 ext_ffi::casper_new_uref(uref_non_null_ptr.as_ptr(), cl_value_ptr, cl_value_size); Vec::from_raw_parts(
94 uref_non_null_ptr.as_ptr(),
95 UREF_SERIALIZED_LENGTH,
96 UREF_SERIALIZED_LENGTH,
97 )
98 };
99 bytesrepr::deserialize(bytes).unwrap_or_revert()
100}
101
102pub fn new_contract(
112 entry_points: EntryPoints,
113 named_keys: Option<NamedKeys>,
114 hash_name: Option<String>,
115 uref_name: Option<String>,
116 message_topics: Option<BTreeMap<String, MessageTopicOperation>>,
117) -> (ContractHash, EntityVersion) {
118 create_contract(
119 entry_points,
120 named_keys,
121 hash_name,
122 uref_name,
123 message_topics,
124 false,
125 )
126}
127
128pub fn new_locked_contract(
137 entry_points: EntryPoints,
138 named_keys: Option<NamedKeys>,
139 hash_name: Option<String>,
140 uref_name: Option<String>,
141 message_topics: Option<BTreeMap<String, MessageTopicOperation>>,
142) -> (ContractHash, EntityVersion) {
143 create_contract(
144 entry_points,
145 named_keys,
146 hash_name,
147 uref_name,
148 message_topics,
149 true,
150 )
151}
152
153fn create_contract(
154 entry_points: EntryPoints,
155 named_keys: Option<NamedKeys>,
156 hash_name: Option<String>,
157 uref_name: Option<String>,
158 message_topics: Option<BTreeMap<String, MessageTopicOperation>>,
159 is_locked: bool,
160) -> (ContractHash, EntityVersion) {
161 let (contract_package_hash, access_uref) = create_contract_package(is_locked);
162
163 if let Some(hash_name) = hash_name {
164 runtime::put_key(&hash_name, Key::Hash(contract_package_hash.value()));
165 };
166
167 if let Some(uref_name) = uref_name {
168 runtime::put_key(&uref_name, access_uref.into());
169 };
170
171 let named_keys = named_keys.unwrap_or_default();
172
173 let message_topics = message_topics.unwrap_or_default();
174
175 add_contract_version(
176 contract_package_hash,
177 entry_points,
178 named_keys,
179 message_topics,
180 )
181}
182
183pub fn create_contract_package_at_hash() -> (ContractPackageHash, URef) {
187 create_contract_package(false)
188}
189
190fn create_contract_package(is_locked: bool) -> (ContractPackageHash, URef) {
191 let mut hash_addr: HashAddr = ContractPackageHash::default().value();
192 let mut access_addr = [0u8; 32];
193 unsafe {
194 ext_ffi::casper_create_contract_package_at_hash(
195 hash_addr.as_mut_ptr(),
196 access_addr.as_mut_ptr(),
197 is_locked,
198 );
199 }
200 let contract_package_hash: ContractPackageHash = hash_addr.into();
201 let access_uref = URef::new(access_addr, AccessRights::READ_ADD_WRITE);
202
203 (contract_package_hash, access_uref)
204}
205
206pub fn create_contract_user_group(
214 contract_package_hash: ContractPackageHash,
215 group_label: &str,
216 num_new_urefs: u8, existing_urefs: BTreeSet<URef>, ) -> Result<Vec<URef>, ApiError> {
219 let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
220 contract_api::to_ptr(contract_package_hash);
221 let (label_ptr, label_size, _bytes3) = contract_api::to_ptr(group_label);
222 let (existing_urefs_ptr, existing_urefs_size, _bytes4) = contract_api::to_ptr(existing_urefs);
223
224 let value_size = {
225 let mut output_size = MaybeUninit::uninit();
226 let ret = unsafe {
227 ext_ffi::casper_create_contract_user_group(
228 contract_package_hash_ptr,
229 contract_package_hash_size,
230 label_ptr,
231 label_size,
232 num_new_urefs,
233 existing_urefs_ptr,
234 existing_urefs_size,
235 output_size.as_mut_ptr(),
236 )
237 };
238 api_error::result_from(ret).unwrap_or_revert();
239 unsafe { output_size.assume_init() }
240 };
241
242 let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
243 Ok(bytesrepr::deserialize(value_bytes).unwrap_or_revert())
244}
245
246pub fn provision_contract_user_group_uref(
248 package_hash: ContractPackageHash,
249 label: &str,
250) -> Result<URef, ApiError> {
251 let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
252 contract_api::to_ptr(package_hash);
253 let (label_ptr, label_size, _bytes2) = contract_api::to_ptr(label);
254 let value_size = {
255 let mut value_size = MaybeUninit::uninit();
256 let ret = unsafe {
257 ext_ffi::casper_provision_contract_user_group_uref(
258 contract_package_hash_ptr,
259 contract_package_hash_size,
260 label_ptr,
261 label_size,
262 value_size.as_mut_ptr(),
263 )
264 };
265 api_error::result_from(ret)?;
266 unsafe { value_size.assume_init() }
267 };
268 let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
269 Ok(bytesrepr::deserialize(value_bytes).unwrap_or_revert())
270}
271
272pub fn remove_contract_user_group_urefs(
274 package_hash: ContractPackageHash,
275 label: &str,
276 urefs: BTreeSet<URef>,
277) -> Result<(), ApiError> {
278 let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
279 contract_api::to_ptr(package_hash);
280 let (label_ptr, label_size, _bytes3) = contract_api::to_ptr(label);
281 let (urefs_ptr, urefs_size, _bytes4) = contract_api::to_ptr(urefs);
282 let ret = unsafe {
283 ext_ffi::casper_remove_contract_user_group_urefs(
284 contract_package_hash_ptr,
285 contract_package_hash_size,
286 label_ptr,
287 label_size,
288 urefs_ptr,
289 urefs_size,
290 )
291 };
292 api_error::result_from(ret)
293}
294
295pub fn remove_contract_user_group(
297 package_hash: ContractPackageHash,
298 label: &str,
299) -> Result<(), ApiError> {
300 let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
301 contract_api::to_ptr(package_hash);
302 let (label_ptr, label_size, _bytes3) = contract_api::to_ptr(label);
303 let ret = unsafe {
304 ext_ffi::casper_remove_contract_user_group(
305 contract_package_hash_ptr,
306 contract_package_hash_size,
307 label_ptr,
308 label_size,
309 )
310 };
311 api_error::result_from(ret)
312}
313
314pub fn add_contract_version(
316 package_hash: ContractPackageHash,
317 entry_points: EntryPoints,
318 named_keys: NamedKeys,
319 message_topics: BTreeMap<String, MessageTopicOperation>,
320) -> (ContractHash, EntityVersion) {
321 let (package_hash_ptr, package_hash_size, _package_hash_bytes) =
323 contract_api::to_ptr(package_hash);
324 let (entry_points_ptr, entry_points_size, _entry_point_bytes) =
325 contract_api::to_ptr(entry_points);
326 let (named_keys_ptr, named_keys_size, _named_keys_bytes) = contract_api::to_ptr(named_keys);
327 let (message_topics_ptr, message_topics_size, _message_topics) =
328 contract_api::to_ptr(message_topics);
329
330 let mut output_ptr = vec![0u8; 32];
331 let mut entity_version: ContractVersion = 0;
334
335 let ret = unsafe {
336 ext_ffi::casper_add_contract_version_with_message_topics(
337 package_hash_ptr,
338 package_hash_size,
339 &mut entity_version as *mut ContractVersion, entry_points_ptr,
341 entry_points_size,
342 named_keys_ptr,
343 named_keys_size,
344 message_topics_ptr,
345 message_topics_size,
346 output_ptr.as_mut_ptr(),
347 output_ptr.len(),
348 )
350 };
351 match api_error::result_from(ret) {
352 Ok(_) => {}
353 Err(e) => revert(e),
354 }
355 let entity_hash: ContractHash = match bytesrepr::deserialize(output_ptr) {
357 Ok(hash) => hash,
358 Err(err) => panic!("{}", format!("{:?}", err)),
359 };
360 (entity_hash, entity_version)
361}
362
363pub fn disable_contract_version(
378 contract_package_hash: ContractPackageHash,
379 contract_hash: ContractHash,
380) -> Result<(), ApiError> {
381 let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
382 contract_api::to_ptr(contract_package_hash);
383 let (contract_hash_ptr, contract_hash_size, _bytes2) = contract_api::to_ptr(contract_hash);
384
385 let result = unsafe {
386 ext_ffi::casper_disable_contract_version(
387 contract_package_hash_ptr,
388 contract_package_hash_size,
389 contract_hash_ptr,
390 contract_hash_size,
391 )
392 };
393
394 api_error::result_from(result)
395}
396
397pub fn enable_contract_version(
409 contract_package_hash: ContractPackageHash,
410 contract_hash: ContractHash,
411) -> Result<(), ApiError> {
412 let (contract_package_hash_ptr, contract_package_hash_size, _bytes1) =
413 contract_api::to_ptr(contract_package_hash);
414 let (contract_hash_ptr, contract_hash_size, _bytes2) = contract_api::to_ptr(contract_hash);
415
416 let result = unsafe {
417 ext_ffi::casper_enable_contract_version(
418 contract_package_hash_ptr,
419 contract_package_hash_size,
420 contract_hash_ptr,
421 contract_hash_size,
422 )
423 };
424
425 api_error::result_from(result)
426}
427
428pub fn new_dictionary(dictionary_name: &str) -> Result<URef, ApiError> {
431 if dictionary_name.is_empty() || runtime::has_key(dictionary_name) {
432 return Err(ApiError::InvalidArgument);
433 }
434
435 let value_size = {
436 let mut value_size = MaybeUninit::uninit();
437 let ret = unsafe { ext_ffi::casper_new_dictionary(value_size.as_mut_ptr()) };
438 api_error::result_from(ret)?;
439 unsafe { value_size.assume_init() }
440 };
441 let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
442 let uref: URef = bytesrepr::deserialize(value_bytes).unwrap_or_revert();
443 runtime::put_key(dictionary_name, Key::from(uref));
444 Ok(uref)
445}
446
447pub fn dictionary_get<V: CLTyped + FromBytes>(
450 dictionary_seed_uref: URef,
451 dictionary_item_key: &str,
452) -> Result<Option<V>, bytesrepr::Error> {
453 let (uref_ptr, uref_size, _bytes1) = contract_api::to_ptr(dictionary_seed_uref);
454 let (dictionary_item_key_ptr, dictionary_item_key_size) =
455 contract_api::dictionary_item_key_to_ptr(dictionary_item_key);
456
457 if dictionary_item_key_size > DICTIONARY_ITEM_KEY_MAX_LENGTH {
458 revert(ApiError::DictionaryItemKeyExceedsLength)
459 }
460
461 let value_size = {
462 let mut value_size = MaybeUninit::uninit();
463 let ret = unsafe {
464 ext_ffi::casper_dictionary_get(
465 uref_ptr,
466 uref_size,
467 dictionary_item_key_ptr,
468 dictionary_item_key_size,
469 value_size.as_mut_ptr(),
470 )
471 };
472 match api_error::result_from(ret) {
473 Ok(_) => unsafe { value_size.assume_init() },
474 Err(ApiError::ValueNotFound) => return Ok(None),
475 Err(e) => runtime::revert(e),
476 }
477 };
478
479 let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
480 Ok(Some(bytesrepr::deserialize(value_bytes)?))
481}
482
483pub fn dictionary_put<V: CLTyped + ToBytes>(
485 dictionary_seed_uref: URef,
486 dictionary_item_key: &str,
487 value: V,
488) {
489 let (uref_ptr, uref_size, _bytes1) = contract_api::to_ptr(dictionary_seed_uref);
490 let (dictionary_item_key_ptr, dictionary_item_key_size) =
491 contract_api::dictionary_item_key_to_ptr(dictionary_item_key);
492
493 if dictionary_item_key_size > DICTIONARY_ITEM_KEY_MAX_LENGTH {
494 revert(ApiError::DictionaryItemKeyExceedsLength)
495 }
496
497 let cl_value = CLValue::from_t(value).unwrap_or_revert();
498 let (cl_value_ptr, cl_value_size, _bytes) = contract_api::to_ptr(cl_value);
499
500 let result = unsafe {
501 let ret = ext_ffi::casper_dictionary_put(
502 uref_ptr,
503 uref_size,
504 dictionary_item_key_ptr,
505 dictionary_item_key_size,
506 cl_value_ptr,
507 cl_value_size,
508 );
509 api_error::result_from(ret)
510 };
511
512 result.unwrap_or_revert()
513}
514
515pub fn dictionary_read<T: CLTyped + FromBytes>(dictionary_key: Key) -> Result<Option<T>, ApiError> {
517 if !dictionary_key.is_dictionary_key() {
518 return Err(ApiError::UnexpectedKeyVariant);
519 }
520
521 let (key_ptr, key_size, _bytes) = contract_api::to_ptr(dictionary_key);
522
523 let value_size = {
524 let mut value_size = MaybeUninit::uninit();
525 let ret =
526 unsafe { ext_ffi::casper_dictionary_read(key_ptr, key_size, value_size.as_mut_ptr()) };
527 match api_error::result_from(ret) {
528 Ok(_) => unsafe { value_size.assume_init() },
529 Err(ApiError::ValueNotFound) => return Ok(None),
530 Err(e) => runtime::revert(e),
531 }
532 };
533
534 let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
535 Ok(Some(bytesrepr::deserialize(value_bytes)?))
536}
537
538fn get_named_uref(name: &str) -> URef {
539 match runtime::get_key(name).unwrap_or_revert_with(ApiError::GetKey) {
540 Key::URef(uref) => uref,
541 _ => revert(ApiError::UnexpectedKeyVariant),
542 }
543}
544
545pub fn named_dictionary_get<V: CLTyped + FromBytes>(
547 dictionary_name: &str,
548 dictionary_item_key: &str,
549) -> Result<Option<V>, bytesrepr::Error> {
550 dictionary_get(get_named_uref(dictionary_name), dictionary_item_key)
551}
552
553pub fn named_dictionary_put<V: CLTyped + ToBytes>(
555 dictionary_name: &str,
556 dictionary_item_key: &str,
557 value: V,
558) {
559 dictionary_put(get_named_uref(dictionary_name), dictionary_item_key, value)
560}