Skip to main content

casper_rust_wasm_sdk/sdk/rpcs/
get_state_root_hash.rs

1#[cfg(all(feature = "js", target_arch = "wasm32"))]
2use crate::types::digest::Digest;
3#[cfg(all(feature = "js", target_arch = "wasm32"))]
4use crate::types::identifier::block_identifier::BlockIdentifier;
5use crate::{
6    types::{
7        identifier::block_identifier::BlockIdentifierInput, sdk_error::SdkError,
8        verbosity::Verbosity,
9    },
10    SDK,
11};
12use casper_client::{
13    cli::get_state_root_hash as get_state_root_hash_cli,
14    get_state_root_hash as get_state_root_hash_lib,
15    rpcs::results::GetStateRootHashResult as _GetStateRootHashResult, JsonRpcId, SuccessResponse,
16};
17#[cfg(all(feature = "js", target_arch = "wasm32"))]
18use gloo_utils::format::JsValueSerdeExt;
19use rand::RngExt;
20#[cfg(all(feature = "js", target_arch = "wasm32"))]
21use serde::{Deserialize, Serialize};
22#[cfg(all(feature = "js", target_arch = "wasm32"))]
23use wasm_bindgen::prelude::*;
24
25/// Wrapper struct for the `GetStateRootHashResult` from casper_client.
26#[cfg(all(feature = "js", target_arch = "wasm32"))]
27#[derive(Debug, Deserialize, Clone, Serialize)]
28#[cfg_attr(feature = "js", wasm_bindgen)]
29pub struct GetStateRootHashResult(_GetStateRootHashResult);
30
31#[cfg(all(feature = "js", target_arch = "wasm32"))]
32impl From<GetStateRootHashResult> for _GetStateRootHashResult {
33    fn from(result: GetStateRootHashResult) -> Self {
34        result.0
35    }
36}
37
38#[cfg(all(feature = "js", target_arch = "wasm32"))]
39impl From<_GetStateRootHashResult> for GetStateRootHashResult {
40    fn from(result: _GetStateRootHashResult) -> Self {
41        GetStateRootHashResult(result)
42    }
43}
44
45#[cfg(all(feature = "js", target_arch = "wasm32"))]
46#[cfg_attr(feature = "js", wasm_bindgen)]
47impl GetStateRootHashResult {
48    /// Gets the API version as a JsValue.
49    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
50    #[cfg(feature = "js")]
51    pub fn api_version(&self) -> JsValue {
52        JsValue::from_serde(&self.0.api_version).unwrap()
53    }
54
55    /// Gets the state root hash as an Option<Digest>.
56    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
57    pub fn state_root_hash(&self) -> Option<Digest> {
58        self.0.state_root_hash.map(Into::into)
59    }
60
61    /// Gets the state root hash as a String.
62    #[cfg_attr(feature = "js", wasm_bindgen(getter))]
63    pub fn state_root_hash_as_string(&self) -> String {
64        self.0
65            .state_root_hash
66            .map(Into::<Digest>::into)
67            .map(|digest| digest.to_string())
68            .unwrap_or_default()
69    }
70
71    /// Alias for state_root_hash_as_string
72    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "toString"))]
73    pub fn to_string_js_alias(&self) -> String {
74        // You can still use to_string method for compatibility
75        self.state_root_hash_as_string()
76    }
77
78    /// Converts the GetStateRootHashResult to a JsValue.
79    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "toJson"))]
80    #[cfg(feature = "js")]
81    pub fn to_json(&self) -> JsValue {
82        JsValue::from_serde(&self.0).unwrap_or(JsValue::null())
83    }
84}
85
86/// Options for the `get_state_root_hash` method.
87#[derive(Debug, Deserialize, Clone, Default, Serialize)]
88#[cfg(all(feature = "js", target_arch = "wasm32"))]
89#[cfg_attr(
90    feature = "js",
91    wasm_bindgen(js_name = "getStateRootHashOptions", getter_with_clone)
92)]
93pub struct GetStateRootHashOptions {
94    pub maybe_block_id_as_string: Option<String>,
95    pub maybe_block_identifier: Option<BlockIdentifier>,
96    pub rpc_address: Option<String>,
97    pub verbosity: Option<Verbosity>,
98}
99
100#[cfg(all(feature = "js", target_arch = "wasm32"))]
101#[cfg_attr(feature = "js", wasm_bindgen)]
102impl SDK {
103    /// Parses state root hash options from a JsValue.
104    ///
105    /// # Arguments
106    ///
107    /// * `options` - A JsValue containing state root hash options to be parsed.
108    ///
109    /// # Returns
110    ///
111    /// Parsed state root hash options as a `GetStateRootHashOptions` struct.
112    #[cfg(feature = "js")]
113    pub fn get_state_root_hash_options(
114        &self,
115        options: JsValue,
116    ) -> Result<GetStateRootHashOptions, JsError> {
117        options
118            .into_serde::<GetStateRootHashOptions>()
119            .map_err(|err| JsError::new(&format!("Error deserializing options: {err:?}")))
120    }
121
122    /// Retrieves state root hash information using the provided options.
123    ///
124    /// # Arguments
125    ///
126    /// * `options` - An optional `GetStateRootHashOptions` struct containing retrieval options.
127    ///
128    /// # Returns
129    ///
130    /// A `Result` containing either a `GetStateRootHashResult` or a `JsError` in case of an error.
131    ///
132    /// # Errors
133    ///
134    /// Returns a `JsError` if there is an error during the retrieval process.
135    #[cfg_attr(feature = "js", wasm_bindgen(js_name = "get_state_root_hash"))]
136    #[cfg(feature = "js")]
137    pub async fn get_state_root_hash_js_alias(
138        &self,
139        options: Option<GetStateRootHashOptions>,
140    ) -> Result<GetStateRootHashResult, JsError> {
141        let GetStateRootHashOptions {
142            maybe_block_id_as_string,
143            maybe_block_identifier,
144            verbosity,
145            rpc_address,
146        } = options.unwrap_or_default();
147
148        let maybe_block_identifier = if let Some(maybe_block_identifier) = maybe_block_identifier {
149            Some(BlockIdentifierInput::BlockIdentifier(
150                maybe_block_identifier,
151            ))
152        } else {
153            maybe_block_id_as_string.map(BlockIdentifierInput::String)
154        };
155
156        let result = self
157            .get_state_root_hash(maybe_block_identifier, verbosity, rpc_address)
158            .await;
159        match result {
160            Ok(data) => Ok(data.result.into()),
161            Err(err) => {
162                let err = &format!("Error occurred with {err:?}");
163                Err(JsError::new(err))
164            }
165        }
166    }
167
168    /// Retrieves state root hash information using the provided options (alias for `get_state_root_hash`).
169    ///
170    /// # Arguments
171    ///
172    /// * `options` - An optional `GetStateRootHashOptions` struct containing retrieval options.
173    ///
174    /// # Returns
175    ///
176    /// A `Result` containing either a `GetStateRootHashResult` or a `JsError` in case of an error.
177    ///
178    /// # Errors
179    ///
180    /// Returns a `JsError` if there is an error during the retrieval process.
181    #[deprecated(note = "This function is an alias. Please use `get_state_root_hash` instead.")]
182    #[allow(deprecated)]
183    #[cfg(feature = "js")]
184    pub async fn chain_get_state_root_hash(
185        &self,
186        options: Option<GetStateRootHashOptions>,
187    ) -> Result<GetStateRootHashResult, JsError> {
188        self.get_state_root_hash_js_alias(options).await
189    }
190}
191
192impl SDK {
193    /// Retrieves state root hash information based on the provided options.
194    ///
195    /// # Arguments
196    ///
197    /// * `maybe_block_identifier` - An optional `BlockIdentifierInput` for specifying a block identifier.
198    /// * `verbosity` - An optional `Verbosity` level for controlling the output verbosity.
199    /// * `rpc_address` - An optional string specifying the rpc address to use for the request.
200    ///
201    /// # Returns
202    ///
203    /// A `Result` containing either a `_GetStateRootHashResult` or a `SdkError` in case of an error.
204    ///
205    /// # Errors
206    ///
207    /// Returns a `SdkError` if there is an error during the retrieval process.
208    pub async fn get_state_root_hash(
209        &self,
210        maybe_block_identifier: Option<BlockIdentifierInput>,
211        verbosity: Option<Verbosity>,
212        rpc_address: Option<String>,
213    ) -> Result<SuccessResponse<_GetStateRootHashResult>, SdkError> {
214        //log("get_state_root_hash!");
215        let random_id = rand::rng().random::<u64>().to_string();
216
217        if let Some(BlockIdentifierInput::String(maybe_block_id)) = maybe_block_identifier {
218            get_state_root_hash_cli(
219                &random_id,
220                &self.get_rpc_address(rpc_address),
221                self.get_verbosity(verbosity).into(),
222                &maybe_block_id,
223            )
224            .await
225            .map_err(SdkError::from)
226        } else {
227            let maybe_block_identifier =
228                if let Some(BlockIdentifierInput::BlockIdentifier(maybe_block_identifier)) =
229                    maybe_block_identifier
230                {
231                    Some(maybe_block_identifier)
232                } else {
233                    None
234                };
235
236            get_state_root_hash_lib(
237                JsonRpcId::from(random_id),
238                &self.get_rpc_address(rpc_address),
239                self.get_verbosity(verbosity).into(),
240                maybe_block_identifier.map(Into::into),
241            )
242            .await
243            .map_err(SdkError::from)
244        }
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251    use crate::types::{
252        hash::block_hash::BlockHash, identifier::block_identifier::BlockIdentifier,
253    };
254    use sdk_tests::tests::helpers::get_network_constants;
255
256    #[tokio::test]
257    async fn test_get_state_root_hash_with_none_values() {
258        // Arrange
259        let sdk = SDK::new(None, None, None);
260        let error_message = "failed to parse node address as valid URL";
261
262        // Act
263        let result = sdk.get_state_root_hash(None, None, None).await;
264
265        // Assert
266        assert!(result.is_err());
267        let err_string = result.err().unwrap().to_string();
268        assert!(err_string.contains(error_message));
269    }
270
271    #[tokio::test]
272    async fn test_get_state_root_hash_with_block_id_string() {
273        // Arrange
274        let sdk = SDK::new(None, None, None);
275        let verbosity = Some(Verbosity::High);
276        let (rpc_address, _, _, _, _) = get_network_constants();
277        let result = sdk
278            .get_block(None, verbosity, Some(rpc_address.clone()))
279            .await;
280        let block_hash = BlockHash::from(
281            *result
282                .unwrap()
283                .result
284                .block_with_signatures
285                .unwrap()
286                .block
287                .hash(),
288        )
289        .to_string();
290        let block_identifier = BlockIdentifierInput::String(block_hash.to_string());
291
292        // Act
293        let result = sdk
294            .get_state_root_hash(Some(block_identifier), verbosity, Some(rpc_address))
295            .await;
296        // Assert
297        assert!(result.is_ok());
298    }
299
300    #[tokio::test]
301    async fn test_get_state_root_hash_with_block_identifier() {
302        // Arrange
303        let sdk = SDK::new(None, None, None);
304        let block_identifier =
305            BlockIdentifierInput::BlockIdentifier(BlockIdentifier::from_height(1));
306        let verbosity = Some(Verbosity::High);
307        let (rpc_address, _, _, _, _) = get_network_constants();
308
309        // Act
310        let result = sdk
311            .get_state_root_hash(Some(block_identifier), verbosity, Some(rpc_address.clone()))
312            .await;
313
314        // Assert
315        assert!(result.is_ok());
316    }
317
318    #[tokio::test]
319    async fn test_get_state_root_hash_with_error() {
320        let sdk = SDK::new(Some("http://localhost".to_string()), None, None);
321
322        let error_message = "error sending request";
323
324        // Act
325        let result = sdk.get_state_root_hash(None, None, None).await;
326
327        // Assert
328        assert!(result.is_err());
329        let err_string = result.err().unwrap().to_string();
330        assert!(err_string.contains(error_message));
331    }
332}