junobuild_shared/assert.rs
1use crate::canister::memory_size;
2use crate::msg::{
3 JUNO_ERROR_NO_TIMESTAMP, JUNO_ERROR_NO_VERSION, JUNO_ERROR_TIMESTAMP_OUTDATED_OR_FUTURE,
4 JUNO_ERROR_VERSION_OUTDATED_OR_FUTURE,
5};
6use crate::types::config::ConfigMaxMemorySize;
7use crate::types::interface::MemorySize;
8use crate::types::state::Version;
9
10/// Asserts the validity of a given user timestamp against the current timestamp.
11/// e.g. the timestamp of an existing entity persisted in a smart contract.
12///
13/// This function checks if the provided user timestamp matches the current system timestamp.
14/// It is designed to ensure that operations relying on timestamps are executed with current
15/// or synchronized timestamps to prevent replay or outdated requests.
16///
17/// # Parameters
18/// - `user_timestamp`: An `Option<u64>` representing the user-provided timestamp. This can be `None`
19/// if the user did not provide a timestamp, or `Some(u64)` if a timestamp was provided.
20/// - `current_timestamp`: A `u64` representing the current system timestamp. This should be
21/// the accurate current time in a format consistent with `user_timestamp`.
22///
23/// # Returns
24/// - `Ok(())` if the `user_timestamp` matches the `current_timestamp`.
25/// - `Err(String)` if:
26/// - The `user_timestamp` is `None`, indicating no timestamp was provided. The error string
27/// will be `ERROR_NO_TIMESTAMP.to_string()`, where `ERROR_NO_TIMESTAMP` is a constant string
28/// describing the error.
29/// - The `user_timestamp` does not match the `current_timestamp`, indicating either an outdated
30/// or a future timestamp. The error string will format to include the error description
31/// (from a constant `ERROR_TIMESTAMP_OUTDATED_OR_FUTURE`), the current timestamp, and the
32/// provided user timestamp.
33///
34/// # Examples
35/// ```
36/// let current_timestamp = 1625097600; // Example timestamp
37/// let user_timestamp = Some(1625097600);
38/// assert_eq!(assert_timestamp(user_timestamp, current_timestamp), Ok(()));
39///
40/// let wrong_timestamp = Some(1625097601);
41/// assert!(assert_timestamp(wrong_timestamp, current_timestamp).is_err());
42///
43/// let no_timestamp = None;
44/// assert!(assert_timestamp(no_timestamp, current_timestamp).is_err());
45/// ```
46///
47#[deprecated]
48pub fn assert_timestamp(user_timestamp: Option<u64>, current_timestamp: u64) -> Result<(), String> {
49 match user_timestamp {
50 None => {
51 return Err(JUNO_ERROR_NO_TIMESTAMP.to_string());
52 }
53 Some(user_timestamp) => {
54 if current_timestamp != user_timestamp {
55 return Err(format!(
56 "{} ({} - {})",
57 JUNO_ERROR_TIMESTAMP_OUTDATED_OR_FUTURE, current_timestamp, user_timestamp
58 ));
59 }
60 }
61 }
62
63 Ok(())
64}
65
66/// Asserts the validity of a given user version against the required version.
67/// This function checks if the provided user version matches the current system version.
68/// It is designed to ensure that operations relying on version numbers are executed with the
69/// correct versions to prevent issues with compatibility or outdated requests.
70///
71/// # Parameters
72/// - `user_version`: An `Option<u64>` representing the user-provided version. This can be `None`
73/// if the user did not provide a version, or `Some(u64)` if a version was provided.
74/// - `current_version`: An `Option<u64>` representing the required system version. This can be `None`
75/// which means no specific version requirement is needed.
76///
77/// # Returns
78/// - `Ok(())` if the `user_version` matches the `current_version` or if no specific `current_version` is provided.
79/// - `Err(String)` if:
80/// - The `user_version` is `None`, indicating no version was provided. The error string
81/// will be `ERROR_NO_VERSION.to_string()`, where `ERROR_NO_VERSION` is a constant string
82/// describing the error.
83/// - The `user_version` does not match the `current_version`, indicating either an incorrect
84/// or incompatible version. The error string will format to include the error description
85/// (from a constant `ERROR_VERSION_MISMATCH`), the required version, and the provided user version.
86///
87/// # Examples
88/// ```
89/// let current_version = Some(3); // Example version
90/// let user_version = Some(3);
91/// assert_eq!(assert_version(user_version, current_version), Ok(()));
92///
93/// let wrong_version = Some(2);
94/// assert!(assert_version(wrong_version, current_version).is_err());
95///
96/// let no_version = None;
97/// let no_current_version = None;
98/// assert!(assert_version(no_version, no_current_version).is_ok());
99/// ```
100///
101pub fn assert_version(
102 user_version: Option<Version>,
103 current_version: Option<Version>,
104) -> Result<(), String> {
105 match current_version {
106 None => (),
107 Some(current_version) => match user_version {
108 None => {
109 return Err(JUNO_ERROR_NO_VERSION.to_string());
110 }
111 Some(user_version) => {
112 if current_version != user_version {
113 return Err(format!(
114 "{} ({} - {})",
115 JUNO_ERROR_VERSION_OUTDATED_OR_FUTURE, current_version, user_version
116 ));
117 }
118 }
119 },
120 }
121
122 Ok(())
123}
124
125/// Validates the length of a description field in entities such as documents or assets.
126///
127/// Ensures that the description does not exceed 1024 characters. If the description exceeds
128/// this limit, the function returns an error message.
129///
130/// # Parameters
131///
132/// - `description`: An optional reference to a `String` that represents the description field
133/// of an entity. If the description is `None`, the function considers it valid and does not
134/// perform any length checks.
135///
136/// # Returns
137///
138/// - `Ok(())`: If the description is valid (either `None` or within the length limit).
139/// - `Err(String)`: If the description exceeds 1024 characters, containing an error message.
140///
141/// # Examples
142///
143/// ```
144/// let valid_description = Some(String::from("This is a valid description."));
145/// assert_eq!(assert_description_length(&valid_description), Ok(()));
146///
147/// let invalid_description = Some(String::from("a".repeat(1025)));
148/// assert_eq!(
149/// assert_description_length(&invalid_description),
150/// Err(String::from("Description field should not contains more than 1024 characters."))
151/// );
152///
153/// let none_description: Option<String> = None;
154/// assert_eq!(assert_description_length(&none_description), Ok(()));
155/// ```
156pub fn assert_description_length(description: &Option<String>) -> Result<(), String> {
157 match description {
158 None => (),
159 Some(description) => {
160 if description.len() > 1024 {
161 return Err(
162 "Description field should not contains more than 1024 characters.".to_string(),
163 );
164 }
165 }
166 }
167
168 Ok(())
169}
170
171pub fn assert_max_memory_size(
172 config_max_memory_size: &Option<ConfigMaxMemorySize>,
173) -> Result<(), String> {
174 if let Some(max_memory_size) = &config_max_memory_size {
175 let MemorySize { heap, stable } = memory_size();
176
177 if let Some(max_heap) = max_memory_size.heap {
178 if heap > max_heap {
179 return Err(format!(
180 "Heap memory usage exceeded: {} bytes used, {} bytes allowed.",
181 heap, max_heap
182 ));
183 }
184 }
185
186 if let Some(max_stable) = max_memory_size.stable {
187 if stable > max_stable {
188 return Err(format!(
189 "Stable memory usage exceeded: {} bytes used, {} bytes allowed.",
190 stable, max_stable
191 ));
192 }
193 }
194 }
195
196 Ok(())
197}