Skip to content

Add error codes to each stratum API #526

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions core/src/miner/stratum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use std::net::{AddrParseError, SocketAddr};
use std::sync::Arc;

use super::super::error::Error as MinerError;
use cstratum::{Error as StratumServiceError, JobDispatcher, PushWorkHandler, Stratum as StratumService};
use primitives::{Bytes, H256, U256};

Expand Down Expand Up @@ -56,14 +57,14 @@ impl JobDispatcher for StratumJobDispatcher {

if !self.miner.can_produce_work_package() {
cwarn!(STRATUM, "Cannot get work package - engine seals internally.");
return Err(StratumServiceError::NoWork)
return Err(StratumServiceError::InternalError)
}

match self.miner.submit_seal(&*self.client, pow_hash, seal) {
Ok(_) => Ok(()),
Err(e) => {
cwarn!(STRATUM, "submit_seal error: {:?}", e);
Err(StratumServiceError::Dispatch(e.to_string()))
Err(StratumServiceError::from(e))
}
}
}
Expand Down Expand Up @@ -98,6 +99,16 @@ pub enum Error {
Address(AddrParseError),
}

impl From<MinerError> for StratumServiceError {
fn from(err: MinerError) -> Self {
match err {
MinerError::PowHashInvalid => StratumServiceError::PowHashInvalid,
MinerError::PowInvalid => StratumServiceError::PowInvalid,
_ => StratumServiceError::InternalError,
}
}
}

impl From<StratumServiceError> for Error {
fn from(service_err: StratumServiceError) -> Error {
Error::Service(service_err)
Expand Down
21 changes: 9 additions & 12 deletions spec/Stratum.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Params:
1. powHash: `string`
2. seal: `string[]`

Return Type: `bool`
Return Type: `null`

Request Example
```
Expand All @@ -118,27 +118,24 @@ Response Example
{
"jsonrpc": "2.0",
"id": 4,
"result": true,
"result": null,
"error": null
}
```

## Exception Handling (DRAFT)
## Exception Handling
Stratum defines simple exception handling. Example of a rejected share looks like:
```
{
"jsonrpc": "2.0",
"id": 5,
"result": null,
"error": (21, "Job not found", null)
"error": {"code":21, "message":"Invalid Pow hash"}
}
```

Where the error field is defined as (error_code, human_readable_message, traceback). Traceback may contain additional information about debugging errors.
Where the error field is defined as (error_code, human_readable_message).
Proposed error codes for mining services are:
* 20 - Other/Unknown
* 21 - Job not found (=stale)
* 22 - Duplicate share
* 23 - Low target share
* 24 - Unauthorized worker
* 25 - Not subscribed
* 20 - Internal Error
* 21 - Invalid Pow hash (=stale)
* 22 - Invalid the nonce
* 23 - Unauthorized worker
64 changes: 48 additions & 16 deletions stratum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,24 @@ impl StratumImpl {

/// rpc method `mining.submit`
fn submit(&self, params: Params, meta: SocketMetadata) -> RpcResult {
params
.parse::<(H256, Vec<String>)>()
.map(|(pow_hash, seal)| {
let seal = seal.iter().cloned().map(Into::into).collect();
match self.dispatcher.submit((pow_hash, seal)) {
Ok(()) => {
self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized"));
to_value(true)
}
Err(submit_err) => {
cwarn!(STRATUM, "Error while submitting share: {:?}", submit_err);
to_value(false)
}
let workers = self.workers.read();
if workers.contains_key(&meta.addr) == false {
return Err(Error::UnauthorizedWorker.into())
}

params.parse::<(H256, Vec<String>)>().and_then(|(pow_hash, seal)| {
let seal = seal.iter().cloned().map(Into::into).collect();
match self.dispatcher.submit((pow_hash, seal)) {
Ok(()) => {
self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized"));
Ok(jsonrpc_core::Value::Null)
}
})
.map(|v| v.expect("Only true/false is returned and it's always serializable"))
Err(submit_err) => {
cwarn!(STRATUM, "Error while submitting share: {:?}", submit_err);
Err(submit_err.into())
}
}
})
}

/// Helper method
Expand Down Expand Up @@ -524,6 +526,36 @@ mod tests {

let response = String::from_utf8(core.run(stream).expect("Core should run with no errors"))
.expect("Response should be utf-8");
assert_eq!("{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":2}\n", response);
assert_eq!("{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":2}\n", response);
}

#[test]
fn should_return_error_when_unauthorized_worker_submits() {
let addr = SocketAddr::from_str("127.0.0.1:19991").unwrap();
let _stratum =
Stratum::start(&addr, Arc::new(DummyManager::build().of_initial(r#"["dummy authorize payload"]"#)), None)
.expect("There should be no error starting stratum");

let mut submit_request =
r#"{"jsonrpc": "2.0", "method": "mining.submit", "params": ["0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", ["0x56642f04d519ae3262c7ba6facf1c5b11450ebaeb7955337cfbc45420d573077"]], "id": 2}"#.as_bytes()
.to_vec();
submit_request.extend(b"\n");

let mut core = Core::new().expect("Tokio Core should be created with no errors");
let mut buffer = vec![0u8; 2048];
let stream = TcpStream::connect(&addr, &core.handle())
.and_then(|stream| io::write_all(stream, &submit_request))
.and_then(|(stream, _)| io::read(stream, &mut buffer))
.and_then(|(_, read_buf, len)| {
ctrace!(STRATUM, "Received result from server");
future::ok(read_buf[0..len].to_vec())
});

let response = String::from_utf8(core.run(stream).expect("Core should run with no errors"))
.expect("Response should be utf-8");
assert_eq!(
"{\"jsonrpc\":\"2.0\",\"error\":{\"code\":23,\"message\":\"Unauthorized worker\"},\"id\":2}\n",
response
);
}
}
23 changes: 22 additions & 1 deletion stratum/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@
use std;
use std::error::Error as StdError;

use jsonrpc_core::{Error as JsonError, ErrorCode as JsonErrorCode};
use jsonrpc_tcp_server::PushMessageError;
use primitives::{Bytes, H256};

#[derive(Debug, Clone)]
pub enum Error {
InternalError,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove Dispatch error message which is no longer used.

PowHashInvalid,
PowInvalid,
UnauthorizedWorker,
NoWork,
NoWorkers,
Io(String),
Tcp(String),
Dispatch(String),
}

impl From<std::io::Error> for Error {
Expand All @@ -41,6 +45,23 @@ impl From<PushMessageError> for Error {
}
}

impl Into<JsonError> for Error {
fn into(self) -> JsonError {
let (code, message) = match self {
Error::PowHashInvalid => (21, format!("Invalid Pow hash")),
Error::PowInvalid => (22, format!("Invalid the nonce")),
Error::UnauthorizedWorker => (23, format!("Unauthorized worker")),
_ => (20, format!("Internal error")),
};

JsonError {
code: JsonErrorCode::ServerError(code),
message,
data: None,
}
}
}

/// Interface that can provide pow/blockchain-specific responses for the clients
pub trait JobDispatcher: Send + Sync {
// json for initial client handshake
Expand Down