odra_core/
contract_env.rs

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