odra_core/
contract_env.rs

1use casper_event_standard::EventInstance;
2
3use crate::args::EntrypointArgument;
4use crate::call_def::CallDef;
5use crate::casper_types::bytesrepr::{deserialize_from_slice, Bytes, FromBytes, ToBytes};
6use crate::casper_types::crypto::PublicKey;
7use crate::casper_types::{CLTyped, CLValue, BLAKE2B_DIGEST_LENGTH, U512};
8use crate::module::Revertible;
9pub use crate::ContractContext;
10use crate::{consts, prelude::*, utils};
11
12const INDEX_SIZE: usize = 4;
13const KEY_LEN: usize = 64;
14pub(crate) type StorageKey = [u8; KEY_LEN];
15
16/// Trait that needs to be implemented by all contract refs.
17pub trait ContractRef {
18    /// Creates a new instance of the Contract Ref.
19    fn new(env: Rc<ContractEnv>, address: Address) -> Self;
20    /// Returns the address of the contract.
21    fn address(&self) -> &Address;
22}
23
24/// Represents the environment accessible in the contract context.
25///
26/// The `ContractEnv` struct provides methods for interacting with the contract environment,
27/// such as accessing storage, calling other contracts, and handling various contract-related operations.
28///
29/// The `ContractEnv` is available for the user to use in the module code.
30#[derive(Clone)]
31pub struct ContractEnv {
32    index: u32,
33    mapping_data: Vec<u8>,
34    backend: Rc<RefCell<dyn ContractContext>>
35}
36
37impl Revertible for ContractEnv {
38    fn revert<E: Into<OdraError>>(&self, e: E) -> ! {
39        self.revert(e)
40    }
41}
42
43impl ContractEnv {
44    /// Creates a new ContractEnv instance.
45    pub const fn new(index: u32, backend: Rc<RefCell<dyn ContractContext>>) -> Self {
46        Self {
47            index,
48            mapping_data: Vec::new(),
49            backend
50        }
51    }
52
53    /// Returns the current storage key for the contract environment.
54    pub(crate) fn current_key(&self) -> StorageKey {
55        let mut result = [0u8; KEY_LEN];
56        let mut key = Vec::with_capacity(INDEX_SIZE + self.mapping_data.len());
57        key.extend_from_slice(self.index.to_be_bytes().as_ref());
58        key.extend_from_slice(&self.mapping_data);
59        let hashed_key = self.backend.borrow().hash(key.as_slice());
60        utils::hex_to_slice(&hashed_key, &mut result);
61        result
62    }
63
64    /// Adds the given data to the mapping data of the contract environment.
65    pub(crate) fn add_to_mapping_data(&mut self, data: &[u8]) {
66        self.mapping_data.extend_from_slice(data);
67    }
68
69    /// Returns a child contract environment with the specified index.
70    pub(crate) fn child(&self, index: u8) -> Self {
71        Self {
72            index: (self.index << 4) + index as u32,
73            mapping_data: self.mapping_data.clone(),
74            backend: self.backend.clone()
75        }
76    }
77
78    /// Retrieves the value associated with the given key from the contract storage.
79    ///
80    /// # Returns
81    ///
82    /// The value associated with the key, if it exists.
83    pub fn get_value<T: FromBytes>(&self, key: &[u8]) -> Option<T> {
84        self.backend
85            .borrow()
86            .get_value(key)
87            .map(|bytes| deserialize_from_slice(bytes).unwrap_or_revert(self))
88    }
89
90    /// Sets the value associated with the given key in the contract storage.
91    pub fn set_value<T: ToBytes + CLTyped>(&self, key: &[u8], value: T) {
92        let result = value.to_bytes().map_err(ExecutionError::from);
93        let bytes = result.unwrap_or_revert(self);
94        self.backend.borrow().set_value(key, bytes.into());
95    }
96
97    /// Retrieves the value associated with the given named key from the contract storage.
98    pub fn get_named_value<T: FromBytes + CLTyped, U: AsRef<str>>(&self, name: U) -> Option<T> {
99        let key = name.as_ref();
100        let bytes = self.backend.borrow().get_named_value(key);
101        bytes.map(|b| deserialize_from_slice(b).unwrap_or_revert(self))
102    }
103
104    /// Sets the value associated with the given named key in the contract storage.
105    pub fn set_named_value<T: CLTyped + ToBytes, U: AsRef<str>>(&self, name: U, value: T) {
106        let key = name.as_ref();
107        // todo: map errors to correct Odra errors
108        let cl_value = CLValue::from_t(value).unwrap_or_revert(self);
109        self.backend.borrow().set_named_value(key, cl_value);
110    }
111
112    /// Retrieves the value associated with the given named key from the named dictionary in the contract storage.
113    pub fn get_dictionary_value<T: FromBytes + CLTyped, U: AsRef<str>>(
114        &self,
115        dictionary_name: U,
116        key: &[u8]
117    ) -> Option<T> {
118        let dictionary_name = dictionary_name.as_ref();
119        let bytes = self
120            .backend
121            .borrow()
122            .get_dictionary_value(dictionary_name, key);
123        bytes.map(|b| {
124            deserialize_from_slice(b)
125                .map_err(|_| ExecutionError::Formatting)
126                .unwrap_or_revert(self)
127        })
128    }
129
130    /// Sets the value associated with the given named key in the named dictionary in the contract storage.
131    pub fn set_dictionary_value<T: CLTyped + ToBytes, U: AsRef<str>>(
132        &self,
133        dictionary_name: U,
134        key: &[u8],
135        value: T
136    ) {
137        let dictionary_name = dictionary_name.as_ref();
138        let cl_value = CLValue::from_t(value)
139            .map_err(|_| ExecutionError::Formatting)
140            .unwrap_or_revert(self);
141        self.backend
142            .borrow()
143            .set_dictionary_value(dictionary_name, key, cl_value);
144    }
145
146    /// Removes the dictionary from the contract storage.
147    pub fn remove_dictionary<U: AsRef<str>>(&self, dictionary_name: U) {
148        let dictionary_name = dictionary_name.as_ref();
149        self.backend.borrow().remove_dictionary(dictionary_name);
150    }
151
152    /// Returns the address of the caller of the contract.
153    pub fn caller(&self) -> Address {
154        let backend = self.backend.borrow();
155        backend.caller()
156    }
157
158    /// Calls another contract with the specified address and call definition.
159    ///
160    /// # Returns
161    ///
162    /// The result of the contract call. If any error occurs during the call, the contract will revert.
163    pub fn call_contract<T: FromBytes>(&self, address: Address, call: CallDef) -> T {
164        let backend = self.backend.borrow();
165        let bytes = backend.call_contract(address, call);
166        deserialize_from_slice(bytes).unwrap_or_revert(self)
167    }
168
169    /// Returns the address of the current contract.
170    pub fn self_address(&self) -> Address {
171        let backend = self.backend.borrow();
172        backend.self_address()
173    }
174
175    /// Transfers tokens to the specified address.
176    pub fn transfer_tokens(&self, to: &Address, amount: &U512) {
177        let backend = self.backend.borrow();
178        backend.transfer_tokens(to, amount)
179    }
180
181    /// Returns the current block time as u64 value.
182    pub fn get_block_time(&self) -> u64 {
183        let backend = self.backend.borrow();
184        backend.get_block_time()
185    }
186
187    /// Returns the value attached to the contract call.
188    pub fn attached_value(&self) -> U512 {
189        let backend = self.backend.borrow();
190        backend.attached_value()
191    }
192
193    /// Returns the CSPR balance of the current contract.
194    pub fn self_balance(&self) -> U512 {
195        let backend = self.backend.borrow();
196        backend.self_balance()
197    }
198
199    /// Reverts the contract execution with the specified error.
200    pub fn revert<E: Into<OdraError>>(&self, error: E) -> ! {
201        let backend = self.backend.borrow();
202        backend.revert(error.into())
203    }
204
205    /// Emits an event with the specified data.
206    pub fn emit_event<T: ToBytes + EventInstance>(&self, event: T) {
207        let backend = self.backend.borrow();
208        let result = event.to_bytes().map_err(ExecutionError::from);
209        let bytes = result.unwrap_or_revert(self);
210        backend.emit_event(&bytes.into())
211    }
212
213    /// Verifies the signature of a message using the specified signature, public key, and message.
214    ///
215    /// # Arguments
216    ///
217    /// * `message` - The message to verify.
218    /// * `signature` - The signature to verify.
219    /// * `public_key` - The public key to use for verification.
220    ///
221    /// # Returns
222    ///
223    /// `true` if the signature is valid, `false` otherwise.
224    pub fn verify_signature(
225        &self,
226        message: &Bytes,
227        signature: &Bytes,
228        public_key: &PublicKey
229    ) -> bool {
230        let (signature, _) = casper_types::crypto::Signature::from_bytes(signature.as_slice())
231            .unwrap_or_else(|_| self.revert(ExecutionError::CouldNotDeserializeSignature));
232        casper_types::crypto::verify(message.as_slice(), &signature, public_key).is_ok()
233    }
234
235    /// Hashes the specified value.
236    ///
237    /// # Returns
238    ///
239    /// The hash value as a 32-byte array.
240    pub fn hash<T: AsRef<[u8]>>(&self, value: T) -> [u8; BLAKE2B_DIGEST_LENGTH] {
241        self.backend.borrow().hash(value.as_ref())
242    }
243}
244
245/// Represents the environment accessible in the contract execution context.
246///
247/// `ExecutionEnv` provides pre- and post-execution methods for the contract, such as performing non-reentrant checks
248/// and handling the attached value.
249pub struct ExecutionEnv {
250    env: Rc<ContractEnv>
251}
252
253impl Revertible for ExecutionEnv {
254    fn revert<E: Into<OdraError>>(&self, e: E) -> ! {
255        self.env.revert(e)
256    }
257}
258
259impl ExecutionEnv {
260    /// Creates a new ExecutionEnv instance.
261    pub fn new(env: Rc<ContractEnv>) -> Self {
262        Self { env }
263    }
264
265    /// Performs non-reentrant checks before executing a function.
266    pub fn non_reentrant_before(&self) {
267        // Check if reentrancy guard is set to true
268        let status: bool = self
269            .env
270            .get_value(consts::REENTRANCY_GUARD.as_slice())
271            .unwrap_or_default();
272        if status {
273            // Revert execution with ReentrantCall error
274            self.env.revert(ExecutionError::ReentrantCall);
275        }
276        // Set reentrancy guard to true
277        self.env
278            .set_value(consts::REENTRANCY_GUARD.as_slice(), true);
279    }
280
281    /// Resets the reentrancy guard after executing a function.
282    pub fn non_reentrant_after(&self) {
283        // Set reentrancy guard to false
284        self.env
285            .set_value(consts::REENTRANCY_GUARD.as_slice(), false);
286    }
287
288    /// Handles the attached value in the execution environment.
289    pub fn handle_attached_value(&self) {
290        self.env.backend.borrow().handle_attached_value();
291    }
292
293    /// Clears the attached value in the execution environment.
294    pub fn clear_attached_value(&self) {
295        self.env.backend.borrow().clear_attached_value();
296    }
297
298    /// Retrieves the value of a named argument from the execution environment.
299    ///
300    /// # Returns
301    ///
302    /// The deserialized value of the named argument. If the argument does not exist or deserialization fails,
303    /// the contract will revert.
304    pub fn get_named_arg<T: FromBytes + EntrypointArgument>(&self, name: &str) -> T {
305        if T::is_required() {
306            let result = self.env.backend.borrow().get_named_arg_bytes(name);
307            match result {
308                Ok(bytes) => deserialize_from_slice(bytes).unwrap_or_revert(self),
309                Err(err) => self.env.revert(err)
310            }
311        } else {
312            let bytes = self.env.backend.borrow().get_opt_named_arg_bytes(name);
313            let result = bytes.map(|bytes| deserialize_from_slice(bytes).unwrap_or_revert(self));
314            T::unwrap(result, &self.env)
315        }
316    }
317}