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
67 .validator
68 .read()
69 .await
70 .blockchain
71 .get_blocks_by_heights(&[block_height])
72 {
73 Ok(v) => v,
74 Err(e) => {
75 error!(target: "darkfid::rpc::blockchain_get_block", "Failed fetching block by height: {e}");
76 return JsonError::new(InternalError, None, id).into()
77 }
78 };
79
80 if blocks.is_empty() {
81 return server_error(RpcError::UnknownBlockHeight, id, None)
82 }
83
84 let block = base64::encode(&serialize_async(&blocks[0]).await);
85 JsonResponse::new(JsonValue::String(block), id).into()
86 }
87
88 pub async fn blockchain_get_tx(&self, id: u16, params: JsonValue) -> JsonResult {
105 let Some(params) = params.get::<Vec<JsonValue>>() else {
106 return JsonError::new(InvalidParams, None, id).into()
107 };
108 if params.len() != 1 || !params[0].is_string() {
109 return JsonError::new(InvalidParams, None, id).into()
110 }
111
112 let tx_hash = params[0].get::<String>().unwrap();
113 let tx_hash = match TransactionHash::from_str(tx_hash) {
114 Ok(v) => v,
115 Err(_) => return JsonError::new(ParseError, None, id).into(),
116 };
117
118 let txs = match self.validator.read().await.blockchain.transactions.get(&[tx_hash], true) {
119 Ok(txs) => txs,
120 Err(e) => {
121 error!(target: "darkfid::rpc::blockchain_get_tx", "Failed fetching tx by hash: {e}");
122 return JsonError::new(InternalError, None, id).into()
123 }
124 };
125 assert_eq!(txs.len(), 1);
127 let tx = txs[0].as_ref().unwrap();
129
130 let tx_enc = base64::encode(&serialize_async(tx).await);
131 JsonResponse::new(JsonValue::String(tx_enc), id).into()
132 }
133
134 pub async fn blockchain_get_difficulty(&self, id: u16, params: JsonValue) -> JsonResult {
148 let Some(params) = params.get::<Vec<JsonValue>>() else {
149 return JsonError::new(InvalidParams, None, id).into()
150 };
151 if params.len() != 1 || !params[0].is_number() {
152 return JsonError::new(InvalidParams, None, id).into()
153 }
154
155 let height = *params[0].get::<f64>().unwrap() as u32;
156
157 if height == 0 {
158 return JsonResponse::new(JsonValue::Array(vec![1_f64.into(), 1_f64.into()]), id).into()
159 }
160
161 let Ok(diff) =
162 self.validator.read().await.blockchain.blocks.get_difficulty(&[height], true)
163 else {
164 return server_error(RpcError::UnknownBlockHeight, id, None)
165 };
166
167 let block_diff = diff[0].clone().unwrap();
168
169 let difficulty: f64 = block_diff.difficulty.to_string().parse().unwrap();
170 let cumulative: f64 = block_diff.cumulative_difficulty.to_string().parse().unwrap();
171
172 JsonResponse::new(JsonValue::Array(vec![difficulty.into(), cumulative.into()]), id).into()
173 }
174
175 pub async fn blockchain_last_confirmed_block(&self, id: u16, params: JsonValue) -> JsonResult {
188 let Some(params) = params.get::<Vec<JsonValue>>() else {
189 return JsonError::new(InvalidParams, None, id).into()
190 };
191 if !params.is_empty() {
192 return JsonError::new(InvalidParams, None, id).into()
193 }
194
195 let Ok((height, hash)) = self.validator.read().await.blockchain.last() else {
196 return JsonError::new(InternalError, None, id).into()
197 };
198
199 JsonResponse::new(
200 JsonValue::Array(vec![
201 JsonValue::Number(height as f64),
202 JsonValue::String(hash.to_string()),
203 ]),
204 id,
205 )
206 .into()
207 }
208
209 pub async fn blockchain_best_fork_next_block_height(
221 &self,
222 id: u16,
223 params: JsonValue,
224 ) -> JsonResult {
225 let Some(params) = params.get::<Vec<JsonValue>>() else {
226 return JsonError::new(InvalidParams, None, id).into()
227 };
228 if !params.is_empty() {
229 return JsonError::new(InvalidParams, None, id).into()
230 }
231
232 let Ok(next_block_height) = self.validator.read().await.best_fork_next_block_height().await
233 else {
234 return JsonError::new(InternalError, None, id).into()
235 };
236
237 JsonResponse::new(JsonValue::Number(next_block_height as f64), id).into()
238 }
239
240 pub async fn blockchain_block_target(&self, id: u16, params: JsonValue) -> JsonResult {
252 let Some(params) = params.get::<Vec<JsonValue>>() else {
253 return JsonError::new(InvalidParams, None, id).into()
254 };
255 if !params.is_empty() {
256 return JsonError::new(InvalidParams, None, id).into()
257 }
258
259 let block_target = self.validator.read().await.consensus.module.target;
260
261 JsonResponse::new(JsonValue::Number(block_target as f64), id).into()
262 }
263
264 pub async fn blockchain_subscribe_blocks(&self, id: u16, params: JsonValue) -> JsonResult {
279 let Some(params) = params.get::<Vec<JsonValue>>() else {
280 return JsonError::new(InvalidParams, None, id).into()
281 };
282 if !params.is_empty() {
283 return JsonError::new(InvalidParams, None, id).into()
284 }
285
286 self.subscribers.get("blocks").unwrap().clone().into()
287 }
288
289 pub async fn blockchain_subscribe_txs(&self, id: u16, params: JsonValue) -> JsonResult {
300 let Some(params) = params.get::<Vec<JsonValue>>() else {
301 return JsonError::new(InvalidParams, None, id).into()
302 };
303 if !params.is_empty() {
304 return JsonError::new(InvalidParams, None, id).into()
305 }
306
307 self.subscribers.get("txs").unwrap().clone().into()
308 }
309
310 pub async fn blockchain_subscribe_proposals(&self, id: u16, params: JsonValue) -> JsonResult {
323 let Some(params) = params.get::<Vec<JsonValue>>() else {
324 return JsonError::new(InvalidParams, None, id).into()
325 };
326 if !params.is_empty() {
327 return JsonError::new(InvalidParams, None, id).into()
328 }
329
330 self.subscribers.get("proposals").unwrap().clone().into()
331 }
332
333 pub async fn blockchain_lookup_zkas(&self, id: u16, params: JsonValue) -> JsonResult {
351 let Some(params) = params.get::<Vec<JsonValue>>() else {
352 return JsonError::new(InvalidParams, None, id).into()
353 };
354 if params.len() != 1 || !params[0].is_string() {
355 return JsonError::new(InvalidParams, None, id).into()
356 }
357
358 let contract_id = params[0].get::<String>().unwrap();
359 let contract_id = match ContractId::from_str(contract_id) {
360 Ok(v) => v,
361 Err(e) => {
362 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Error decoding string to ContractId: {e}");
363 return JsonError::new(InvalidParams, None, id).into()
364 }
365 };
366
367 let validator = self.validator.read().await;
368 let Ok(zkas_db) = validator.blockchain.contracts.lookup(
369 &validator.blockchain.sled_db,
370 &contract_id,
371 SMART_CONTRACT_ZKAS_DB_NAME,
372 ) else {
373 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Did not find zkas db for ContractId: {contract_id}");
374 return server_error(RpcError::ContractZkasDbNotFound, id, None)
375 };
376 drop(validator);
377
378 let mut ret = vec![];
379
380 for i in zkas_db.iter() {
381 debug!(target: "darkfid::rpc::blockchain_lookup_zkas", "Iterating over zkas db");
382 let Ok((zkas_ns, zkas_bytes)) = i else {
383 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Internal sled error iterating db");
384 return JsonError::new(InternalError, None, id).into()
385 };
386
387 let Ok(zkas_ns) = deserialize_async(&zkas_ns).await else {
388 return JsonError::new(InternalError, None, id).into()
389 };
390
391 let (zkbin, _): (Vec<u8>, Vec<u8>) = match deserialize_async(&zkas_bytes).await {
392 Ok(pair) => pair,
393 Err(_) => return JsonError::new(InternalError, None, id).into(),
394 };
395
396 let zkas_bincode = base64::encode(&zkbin);
397 ret.push(JsonValue::Array(vec![
398 JsonValue::String(zkas_ns),
399 JsonValue::String(zkas_bincode),
400 ]));
401 }
402
403 JsonResponse::new(JsonValue::Array(ret), id).into()
404 }
405
406 pub async fn blockchain_lookup_wasm(&self, id: u16, params: JsonValue) -> JsonResult {
419 let Some(params) = params.get::<Vec<JsonValue>>() else {
420 return JsonError::new(InvalidParams, None, id).into()
421 };
422 if params.len() != 1 || !params[0].is_string() {
423 return JsonError::new(InvalidParams, None, id).into()
424 }
425
426 let contract_id = params[0].get::<String>().unwrap();
427 let Ok(contract_id) = ContractId::from_str(contract_id) else {
428 return server_error(RpcError::ParseError, id, None)
429 };
430
431 let Ok(bincode) = self.validator.read().await.blockchain.contracts.get(contract_id) else {
432 return server_error(RpcError::ContractWasmNotFound, id, None)
433 };
434
435 let encoded = base64::encode(&bincode);
436 JsonResponse::new(encoded.to_string().into(), id).into()
437 }
438
439 pub async fn blockchain_get_contract_state(&self, id: u16, params: JsonValue) -> JsonResult {
453 let Some(params) = params.get::<Vec<JsonValue>>() else {
454 return JsonError::new(InvalidParams, None, id).into()
455 };
456 if params.len() != 2 || !params[0].is_string() || !params[1].is_string() {
457 return JsonError::new(InvalidParams, None, id).into()
458 }
459
460 let contract_id = params[0].get::<String>().unwrap();
461 let contract_id = match ContractId::from_str(contract_id) {
462 Ok(v) => v,
463 Err(e) => {
464 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Error decoding string to ContractId: {e}");
465 return JsonError::new(InvalidParams, None, id).into()
466 }
467 };
468
469 let tree_name = params[1].get::<String>().unwrap();
470
471 let validator = self.validator.read().await;
472 match validator.blockchain.contracts.get_state_tree_records(
473 &validator.blockchain.sled_db,
474 &contract_id,
475 tree_name,
476 ) {
477 Ok(records) => JsonResponse::new(
478 JsonValue::String(base64::encode(&serialize_async(&records).await)),
479 id,
480 )
481 .into(),
482 Err(e) => {
483 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Failed fetching contract state records: {e}");
484 server_error(RpcError::ContractStateNotFound, id, None)
485 }
486 }
487 }
488
489 pub async fn blockchain_get_contract_state_key(
504 &self,
505 id: u16,
506 params: JsonValue,
507 ) -> JsonResult {
508 let Some(params) = params.get::<Vec<JsonValue>>() else {
509 return JsonError::new(InvalidParams, None, id).into()
510 };
511 if params.len() != 3 ||
512 !params[0].is_string() ||
513 !params[1].is_string() ||
514 !params[2].is_string()
515 {
516 return JsonError::new(InvalidParams, None, id).into()
517 }
518
519 let contract_id = params[0].get::<String>().unwrap();
520 let contract_id = match ContractId::from_str(contract_id) {
521 Ok(v) => v,
522 Err(e) => {
523 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Error decoding string to ContractId: {e}");
524 return JsonError::new(InvalidParams, None, id).into()
525 }
526 };
527
528 let tree_name = params[1].get::<String>().unwrap();
529
530 let key_enc = params[2].get::<String>().unwrap().trim();
531 let Some(key) = base64::decode(key_enc) else {
532 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed decoding base64 key");
533 return server_error(RpcError::ParseError, id, None)
534 };
535
536 let validator = self.validator.read().await;
537 match validator.blockchain.contracts.get_state_tree_value(
538 &validator.blockchain.sled_db,
539 &contract_id,
540 tree_name,
541 &key,
542 ) {
543 Ok(value) => JsonResponse::new(JsonValue::String(base64::encode(&value)), id).into(),
544 Err(e) => {
545 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed fetching contract state key value: {e}");
546 server_error(RpcError::ContractStateKeyNotFound, id, None)
547 }
548 }
549 }
550}