diff --git a/servers/src/mining/stratumserver.rs b/servers/src/mining/stratumserver.rs index f5e4f91bf..4da04a2d9 100644 --- a/servers/src/mining/stratumserver.rs +++ b/servers/src/mining/stratumserver.rs @@ -15,6 +15,7 @@ //! Mining Stratum Server use bufstream::BufStream; use serde_json; +use serde_json::Value; use std::error::Error; use std::io::{BufRead, ErrorKind, Write}; use std::net::{TcpListener, TcpStream}; @@ -47,7 +48,7 @@ struct RpcRequest { id: String, jsonrpc: String, method: String, - params: Option, + params: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -55,8 +56,8 @@ struct RpcResponse { id: String, jsonrpc: String, method: String, - result: Option, - error: Option, + result: Option, + error: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -149,6 +150,7 @@ fn accept_workers( pub struct Worker { id: String, + agent: String, login: Option, stream: BufStream, error: bool, @@ -160,6 +162,7 @@ impl Worker { pub fn new(id: String, stream: BufStream) -> Worker { Worker { id: id, + agent: String::from(""), login: None, stream: stream, error: false, @@ -322,9 +325,11 @@ impl StratumServer { "keepalive" => self.handle_keepalive(), "getjobtemplate" => { if self.currently_syncing.load(Ordering::Relaxed) { - let e = r#"{"code": -32701, "message": "Node is syncing - Please wait"}"#; - let err = e.to_string(); - (err, true) + let e = RpcError { + code: -32701, + message: "Node is syncing - Please wait".to_string(), + }; + (serde_json::to_value(e).unwrap(), true) } else { let b = self.current_block.header.clone(); self.handle_getjobtemplate(b) @@ -335,22 +340,23 @@ impl StratumServer { } _ => { // Called undefined method - let e = r#"{"code": -32601, "message": "Method not found"}"#; - let err = e.to_string(); - (err, true) + let e = RpcError { + code: -32601, + message: "Method not found".to_string(), + }; + (serde_json::to_value(e).unwrap(), true) } }; // Package the reply as RpcResponse json let rpc_response: String; if err == true { - let rpc_err: RpcError = serde_json::from_str(&response).unwrap(); let resp = RpcResponse { id: workers_l[num].id.clone(), jsonrpc: String::from("2.0"), method: request.method, result: None, - error: Some(rpc_err), + error: Some(response), }; rpc_response = serde_json::to_string(&resp).unwrap(); } else { @@ -373,7 +379,7 @@ impl StratumServer { } // Handle STATUS message - fn handle_status(&self, worker_stats: &WorkerStats) -> (String, bool) { + fn handle_status(&self, worker_stats: &WorkerStats) -> (Value, bool) { // Return worker status in json for use by a dashboard or healthcheck. let status = WorkerStatus { id: worker_stats.id.clone(), @@ -383,74 +389,72 @@ impl StratumServer { rejected: worker_stats.num_rejected, stale: worker_stats.num_stale, }; - let response = serde_json::to_string(&status).unwrap(); + let response = serde_json::to_value(&status).unwrap(); return (response, false); } // Handle GETJOBTEMPLATE message - fn handle_getjobtemplate(&self, bh: BlockHeader) -> (String, bool) { + fn handle_getjobtemplate(&self, bh: BlockHeader) -> (Value, bool) { // Build a JobTemplate from a BlockHeader and return JSON let job_template = self.build_block_template(bh); - let job_template_json = serde_json::to_string(&job_template).unwrap(); - return (job_template_json, false); + let response = serde_json::to_value(&job_template).unwrap(); + return (response, false); } // Handle KEEPALIVE message - fn handle_keepalive(&self) -> (String, bool) { - return (String::from("ok"), false); + fn handle_keepalive(&self) -> (Value, bool) { + return (serde_json::to_value("ok".to_string()).unwrap(), false); } // Handle LOGIN message - fn handle_login(&self, params: Option, worker: &mut Worker) -> (String, bool) { - // Extract the params string into a LoginParams struct - let params_str = match params { - Some(val) => val, - None => String::from("{}"), - }; - let login_params: LoginParams = match serde_json::from_str(¶ms_str) { - Ok(val) => val, - Err(_e) => { - let r = r#"{"code": -32600, "message": "Invalid Request"}"#; - return (String::from(r), true); + fn handle_login(&self, params: Option, worker: &mut Worker) -> (Value, bool) { + let params: LoginParams = match params { + Some(val) => serde_json::from_value(val).unwrap(), + None => { + let e = RpcError { + code: -32600, + message: "Invalid Request".to_string(), + }; + return (serde_json::to_value(e).unwrap(), true); } }; - worker.login = Some(login_params.login); - // XXX TODO Future? - Validate login and password + worker.login = Some(params.login); + // XXX TODO Future - Validate password? + worker.agent = params.agent; worker.authenticated = true; - return (String::from("ok"), false); + return (serde_json::to_value("ok".to_string()).unwrap(), false); } // Handle SUBMIT message - // params contains a solved block header + // params contains a solved block header // We accept and log valid shares of all difficulty above configured minimum // Accepted shares that are full solutions will also be submitted to the // network fn handle_submit( &self, - params: Option, + params: Option, worker: &mut Worker, worker_stats: &mut WorkerStats, - ) -> (String, bool) { - // Extract the params string into a SubmitParams struct - let params_str = match params { - Some(val) => val, - None => String::from("{}"), - }; - let submit_params: SubmitParams = match serde_json::from_str(¶ms_str) { - Ok(val) => val, - Err(_e) => { - let r = r#"{"code": -32600, "message": "Invalid Request"}"#; - return (String::from(r), true); + ) -> (Value, bool) { + // Validate parameters + let params: SubmitParams = match params { + Some(val) => serde_json::from_value(val).unwrap(), + None => { + let e = RpcError { + code: -32600, + message: "Invalid Request".to_string(), + }; + return (serde_json::to_value(e).unwrap(), true); } }; let mut b: Block; let share_difficulty: u64; - if submit_params.height == self.current_block.header.height { + if params.height == self.current_block.header.height { // Reconstruct the block header with this nonce and pow added b = self.current_block.clone(); - b.header.nonce = submit_params.nonce; - b.header.pow.nonces = submit_params.pow; + b.header.nonce = params.nonce; + b.header.pow.nonces = params.pow; // Get share difficulty share_difficulty = b.header.pow.to_difficulty().to_num(); // If the difficulty is too low its an error @@ -464,9 +468,11 @@ impl StratumServer { self.minimum_share_difficulty, ); worker_stats.num_rejected += 1; - let e = r#"{"code": -32501, "message": "Share rejected due to low difficulty"}"#; - let err = e.to_string(); - return (err, true); + let e = RpcError { + code: -32501, + message: "Share rejected due to low difficulty".to_string(), + }; + return (serde_json::to_value(e).unwrap(), true); } // If the difficulty is high enough, submit it (which also validates it) if share_difficulty >= self.current_difficulty { @@ -477,13 +483,15 @@ impl StratumServer { LOGGER, "(Server ID: {}) Failed to validate solution at height {}: {:?}", self.id, - submit_params.height, + params.height, e ); worker_stats.num_rejected += 1; - let e = r#"{"code": -32502, "message": "Failed to validate solution"}"#; - let err = e.to_string(); - return (err, true); + let e = RpcError { + code: -32502, + message: "Failed to validate solution".to_string(), + }; + return (serde_json::to_value(e).unwrap(), true); } // Success case falls through to be logged } else { @@ -495,27 +503,29 @@ impl StratumServer { LOGGER, "(Server ID: {}) Failed to validate share at height {} with nonce {}", self.id, - submit_params.height, + params.height, b.header.nonce ); worker_stats.num_rejected += 1; - let e = r#"{"code": -32502, "message": "Failed to validate share"}"#; - let err = e.to_string(); - return (err, true); + let e = RpcError { + code: -32502, + message: "Failed to validate solution".to_string(), + }; + return (serde_json::to_value(e).unwrap(), true); } } } else { // Return error status error!( LOGGER, - "(Server ID: {}) Share at height {} submitted too late", - self.id, - submit_params.height + "(Server ID: {}) Share at height {} submitted too late", self.id, params.height, ); worker_stats.num_stale += 1; - let e = r#"{"code": -32503, "message": "Solution submitted too late"}"#; - let err = e.to_string(); - return (err, true); + let e = RpcError { + code: -32503, + message: "Solution submitted too late".to_string(), + }; + return (serde_json::to_value(e).unwrap(), true); } // Log this as a valid share let submitted_by = match worker.login.clone() { @@ -534,7 +544,7 @@ impl StratumServer { submitted_by, ); worker_stats.num_accepted += 1; - return (String::from("ok"), false); + return (serde_json::to_value("ok".to_string()).unwrap(), false); } // handle submit a solution // Purge dead/sick workers - remove all workers marked in error state @@ -585,11 +595,13 @@ impl StratumServer { // Package new block into RpcRequest let job_template = self.build_block_template(self.current_block.header.clone()); let job_template_json = serde_json::to_string(&job_template).unwrap(); + // Issue #1159 - use a serde_json Value type to avoid extra quoting + let job_template_value: Value = serde_json::from_str(&job_template_json).unwrap(); let job_request = RpcRequest { id: String::from("Stratum"), jsonrpc: String::from("2.0"), method: String::from("job"), - params: Some(job_template_json), + params: Some(job_template_value), }; let job_request_json = serde_json::to_string(&job_request).unwrap();