1use std::str::FromStr;
20
21use darkfi_sdk::{
22 crypto::contract_id::{ContractId, SMART_CONTRACT_ZKAS_DB_NAME},
23 tx::TransactionHash,
24};
25use darkfi_serial::{deserialize_async, serialize_async};
26use log::{debug, error};
27use tinyjson::JsonValue;
28
29use darkfi::{
30 rpc::jsonrpc::{
31 ErrorCode::{InternalError, InvalidParams, ParseError},
32 JsonError, JsonResponse, JsonResult,
33 },
34 util::encoding::base64,
35};
36
37use crate::{server_error, DarkfiNode, RpcError};
38
39impl DarkfiNode {
40 pub async fn blockchain_get_block(&self, id: u16, params: JsonValue) -> JsonResult {
54 let params = params.get::<Vec<JsonValue>>().unwrap();
55 if params.len() != 1 || !params[0].is_string() {
56 return JsonError::new(InvalidParams, None, id).into()
57 }
58
59 let block_height = match params[0].get::<String>().unwrap().parse::<u32>() {
60 Ok(v) => v,
61 Err(_) => return JsonError::new(ParseError, None, id).into(),
62 };
63
64 let blocks = match self.validator.blockchain.get_blocks_by_heights(&[block_height]) {
65 Ok(v) => v,
66 Err(e) => {
67 error!(target: "darkfid::rpc::blockchain_get_block", "Failed fetching block by height: {e}");
68 return JsonError::new(InternalError, None, id).into()
69 }
70 };
71
72 if blocks.is_empty() {
73 return server_error(RpcError::UnknownBlockHeight, id, None)
74 }
75
76 let block = base64::encode(&serialize_async(&blocks[0]).await);
77 JsonResponse::new(JsonValue::String(block), id).into()
78 }
79
80 pub async fn blockchain_get_tx(&self, id: u16, params: JsonValue) -> JsonResult {
94 let params = params.get::<Vec<JsonValue>>().unwrap();
95 if params.len() != 1 || !params[0].is_string() {
96 return JsonError::new(InvalidParams, None, id).into()
97 }
98
99 let tx_hash = params[0].get::<String>().unwrap();
100 let tx_hash = match TransactionHash::from_str(tx_hash) {
101 Ok(v) => v,
102 Err(_) => return JsonError::new(ParseError, None, id).into(),
103 };
104
105 let txs = match self.validator.blockchain.transactions.get(&[tx_hash], true) {
106 Ok(txs) => txs,
107 Err(e) => {
108 error!(target: "darkfid::rpc::blockchain_get_tx", "Failed fetching tx by hash: {e}");
109 return JsonError::new(InternalError, None, id).into()
110 }
111 };
112 assert_eq!(txs.len(), 1);
114 let tx = txs[0].as_ref().unwrap();
116
117 let tx_enc = base64::encode(&serialize_async(tx).await);
118 JsonResponse::new(JsonValue::String(tx_enc), id).into()
119 }
120
121 pub async fn blockchain_last_confirmed_block(&self, id: u16, params: JsonValue) -> JsonResult {
134 let params = params.get::<Vec<JsonValue>>().unwrap();
135 if !params.is_empty() {
136 return JsonError::new(InvalidParams, None, id).into()
137 }
138
139 let Ok((height, hash)) = self.validator.blockchain.last() else {
140 return JsonError::new(InternalError, None, id).into()
141 };
142
143 JsonResponse::new(
144 JsonValue::Array(vec![
145 JsonValue::Number(height as f64),
146 JsonValue::String(hash.to_string()),
147 ]),
148 id,
149 )
150 .into()
151 }
152
153 pub async fn blockchain_best_fork_next_block_height(
165 &self,
166 id: u16,
167 params: JsonValue,
168 ) -> JsonResult {
169 let params = params.get::<Vec<JsonValue>>().unwrap();
170 if !params.is_empty() {
171 return JsonError::new(InvalidParams, None, id).into()
172 }
173
174 let Ok(next_block_height) = self.validator.best_fork_next_block_height().await else {
175 return JsonError::new(InternalError, None, id).into()
176 };
177
178 JsonResponse::new(JsonValue::Number(next_block_height as f64), id).into()
179 }
180
181 pub async fn blockchain_block_target(&self, id: u16, params: JsonValue) -> JsonResult {
193 let params = params.get::<Vec<JsonValue>>().unwrap();
194 if !params.is_empty() {
195 return JsonError::new(InvalidParams, None, id).into()
196 }
197
198 let block_target = self.validator.consensus.module.read().await.target;
199
200 JsonResponse::new(JsonValue::Number(block_target as f64), id).into()
201 }
202
203 pub async fn blockchain_subscribe_blocks(&self, id: u16, params: JsonValue) -> JsonResult {
211 let params = params.get::<Vec<JsonValue>>().unwrap();
212 if !params.is_empty() {
213 return JsonError::new(InvalidParams, None, id).into()
214 }
215
216 self.subscribers.get("blocks").unwrap().clone().into()
217 }
218
219 pub async fn blockchain_subscribe_txs(&self, id: u16, params: JsonValue) -> JsonResult {
227 let params = params.get::<Vec<JsonValue>>().unwrap();
228 if !params.is_empty() {
229 return JsonError::new(InvalidParams, None, id).into()
230 }
231
232 self.subscribers.get("txs").unwrap().clone().into()
233 }
234
235 pub async fn blockchain_subscribe_proposals(&self, id: u16, params: JsonValue) -> JsonResult {
242 let params = params.get::<Vec<JsonValue>>().unwrap();
243 if !params.is_empty() {
244 return JsonError::new(InvalidParams, None, id).into()
245 }
246
247 self.subscribers.get("proposals").unwrap().clone().into()
248 }
249
250 pub async fn blockchain_lookup_zkas(&self, id: u16, params: JsonValue) -> JsonResult {
265 let params = params.get::<Vec<JsonValue>>().unwrap();
266 if params.len() != 1 || !params[0].is_string() {
267 return JsonError::new(InvalidParams, None, id).into()
268 }
269
270 let contract_id = params[0].get::<String>().unwrap();
271 let contract_id = match ContractId::from_str(contract_id) {
272 Ok(v) => v,
273 Err(e) => {
274 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Error decoding string to ContractId: {e}");
275 return JsonError::new(InvalidParams, None, id).into()
276 }
277 };
278
279 let Ok(zkas_db) = self.validator.blockchain.contracts.lookup(
280 &self.validator.blockchain.sled_db,
281 &contract_id,
282 SMART_CONTRACT_ZKAS_DB_NAME,
283 ) else {
284 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Did not find zkas db for ContractId: {contract_id}");
285 return server_error(RpcError::ContractZkasDbNotFound, id, None)
286 };
287
288 let mut ret = vec![];
289
290 for i in zkas_db.iter() {
291 debug!(target: "darkfid::rpc::blockchain_lookup_zkas", "Iterating over zkas db");
292 let Ok((zkas_ns, zkas_bytes)) = i else {
293 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Internal sled error iterating db");
294 return JsonError::new(InternalError, None, id).into()
295 };
296
297 let Ok(zkas_ns) = deserialize_async(&zkas_ns).await else {
298 return JsonError::new(InternalError, None, id).into()
299 };
300
301 let (zkbin, _): (Vec<u8>, Vec<u8>) = match deserialize_async(&zkas_bytes).await {
302 Ok(pair) => pair,
303 Err(_) => return JsonError::new(InternalError, None, id).into(),
304 };
305
306 let zkas_bincode = base64::encode(&zkbin);
307 ret.push(JsonValue::Array(vec![
308 JsonValue::String(zkas_ns),
309 JsonValue::String(zkas_bincode),
310 ]));
311 }
312
313 JsonResponse::new(JsonValue::Array(ret), id).into()
314 }
315
316 pub async fn blockchain_get_contract_state(&self, id: u16, params: JsonValue) -> JsonResult {
330 let params = params.get::<Vec<JsonValue>>().unwrap();
331 if params.len() != 2 || !params[0].is_string() || !params[1].is_string() {
332 return JsonError::new(InvalidParams, None, id).into()
333 }
334
335 let contract_id = params[0].get::<String>().unwrap();
336 let contract_id = match ContractId::from_str(contract_id) {
337 Ok(v) => v,
338 Err(e) => {
339 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Error decoding string to ContractId: {e}");
340 return JsonError::new(InvalidParams, None, id).into()
341 }
342 };
343
344 let tree_name = params[1].get::<String>().unwrap();
345
346 match self.validator.blockchain.contracts.get_state_tree_records(
347 &self.validator.blockchain.sled_db,
348 &contract_id,
349 tree_name,
350 ) {
351 Ok(records) => JsonResponse::new(
352 JsonValue::String(base64::encode(&serialize_async(&records).await)),
353 id,
354 )
355 .into(),
356 Err(e) => {
357 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Failed fetching contract state records: {e}");
358 server_error(RpcError::ContractStateNotFound, id, None)
359 }
360 }
361 }
362
363 pub async fn blockchain_get_contract_state_key(
378 &self,
379 id: u16,
380 params: JsonValue,
381 ) -> JsonResult {
382 let params = params.get::<Vec<JsonValue>>().unwrap();
383 if params.len() != 3 ||
384 !params[0].is_string() ||
385 !params[1].is_string() ||
386 !params[2].is_string()
387 {
388 return JsonError::new(InvalidParams, None, id).into()
389 }
390
391 let contract_id = params[0].get::<String>().unwrap();
392 let contract_id = match ContractId::from_str(contract_id) {
393 Ok(v) => v,
394 Err(e) => {
395 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Error decoding string to ContractId: {e}");
396 return JsonError::new(InvalidParams, None, id).into()
397 }
398 };
399
400 let tree_name = params[1].get::<String>().unwrap();
401
402 let key_enc = params[2].get::<String>().unwrap().trim();
403 let Some(key) = base64::decode(key_enc) else {
404 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed decoding base64 key");
405 return server_error(RpcError::ParseError, id, None)
406 };
407
408 match self.validator.blockchain.contracts.get_state_tree_value(
409 &self.validator.blockchain.sled_db,
410 &contract_id,
411 tree_name,
412 &key,
413 ) {
414 Ok(value) => JsonResponse::new(JsonValue::String(base64::encode(&value)), id).into(),
415 Err(e) => {
416 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed fetching contract state key value: {e}");
417 server_error(RpcError::ContractStateKeyNotFound, id, None)
418 }
419 }
420 }
421}