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