1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use alloc::{collections::BTreeMap, string::String, vec::Vec};
use core::{convert::From, mem::MaybeUninit};

use crate::{
    bytesrepr::{self, FromBytes, ToBytes},
    contract_api::{self, error, runtime, ContractRef, Error, TURef},
    ext_ffi,
    key::{Key, KEY_UREF_SERIALIZED_LENGTH},
    unwrap_or_revert::UnwrapOrRevert,
    uref::{AccessRights, URef},
    value::{CLTyped, CLValue},
};

/// Reads value under `turef` in the global state.
pub fn read<T: CLTyped + FromBytes>(turef: TURef<T>) -> Result<Option<T>, bytesrepr::Error> {
    let key: Key = turef.into();
    let (key_ptr, key_size, _bytes) = contract_api::to_ptr(key);

    let value_size = {
        let mut value_size = MaybeUninit::uninit();
        let ret = unsafe { ext_ffi::read_value(key_ptr, key_size, value_size.as_mut_ptr()) };
        match error::result_from(ret) {
            Ok(_) => unsafe { value_size.assume_init() },
            Err(Error::ValueNotFound) => return Ok(None),
            Err(e) => runtime::revert(e),
        }
    };

    let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
    Ok(Some(bytesrepr::deserialize(value_bytes)?))
}

/// Reads the value under `key` in the context-local partition of global state.
pub fn read_local<K: ToBytes, V: CLTyped + FromBytes>(
    key: &K,
) -> Result<Option<V>, bytesrepr::Error> {
    let key_bytes = key.to_bytes()?;

    let value_size = {
        let mut value_size = MaybeUninit::uninit();
        let ret = unsafe {
            ext_ffi::read_value_local(key_bytes.as_ptr(), key_bytes.len(), value_size.as_mut_ptr())
        };
        match error::result_from(ret) {
            Ok(_) => unsafe { value_size.assume_init() },
            Err(Error::ValueNotFound) => return Ok(None),
            Err(e) => runtime::revert(e),
        }
    };

    let value_bytes = runtime::read_host_buffer(value_size).unwrap_or_revert();
    Ok(Some(bytesrepr::deserialize(value_bytes)?))
}

/// Writes `value` under `turef` in the global state.
pub fn write<T: CLTyped + ToBytes>(turef: TURef<T>, value: T) {
    let key = Key::from(turef);
    let (key_ptr, key_size, _bytes1) = contract_api::to_ptr(key);

    let cl_value = CLValue::from_t(value).unwrap_or_revert();
    let (cl_value_ptr, cl_value_size, _bytes2) = contract_api::to_ptr(cl_value);

    unsafe {
        ext_ffi::write(key_ptr, key_size, cl_value_ptr, cl_value_size);
    }
}

/// Writes `value` under `key` in the context-local partition of global state.
pub fn write_local<K: ToBytes, V: CLTyped + ToBytes>(key: K, value: V) {
    let (key_ptr, key_size, _bytes1) = contract_api::to_ptr(key);

    let cl_value = CLValue::from_t(value).unwrap_or_revert();
    let (cl_value_ptr, cl_value_size, _bytes) = contract_api::to_ptr(cl_value);

    unsafe {
        ext_ffi::write_local(key_ptr, key_size, cl_value_ptr, cl_value_size);
    }
}

/// Adds `value` to the one currently under `turef` in the global state.
pub fn add<T: CLTyped + ToBytes>(turef: TURef<T>, value: T) {
    let key = Key::from(turef);
    let (key_ptr, key_size, _bytes1) = contract_api::to_ptr(key);

    let cl_value = CLValue::from_t(value).unwrap_or_revert();
    let (cl_value_ptr, cl_value_size, _bytes2) = contract_api::to_ptr(cl_value);

    unsafe {
        // Could panic if `value` cannot be added to the given value in memory.
        ext_ffi::add(key_ptr, key_size, cl_value_ptr, cl_value_size);
    }
}

/// Adds `value` to the one currently under `key` in the global state.
pub fn add_local<K: ToBytes, V: CLTyped + ToBytes>(key: K, value: V) {
    let (key_ptr, key_size, _bytes1) = contract_api::to_ptr(key);

    let cl_value = CLValue::from_t(value).unwrap_or_revert();
    let (cl_value_ptr, cl_value_size, _bytes) = contract_api::to_ptr(cl_value);

    unsafe {
        ext_ffi::add_local(key_ptr, key_size, cl_value_ptr, cl_value_size);
    }
}

/// Stores the serialized bytes of an exported function under a URef generated by the host.
pub fn store_function(name: &str, named_keys: BTreeMap<String, Key>) -> ContractRef {
    let (fn_ptr, fn_size, _bytes1) = contract_api::to_ptr(name);
    let (keys_ptr, keys_size, _bytes2) = contract_api::to_ptr(named_keys);
    let mut addr = [0u8; 32];
    unsafe {
        ext_ffi::store_function(fn_ptr, fn_size, keys_ptr, keys_size, addr.as_mut_ptr());
    }
    ContractRef::URef(URef::new(addr, AccessRights::READ_ADD_WRITE))
}

/// Stores the serialized bytes of an exported function at an immutable address generated by the
/// host.
pub fn store_function_at_hash(name: &str, named_keys: BTreeMap<String, Key>) -> ContractRef {
    let (fn_ptr, fn_size, _bytes1) = contract_api::to_ptr(name);
    let (keys_ptr, keys_size, _bytes2) = contract_api::to_ptr(named_keys);
    let mut addr = [0u8; 32];
    unsafe {
        ext_ffi::store_function_at_hash(fn_ptr, fn_size, keys_ptr, keys_size, addr.as_mut_ptr());
    }
    ContractRef::Hash(addr)
}

/// Returns a new unforgable pointer, where value is initialized to `init`
pub fn new_turef<T: CLTyped + ToBytes>(init: T) -> TURef<T> {
    let key_ptr = contract_api::alloc_bytes(KEY_UREF_SERIALIZED_LENGTH);
    let cl_value = CLValue::from_t(init).unwrap_or_revert();
    let (cl_value_ptr, cl_value_size, _cl_value_bytes) = contract_api::to_ptr(cl_value);
    let bytes = unsafe {
        ext_ffi::new_uref(key_ptr, cl_value_ptr, cl_value_size); // URef has `READ_ADD_WRITE` access
        Vec::from_raw_parts(
            key_ptr,
            KEY_UREF_SERIALIZED_LENGTH,
            KEY_UREF_SERIALIZED_LENGTH,
        )
    };
    let key: Key = bytesrepr::deserialize(bytes).unwrap_or_revert();
    if let Key::URef(uref) = key {
        TURef::from_uref(uref).unwrap_or_revert()
    } else {
        runtime::revert(Error::UnexpectedKeyVariant);
    }
}