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 tinyjson::JsonValue;
27use tracing::{debug, error};
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 {
57 let Some(params) = params.get::<Vec<JsonValue>>() else {
58 return JsonError::new(InvalidParams, None, id).into()
59 };
60 if params.len() != 1 || !params[0].is_number() {
61 return JsonError::new(InvalidParams, None, id).into()
62 }
63
64 let block_height = *params[0].get::<f64>().unwrap() as u32;
65
66 let blocks = match self.validator.blockchain.get_blocks_by_heights(&[block_height]) {
67 Ok(v) => v,
68 Err(e) => {
69 error!(target: "darkfid::rpc::blockchain_get_block", "Failed fetching block by height: {e}");
70 return JsonError::new(InternalError, None, id).into()
71 }
72 };
73
74 if blocks.is_empty() {
75 return server_error(RpcError::UnknownBlockHeight, id, None)
76 }
77
78 let block = base64::encode(&serialize_async(&blocks[0]).await);
79 JsonResponse::new(JsonValue::String(block), id).into()
80 }
81
82 pub async fn blockchain_get_tx(&self, id: u16, params: JsonValue) -> JsonResult {
99 let Some(params) = params.get::<Vec<JsonValue>>() else {
100 return JsonError::new(InvalidParams, None, id).into()
101 };
102 if params.len() != 1 || !params[0].is_string() {
103 return JsonError::new(InvalidParams, None, id).into()
104 }
105
106 let tx_hash = params[0].get::<String>().unwrap();
107 let tx_hash = match TransactionHash::from_str(tx_hash) {
108 Ok(v) => v,
109 Err(_) => return JsonError::new(ParseError, None, id).into(),
110 };
111
112 let txs = match self.validator.blockchain.transactions.get(&[tx_hash], true) {
113 Ok(txs) => txs,
114 Err(e) => {
115 error!(target: "darkfid::rpc::blockchain_get_tx", "Failed fetching tx by hash: {e}");
116 return JsonError::new(InternalError, None, id).into()
117 }
118 };
119 assert_eq!(txs.len(), 1);
121 let tx = txs[0].as_ref().unwrap();
123
124 let tx_enc = base64::encode(&serialize_async(tx).await);
125 JsonResponse::new(JsonValue::String(tx_enc), id).into()
126 }
127
128 pub async fn blockchain_get_difficulty(&self, id: u16, params: JsonValue) -> JsonResult {
142 let Some(params) = params.get::<Vec<JsonValue>>() else {
143 return JsonError::new(InvalidParams, None, id).into()
144 };
145 if params.len() != 1 || !params[0].is_number() {
146 return JsonError::new(InvalidParams, None, id).into()
147 }
148
149 let height = *params[0].get::<f64>().unwrap() as u32;
150
151 if height == 0 {
152 return JsonResponse::new(JsonValue::Array(vec![1_f64.into(), 1_f64.into()]), id).into()
153 }
154
155 let Ok(diff) = self.validator.blockchain.blocks.get_difficulty(&[height], true) else {
156 return server_error(RpcError::UnknownBlockHeight, id, None)
157 };
158
159 let block_diff = diff[0].clone().unwrap();
160
161 let difficulty: f64 = block_diff.difficulty.to_string().parse().unwrap();
162 let cumulative: f64 = block_diff.cumulative_difficulty.to_string().parse().unwrap();
163
164 JsonResponse::new(JsonValue::Array(vec![difficulty.into(), cumulative.into()]), id).into()
165 }
166
167 pub async fn blockchain_last_confirmed_block(&self, id: u16, params: JsonValue) -> JsonResult {
180 let Some(params) = params.get::<Vec<JsonValue>>() else {
181 return JsonError::new(InvalidParams, None, id).into()
182 };
183 if !params.is_empty() {
184 return JsonError::new(InvalidParams, None, id).into()
185 }
186
187 let Ok((height, hash)) = self.validator.blockchain.last() else {
188 return JsonError::new(InternalError, None, id).into()
189 };
190
191 JsonResponse::new(
192 JsonValue::Array(vec![
193 JsonValue::Number(height as f64),
194 JsonValue::String(hash.to_string()),
195 ]),
196 id,
197 )
198 .into()
199 }
200
201 pub async fn blockchain_best_fork_next_block_height(
213 &self,
214 id: u16,
215 params: JsonValue,
216 ) -> JsonResult {
217 let Some(params) = params.get::<Vec<JsonValue>>() else {
218 return JsonError::new(InvalidParams, None, id).into()
219 };
220 if !params.is_empty() {
221 return JsonError::new(InvalidParams, None, id).into()
222 }
223
224 let Ok(next_block_height) = self.validator.best_fork_next_block_height().await else {
225 return JsonError::new(InternalError, None, id).into()
226 };
227
228 JsonResponse::new(JsonValue::Number(next_block_height as f64), id).into()
229 }
230
231 pub async fn blockchain_block_target(&self, id: u16, params: JsonValue) -> JsonResult {
243 let Some(params) = params.get::<Vec<JsonValue>>() else {
244 return JsonError::new(InvalidParams, None, id).into()
245 };
246 if !params.is_empty() {
247 return JsonError::new(InvalidParams, None, id).into()
248 }
249
250 let block_target = self.validator.consensus.module.read().await.target;
251
252 JsonResponse::new(JsonValue::Number(block_target as f64), id).into()
253 }
254
255 pub async fn blockchain_subscribe_blocks(&self, id: u16, params: JsonValue) -> JsonResult {
270 let Some(params) = params.get::<Vec<JsonValue>>() else {
271 return JsonError::new(InvalidParams, None, id).into()
272 };
273 if !params.is_empty() {
274 return JsonError::new(InvalidParams, None, id).into()
275 }
276
277 self.subscribers.get("blocks").unwrap().clone().into()
278 }
279
280 pub async fn blockchain_subscribe_txs(&self, id: u16, params: JsonValue) -> JsonResult {
291 let Some(params) = params.get::<Vec<JsonValue>>() else {
292 return JsonError::new(InvalidParams, None, id).into()
293 };
294 if !params.is_empty() {
295 return JsonError::new(InvalidParams, None, id).into()
296 }
297
298 self.subscribers.get("txs").unwrap().clone().into()
299 }
300
301 pub async fn blockchain_subscribe_proposals(&self, id: u16, params: JsonValue) -> JsonResult {
314 let Some(params) = params.get::<Vec<JsonValue>>() else {
315 return JsonError::new(InvalidParams, None, id).into()
316 };
317 if !params.is_empty() {
318 return JsonError::new(InvalidParams, None, id).into()
319 }
320
321 self.subscribers.get("proposals").unwrap().clone().into()
322 }
323
324 pub async fn blockchain_lookup_zkas(&self, id: u16, params: JsonValue) -> JsonResult {
342 let Some(params) = params.get::<Vec<JsonValue>>() else {
343 return JsonError::new(InvalidParams, None, id).into()
344 };
345 if params.len() != 1 || !params[0].is_string() {
346 return JsonError::new(InvalidParams, None, id).into()
347 }
348
349 let contract_id = params[0].get::<String>().unwrap();
350 let contract_id = match ContractId::from_str(contract_id) {
351 Ok(v) => v,
352 Err(e) => {
353 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Error decoding string to ContractId: {e}");
354 return JsonError::new(InvalidParams, None, id).into()
355 }
356 };
357
358 let Ok(zkas_db) = self.validator.blockchain.contracts.lookup(
359 &self.validator.blockchain.sled_db,
360 &contract_id,
361 SMART_CONTRACT_ZKAS_DB_NAME,
362 ) else {
363 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Did not find zkas db for ContractId: {contract_id}");
364 return server_error(RpcError::ContractZkasDbNotFound, id, None)
365 };
366
367 let mut ret = vec![];
368
369 for i in zkas_db.iter() {
370 debug!(target: "darkfid::rpc::blockchain_lookup_zkas", "Iterating over zkas db");
371 let Ok((zkas_ns, zkas_bytes)) = i else {
372 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Internal sled error iterating db");
373 return JsonError::new(InternalError, None, id).into()
374 };
375
376 let Ok(zkas_ns) = deserialize_async(&zkas_ns).await else {
377 return JsonError::new(InternalError, None, id).into()
378 };
379
380 let (zkbin, _): (Vec<u8>, Vec<u8>) = match deserialize_async(&zkas_bytes).await {
381 Ok(pair) => pair,
382 Err(_) => return JsonError::new(InternalError, None, id).into(),
383 };
384
385 let zkas_bincode = base64::encode(&zkbin);
386 ret.push(JsonValue::Array(vec![
387 JsonValue::String(zkas_ns),
388 JsonValue::String(zkas_bincode),
389 ]));
390 }
391
392 JsonResponse::new(JsonValue::Array(ret), id).into()
393 }
394
395 pub async fn blockchain_lookup_wasm(&self, id: u16, params: JsonValue) -> JsonResult {
408 let Some(params) = params.get::<Vec<JsonValue>>() else {
409 return JsonError::new(InvalidParams, None, id).into()
410 };
411 if params.len() != 1 || !params[0].is_string() {
412 return JsonError::new(InvalidParams, None, id).into()
413 }
414
415 let contract_id = params[0].get::<String>().unwrap();
416 let Ok(contract_id) = ContractId::from_str(contract_id) else {
417 return server_error(RpcError::ParseError, id, None)
418 };
419
420 let Ok(bincode) = self.validator.blockchain.contracts.get(contract_id) else {
421 return server_error(RpcError::ContractWasmNotFound, id, None)
422 };
423
424 let encoded = base64::encode(&bincode);
425 JsonResponse::new(encoded.to_string().into(), id).into()
426 }
427
428 pub async fn blockchain_get_contract_state(&self, id: u16, params: JsonValue) -> JsonResult {
442 let Some(params) = params.get::<Vec<JsonValue>>() else {
443 return JsonError::new(InvalidParams, None, id).into()
444 };
445 if params.len() != 2 || !params[0].is_string() || !params[1].is_string() {
446 return JsonError::new(InvalidParams, None, id).into()
447 }
448
449 let contract_id = params[0].get::<String>().unwrap();
450 let contract_id = match ContractId::from_str(contract_id) {
451 Ok(v) => v,
452 Err(e) => {
453 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Error decoding string to ContractId: {e}");
454 return JsonError::new(InvalidParams, None, id).into()
455 }
456 };
457
458 let tree_name = params[1].get::<String>().unwrap();
459
460 match self.validator.blockchain.contracts.get_state_tree_records(
461 &self.validator.blockchain.sled_db,
462 &contract_id,
463 tree_name,
464 ) {
465 Ok(records) => JsonResponse::new(
466 JsonValue::String(base64::encode(&serialize_async(&records).await)),
467 id,
468 )
469 .into(),
470 Err(e) => {
471 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Failed fetching contract state records: {e}");
472 server_error(RpcError::ContractStateNotFound, id, None)
473 }
474 }
475 }
476
477 pub async fn blockchain_get_contract_state_key(
492 &self,
493 id: u16,
494 params: JsonValue,
495 ) -> JsonResult {
496 let Some(params) = params.get::<Vec<JsonValue>>() else {
497 return JsonError::new(InvalidParams, None, id).into()
498 };
499 if params.len() != 3 ||
500 !params[0].is_string() ||
501 !params[1].is_string() ||
502 !params[2].is_string()
503 {
504 return JsonError::new(InvalidParams, None, id).into()
505 }
506
507 let contract_id = params[0].get::<String>().unwrap();
508 let contract_id = match ContractId::from_str(contract_id) {
509 Ok(v) => v,
510 Err(e) => {
511 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Error decoding string to ContractId: {e}");
512 return JsonError::new(InvalidParams, None, id).into()
513 }
514 };
515
516 let tree_name = params[1].get::<String>().unwrap();
517
518 let key_enc = params[2].get::<String>().unwrap().trim();
519 let Some(key) = base64::decode(key_enc) else {
520 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed decoding base64 key");
521 return server_error(RpcError::ParseError, id, None)
522 };
523
524 match self.validator.blockchain.contracts.get_state_tree_value(
525 &self.validator.blockchain.sled_db,
526 &contract_id,
527 tree_name,
528 &key,
529 ) {
530 Ok(value) => JsonResponse::new(JsonValue::String(base64::encode(&value)), id).into(),
531 Err(e) => {
532 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed fetching contract state key value: {e}");
533 server_error(RpcError::ContractStateKeyNotFound, id, None)
534 }
535 }
536 }
537}