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}