rpc.rs
1 /* This file is part of DarkFi (https://dark.fi) 2 * 3 * Copyright (C) 2020-2025 Dyne.org foundation 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation, either version 3 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19 use std::collections::HashSet; 20 21 use num_bigint::BigUint; 22 use smol::lock::MutexGuard; 23 use tracing::{debug, error, info}; 24 25 use 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 }; 36 use darkfi_sdk::num_traits::Num; 37 use darkfi_serial::{async_trait, deserialize_async}; 38 39 use crate::{ 40 error::{server_error, RpcError}, 41 MinerNode, 42 }; 43 44 #[async_trait] 45 impl 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 62 impl MinerNode { 63 // RPCAPI: 64 // Signals miner daemon to abort mining pending request. 65 // Returns `true` on success. 66 // 67 // --> {"jsonrpc": "2.0", "method": "abort", "params": [], "id": 42} 68 // <-- {"jsonrpc": "2.0", "result": "true", "id": 42} 69 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 // RPCAPI: 77 // Mine provided block for requested mine target, and return the corresponding nonce value. 78 // 79 // --> {"jsonrpc": "2.0", "method": "mine", "params": ["target", "block"], "id": 42} 80 // --> {"jsonrpc": "2.0", "result": "nonce", "id": 42} 81 async fn mine(&self, id: u16, params: JsonValue) -> JsonResult { 82 // Verify parameters 83 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 // Parse parameters 92 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 {block_hash} for target: {target}"); 106 107 // If we have a requested mining height, we'll keep dropping here. 108 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 // Check if another request is being processed 114 if let Some(e) = self.abort_pending(id).await { 115 return e 116 }; 117 118 // Mine provided block 119 info!(target: "minerd::rpc", "Mining block {block_hash} for target: {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 {block_hash} with error: {e}"); 122 return server_error(RpcError::MiningFailed, id, None) 123 } 124 info!(target: "minerd::rpc", "Mined block {block_hash} with nonce: {}", block.header.nonce); 125 126 // Return block nonce 127 JsonResponse::new(JsonValue::Number(block.header.nonce as f64), id).into() 128 } 129 130 /// Auxiliary function to abort pending request. 131 async fn abort_pending(&self, id: u16) -> Option<JsonResult> { 132 // Check if a pending request is being processed 133 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 // Send stop signal to worker 141 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 // Wait for worker to terminate 147 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 // Consume channel item so its empty again 154 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 }