rpc_blockchain.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 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 darkfi_sdk::crypto::ContractId;
  20. use darkfi_serial::{deserialize, serialize};
  21. use log::{debug, error};
  22. use tinyjson::JsonValue;
  23. use darkfi::{
  24. blockchain::contract_store::SMART_CONTRACT_ZKAS_DB_NAME,
  25. rpc::jsonrpc::{
  26. ErrorCode::{InternalError, InvalidParams, ParseError},
  27. JsonError, JsonResponse, JsonResult,
  28. },
  29. util::encoding::base64,
  30. };
  31. use super::Darkfid;
  32. use crate::{server_error, RpcError};
  33. impl Darkfid {
  34. // RPCAPI:
  35. // Queries the blockchain database for a block in the given slot.
  36. // Returns a readable block upon success.
  37. //
  38. // **Params:**
  39. // * `array[0]`: `u64` slot ID (as string)
  40. //
  41. // **Returns:**
  42. // * [`BlockInfo`](https://darkrenaissance.github.io/darkfi/development/darkfi/consensus/block/struct.BlockInfo.html)
  43. // struct serialized into base64.
  44. //
  45. // --> {"jsonrpc": "2.0", "method": "blockchain.get_slot", "params": ["0"], "id": 1}
  46. // <-- {"jsonrpc": "2.0", "result": "ABCD...", "id": 1}
  47. pub async fn blockchain_get_slot(&self, id: u16, params: JsonValue) -> JsonResult {
  48. let params = params.get::<Vec<JsonValue>>().unwrap();
  49. if params.len() != 1 || !params[0].is_string() {
  50. return JsonError::new(InvalidParams, None, id).into()
  51. }
  52. let slot = match params[0].get::<String>().unwrap().parse::<u64>() {
  53. Ok(v) => v,
  54. Err(_) => return JsonError::new(ParseError, None, id).into(),
  55. };
  56. let validator_state = self.validator_state.read().await;
  57. let blocks = match validator_state.blockchain.get_blocks_by_slot(&[slot]) {
  58. Ok(v) => {
  59. drop(validator_state);
  60. v
  61. }
  62. Err(e) => {
  63. error!("[RPC] blockchain.get_slot: Failed fetching block by slot: {}", e);
  64. return JsonError::new(InternalError, None, id).into()
  65. }
  66. };
  67. if blocks.is_empty() {
  68. return server_error(RpcError::UnknownSlot, id, None)
  69. }
  70. let block = base64::encode(&serialize(&blocks[0]));
  71. JsonResponse::new(JsonValue::String(block), id).into()
  72. }
  73. // RPCAPI:
  74. // Queries the blockchain database for a given transaction.
  75. // Returns a serialized `Transaction` object.
  76. //
  77. // **Params:**
  78. // * `array[0]`: Hex-encoded transaction hash string
  79. //
  80. // **Returns:**
  81. // * Serialized [`Transaction`](https://darkrenaissance.github.io/darkfi/development/darkfi/tx/struct.Transaction.html)
  82. // object encoded with base64
  83. //
  84. // --> {"jsonrpc": "2.0", "method": "blockchain.get_tx", "params": ["TxHash"], "id": 1}
  85. // <-- {"jsonrpc": "2.0", "result": "ABCD...", "id": 1}
  86. pub async fn blockchain_get_tx(&self, id: u16, params: JsonValue) -> JsonResult {
  87. let params = params.get::<Vec<JsonValue>>().unwrap();
  88. if params.len() != 1 || !params[0].is_string() {
  89. return JsonError::new(InvalidParams, None, id).into()
  90. }
  91. let tx_hash = params[0].get::<String>().unwrap();
  92. let tx_hash = match blake3::Hash::from_hex(tx_hash) {
  93. Ok(v) => v,
  94. Err(_) => return JsonError::new(ParseError, None, id).into(),
  95. };
  96. let validator_state = self.validator_state.read().await;
  97. let txs = match validator_state.blockchain.transactions.get(&[tx_hash], true) {
  98. Ok(txs) => {
  99. drop(validator_state);
  100. txs
  101. }
  102. Err(e) => {
  103. error!("[RPC] blockchain.get_tx: Failed fetching tx by hash: {}", e);
  104. return JsonError::new(InternalError, None, id).into()
  105. }
  106. };
  107. // This would be an logic error somewhere
  108. assert_eq!(txs.len(), 1);
  109. // and strict was used during .get()
  110. let tx = txs[0].as_ref().unwrap();
  111. let tx_enc = base64::encode(&serialize(tx));
  112. JsonResponse::new(JsonValue::String(tx_enc), id).into()
  113. }
  114. // RPCAPI:
  115. // Queries the blockchain database to find the last known slot
  116. //
  117. // **Params:**
  118. // * `None`
  119. //
  120. // **Returns:**
  121. // * `u64` ID of the last known slot, as string
  122. //
  123. // --> {"jsonrpc": "2.0", "method": "blockchain.last_known_slot", "params": [], "id": 1}
  124. // <-- {"jsonrpc": "2.0", "result": "1234", "id": 1}
  125. pub async fn blockchain_last_known_slot(&self, id: u16, params: JsonValue) -> JsonResult {
  126. let params = params.get::<Vec<JsonValue>>().unwrap();
  127. if !params.is_empty() {
  128. return JsonError::new(InvalidParams, None, id).into()
  129. }
  130. let blockchain = { self.validator_state.read().await.blockchain.clone() };
  131. let Ok(last_slot) = blockchain.last() else {
  132. return JsonError::new(InternalError, None, id).into()
  133. };
  134. JsonResponse::new(JsonValue::String(last_slot.0.to_string()), id).into()
  135. }
  136. // RPCAPI:
  137. // Initializes a subscription to new incoming blocks.
  138. // Once a subscription is established, `darkfid` will send JSON-RPC notifications of
  139. // new incoming blocks to the subscriber.
  140. //
  141. // --> {"jsonrpc": "2.0", "method": "blockchain.subscribe_blocks", "params": [], "id": 1}
  142. // <-- {"jsonrpc": "2.0", "method": "blockchain.subscribe_blocks", "params": [`blockinfo`]}
  143. pub async fn blockchain_subscribe_blocks(&self, id: u16, params: JsonValue) -> JsonResult {
  144. let params = params.get::<Vec<JsonValue>>().unwrap();
  145. if !params.is_empty() {
  146. return JsonError::new(InvalidParams, None, id).into()
  147. }
  148. self.validator_state.read().await.subscribers.get("blocks").unwrap().clone().into()
  149. }
  150. // RPCAPI:
  151. // Initializes a subscription to erroneous transactions notifications.
  152. // Once a subscription is established, `darkfid` will send JSON-RPC notifications of
  153. // erroneous transactions to the subscriber.
  154. //
  155. // --> {"jsonrpc": "2.0", "method": "blockchain.subscribe_err_txs", "params": [], "id": 1}
  156. // <-- {"jsonrpc": "2.0", "method": "blockchain.subscribe_err_txs", "params": [`tx_hash`]}
  157. pub async fn blockchain_subscribe_err_txs(&self, id: u16, params: JsonValue) -> JsonResult {
  158. let params = params.get::<Vec<JsonValue>>().unwrap();
  159. if !params.is_empty() {
  160. return JsonError::new(InvalidParams, None, id).into()
  161. }
  162. self.validator_state.read().await.subscribers.get("err_txs").unwrap().clone().into()
  163. }
  164. // RPCAPI:
  165. // Performs a lookup of zkas bincodes for a given contract ID and returns all of
  166. // them, including their namespace.
  167. //
  168. // **Params:**
  169. // * `array[0]`: base58-encoded contract ID string
  170. //
  171. // **Returns:**
  172. // * `array[n]`: Pairs of: `zkas_namespace` string, serialized and base64-encoded
  173. // [`ZkBinary`](https://darkrenaissance.github.io/darkfi/development/darkfi/zkas/decoder/struct.ZkBinary.html)
  174. // object
  175. //
  176. // --> {"jsonrpc": "2.0", "method": "blockchain.lookup_zkas", "params": ["6Ef42L1KLZXBoxBuCDto7coi9DA2D2SRtegNqNU4sd74"], "id": 1}
  177. // <-- {"jsonrpc": "2.0", "result": [["Foo", "ABCD..."], ["Bar", "EFGH..."]], "id": 1}
  178. pub async fn blockchain_lookup_zkas(&self, id: u16, params: JsonValue) -> JsonResult {
  179. let params = params.get::<Vec<JsonValue>>().unwrap();
  180. if params.len() != 1 || !params[0].is_string() {
  181. return JsonError::new(InvalidParams, None, id).into()
  182. }
  183. let contract_id = params[0].get::<String>().unwrap();
  184. let contract_id = match ContractId::from_str(contract_id) {
  185. Ok(v) => v,
  186. Err(e) => {
  187. error!("[RPC] blockchain.lookup_zkas: Error decoding string to ContractId: {}", e);
  188. return JsonError::new(InvalidParams, None, id).into()
  189. }
  190. };
  191. let blockchain = { self.validator_state.read().await.blockchain.clone() };
  192. let Ok(zkas_db) = blockchain.contracts.lookup(
  193. &blockchain.sled_db,
  194. &contract_id,
  195. SMART_CONTRACT_ZKAS_DB_NAME,
  196. ) else {
  197. error!(
  198. "[RPC] blockchain.lookup_zkas: Did not find zkas db for ContractId: {}",
  199. contract_id
  200. );
  201. return server_error(RpcError::ContractZkasDbNotFound, id, None)
  202. };
  203. let mut ret = vec![];
  204. for i in zkas_db.iter() {
  205. debug!("Iterating over zkas db");
  206. let Ok((zkas_ns, zkas_bytes)) = i else {
  207. error!("Internal sled error iterating db");
  208. return JsonError::new(InternalError, None, id).into()
  209. };
  210. let Ok(zkas_ns) = deserialize(&zkas_ns) else {
  211. return JsonError::new(InternalError, None, id).into()
  212. };
  213. let zkas_bincode = base64::encode(&zkas_bytes);
  214. ret.push(JsonValue::Array(vec![
  215. JsonValue::String(zkas_ns),
  216. JsonValue::String(zkas_bincode),
  217. ]));
  218. }
  219. JsonResponse::new(JsonValue::Array(ret), id).into()
  220. }
  221. }