1use crate::authority_state::StateRead;
5use crate::error::{Error, SuiRpcInputError};
6use crate::{SuiRpcModule, with_tracing};
7use async_trait::async_trait;
8use jsonrpsee::RpcModule;
9use jsonrpsee::core::RpcResult;
10#[cfg(test)]
11use mockall::automock;
12use move_binary_format::{binary_config::BinaryConfig, normalized};
13use move_core_types::identifier::Identifier;
14use std::collections::BTreeMap;
15use std::sync::Arc;
16use sui_core::authority::AuthorityState;
17use sui_json_rpc_api::{MoveUtilsOpenRpc, MoveUtilsServer};
18use sui_json_rpc_types::{
19    MoveFunctionArgType, ObjectValueKind, SuiMoveNormalizedFunction, SuiMoveNormalizedModule,
20    SuiMoveNormalizedStruct,
21};
22use sui_open_rpc::Module;
23use sui_types::base_types::ObjectID;
24use sui_types::move_package::normalize_modules;
25use sui_types::object::{Data, ObjectRead};
26use tap::TapFallible;
27use tracing::{error, instrument, warn};
28
29type NormalizedModule = normalized::Module<normalized::RcIdentifier>;
30type Type = normalized::Type<normalized::RcIdentifier>;
31
32#[cfg_attr(test, automock)]
33#[async_trait]
34pub trait MoveUtilsInternalTrait {
35    fn get_state(&self) -> &dyn StateRead;
36
37    async fn get_move_module(
38        &self,
39        package: ObjectID,
40        module_name: String,
41    ) -> Result<NormalizedModule, Error>;
42
43    async fn get_move_modules_by_package(
44        &self,
45        package: ObjectID,
46    ) -> Result<BTreeMap<String, NormalizedModule>, Error>;
47
48    fn get_object_read(&self, package: ObjectID) -> Result<ObjectRead, Error>;
49}
50
51pub struct MoveUtilsInternal {
52    state: Arc<dyn StateRead>,
53}
54
55impl MoveUtilsInternal {
56    pub fn new(state: Arc<AuthorityState>) -> Self {
57        Self { state }
58    }
59}
60
61#[async_trait]
62impl MoveUtilsInternalTrait for MoveUtilsInternal {
63    fn get_state(&self) -> &dyn StateRead {
64        Arc::as_ref(&self.state)
65    }
66
67    async fn get_move_module(
68        &self,
69        package: ObjectID,
70        module_name: String,
71    ) -> Result<NormalizedModule, Error> {
72        let mut normalized = self.get_move_modules_by_package(package).await?;
73        Ok(match normalized.remove(&module_name) {
74            Some(module) => Ok(module),
75            None => Err(SuiRpcInputError::GenericNotFound(format!(
76                "No module found with module name {}",
77                module_name
78            ))),
79        }?)
80    }
81
82    async fn get_move_modules_by_package(
83        &self,
84        package: ObjectID,
85    ) -> Result<BTreeMap<String, NormalizedModule>, Error> {
86        let object_read = self.get_state().get_object_read(&package).tap_err(|_| {
87            warn!("Failed to call get_move_modules_by_package for package: {package:?}");
88        })?;
89        let pool = &mut normalized::RcPool::new();
90        match object_read {
91            ObjectRead::Exists(_obj_ref, object, _layout) => {
92                match object.into_inner().data {
93                    Data::Package(p) => {
94                        let binary_config = BinaryConfig::legacy_with_flags(
97                            false,
98                            false,
99                        );
100                        normalize_modules(
101                            pool,
102                            p.serialized_module_map().values(),
103                            &binary_config,
104                            false,
105                        )
106                        .map_err(|e| {
107                            error!("Failed to call get_move_modules_by_package for package: {package:?}");
108                            Error::from(e)
109                        })
110                    }
111                    _ => Err(SuiRpcInputError::GenericInvalid(format!(
112                        "Object is not a package with ID {}",
113                        package
114                    )))?,
115                }
116            }
117            _ => Err(SuiRpcInputError::GenericNotFound(format!(
118                "Package object does not exist with ID {}",
119                package
120            )))?,
121        }
122    }
123
124    fn get_object_read(&self, package: ObjectID) -> Result<ObjectRead, Error> {
125        self.state.get_object_read(&package).map_err(Error::from)
126    }
127}
128
129pub struct MoveUtils {
130    internal: Arc<dyn MoveUtilsInternalTrait + Send + Sync>,
131}
132
133impl MoveUtils {
134    pub fn new(state: Arc<AuthorityState>) -> Self {
135        Self {
136            internal: Arc::new(MoveUtilsInternal::new(state))
137                as Arc<dyn MoveUtilsInternalTrait + Send + Sync>,
138        }
139    }
140}
141
142impl SuiRpcModule for MoveUtils {
143    fn rpc(self) -> RpcModule<Self> {
144        self.into_rpc()
145    }
146
147    fn rpc_doc_module() -> Module {
148        MoveUtilsOpenRpc::module_doc()
149    }
150}
151
152#[async_trait]
153impl MoveUtilsServer for MoveUtils {
154    #[instrument(skip(self))]
155    async fn get_normalized_move_modules_by_package(
156        &self,
157        package: ObjectID,
158    ) -> RpcResult<BTreeMap<String, SuiMoveNormalizedModule>> {
159        with_tracing!(async move {
160            let modules = self.internal.get_move_modules_by_package(package).await?;
161            Ok(modules
162                .into_iter()
163                .map(|(name, module)| (name, (&module).into()))
164                .collect::<BTreeMap<String, SuiMoveNormalizedModule>>())
165        })
166    }
167
168    #[instrument(skip(self))]
169    async fn get_normalized_move_module(
170        &self,
171        package: ObjectID,
172        module_name: String,
173    ) -> RpcResult<SuiMoveNormalizedModule> {
174        with_tracing!(async move {
175            let module = &self.internal.get_move_module(package, module_name).await?;
176            Ok(module.into())
177        })
178    }
179
180    #[instrument(skip(self))]
181    async fn get_normalized_move_struct(
182        &self,
183        package: ObjectID,
184        module_name: String,
185        struct_name: String,
186    ) -> RpcResult<SuiMoveNormalizedStruct> {
187        with_tracing!(async move {
188            let module = self.internal.get_move_module(package, module_name).await?;
189            let structs = module.structs;
190            let identifier = Identifier::new(struct_name.as_str())
191                .map_err(|e| SuiRpcInputError::GenericInvalid(format!("{e}")))?;
192            match structs.get(&identifier) {
193                Some(struct_) => Ok((&**struct_).into()),
194                None => Err(SuiRpcInputError::GenericNotFound(format!(
195                    "No struct was found with struct name {}",
196                    struct_name
197                )))?,
198            }
199        })
200    }
201
202    #[instrument(skip(self))]
203    async fn get_normalized_move_function(
204        &self,
205        package: ObjectID,
206        module_name: String,
207        function_name: String,
208    ) -> RpcResult<SuiMoveNormalizedFunction> {
209        with_tracing!(async move {
210            let module = self.internal.get_move_module(package, module_name).await?;
211            let functions = module.functions;
212            let identifier = Identifier::new(function_name.as_str())
213                .map_err(|e| SuiRpcInputError::GenericInvalid(format!("{e}")))?;
214            match functions.get(&identifier) {
215                Some(function) => Ok((&**function).into()),
216                None => Err(SuiRpcInputError::GenericNotFound(format!(
217                    "No function was found with function name {}",
218                    function_name
219                )))?,
220            }
221        })
222    }
223
224    #[instrument(skip(self))]
225    async fn get_move_function_arg_types(
226        &self,
227        package: ObjectID,
228        module: String,
229        function: String,
230    ) -> RpcResult<Vec<MoveFunctionArgType>> {
231        with_tracing!(async move {
232            let object_read = self.internal.get_object_read(package)?;
233
234            let pool = &mut normalized::RcPool::new();
235            let normalized = match object_read {
236                ObjectRead::Exists(_obj_ref, object, _layout) => match object.into_inner().data {
237                    Data::Package(p) => {
238                        let binary_config = BinaryConfig::legacy_with_flags(
241                            false,
242                            false,
243                        );
244                        normalize_modules(
245                            pool,
246                            p.serialized_module_map().values(),
247                            &binary_config,
248                            false,
249                        )
250                        .map_err(Error::from)
251                    }
252                    _ => Err(SuiRpcInputError::GenericInvalid(format!(
253                        "Object is not a package with ID {}",
254                        package
255                    )))?,
256                },
257                _ => Err(SuiRpcInputError::GenericNotFound(format!(
258                    "Package object does not exist with ID {}",
259                    package
260                )))?,
261            }?;
262
263            let identifier = Identifier::new(function.as_str())
264                .map_err(|e| SuiRpcInputError::GenericInvalid(format!("{e}")))?;
265            let parameters = normalized
266                .get(&module)
267                .and_then(|m| m.functions.get(&identifier).map(|f| f.parameters.clone()));
268
269            match parameters {
270                Some(parameters) => Ok(parameters
271                    .iter()
272                    .map(|p| match &**p {
273                        Type::Datatype(_) => MoveFunctionArgType::Object(ObjectValueKind::ByValue),
274                        Type::Reference(false, _) => {
275                            MoveFunctionArgType::Object(ObjectValueKind::ByImmutableReference)
276                        }
277                        Type::Reference(true, _) => {
278                            MoveFunctionArgType::Object(ObjectValueKind::ByMutableReference)
279                        }
280                        _ => MoveFunctionArgType::Pure,
281                    })
282                    .collect::<Vec<MoveFunctionArgType>>()),
283                None => Err(SuiRpcInputError::GenericNotFound(format!(
284                    "No parameters found for function {}",
285                    function
286                )))?,
287            }
288        })
289    }
290}
291
292#[cfg(test)]
293mod tests {
294
295    mod get_normalized_move_module_tests {
296        use super::super::*;
297        use move_binary_format::file_format::basic_test_module;
298
299        fn setup() -> (ObjectID, String) {
300            (ObjectID::random(), String::from("test_module"))
301        }
302
303        #[tokio::test]
304        async fn test_success_response() {
305            let (package, module_name) = setup();
306            let mut mock_internal = MockMoveUtilsInternalTrait::new();
307
308            let m = basic_test_module();
309            let normalized_module = &NormalizedModule::new(
310                &mut normalized::RcPool::new(),
311                &m,
312                false,
313            );
314            let expected_module: SuiMoveNormalizedModule = normalized_module.into();
315
316            mock_internal
317                .expect_get_move_module()
318                .return_once(move |_package, _module_name| {
319                    Ok(NormalizedModule::new(
320                        &mut normalized::RcPool::new(),
321                        &m,
322                        false,
323                    ))
324                });
325
326            let move_utils = MoveUtils {
327                internal: Arc::new(mock_internal),
328            };
329
330            let response = move_utils
331                .get_normalized_move_module(package, module_name)
332                .await;
333
334            assert!(response.is_ok());
335            let result = response.unwrap();
336            assert_eq!(result, expected_module);
337        }
338
339        #[tokio::test]
340        async fn test_no_module_found() {
341            let (package, module_name) = setup();
342            let mut mock_internal = MockMoveUtilsInternalTrait::new();
343            let error_string = format!("No module found with module name {module_name}");
344            let expected_error =
345                Error::SuiRpcInputError(SuiRpcInputError::GenericNotFound(error_string.clone()));
346            mock_internal
347                .expect_get_move_module()
348                .return_once(move |_package, _module_name| Err(expected_error));
349            let move_utils = MoveUtils {
350                internal: Arc::new(mock_internal),
351            };
352
353            let response = move_utils
354                .get_normalized_move_module(package, module_name)
355                .await;
356            let error_object = response.unwrap_err();
357
358            assert_eq!(error_object.code(), -32602);
359            assert_eq!(error_object.message(), &error_string);
360        }
361    }
362}