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