1use std::collections::HashSet;
20
21use log::{debug, error, info};
22use num_bigint::BigUint;
23use smol::lock::MutexGuard;
24
25use darkfi::{
26 blockchain::BlockInfo,
27 rpc::{
28 jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResponse, JsonResult},
29 server::RequestHandler,
30 util::JsonValue,
31 },
32 system::{sleep, StoppableTaskPtr},
33 util::encoding::base64,
34 validator::pow::mine_block,
35};
36use darkfi_sdk::num_traits::Num;
37use darkfi_serial::{async_trait, deserialize_async};
38
39use crate::{
40 error::{server_error, RpcError},
41 MinerNode,
42};
43
44#[async_trait]
45impl RequestHandler<()> for MinerNode {
46 async fn handle_request(&self, req: JsonRequest) -> JsonResult {
47 debug!(target: "minerd::rpc", "--> {}", req.stringify().unwrap());
48
49 match req.method.as_str() {
50 "ping" => self.pong(req.id, req.params).await,
51 "abort" => self.abort(req.id, req.params).await,
52 "mine" => self.mine(req.id, req.params).await,
53 _ => JsonError::new(ErrorCode::MethodNotFound, None, req.id).into(),
54 }
55 }
56
57 async fn connections_mut(&self) -> MutexGuard<'life0, HashSet<StoppableTaskPtr>> {
58 self.rpc_connections.lock().await
59 }
60}
61
62impl MinerNode {
63 async fn abort(&self, id: u16, _params: JsonValue) -> JsonResult {
70 if let Some(e) = self.abort_pending(id).await {
71 return e
72 };
73 JsonResponse::new(JsonValue::Boolean(true), id).into()
74 }
75
76 async fn mine(&self, id: u16, params: JsonValue) -> JsonResult {
82 if !params.is_array() {
84 return JsonError::new(ErrorCode::InvalidParams, None, id).into()
85 }
86 let params = params.get::<Vec<JsonValue>>().unwrap();
87 if params.len() != 2 || !params[0].is_string() || !params[1].is_string() {
88 return JsonError::new(ErrorCode::InvalidParams, None, id).into()
89 }
90
91 let Ok(target) = BigUint::from_str_radix(params[0].get::<String>().unwrap(), 10) else {
93 error!(target: "minerd::rpc", "Failed to parse target");
94 return server_error(RpcError::TargetParseError, id, None)
95 };
96 let Some(block_bytes) = base64::decode(params[1].get::<String>().unwrap()) else {
97 error!(target: "minerd::rpc", "Failed to parse block bytes");
98 return server_error(RpcError::BlockParseError, id, None)
99 };
100 let Ok(mut block) = deserialize_async::<BlockInfo>(&block_bytes).await else {
101 error!(target: "minerd::rpc", "Failed to parse block");
102 return server_error(RpcError::BlockParseError, id, None)
103 };
104 let block_hash = block.hash();
105 info!(target: "minerd::rpc", "Received request to mine block {} for target: {}", block_hash, target);
106
107 if self.stop_at_height > 0 && block.header.height >= self.stop_at_height {
109 info!(target: "minerd::rpc", "Reached requested mining height {}", self.stop_at_height);
110 return server_error(RpcError::MiningFailed, id, None)
111 }
112
113 if let Some(e) = self.abort_pending(id).await {
115 return e
116 };
117
118 info!(target: "minerd::rpc", "Mining block {} for target: {}", block_hash, target);
120 if let Err(e) = mine_block(&target, &mut block, self.threads, &self.stop_signal.clone()) {
121 error!(target: "minerd::rpc", "Failed mining block {} with error: {}", block_hash, e);
122 return server_error(RpcError::MiningFailed, id, None)
123 }
124 info!(target: "minerd::rpc", "Mined block {} with nonce: {}", block_hash, block.header.nonce);
125
126 JsonResponse::new(JsonValue::Number(block.header.nonce as f64), id).into()
128 }
129
130 async fn abort_pending(&self, id: u16) -> Option<JsonResult> {
132 info!(target: "minerd::rpc", "Checking if a pending request is being processed...");
134 if self.stop_signal.receiver_count() <= 1 {
135 info!(target: "minerd::rpc", "No pending requests!");
136 return None
137 }
138
139 info!(target: "minerd::rpc", "Pending request is in progress, sending stop signal...");
140 if self.sender.send(()).await.is_err() {
142 error!(target: "minerd::rpc", "Failed to stop pending request");
143 return Some(server_error(RpcError::StopFailed, id, None))
144 }
145
146 info!(target: "minerd::rpc", "Waiting for request to terminate...");
148 while self.stop_signal.receiver_count() > 1 {
149 sleep(1).await;
150 }
151 info!(target: "minerd::rpc", "Pending request terminated!");
152
153 if self.stop_signal.recv().await.is_err() {
155 error!(target: "minerd::rpc", "Failed to cleanup stop signal channel");
156 return Some(server_error(RpcError::StopFailed, id, None))
157 }
158
159 None
160 }
161}