jsonrpc.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  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. //! JSON-RPC 2.0 object definitions
  19. use std::collections::HashMap;
  20. use rand::{rngs::OsRng, Rng};
  21. use tinyjson::JsonValue;
  22. use crate::{
  23. error::RpcError,
  24. system::{Publisher, PublisherPtr},
  25. Result,
  26. };
  27. /// Parse a JSON field into i64. Accepts numeric values and numeric strings.
  28. /// Note this is not fully spec-compliant, but the vast majority of RPC
  29. /// clients use numeric IDs.
  30. fn parse_id_field(v: &JsonValue, accept_string: bool) -> std::result::Result<i64, RpcError> {
  31. let n = if let Some(num) = v.get::<f64>() {
  32. *num
  33. } else if accept_string {
  34. match v.get::<String>() {
  35. Some(s) => s
  36. .parse::<f64>()
  37. .map_err(|_| RpcError::InvalidJson("id string is not numeric".to_string()))?,
  38. None => return Err(RpcError::InvalidJson("id is not a number or string".to_string())),
  39. }
  40. } else {
  41. return Err(RpcError::InvalidJson("id is not a number".to_string()))
  42. };
  43. if !n.is_finite() || n.fract() != 0.0 {
  44. return Err(RpcError::InvalidJson("id must be a finite integer".to_string()))
  45. }
  46. if n < i64::MIN as f64 || n > i64::MAX as f64 {
  47. return Err(RpcError::InvalidJson("id out of i64 range".to_string()))
  48. }
  49. Ok(n as i64)
  50. }
  51. /// Parse a JSON number into i32 with the same bounds-checking discipline
  52. fn parse_i32_field(v: &JsonValue, name: &str) -> std::result::Result<i32, RpcError> {
  53. let n =
  54. *v.get::<f64>().ok_or_else(|| RpcError::InvalidJson(format!("{name} is not a number")))?;
  55. if !n.is_finite() || n.fract() != 0.0 {
  56. return Err(RpcError::InvalidJson(format!("{name} must be a finite integer")))
  57. }
  58. if n < i32::MIN as f64 || n > i32::MAX as f64 {
  59. return Err(RpcError::InvalidJson(format!("{name} out of i32 range")))
  60. }
  61. Ok(n as i32)
  62. }
  63. /// JSON-RPC error codes.
  64. /// The error codes `[-32768, -32000]` are reserved for predefined errors.
  65. #[derive(Copy, Clone, Debug)]
  66. pub enum ErrorCode {
  67. /// Invalid JSON was received by the server.
  68. /// An error occurred on the server while parsing the JSON text.
  69. ParseError,
  70. /// The JSON sent is not a valid Request object.
  71. InvalidRequest,
  72. /// The method does not exist / is not available.
  73. MethodNotFound,
  74. /// Invalid method parameter(s).
  75. InvalidParams,
  76. /// Internal JSON-RPC error.
  77. InternalError,
  78. /// ID mismatch
  79. IdMismatch,
  80. /// Invalid/Unexpected reply
  81. InvalidReply,
  82. /// Reserved for implementation-defined server-errors.
  83. ServerError(i32),
  84. }
  85. impl ErrorCode {
  86. pub fn code(&self) -> i32 {
  87. match *self {
  88. Self::ParseError => -32700,
  89. Self::InvalidRequest => -32600,
  90. Self::MethodNotFound => -32601,
  91. Self::InvalidParams => -32602,
  92. Self::InternalError => -32603,
  93. Self::IdMismatch => -32360,
  94. Self::InvalidReply => -32361,
  95. Self::ServerError(c) => c,
  96. }
  97. }
  98. pub fn message(&self) -> String {
  99. match *self {
  100. Self::ParseError => "parse error".to_string(),
  101. Self::InvalidRequest => "invalid request".to_string(),
  102. Self::MethodNotFound => "method not found".to_string(),
  103. Self::InvalidParams => "invalid params".to_string(),
  104. Self::InternalError => "internal error".to_string(),
  105. Self::IdMismatch => "id mismatch".to_string(),
  106. Self::InvalidReply => "invalid reply".to_string(),
  107. Self::ServerError(_) => "server error".to_string(),
  108. }
  109. }
  110. pub fn desc(&self) -> JsonValue {
  111. JsonValue::String(self.message())
  112. }
  113. }
  114. // ANCHOR: jsonresult
  115. /// Wrapping enum around the available JSON-RPC object types
  116. #[derive(Clone, Debug)]
  117. pub enum JsonResult {
  118. Response(JsonResponse),
  119. Error(JsonError),
  120. Notification(JsonNotification),
  121. /// Subscriber is a special object that yields a channel
  122. Subscriber(JsonSubscriber),
  123. SubscriberWithReply(JsonSubscriber, JsonResponse),
  124. Request(JsonRequest),
  125. }
  126. impl JsonResult {
  127. pub fn try_from_value(value: &JsonValue) -> Result<Self> {
  128. if let Ok(response) = JsonResponse::try_from(value) {
  129. return Ok(Self::Response(response))
  130. }
  131. if let Ok(error) = JsonError::try_from(value) {
  132. return Ok(Self::Error(error))
  133. }
  134. if let Ok(notification) = JsonNotification::try_from(value) {
  135. return Ok(Self::Notification(notification))
  136. }
  137. Err(RpcError::InvalidJson("Invalid JSON Result".to_string()).into())
  138. }
  139. }
  140. impl From<JsonResponse> for JsonResult {
  141. fn from(resp: JsonResponse) -> Self {
  142. Self::Response(resp)
  143. }
  144. }
  145. impl From<JsonError> for JsonResult {
  146. fn from(err: JsonError) -> Self {
  147. Self::Error(err)
  148. }
  149. }
  150. impl From<JsonNotification> for JsonResult {
  151. fn from(notif: JsonNotification) -> Self {
  152. Self::Notification(notif)
  153. }
  154. }
  155. impl From<JsonSubscriber> for JsonResult {
  156. fn from(sub: JsonSubscriber) -> Self {
  157. Self::Subscriber(sub)
  158. }
  159. }
  160. impl From<(JsonSubscriber, JsonResponse)> for JsonResult {
  161. fn from(tuple: (JsonSubscriber, JsonResponse)) -> Self {
  162. Self::SubscriberWithReply(tuple.0, tuple.1)
  163. }
  164. }
  165. // ANCHOR: jsonrequest
  166. /// A JSON-RPC request object
  167. #[derive(Clone, Debug)]
  168. pub struct JsonRequest {
  169. /// JSON-RPC version
  170. pub jsonrpc: &'static str,
  171. /// Request ID
  172. pub id: i64,
  173. /// Request method
  174. pub method: String,
  175. /// Request parameters
  176. pub params: JsonValue,
  177. }
  178. // ANCHOR_END: jsonrequest
  179. impl JsonRequest {
  180. /// Create a new [`JsonRequest`] object with the given method and parameters.
  181. /// The request ID is chosen randomly.
  182. pub fn new(method: &str, params: JsonValue) -> Self {
  183. assert!(params.is_object() || params.is_array());
  184. let id: i64 = OsRng::gen_range(&mut OsRng, 0..(1i64 << 53));
  185. Self { jsonrpc: "2.0", id, method: method.to_string(), params }
  186. }
  187. /// Convert the object into a JSON string
  188. pub fn stringify(&self) -> Result<String> {
  189. let v: JsonValue = self.into();
  190. Ok(v.stringify()?)
  191. }
  192. }
  193. impl From<&JsonRequest> for JsonValue {
  194. fn from(req: &JsonRequest) -> JsonValue {
  195. JsonValue::Object(HashMap::from([
  196. ("jsonrpc".to_string(), JsonValue::String(req.jsonrpc.to_string())),
  197. ("id".to_string(), JsonValue::Number(req.id as f64)),
  198. ("method".to_string(), JsonValue::String(req.method.clone())),
  199. ("params".to_string(), req.params.clone()),
  200. ]))
  201. }
  202. }
  203. impl TryFrom<&JsonValue> for JsonRequest {
  204. type Error = RpcError;
  205. fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
  206. if !value.is_object() {
  207. return Err(RpcError::InvalidJson("JSON is not an Object".to_string()))
  208. }
  209. // We have to allocate the value here another time in order to mutate
  210. // it if necessary.
  211. let mut value = value.clone();
  212. let map: &mut HashMap<String, JsonValue> = value.get_mut().unwrap();
  213. if !map.contains_key("jsonrpc") ||
  214. !map["jsonrpc"].is_string() ||
  215. map["jsonrpc"] != JsonValue::String("2.0".to_string())
  216. {
  217. return Err(RpcError::InvalidJson(
  218. "Request does not contain valid \"jsonrpc\" field".to_string(),
  219. ))
  220. }
  221. if !map.contains_key("id")
  222. /* || !map["id"].is_number() */
  223. {
  224. return Err(RpcError::InvalidJson(
  225. "Request does not contain valid \"id\" field".to_string(),
  226. ))
  227. }
  228. if !map.contains_key("method") || !map["method"].is_string() {
  229. return Err(RpcError::InvalidJson(
  230. "Request does not contain valid \"method\" field".to_string(),
  231. ))
  232. }
  233. if !map.contains_key("params") {
  234. // HACK ALERT:
  235. // On nonexisting `params`, we'll just make them into something.
  236. map.insert("params".to_string(), JsonValue::from(vec![]));
  237. }
  238. if !map["params"].is_object() && !map["params"].is_array() {
  239. return Err(RpcError::InvalidJson(
  240. "Request does not contain valid \"params\" field".to_string(),
  241. ))
  242. }
  243. let id = parse_id_field(&map["id"], true)?;
  244. Ok(Self {
  245. jsonrpc: "2.0",
  246. id,
  247. method: map["method"].get::<String>().unwrap().clone(),
  248. params: map["params"].clone(),
  249. })
  250. }
  251. }
  252. /// A JSON-RPC notification object
  253. #[derive(Clone, Debug)]
  254. pub struct JsonNotification {
  255. /// JSON-RPC version
  256. pub jsonrpc: &'static str,
  257. /// Notification method
  258. pub method: String,
  259. /// Notification parameters
  260. pub params: JsonValue,
  261. }
  262. impl JsonNotification {
  263. /// Create a new [`JsonNotification`] object with the given method and parameters.
  264. pub fn new(method: &str, params: JsonValue) -> Self {
  265. assert!(params.is_object() || params.is_array());
  266. Self { jsonrpc: "2.0", method: method.to_string(), params }
  267. }
  268. /// Convert the object into a JSON string
  269. pub fn stringify(&self) -> Result<String> {
  270. let v: JsonValue = self.into();
  271. Ok(v.stringify()?)
  272. }
  273. }
  274. impl From<&JsonNotification> for JsonValue {
  275. fn from(notif: &JsonNotification) -> JsonValue {
  276. JsonValue::Object(HashMap::from([
  277. ("jsonrpc".to_string(), JsonValue::String(notif.jsonrpc.to_string())),
  278. ("method".to_string(), JsonValue::String(notif.method.clone())),
  279. ("params".to_string(), notif.params.clone()),
  280. ]))
  281. }
  282. }
  283. impl TryFrom<&JsonValue> for JsonNotification {
  284. type Error = RpcError;
  285. fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
  286. if !value.is_object() {
  287. return Err(RpcError::InvalidJson("JSON is not an Object".to_string()))
  288. }
  289. let map: &HashMap<String, JsonValue> = value.get().unwrap();
  290. if !map.contains_key("jsonrpc") ||
  291. !map["jsonrpc"].is_string() ||
  292. map["jsonrpc"] != JsonValue::String("2.0".to_string())
  293. {
  294. return Err(RpcError::InvalidJson(
  295. "Notification does not contain valid \"jsonrpc\" field".to_string(),
  296. ))
  297. }
  298. if !map.contains_key("method") || !map["method"].is_string() {
  299. return Err(RpcError::InvalidJson(
  300. "Notification does not contain valid \"method\" field".to_string(),
  301. ))
  302. }
  303. if !map.contains_key("params") {
  304. return Err(RpcError::InvalidJson(
  305. "Notification does not contain valid \"params\" field".to_string(),
  306. ))
  307. }
  308. if !map["params"].is_object() && !map["params"].is_array() {
  309. return Err(RpcError::InvalidJson(
  310. "Request does not contain valid \"params\" field".to_string(),
  311. ))
  312. }
  313. Ok(Self {
  314. jsonrpc: "2.0",
  315. method: map["method"].get::<String>().unwrap().clone(),
  316. params: map["params"].clone(),
  317. })
  318. }
  319. }
  320. /// A JSON-RPC response object
  321. #[derive(Clone, Debug)]
  322. pub struct JsonResponse {
  323. /// JSON-RPC version
  324. pub jsonrpc: &'static str,
  325. /// Request ID
  326. pub id: i64,
  327. /// Response result
  328. pub result: JsonValue,
  329. }
  330. impl JsonResponse {
  331. /// Create a new [`JsonResponse`] object with the given ID and result value.
  332. /// Creating a `JsonResponse` implies that the method call was successful.
  333. pub fn new(result: JsonValue, id: i64) -> Self {
  334. Self { jsonrpc: "2.0", id, result }
  335. }
  336. /// Convert the object into a JSON string
  337. pub fn stringify(&self) -> Result<String> {
  338. let v: JsonValue = self.into();
  339. Ok(v.stringify()?)
  340. }
  341. }
  342. impl From<&JsonResponse> for JsonValue {
  343. fn from(rep: &JsonResponse) -> JsonValue {
  344. JsonValue::Object(HashMap::from([
  345. ("jsonrpc".to_string(), JsonValue::String(rep.jsonrpc.to_string())),
  346. ("id".to_string(), JsonValue::Number(rep.id as f64)),
  347. ("result".to_string(), rep.result.clone()),
  348. ]))
  349. }
  350. }
  351. impl TryFrom<&JsonValue> for JsonResponse {
  352. type Error = RpcError;
  353. fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
  354. if !value.is_object() {
  355. return Err(RpcError::InvalidJson("Json is not an Object".to_string()))
  356. }
  357. let map: &HashMap<String, JsonValue> = value.get().unwrap();
  358. if !map.contains_key("jsonrpc") ||
  359. !map["jsonrpc"].is_string() ||
  360. map["jsonrpc"] != JsonValue::String("2.0".to_string())
  361. {
  362. return Err(RpcError::InvalidJson(
  363. "Response does not contain valid \"jsonrpc\" field".to_string(),
  364. ))
  365. }
  366. if !map.contains_key("id") || !map["id"].is_number() {
  367. return Err(RpcError::InvalidJson(
  368. "Response does not contain valid \"id\" field".to_string(),
  369. ))
  370. }
  371. if !map.contains_key("result") {
  372. return Err(RpcError::InvalidJson(
  373. "Response does not contain valid \"result\" field".to_string(),
  374. ))
  375. }
  376. Ok(Self {
  377. jsonrpc: "2.0",
  378. id: parse_id_field(&map["id"], false)?,
  379. result: map["result"].clone(),
  380. })
  381. }
  382. }
  383. impl TryFrom<JsonResult> for JsonResponse {
  384. type Error = RpcError;
  385. /// Converts [`JsonResult`] to [`JsonResponse`], returning the response or an `InvalidJson`
  386. /// error if the structure is not a `JsonResponse`.
  387. fn try_from(result: JsonResult) -> std::result::Result<Self, Self::Error> {
  388. match result {
  389. JsonResult::Response(response) => Ok(response),
  390. _ => Err(RpcError::InvalidJson("Not a JsonResult::Response".to_string())),
  391. }
  392. }
  393. }
  394. /// A JSON-RPC error object
  395. #[derive(Clone, Debug)]
  396. pub struct JsonError {
  397. /// JSON-RPC version
  398. pub jsonrpc: &'static str,
  399. /// Request ID
  400. pub id: i64,
  401. /// JSON-RPC error (code and message)
  402. pub error: JsonErrorVal,
  403. }
  404. /// A JSON-RPC error value (code and message)
  405. #[derive(Clone, Debug)]
  406. pub struct JsonErrorVal {
  407. /// Error code
  408. pub code: i32,
  409. /// Error message
  410. pub message: String,
  411. }
  412. impl JsonError {
  413. /// Create a new [`JsonError`] object with the given error code, optional
  414. /// message, and a response ID.
  415. /// Creating a `JsonError` implies that the method call was unsuccessful.
  416. pub fn new(c: ErrorCode, message: Option<String>, id: i64) -> Self {
  417. let error = JsonErrorVal { code: c.code(), message: message.unwrap_or(c.message()) };
  418. Self { jsonrpc: "2.0", id, error }
  419. }
  420. /// Convert the object into a JSON string
  421. pub fn stringify(&self) -> Result<String> {
  422. let v: JsonValue = self.into();
  423. Ok(v.stringify()?)
  424. }
  425. }
  426. impl From<&JsonError> for JsonValue {
  427. fn from(err: &JsonError) -> JsonValue {
  428. let errmap = JsonValue::Object(HashMap::from([
  429. ("code".to_string(), JsonValue::Number(err.error.code.into())),
  430. ("message".to_string(), JsonValue::String(err.error.message.clone())),
  431. ]));
  432. JsonValue::Object(HashMap::from([
  433. ("jsonrpc".to_string(), JsonValue::String(err.jsonrpc.to_string())),
  434. ("id".to_string(), JsonValue::Number(err.id as f64)),
  435. ("error".to_string(), errmap),
  436. ]))
  437. }
  438. }
  439. impl TryFrom<JsonResult> for JsonError {
  440. type Error = RpcError;
  441. /// Converts [`JsonResult`] to [`JsonError`], returning the response or an `InvalidJson`
  442. /// error if the structure is not a `JsonError`.
  443. fn try_from(result: JsonResult) -> std::result::Result<Self, Self::Error> {
  444. match result {
  445. JsonResult::Error(error) => Ok(error),
  446. _ => Err(RpcError::InvalidJson("Not a JsonResult::Error".to_string())),
  447. }
  448. }
  449. }
  450. impl TryFrom<&JsonValue> for JsonError {
  451. type Error = RpcError;
  452. fn try_from(value: &JsonValue) -> std::result::Result<Self, Self::Error> {
  453. if !value.is_object() {
  454. return Err(RpcError::InvalidJson("JSON is not an Object".to_string()))
  455. }
  456. let map: &HashMap<String, JsonValue> = value.get().unwrap();
  457. if !map.contains_key("jsonrpc") ||
  458. !map["jsonrpc"].is_string() ||
  459. map["jsonrpc"] != JsonValue::String("2.0".to_string())
  460. {
  461. return Err(RpcError::InvalidJson(
  462. "Error does not contain valid \"jsonrpc\" field".to_string(),
  463. ))
  464. }
  465. if !map.contains_key("id") || !map["id"].is_number() {
  466. return Err(RpcError::InvalidJson(
  467. "Error does not contain valid \"id\" field".to_string(),
  468. ))
  469. }
  470. if !map.contains_key("error") || !map["error"].is_object() {
  471. return Err(RpcError::InvalidJson(
  472. "Error does not contain valid \"error\" field".to_string(),
  473. ))
  474. }
  475. let err_map: &HashMap<String, JsonValue> = map["error"].get().unwrap();
  476. let code_val = err_map.get("code").ok_or_else(|| {
  477. RpcError::InvalidJson("Error does not contain \"error.code\" field".to_string())
  478. })?;
  479. let message_val = err_map.get("message").ok_or_else(|| {
  480. RpcError::InvalidJson("Error does not contain \"error.message\" field".to_string())
  481. })?;
  482. let code = parse_i32_field(code_val, "error.code")?;
  483. let message = message_val
  484. .get::<String>()
  485. .ok_or_else(|| RpcError::InvalidJson("\"error.message\" is not a string".to_string()))?
  486. .to_string();
  487. Ok(Self {
  488. jsonrpc: "2.0",
  489. id: parse_id_field(&map["id"], false)?,
  490. error: JsonErrorVal { code, message },
  491. })
  492. }
  493. }
  494. /// A JSON-RPC subscriber for notifications
  495. #[derive(Clone, Debug)]
  496. pub struct JsonSubscriber {
  497. /// Notification method
  498. pub method: &'static str,
  499. /// Notification publisher
  500. pub publisher: PublisherPtr<JsonNotification>,
  501. }
  502. impl JsonSubscriber {
  503. pub fn new(method: &'static str) -> Self {
  504. let publisher = Publisher::new();
  505. Self { method, publisher }
  506. }
  507. /// Send a notification to the publisher with the given JSON object
  508. pub async fn notify(&self, params: JsonValue) {
  509. let notification = JsonNotification::new(self.method, params);
  510. self.publisher.notify(notification).await;
  511. }
  512. }
  513. /// Parses a [`JsonValue`] parameter into a `String`.
  514. /// Returns the string if successful or an error if the value is not a valid string.
  515. pub fn parse_json_string(name: &str, value: &JsonValue) -> std::result::Result<String, RpcError> {
  516. value
  517. .get::<String>()
  518. .cloned()
  519. .ok_or_else(|| RpcError::InvalidJson(format!("Parameter '{name}' is not a valid string")))
  520. }
  521. /// Parses a [`JsonValue`] parameter into a `f64`.
  522. /// Returns the number if successful or an error if the value is not a valid number.
  523. pub fn parse_json_number(name: &str, value: &JsonValue) -> std::result::Result<f64, RpcError> {
  524. value.get::<f64>().cloned().ok_or_else(|| {
  525. RpcError::InvalidJson(format!("Parameter '{name}' is not a supported number type"))
  526. })
  527. }
  528. /// Parses the element at the specified index in a [`JsonValue::Array`] into a
  529. /// string. Returns the string if successful, or an error if the parameter is
  530. /// missing, not an array, or not a valid string.
  531. pub fn parse_json_array_string(
  532. name: &str,
  533. index: usize,
  534. array_value: &JsonValue,
  535. ) -> std::result::Result<String, RpcError> {
  536. match array_value {
  537. JsonValue::Array(values) => values
  538. .get(index)
  539. .ok_or_else(|| {
  540. RpcError::InvalidJson(format!("Parameter '{name}' at index {index} is missing"))
  541. })
  542. .and_then(|param| parse_json_string(name, param)),
  543. _ => Err(RpcError::InvalidJson(format!("Parameter '{name}' is not an array"))),
  544. }
  545. }
  546. /// Parses the element at the specified index in a [`JsonValue::Array`] into an
  547. /// `f64` (compatible with [`JsonValue::Number`]). Returns the number if successful,
  548. /// or an error if the parameter is missing, not an array, or is not a valid number.
  549. pub fn parse_json_array_number(
  550. name: &str,
  551. index: usize,
  552. array_value: &JsonValue,
  553. ) -> std::result::Result<f64, RpcError> {
  554. match array_value {
  555. JsonValue::Array(values) => values
  556. .get(index)
  557. .ok_or_else(|| {
  558. RpcError::InvalidJson(format!("Parameter '{name}' at index {index} is missing"))
  559. })
  560. .and_then(|param| parse_json_number(name, param)),
  561. _ => Err(RpcError::InvalidJson(format!("Parameter '{name}' is not an array"))),
  562. }
  563. }
  564. /// Attempts to parse a `JsonResult`, converting it into a `JsonResponse` and
  565. /// extracting a string result from it. Returns an error if conversion or
  566. /// extraction fails, and the extracted string on success.
  567. pub fn parse_json_response_string(
  568. json_result: JsonResult,
  569. ) -> std::result::Result<String, RpcError> {
  570. // Try converting `JsonResult` into a `JsonResponse`.
  571. let json_response: JsonResponse = json_result.try_into().map_err(|_| {
  572. RpcError::InvalidJson("Failed to convert JsonResult into JsonResponse".to_string())
  573. })?;
  574. // Attempt to extract a string result from the JsonResponse
  575. json_response.result.get::<String>().map(|value| value.to_string()).ok_or_else(|| {
  576. RpcError::InvalidJson("Failed to parse string from JsonResponse result".to_string())
  577. })
  578. }
  579. /// Converts the provided JSON-RPC parameters into an array of JSON values,
  580. /// returning a reference to the array if successful, or a JsonResult error containing a
  581. /// JsonError when the input is not a JSON array.
  582. pub fn to_json_array(params: &JsonValue) -> std::result::Result<&Vec<JsonValue>, RpcError> {
  583. if let JsonValue::Array(array) = params {
  584. Ok(array)
  585. } else {
  586. Err(RpcError::InvalidJson(
  587. "Expected an array of values, but received a different JSON type.".to_string(),
  588. ))
  589. }
  590. }
  591. /// Validates whether the provided JSON parameter is an empty array or object, returning success if it is empty or an Error if it contains values.
  592. pub fn validate_empty_params(params: &JsonValue) -> std::result::Result<(), RpcError> {
  593. match to_json_array(params) {
  594. Ok(array) if array.is_empty() => Ok(()),
  595. Ok(_) => Err(RpcError::InvalidJson(format!(
  596. "Parameters not permited, received: {:?}",
  597. params.stringify().unwrap_or("Error converting JSON to string".to_string())
  598. ))),
  599. Err(err) => Err(RpcError::InvalidJson(err.to_string())),
  600. }
  601. }