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_last_confirmed_block(&self, id: u16, params: JsonValue) -> JsonResult {
141 let Some(params) = params.get::<Vec<JsonValue>>() else {
142 return JsonError::new(InvalidParams, None, id).into()
143 };
144 if !params.is_empty() {
145 return JsonError::new(InvalidParams, None, id).into()
146 }
147
148 let Ok((height, hash)) = self.validator.blockchain.last() else {
149 return JsonError::new(InternalError, None, id).into()
150 };
151
152 JsonResponse::new(
153 JsonValue::Array(vec![
154 JsonValue::Number(height as f64),
155 JsonValue::String(hash.to_string()),
156 ]),
157 id,
158 )
159 .into()
160 }
161
162 pub async fn blockchain_best_fork_next_block_height(
174 &self,
175 id: u16,
176 params: JsonValue,
177 ) -> JsonResult {
178 let Some(params) = params.get::<Vec<JsonValue>>() else {
179 return JsonError::new(InvalidParams, None, id).into()
180 };
181 if !params.is_empty() {
182 return JsonError::new(InvalidParams, None, id).into()
183 }
184
185 let Ok(next_block_height) = self.validator.best_fork_next_block_height().await else {
186 return JsonError::new(InternalError, None, id).into()
187 };
188
189 JsonResponse::new(JsonValue::Number(next_block_height as f64), id).into()
190 }
191
192 pub async fn blockchain_block_target(&self, id: u16, params: JsonValue) -> JsonResult {
204 let Some(params) = params.get::<Vec<JsonValue>>() else {
205 return JsonError::new(InvalidParams, None, id).into()
206 };
207 if !params.is_empty() {
208 return JsonError::new(InvalidParams, None, id).into()
209 }
210
211 let block_target = self.validator.consensus.module.read().await.target;
212
213 JsonResponse::new(JsonValue::Number(block_target as f64), id).into()
214 }
215
216 pub async fn blockchain_subscribe_blocks(&self, id: u16, params: JsonValue) -> JsonResult {
231 let Some(params) = params.get::<Vec<JsonValue>>() else {
232 return JsonError::new(InvalidParams, None, id).into()
233 };
234 if !params.is_empty() {
235 return JsonError::new(InvalidParams, None, id).into()
236 }
237
238 self.subscribers.get("blocks").unwrap().clone().into()
239 }
240
241 pub async fn blockchain_subscribe_txs(&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 self.subscribers.get("txs").unwrap().clone().into()
260 }
261
262 pub async fn blockchain_subscribe_proposals(&self, id: u16, params: JsonValue) -> JsonResult {
275 let Some(params) = params.get::<Vec<JsonValue>>() else {
276 return JsonError::new(InvalidParams, None, id).into()
277 };
278 if !params.is_empty() {
279 return JsonError::new(InvalidParams, None, id).into()
280 }
281
282 self.subscribers.get("proposals").unwrap().clone().into()
283 }
284
285 pub async fn blockchain_lookup_zkas(&self, id: u16, params: JsonValue) -> JsonResult {
303 let Some(params) = params.get::<Vec<JsonValue>>() else {
304 return JsonError::new(InvalidParams, None, id).into()
305 };
306 if params.len() != 1 || !params[0].is_string() {
307 return JsonError::new(InvalidParams, None, id).into()
308 }
309
310 let contract_id = params[0].get::<String>().unwrap();
311 let contract_id = match ContractId::from_str(contract_id) {
312 Ok(v) => v,
313 Err(e) => {
314 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Error decoding string to ContractId: {e}");
315 return JsonError::new(InvalidParams, None, id).into()
316 }
317 };
318
319 let Ok(zkas_db) = self.validator.blockchain.contracts.lookup(
320 &self.validator.blockchain.sled_db,
321 &contract_id,
322 SMART_CONTRACT_ZKAS_DB_NAME,
323 ) else {
324 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Did not find zkas db for ContractId: {contract_id}");
325 return server_error(RpcError::ContractZkasDbNotFound, id, None)
326 };
327
328 let mut ret = vec![];
329
330 for i in zkas_db.iter() {
331 debug!(target: "darkfid::rpc::blockchain_lookup_zkas", "Iterating over zkas db");
332 let Ok((zkas_ns, zkas_bytes)) = i else {
333 error!(target: "darkfid::rpc::blockchain_lookup_zkas", "Internal sled error iterating db");
334 return JsonError::new(InternalError, None, id).into()
335 };
336
337 let Ok(zkas_ns) = deserialize_async(&zkas_ns).await else {
338 return JsonError::new(InternalError, None, id).into()
339 };
340
341 let (zkbin, _): (Vec<u8>, Vec<u8>) = match deserialize_async(&zkas_bytes).await {
342 Ok(pair) => pair,
343 Err(_) => return JsonError::new(InternalError, None, id).into(),
344 };
345
346 let zkas_bincode = base64::encode(&zkbin);
347 ret.push(JsonValue::Array(vec![
348 JsonValue::String(zkas_ns),
349 JsonValue::String(zkas_bincode),
350 ]));
351 }
352
353 JsonResponse::new(JsonValue::Array(ret), id).into()
354 }
355
356 pub async fn blockchain_lookup_wasm(&self, id: u16, params: JsonValue) -> JsonResult {
369 let Some(params) = params.get::<Vec<JsonValue>>() else {
370 return JsonError::new(InvalidParams, None, id).into()
371 };
372 if params.len() != 1 || !params[0].is_string() {
373 return JsonError::new(InvalidParams, None, id).into()
374 }
375
376 let contract_id = params[0].get::<String>().unwrap();
377 let Ok(contract_id) = ContractId::from_str(contract_id) else {
378 return server_error(RpcError::ParseError, id, None)
379 };
380
381 let Ok(bincode) = self.validator.blockchain.contracts.get(contract_id) else {
382 return server_error(RpcError::ContractWasmNotFound, id, None)
383 };
384
385 let encoded = base64::encode(&bincode);
386 JsonResponse::new(encoded.to_string().into(), id).into()
387 }
388
389 pub async fn blockchain_get_contract_state(&self, id: u16, params: JsonValue) -> JsonResult {
403 let Some(params) = params.get::<Vec<JsonValue>>() else {
404 return JsonError::new(InvalidParams, None, id).into()
405 };
406 if params.len() != 2 || !params[0].is_string() || !params[1].is_string() {
407 return JsonError::new(InvalidParams, None, id).into()
408 }
409
410 let contract_id = params[0].get::<String>().unwrap();
411 let contract_id = match ContractId::from_str(contract_id) {
412 Ok(v) => v,
413 Err(e) => {
414 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Error decoding string to ContractId: {e}");
415 return JsonError::new(InvalidParams, None, id).into()
416 }
417 };
418
419 let tree_name = params[1].get::<String>().unwrap();
420
421 match self.validator.blockchain.contracts.get_state_tree_records(
422 &self.validator.blockchain.sled_db,
423 &contract_id,
424 tree_name,
425 ) {
426 Ok(records) => JsonResponse::new(
427 JsonValue::String(base64::encode(&serialize_async(&records).await)),
428 id,
429 )
430 .into(),
431 Err(e) => {
432 error!(target: "darkfid::rpc::blockchain_get_contract_state", "Failed fetching contract state records: {e}");
433 server_error(RpcError::ContractStateNotFound, id, None)
434 }
435 }
436 }
437
438 pub async fn blockchain_get_contract_state_key(
453 &self,
454 id: u16,
455 params: JsonValue,
456 ) -> JsonResult {
457 let Some(params) = params.get::<Vec<JsonValue>>() else {
458 return JsonError::new(InvalidParams, None, id).into()
459 };
460 if params.len() != 3 ||
461 !params[0].is_string() ||
462 !params[1].is_string() ||
463 !params[2].is_string()
464 {
465 return JsonError::new(InvalidParams, None, id).into()
466 }
467
468 let contract_id = params[0].get::<String>().unwrap();
469 let contract_id = match ContractId::from_str(contract_id) {
470 Ok(v) => v,
471 Err(e) => {
472 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Error decoding string to ContractId: {e}");
473 return JsonError::new(InvalidParams, None, id).into()
474 }
475 };
476
477 let tree_name = params[1].get::<String>().unwrap();
478
479 let key_enc = params[2].get::<String>().unwrap().trim();
480 let Some(key) = base64::decode(key_enc) else {
481 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed decoding base64 key");
482 return server_error(RpcError::ParseError, id, None)
483 };
484
485 match self.validator.blockchain.contracts.get_state_tree_value(
486 &self.validator.blockchain.sled_db,
487 &contract_id,
488 tree_name,
489 &key,
490 ) {
491 Ok(value) => JsonResponse::new(JsonValue::String(base64::encode(&value)), id).into(),
492 Err(e) => {
493 error!(target: "darkfid::rpc::blockchain_get_contract_state_key", "Failed fetching contract state key value: {e}");
494 server_error(RpcError::ContractStateKeyNotFound, id, None)
495 }
496 }
497 }
498}