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