Skip to content

Commit 87365d5

Browse files
committed
Add error codes to each stratum API
1 parent e0d3426 commit 87365d5

File tree

4 files changed

+92
-30
lines changed

4 files changed

+92
-30
lines changed

core/src/miner/stratum.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use std::net::{AddrParseError, SocketAddr};
2020
use std::sync::Arc;
2121

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

@@ -56,14 +57,14 @@ impl JobDispatcher for StratumJobDispatcher {
5657

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

6263
match self.miner.submit_seal(&*self.client, pow_hash, seal) {
6364
Ok(_) => Ok(()),
6465
Err(e) => {
6566
cwarn!(STRATUM, "submit_seal error: {:?}", e);
66-
Err(StratumServiceError::Dispatch(e.to_string()))
67+
Err(StratumServiceError::from(e))
6768
}
6869
}
6970
}
@@ -98,6 +99,16 @@ pub enum Error {
9899
Address(AddrParseError),
99100
}
100101

102+
impl From<MinerError> for StratumServiceError {
103+
fn from(err: MinerError) -> Self {
104+
match err {
105+
MinerError::PowHashInvalid => StratumServiceError::PowHashInvalid,
106+
MinerError::PowInvalid => StratumServiceError::PowInvalid,
107+
_ => StratumServiceError::InternalError,
108+
}
109+
}
110+
}
111+
101112
impl From<StratumServiceError> for Error {
102113
fn from(service_err: StratumServiceError) -> Error {
103114
Error::Service(service_err)

spec/Stratum.md

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Params:
101101
1. powHash: `string`
102102
2. seal: `string[]`
103103

104-
Return Type: `bool`
104+
Return Type: `null`
105105

106106
Request Example
107107
```
@@ -118,27 +118,24 @@ Response Example
118118
{
119119
"jsonrpc": "2.0",
120120
"id": 4,
121-
"result": true,
121+
"result": null,
122122
"error": null
123123
}
124124
```
125125

126-
## Exception Handling (DRAFT)
126+
## Exception Handling
127127
Stratum defines simple exception handling. Example of a rejected share looks like:
128128
```
129129
{
130130
"jsonrpc": "2.0",
131131
"id": 5,
132-
"result": null,
133-
"error": (21, "Job not found", null)
132+
"error": {"code":21, "message":"Invalid Pow hash"}
134133
}
135134
```
136135

137-
Where the error field is defined as (error_code, human_readable_message, traceback). Traceback may contain additional information about debugging errors.
136+
Where the error field is defined as (error_code, human_readable_message).
138137
Proposed error codes for mining services are:
139-
* 20 - Other/Unknown
140-
* 21 - Job not found (=stale)
141-
* 22 - Duplicate share
142-
* 23 - Low target share
143-
* 24 - Unauthorized worker
144-
* 25 - Not subscribed
138+
* 20 - Internal Error
139+
* 21 - Invalid Pow hash (=stale)
140+
* 22 - Invalid the nonce
141+
* 23 - Unauthorized worker

stratum/src/lib.rs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -181,22 +181,24 @@ impl StratumImpl {
181181

182182
/// rpc method `mining.submit`
183183
fn submit(&self, params: Params, meta: SocketMetadata) -> RpcResult {
184-
params
185-
.parse::<(H256, Vec<String>)>()
186-
.map(|(pow_hash, seal)| {
187-
let seal = seal.iter().cloned().map(Into::into).collect();
188-
match self.dispatcher.submit((pow_hash, seal)) {
189-
Ok(()) => {
190-
self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized"));
191-
to_value(true)
192-
}
193-
Err(submit_err) => {
194-
cwarn!(STRATUM, "Error while submitting share: {:?}", submit_err);
195-
to_value(false)
196-
}
184+
let workers = self.workers.read();
185+
if workers.contains_key(&meta.addr) == false {
186+
return Err(Error::UnauthorizedWorker.into())
187+
}
188+
189+
params.parse::<(H256, Vec<String>)>().and_then(|(pow_hash, seal)| {
190+
let seal = seal.iter().cloned().map(Into::into).collect();
191+
match self.dispatcher.submit((pow_hash, seal)) {
192+
Ok(()) => {
193+
self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized"));
194+
Ok(jsonrpc_core::Value::Null)
197195
}
198-
})
199-
.map(|v| v.expect("Only true/false is returned and it's always serializable"))
196+
Err(submit_err) => {
197+
cwarn!(STRATUM, "Error while submitting share: {:?}", submit_err);
198+
Err(submit_err.into())
199+
}
200+
}
201+
})
200202
}
201203

202204
/// Helper method
@@ -524,6 +526,36 @@ mod tests {
524526

525527
let response = String::from_utf8(core.run(stream).expect("Core should run with no errors"))
526528
.expect("Response should be utf-8");
527-
assert_eq!("{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":2}\n", response);
529+
assert_eq!("{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":2}\n", response);
530+
}
531+
532+
#[test]
533+
fn should_return_error_when_unauthorized_worker_submits() {
534+
let addr = SocketAddr::from_str("127.0.0.1:19991").unwrap();
535+
let _stratum =
536+
Stratum::start(&addr, Arc::new(DummyManager::build().of_initial(r#"["dummy authorize payload"]"#)), None)
537+
.expect("There should be no error starting stratum");
538+
539+
let mut submit_request =
540+
r#"{"jsonrpc": "2.0", "method": "mining.submit", "params": ["0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", ["0x56642f04d519ae3262c7ba6facf1c5b11450ebaeb7955337cfbc45420d573077"]], "id": 2}"#.as_bytes()
541+
.to_vec();
542+
submit_request.extend(b"\n");
543+
544+
let mut core = Core::new().expect("Tokio Core should be created with no errors");
545+
let mut buffer = vec![0u8; 2048];
546+
let stream = TcpStream::connect(&addr, &core.handle())
547+
.and_then(|stream| io::write_all(stream, &submit_request))
548+
.and_then(|(stream, _)| io::read(stream, &mut buffer))
549+
.and_then(|(_, read_buf, len)| {
550+
ctrace!(STRATUM, "Received result from server");
551+
future::ok(read_buf[0..len].to_vec())
552+
});
553+
554+
let response = String::from_utf8(core.run(stream).expect("Core should run with no errors"))
555+
.expect("Response should be utf-8");
556+
assert_eq!(
557+
"{\"jsonrpc\":\"2.0\",\"error\":{\"code\":23,\"message\":\"Unauthorized worker\"},\"id\":2}\n",
558+
response
559+
);
528560
}
529561
}

stratum/src/traits.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@
1717
use std;
1818
use std::error::Error as StdError;
1919

20+
use jsonrpc_core::{Error as JsonError, ErrorCode as JsonErrorCode};
2021
use jsonrpc_tcp_server::PushMessageError;
2122
use primitives::{Bytes, H256};
2223

2324
#[derive(Debug, Clone)]
2425
pub enum Error {
26+
InternalError,
27+
PowHashInvalid,
28+
PowInvalid,
29+
UnauthorizedWorker,
2530
NoWork,
2631
NoWorkers,
2732
Io(String),
@@ -41,6 +46,23 @@ impl From<PushMessageError> for Error {
4146
}
4247
}
4348

49+
impl Into<JsonError> for Error {
50+
fn into(self) -> JsonError {
51+
let (code, message) = match self {
52+
Error::PowHashInvalid => (21, format!("Invalid Pow hash")),
53+
Error::PowInvalid => (22, format!("Invalid the nonce")),
54+
Error::UnauthorizedWorker => (23, format!("Unauthorized worker")),
55+
_ => (20, format!("Internal error")),
56+
};
57+
58+
JsonError {
59+
code: JsonErrorCode::ServerError(code),
60+
message,
61+
data: None,
62+
}
63+
}
64+
}
65+
4466
/// Interface that can provide pow/blockchain-specific responses for the clients
4567
pub trait JobDispatcher: Send + Sync {
4668
// json for initial client handshake

0 commit comments

Comments
 (0)