contracts.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::str::FromStr;
  19. use tinyjson::JsonValue;
  20. use darkfi::{
  21. rpc::jsonrpc::{parse_json_array_string, validate_empty_params},
  22. Result,
  23. };
  24. use darkfi_sdk::crypto::ContractId;
  25. use crate::{error::ExplorerdError, Explorerd};
  26. impl Explorerd {
  27. // RPCAPI:
  28. // Retrieves the native contracts deployed in the DarkFi network.
  29. // Returns a JSON array containing Contract IDs along with their associated metadata upon success.
  30. //
  31. // **Params:**
  32. // * `None`
  33. //
  34. // **Returns:**
  35. // * Array of `ContractRecord`s encoded into a JSON.
  36. //
  37. // **Example API Usage:**
  38. // --> {"jsonrpc": "2.0", "method": "contracts.get_native_contracts", "params": ["5cc...2f9"], "id": 1}
  39. // <-- {"jsonrpc": "2.0", "result": ["BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o", "Money Contract", "The money contract..."], "id": 1}
  40. pub async fn contracts_get_native_contracts(&self, params: &JsonValue) -> Result<JsonValue> {
  41. // Validate that no parameters are provided
  42. validate_empty_params(params)?;
  43. // Retrieve native contracts
  44. let contract_records = self.service.get_native_contracts()?;
  45. // Transform contract records into a JSON array and return result
  46. if contract_records.is_empty() {
  47. Ok(JsonValue::Array(vec![]))
  48. } else {
  49. let json_blocks: Vec<JsonValue> = contract_records
  50. .into_iter()
  51. .map(|contract_record| contract_record.to_json_array())
  52. .collect();
  53. Ok(JsonValue::Array(json_blocks))
  54. }
  55. }
  56. // RPCAPI:
  57. // Retrieves the source code paths for the contract associated with the specified Contract ID.
  58. // Returns a JSON array containing the source code paths upon success.
  59. //
  60. // **Params:**
  61. // * `array[0]`: `String` Contract ID
  62. //
  63. // **Returns:**
  64. // * `JsonArray` containing source code paths for the specified Contract ID.
  65. //
  66. // **Example API Usage:**
  67. // --> {"jsonrpc": "2.0", "method": "contracts.get_contract_source_code_paths", "params": ["BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o"], "id": 1}
  68. // <-- {"jsonrpc": "2.0", "result": ["path/to/source1.rs", "path/to/source2.rs"], "id": 1}
  69. pub async fn contracts_get_contract_source_code_paths(
  70. &self,
  71. params: &JsonValue,
  72. ) -> Result<JsonValue> {
  73. // Extract contract ID
  74. let contact_id_str = parse_json_array_string("contract_id", 0, params)?;
  75. // Convert the contract string to a `ContractId` instance
  76. let contract_id = ContractId::from_str(&contact_id_str)
  77. .map_err(|_| ExplorerdError::InvalidContractId(contact_id_str))?;
  78. // Retrieve source code paths for the contract
  79. let paths = self.service.get_contract_source_paths(&contract_id)?;
  80. // Tranform found paths into `JsonValues`
  81. let json_value_paths = paths.iter().map(|path| JsonValue::String(path.clone())).collect();
  82. Ok(JsonValue::Array(json_value_paths))
  83. }
  84. // RPCAPI:
  85. // Retrieves contract source code content using the provided Contract ID and source path.
  86. // Returns the source code content as a JSON string upon success.
  87. //
  88. // **Params:**
  89. // * `array[0]`: `String` Contract ID
  90. // * `array[1]`: `String` Source path
  91. //
  92. // **Returns:**
  93. // * `String` containing the content of the contract source file.
  94. //
  95. // **Example API Usage:**
  96. // --> {"jsonrpc": "2.0", "method": "contracts.get_contract_source", "params": ["BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o", "client/lib.rs"], "id": 1}
  97. // <-- {"jsonrpc": "2.0", "result": "/* This file is ...", "id": 1}
  98. pub async fn contracts_get_contract_source(&self, params: &JsonValue) -> Result<JsonValue> {
  99. // Extract the contract ID
  100. let contact_id_str = parse_json_array_string("contract_id", 0, params)?;
  101. // Convert the contract string to a `ContractId` instance
  102. let contract_id = ContractId::from_str(&contact_id_str)
  103. .map_err(|_| ExplorerdError::InvalidContractId(contact_id_str))?;
  104. // Extract the source path
  105. let source_path = parse_json_array_string("source_path", 1, params)?;
  106. // Retrieve the contract source code, transform it into a `JsonValue`, and return the result
  107. match self.service.get_contract_source_content(&contract_id, &source_path)? {
  108. Some(source_file) => Ok(JsonValue::String(source_file)),
  109. None => Ok(JsonValue::from(std::collections::HashMap::<String, JsonValue>::new())),
  110. }
  111. }
  112. }
  113. #[cfg(test)]
  114. /// Test module for validating the functionality of RPC methods related to explorer contracts.
  115. /// Focuses on ensuring proper error handling for invalid parameters across several use cases,
  116. /// including cases with missing values, unsupported types, and unparsable inputs.
  117. mod tests {
  118. use tinyjson::JsonValue;
  119. use darkfi::rpc::jsonrpc::ErrorCode;
  120. use crate::test_utils::{
  121. setup, validate_empty_rpc_parameters, validate_invalid_rpc_contract_id,
  122. validate_invalid_rpc_parameter,
  123. };
  124. #[test]
  125. /// Tests the `contracts.get_native_contracts` method to ensure it correctly handles cases where
  126. /// empty parameters are supplied, returning an expected result or error response.
  127. fn test_contracts_get_native_contracts_empty_params() {
  128. smol::block_on(async {
  129. validate_empty_rpc_parameters(&setup(), "contracts.get_native_contracts").await;
  130. });
  131. }
  132. #[test]
  133. /// Tests the `contracts.get_contract_source_code_paths` method to ensure it correctly handles cases
  134. /// with invalid or missing `contract_id` parameters, returning appropriate error responses.
  135. fn test_contracts_get_contract_source_code_paths_invalid_params() {
  136. validate_invalid_rpc_contract_id(&setup(), "contracts.get_contract_source_code_paths");
  137. }
  138. #[test]
  139. /// Tests the `contracts.get_contract_source` method to ensure it correctly handles cases
  140. /// with invalid or missing parameters, returning appropriate error responses.
  141. fn test_contracts_get_contract_source_invalid_params() {
  142. let test_method = "contracts.get_contract_source";
  143. let parameter_name = "source_path";
  144. smol::block_on(async {
  145. // Set up the explorerd instance
  146. let explorerd = setup();
  147. validate_invalid_rpc_contract_id(&explorerd, test_method);
  148. // Test for missing `source_path` parameter
  149. validate_invalid_rpc_parameter(
  150. &explorerd,
  151. test_method,
  152. &[JsonValue::String("BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o".to_string())],
  153. ErrorCode::InvalidParams.code(),
  154. &format!("Parameter '{parameter_name}' at index 1 is missing"),
  155. )
  156. .await;
  157. // Test for invalid `source_path` parameter
  158. validate_invalid_rpc_parameter(
  159. &explorerd,
  160. test_method,
  161. &[
  162. JsonValue::String("BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o".to_string()),
  163. JsonValue::Number(123.0), // Invalid `source_path` type
  164. ],
  165. ErrorCode::InvalidParams.code(),
  166. &format!("Parameter '{parameter_name}' is not a valid string"),
  167. )
  168. .await;
  169. });
  170. }
  171. }