Move a bunch of stuff around (#10101)
* Move devtools. * Merge stop_guard & rename memzero * Move price-info to miner. * Group account management * Clean up workspace members. * Move local store closer to miner. * Move clib examples. * Move registrar and hash-fetch * Move rpc_cli/rpc_client * Move stratum closer to miner. * Fix naming convention of crates. * Update Cpp examples path. * Fix paths for clib-example. * Fix removing build.
This commit is contained in:
@@ -25,16 +25,16 @@ error-chain = { version = "0.12", default-features = false }
|
||||
ethcore-io = { path = "../util/io" }
|
||||
ethcore-logger = { path = "../logger" }
|
||||
ethcore-miner = { path = "../miner" }
|
||||
ethcore-stratum = { path = "./stratum", optional = true }
|
||||
ethcore-stratum = { path = "../miner/stratum", optional = true }
|
||||
ethcore-transaction = { path = "./transaction" }
|
||||
ethereum-types = "0.4"
|
||||
memory-cache = { path = "../util/memory_cache" }
|
||||
memory-cache = { path = "../util/memory-cache" }
|
||||
ethabi = "6.0"
|
||||
ethabi-derive = "6.0"
|
||||
ethabi-contract = "6.0"
|
||||
ethjson = { path = "../json" }
|
||||
ethkey = { path = "../ethkey" }
|
||||
ethstore = { path = "../ethstore" }
|
||||
ethkey = { path = "../accounts/ethkey" }
|
||||
ethstore = { path = "../accounts/ethstore" }
|
||||
evm = { path = "evm" }
|
||||
heapsize = "0.4"
|
||||
itertools = "0.5"
|
||||
@@ -48,17 +48,16 @@ parking_lot = "0.7"
|
||||
rayon = "1.0"
|
||||
rand = "0.4"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_compress = { path = "../util/rlp_compress" }
|
||||
rlp_derive = { path = "../util/rlp_derive" }
|
||||
rlp_compress = { path = "../util/rlp-compress" }
|
||||
rlp_derive = { path = "../util/rlp-derive" }
|
||||
kvdb = "0.1"
|
||||
kvdb-memorydb = "0.1"
|
||||
parity-snappy = "0.1"
|
||||
stop-guard = { path = "../util/stop-guard" }
|
||||
macros = { path = "../util/macros" }
|
||||
rustc-hex = "1.0"
|
||||
stats = { path = "../util/stats" }
|
||||
trace-time = "0.1"
|
||||
using_queue = { path = "../util/using_queue" }
|
||||
using_queue = { path = "../miner/using-queue" }
|
||||
vm = { path = "vm" }
|
||||
wasm = { path = "wasm" }
|
||||
keccak-hash = "0.1"
|
||||
@@ -73,10 +72,10 @@ tempdir = {version="0.3", optional = true}
|
||||
len-caching-lock = { path = "../util/len-caching-lock" }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))'.dependencies]
|
||||
hardware-wallet = { path = "../hw" }
|
||||
hardware-wallet = { path = "../accounts/hw" }
|
||||
|
||||
[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android")))'.dependencies]
|
||||
fake-hardware-wallet = { path = "../util/fake-hardware-wallet" }
|
||||
fake-hardware-wallet = { path = "../accounts/fake-hardware-wallet" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.4"
|
||||
|
||||
@@ -13,7 +13,7 @@ log = "0.4"
|
||||
vm = { path = "../vm" }
|
||||
keccak-hash = "0.1"
|
||||
parking_lot = "0.7"
|
||||
memory-cache = { path = "../../util/memory_cache" }
|
||||
memory-cache = { path = "../../util/memory-cache" }
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-hex = "1.0"
|
||||
|
||||
@@ -23,7 +23,7 @@ vm = { path = "../vm" }
|
||||
fastmap = { path = "../../util/fastmap" }
|
||||
failsafe = { version = "0.3.0", default-features = false, features = ["parking_lot_mutex"] }
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_derive = { path = "../../util/rlp_derive" }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
smallvec = "0.6"
|
||||
futures = "0.1"
|
||||
rand = "0.4"
|
||||
@@ -37,7 +37,7 @@ keccak-hash = "0.1"
|
||||
keccak-hasher = { path = "../../util/keccak-hasher" }
|
||||
triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" }
|
||||
kvdb = "0.1"
|
||||
memory-cache = { path = "../../util/memory_cache" }
|
||||
memory-cache = { path = "../../util/memory-cache" }
|
||||
error-chain = { version = "0.12", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -19,7 +19,7 @@ ethcore-miner = { path = "../../miner" }
|
||||
ethcore-transaction = { path = "../transaction" }
|
||||
ethereum-types = "0.4"
|
||||
ethjson = { path = "../../json" }
|
||||
ethkey = { path = "../../ethkey" }
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
fetch = { path = "../../util/fetch" }
|
||||
futures = "0.1"
|
||||
heapsize = "0.4"
|
||||
@@ -30,7 +30,7 @@ patricia-trie = "0.3.0"
|
||||
patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" }
|
||||
rand = "0.3"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_derive = { path = "../../util/rlp_derive" }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
|
||||
@@ -13,7 +13,6 @@ ethcore-sync = { path = "../sync" }
|
||||
ethereum-types = "0.4"
|
||||
kvdb = "0.1"
|
||||
log = "0.4"
|
||||
stop-guard = { path = "../../util/stop-guard" }
|
||||
trace-time = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -21,7 +21,6 @@ extern crate ethcore_private_tx;
|
||||
extern crate ethcore_sync as sync;
|
||||
extern crate ethereum_types;
|
||||
extern crate kvdb;
|
||||
extern crate stop_guard;
|
||||
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
@@ -37,6 +36,7 @@ extern crate tempdir;
|
||||
|
||||
mod error;
|
||||
mod service;
|
||||
mod stop_guard;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate kvdb_rocksdb;
|
||||
|
||||
40
ethcore/service/src/stop_guard.rs
Normal file
40
ethcore/service/src/stop_guard.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Stop guard mod
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::*;
|
||||
|
||||
/// Stop guard that will set a stop flag on drop
|
||||
pub struct StopGuard {
|
||||
flag: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl StopGuard {
|
||||
/// Create a stop guard
|
||||
pub fn new() -> StopGuard {
|
||||
StopGuard {
|
||||
flag: Arc::new(AtomicBool::new(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StopGuard {
|
||||
fn drop(&mut self) {
|
||||
self.flag.store(true, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,6 @@ extern crate parity_snappy as snappy;
|
||||
extern crate ethabi;
|
||||
extern crate rustc_hex;
|
||||
extern crate stats;
|
||||
extern crate stop_guard;
|
||||
extern crate using_queue;
|
||||
extern crate vm;
|
||||
extern crate wasm;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
description = "Ethcore stratum lib"
|
||||
name = "ethcore-stratum"
|
||||
version = "1.12.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethereum-types = "0.4"
|
||||
keccak-hash = "0.1"
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
|
||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
|
||||
jsonrpc-tcp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-2.2" }
|
||||
log = "0.4"
|
||||
parking_lot = "0.7"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.5"
|
||||
tokio = "0.1"
|
||||
tokio-io = "0.1"
|
||||
ethcore-logger = { path = "../../logger" }
|
||||
@@ -1,504 +0,0 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Stratum protocol implementation for parity ethereum/bitcoin clients
|
||||
|
||||
extern crate jsonrpc_tcp_server;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_macros;
|
||||
extern crate ethereum_types;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate parking_lot;
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
#[cfg(test)] extern crate tokio;
|
||||
#[cfg(test)] extern crate tokio_io;
|
||||
#[cfg(test)] extern crate ethcore_logger;
|
||||
|
||||
mod traits;
|
||||
|
||||
pub use traits::{
|
||||
JobDispatcher, PushWorkHandler, Error, ServiceConfiguration,
|
||||
};
|
||||
|
||||
use jsonrpc_tcp_server::{
|
||||
Server as JsonRpcServer, ServerBuilder as JsonRpcServerBuilder,
|
||||
RequestContext, MetaExtractor, Dispatcher, PushMessageError,
|
||||
};
|
||||
use jsonrpc_core::{MetaIoHandler, Params, to_value, Value, Metadata, Compatibility};
|
||||
use jsonrpc_macros::IoDelegate;
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use hash::keccak;
|
||||
use ethereum_types::H256;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
type RpcResult = Result<jsonrpc_core::Value, jsonrpc_core::Error>;
|
||||
|
||||
const NOTIFY_COUNTER_INITIAL: u32 = 16;
|
||||
|
||||
/// Container which owns rpc server and stratum implementation
|
||||
pub struct Stratum {
|
||||
/// RPC server
|
||||
///
|
||||
/// It is an `Option` so it can be easily closed and released during `drop` phase
|
||||
rpc_server: Option<JsonRpcServer>,
|
||||
/// stratum protocol implementation
|
||||
///
|
||||
/// It is owned by a container and rpc server
|
||||
implementation: Arc<StratumImpl>,
|
||||
/// Message dispatcher (tcp/ip service)
|
||||
///
|
||||
/// Used to push messages to peers
|
||||
tcp_dispatcher: Dispatcher,
|
||||
}
|
||||
|
||||
impl Stratum {
|
||||
pub fn start(
|
||||
addr: &SocketAddr,
|
||||
dispatcher: Arc<JobDispatcher>,
|
||||
secret: Option<H256>,
|
||||
) -> Result<Arc<Stratum>, Error> {
|
||||
|
||||
let implementation = Arc::new(StratumImpl {
|
||||
subscribers: RwLock::default(),
|
||||
job_que: RwLock::default(),
|
||||
dispatcher,
|
||||
workers: Arc::new(RwLock::default()),
|
||||
secret,
|
||||
notify_counter: RwLock::new(NOTIFY_COUNTER_INITIAL),
|
||||
});
|
||||
|
||||
let mut delegate = IoDelegate::<StratumImpl, SocketMetadata>::new(implementation.clone());
|
||||
delegate.add_method_with_meta("mining.subscribe", StratumImpl::subscribe);
|
||||
delegate.add_method_with_meta("mining.authorize", StratumImpl::authorize);
|
||||
delegate.add_method_with_meta("mining.submit", StratumImpl::submit);
|
||||
let mut handler = MetaIoHandler::<SocketMetadata>::with_compatibility(Compatibility::Both);
|
||||
handler.extend_with(delegate);
|
||||
|
||||
let server_builder = JsonRpcServerBuilder::new(handler);
|
||||
let tcp_dispatcher = server_builder.dispatcher();
|
||||
let server_builder = server_builder.session_meta_extractor(PeerMetaExtractor::new(tcp_dispatcher.clone()));
|
||||
let server = server_builder.start(addr)?;
|
||||
|
||||
let stratum = Arc::new(Stratum {
|
||||
rpc_server: Some(server),
|
||||
implementation,
|
||||
tcp_dispatcher,
|
||||
});
|
||||
|
||||
Ok(stratum)
|
||||
}
|
||||
}
|
||||
|
||||
impl PushWorkHandler for Stratum {
|
||||
fn push_work_all(&self, payload: String) -> Result<(), Error> {
|
||||
self.implementation.push_work_all(payload, &self.tcp_dispatcher)
|
||||
}
|
||||
|
||||
fn push_work(&self, payloads: Vec<String>) -> Result<(), Error> {
|
||||
self.implementation.push_work(payloads, &self.tcp_dispatcher)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Stratum {
|
||||
fn drop(&mut self) {
|
||||
// shut down rpc server
|
||||
self.rpc_server.take().map(|server| server.close());
|
||||
}
|
||||
}
|
||||
|
||||
struct StratumImpl {
|
||||
/// Subscribed clients
|
||||
subscribers: RwLock<Vec<SocketAddr>>,
|
||||
/// List of workers supposed to receive job update
|
||||
job_que: RwLock<HashSet<SocketAddr>>,
|
||||
/// Payload manager
|
||||
dispatcher: Arc<JobDispatcher>,
|
||||
/// Authorized workers (socket - worker_id)
|
||||
workers: Arc<RwLock<HashMap<SocketAddr, String>>>,
|
||||
/// Secret if any
|
||||
secret: Option<H256>,
|
||||
/// Dispatch notify couinter
|
||||
notify_counter: RwLock<u32>,
|
||||
}
|
||||
|
||||
impl StratumImpl {
|
||||
/// rpc method `mining.subscribe`
|
||||
fn subscribe(&self, _params: Params, meta: SocketMetadata) -> RpcResult {
|
||||
use std::str::FromStr;
|
||||
|
||||
self.subscribers.write().push(meta.addr().clone());
|
||||
self.job_que.write().insert(meta.addr().clone());
|
||||
trace!(target: "stratum", "Subscription request from {:?}", meta.addr());
|
||||
|
||||
Ok(match self.dispatcher.initial() {
|
||||
Some(initial) => match jsonrpc_core::Value::from_str(&initial) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(e) => {
|
||||
warn!(target: "stratum", "Invalid payload: '{}' ({:?})", &initial, e);
|
||||
to_value(&[0u8; 0])
|
||||
},
|
||||
},
|
||||
None => to_value(&[0u8; 0]),
|
||||
}.expect("Empty slices are serializable; qed"))
|
||||
}
|
||||
|
||||
/// rpc method `mining.authorize`
|
||||
fn authorize(&self, params: Params, meta: SocketMetadata) -> RpcResult {
|
||||
params.parse::<(String, String)>().map(|(worker_id, secret)|{
|
||||
if let Some(valid_secret) = self.secret {
|
||||
let hash = keccak(secret);
|
||||
if hash != valid_secret {
|
||||
return to_value(&false);
|
||||
}
|
||||
}
|
||||
trace!(target: "stratum", "New worker #{} registered", worker_id);
|
||||
self.workers.write().insert(meta.addr().clone(), worker_id);
|
||||
to_value(true)
|
||||
}).map(|v| v.expect("Only true/false is returned and it's always serializable; qed"))
|
||||
}
|
||||
|
||||
/// rpc method `mining.submit`
|
||||
fn submit(&self, params: Params, meta: SocketMetadata) -> RpcResult {
|
||||
Ok(match params {
|
||||
Params::Array(vals) => {
|
||||
// first two elements are service messages (worker_id & job_id)
|
||||
match self.dispatcher.submit(vals.iter().skip(2)
|
||||
.filter_map(|val| match *val {
|
||||
Value::String(ref s) => Some(s.to_owned()),
|
||||
_ => None
|
||||
})
|
||||
.collect::<Vec<String>>()) {
|
||||
Ok(()) => {
|
||||
self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized; qed"));
|
||||
to_value(true)
|
||||
},
|
||||
Err(submit_err) => {
|
||||
warn!("Error while submitting share: {:?}", submit_err);
|
||||
to_value(false)
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
trace!(target: "stratum", "Invalid submit work format {:?}", params);
|
||||
to_value(false)
|
||||
}
|
||||
}.expect("Only true/false is returned and it's always serializable; qed"))
|
||||
}
|
||||
|
||||
/// Helper method
|
||||
fn update_peers(&self, tcp_dispatcher: &Dispatcher) {
|
||||
if let Some(job) = self.dispatcher.job() {
|
||||
if let Err(e) = self.push_work_all(job, tcp_dispatcher) {
|
||||
warn!("Failed to update some of the peers: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_work_all(&self, payload: String, tcp_dispatcher: &Dispatcher) -> Result<(), Error> {
|
||||
let hup_peers = {
|
||||
let workers = self.workers.read();
|
||||
let next_request_id = {
|
||||
let mut counter = self.notify_counter.write();
|
||||
if *counter == ::std::u32::MAX { *counter = NOTIFY_COUNTER_INITIAL; }
|
||||
else { *counter = *counter + 1 }
|
||||
*counter
|
||||
};
|
||||
|
||||
let mut hup_peers = HashSet::with_capacity(0); // most of the cases won't be needed, hence avoid allocation
|
||||
let workers_msg = format!("{{ \"id\": {}, \"method\": \"mining.notify\", \"params\": {} }}", next_request_id, payload);
|
||||
trace!(target: "stratum", "pushing work for {} workers (payload: '{}')", workers.len(), &workers_msg);
|
||||
for (ref addr, _) in workers.iter() {
|
||||
trace!(target: "stratum", "pusing work to {}", addr);
|
||||
match tcp_dispatcher.push_message(addr, workers_msg.clone()) {
|
||||
Err(PushMessageError::NoSuchPeer) => {
|
||||
trace!(target: "stratum", "Worker no longer connected: {}", &addr);
|
||||
hup_peers.insert(*addr.clone());
|
||||
},
|
||||
Err(e) => {
|
||||
warn!(target: "stratum", "Unexpected transport error: {:?}", e);
|
||||
},
|
||||
Ok(_) => { },
|
||||
}
|
||||
}
|
||||
hup_peers
|
||||
};
|
||||
|
||||
if !hup_peers.is_empty() {
|
||||
let mut workers = self.workers.write();
|
||||
for hup_peer in hup_peers { workers.remove(&hup_peer); }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_work(&self, payloads: Vec<String>, tcp_dispatcher: &Dispatcher) -> Result<(), Error> {
|
||||
if !payloads.len() > 0 {
|
||||
return Err(Error::NoWork);
|
||||
}
|
||||
let workers = self.workers.read();
|
||||
let addrs = workers.keys().collect::<Vec<&SocketAddr>>();
|
||||
if !workers.len() > 0 {
|
||||
return Err(Error::NoWorkers);
|
||||
}
|
||||
let mut que = payloads;
|
||||
let mut addr_index = 0;
|
||||
while que.len() > 0 {
|
||||
let next_worker = addrs[addr_index];
|
||||
let mut next_payload = que.drain(0..1);
|
||||
tcp_dispatcher.push_message(
|
||||
next_worker,
|
||||
next_payload.nth(0).expect("drained successfully of 0..1, so 0-th element should exist")
|
||||
)?;
|
||||
addr_index = addr_index + 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SocketMetadata {
|
||||
addr: SocketAddr,
|
||||
// with the new version of jsonrpc-core, SocketMetadata
|
||||
// won't have to implement default, so this field will not
|
||||
// have to be an Option
|
||||
tcp_dispatcher: Option<Dispatcher>,
|
||||
}
|
||||
|
||||
impl Default for SocketMetadata {
|
||||
fn default() -> Self {
|
||||
SocketMetadata {
|
||||
addr: "0.0.0.0:0".parse().unwrap(),
|
||||
tcp_dispatcher: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SocketMetadata {
|
||||
pub fn addr(&self) -> &SocketAddr {
|
||||
&self.addr
|
||||
}
|
||||
}
|
||||
|
||||
impl Metadata for SocketMetadata { }
|
||||
|
||||
pub struct PeerMetaExtractor {
|
||||
tcp_dispatcher: Dispatcher,
|
||||
}
|
||||
|
||||
impl PeerMetaExtractor {
|
||||
fn new(tcp_dispatcher: Dispatcher) -> Self {
|
||||
PeerMetaExtractor {
|
||||
tcp_dispatcher,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MetaExtractor<SocketMetadata> for PeerMetaExtractor {
|
||||
fn extract(&self, context: &RequestContext) -> SocketMetadata {
|
||||
SocketMetadata {
|
||||
addr: context.peer_addr,
|
||||
tcp_dispatcher: Some(self.tcp_dispatcher.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::net::{SocketAddr, Shutdown};
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::{io, runtime::Runtime, timer::timeout::{self, Timeout}, net::TcpStream};
|
||||
use jsonrpc_core::futures::{Future, future};
|
||||
|
||||
use ethcore_logger::init_log;
|
||||
|
||||
pub struct VoidManager;
|
||||
|
||||
impl JobDispatcher for VoidManager {
|
||||
fn submit(&self, _payload: Vec<String>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn dummy_request(addr: &SocketAddr, data: &str) -> Vec<u8> {
|
||||
let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors");
|
||||
|
||||
let mut data_vec = data.as_bytes().to_vec();
|
||||
data_vec.extend(b"\n");
|
||||
|
||||
let stream = TcpStream::connect(addr)
|
||||
.and_then(move |stream| {
|
||||
io::write_all(stream, data_vec)
|
||||
})
|
||||
.and_then(|(stream, _)| {
|
||||
stream.shutdown(Shutdown::Write).unwrap();
|
||||
io::read_to_end(stream, Vec::with_capacity(2048))
|
||||
})
|
||||
.and_then(|(_stream, read_buf)| {
|
||||
future::ok(read_buf)
|
||||
});
|
||||
let result = runtime.block_on(stream).expect("Runtime should run with no errors");
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_started() {
|
||||
let stratum = Stratum::start(&"127.0.0.1:19980".parse().unwrap(), Arc::new(VoidManager), None);
|
||||
assert!(stratum.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn records_subscriber() {
|
||||
init_log();
|
||||
|
||||
let addr = "127.0.0.1:19985".parse().unwrap();
|
||||
let stratum = Stratum::start(&addr, Arc::new(VoidManager), None).unwrap();
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 1}"#;
|
||||
dummy_request(&addr, request);
|
||||
assert_eq!(1, stratum.implementation.subscribers.read().len());
|
||||
}
|
||||
|
||||
struct DummyManager {
|
||||
initial_payload: String
|
||||
}
|
||||
|
||||
impl DummyManager {
|
||||
fn new() -> Arc<DummyManager> {
|
||||
Arc::new(Self::build())
|
||||
}
|
||||
|
||||
fn build() -> DummyManager {
|
||||
DummyManager { initial_payload: r#"[ "dummy payload" ]"#.to_owned() }
|
||||
}
|
||||
|
||||
fn of_initial(mut self, new_initial: &str) -> DummyManager {
|
||||
self.initial_payload = new_initial.to_owned();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl JobDispatcher for DummyManager {
|
||||
fn initial(&self) -> Option<String> {
|
||||
Some(self.initial_payload.clone())
|
||||
}
|
||||
|
||||
fn submit(&self, _payload: Vec<String>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn terminated_str(origin: &'static str) -> String {
|
||||
let mut s = String::new();
|
||||
s.push_str(origin);
|
||||
s.push_str("\n");
|
||||
s
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receives_initial_payload() {
|
||||
let addr = "127.0.0.1:19975".parse().unwrap();
|
||||
let _stratum = Stratum::start(&addr, DummyManager::new(), None).expect("There should be no error starting stratum");
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "mining.subscribe", "params": [], "id": 2}"#;
|
||||
|
||||
let response = String::from_utf8(dummy_request(&addr, request)).unwrap();
|
||||
|
||||
assert_eq!(terminated_str(r#"{"jsonrpc":"2.0","result":["dummy payload"],"id":2}"#), response);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_authorize() {
|
||||
let addr = "127.0.0.1:19970".parse().unwrap();
|
||||
let stratum = Stratum::start(
|
||||
&addr,
|
||||
Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)),
|
||||
None
|
||||
).expect("There should be no error starting stratum");
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "mining.authorize", "params": ["miner1", ""], "id": 1}"#;
|
||||
let response = String::from_utf8(dummy_request(&addr, request)).unwrap();
|
||||
|
||||
assert_eq!(terminated_str(r#"{"jsonrpc":"2.0","result":true,"id":1}"#), response);
|
||||
assert_eq!(1, stratum.implementation.workers.read().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_push_work() {
|
||||
init_log();
|
||||
|
||||
let addr = "127.0.0.1:19995".parse().unwrap();
|
||||
let stratum = Stratum::start(
|
||||
&addr,
|
||||
Arc::new(DummyManager::build().of_initial(r#"["dummy autorize payload"]"#)),
|
||||
None
|
||||
).expect("There should be no error starting stratum");
|
||||
|
||||
let mut auth_request =
|
||||
r#"{"jsonrpc": "2.0", "method": "mining.authorize", "params": ["miner1", ""], "id": 1}"#
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
auth_request.extend(b"\n");
|
||||
|
||||
let auth_response = "{\"jsonrpc\":\"2.0\",\"result\":true,\"id\":1}\n";
|
||||
|
||||
let mut runtime = Runtime::new().expect("Tokio Runtime should be created with no errors");
|
||||
let read_buf0 = vec![0u8; auth_response.len()];
|
||||
let read_buf1 = Vec::with_capacity(2048);
|
||||
let stream = TcpStream::connect(&addr)
|
||||
.and_then(move |stream| {
|
||||
io::write_all(stream, auth_request)
|
||||
})
|
||||
.and_then(|(stream, _)| {
|
||||
io::read_exact(stream, read_buf0)
|
||||
})
|
||||
.map_err(|err| panic!("{:?}", err))
|
||||
.and_then(move |(stream, read_buf0)| {
|
||||
assert_eq!(String::from_utf8(read_buf0).unwrap(), auth_response);
|
||||
trace!(target: "stratum", "Received authorization confirmation");
|
||||
Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100))
|
||||
})
|
||||
.map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err))
|
||||
.and_then(move |stream| {
|
||||
trace!(target: "stratum", "Pusing work to peers");
|
||||
stratum.push_work_all(r#"{ "00040008", "100500" }"#.to_owned())
|
||||
.expect("Pushing work should produce no errors");
|
||||
Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100))
|
||||
})
|
||||
.map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err))
|
||||
.and_then(|stream| {
|
||||
trace!(target: "stratum", "Ready to read work from server");
|
||||
stream.shutdown(Shutdown::Write).unwrap();
|
||||
io::read_to_end(stream, read_buf1)
|
||||
})
|
||||
.and_then(|(_, read_buf1)| {
|
||||
trace!(target: "stratum", "Received work from server");
|
||||
future::ok(read_buf1)
|
||||
});
|
||||
let response = String::from_utf8(
|
||||
runtime.block_on(stream).expect("Runtime should run with no errors")
|
||||
).expect("Response should be utf-8");
|
||||
|
||||
assert_eq!(
|
||||
"{ \"id\": 17, \"method\": \"mining.notify\", \"params\": { \"00040008\", \"100500\" } }\n",
|
||||
response);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std;
|
||||
use std::error::Error as StdError;
|
||||
use ethereum_types::H256;
|
||||
use jsonrpc_tcp_server::PushMessageError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
NoWork,
|
||||
NoWorkers,
|
||||
Io(String),
|
||||
Tcp(String),
|
||||
Dispatch(String),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Self {
|
||||
Error::Io(err.description().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PushMessageError> for Error {
|
||||
fn from(err: PushMessageError) -> Self {
|
||||
Error::Tcp(format!("Push message error: {:?}", err))
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface that can provide pow/blockchain-specific responses for the clients
|
||||
pub trait JobDispatcher: Send + Sync {
|
||||
// json for initial client handshake
|
||||
fn initial(&self) -> Option<String> { None }
|
||||
// json for difficulty dispatch
|
||||
fn difficulty(&self) -> Option<String> { None }
|
||||
// json for job update given worker_id (payload manager should split job!)
|
||||
fn job(&self) -> Option<String> { None }
|
||||
// miner job result
|
||||
fn submit(&self, payload: Vec<String>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Interface that can handle requests to push job for workers
|
||||
pub trait PushWorkHandler: Send + Sync {
|
||||
/// push the same work package for all workers (`payload`: json of pow-specific set of work specification)
|
||||
fn push_work_all(&self, payload: String) -> Result<(), Error>;
|
||||
|
||||
/// push the work packages worker-wise (`payload`: json of pow-specific set of work specification)
|
||||
fn push_work(&self, payloads: Vec<String>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct ServiceConfiguration {
|
||||
pub io_path: String,
|
||||
pub listen_addr: String,
|
||||
pub port: u16,
|
||||
pub secret: Option<H256>,
|
||||
}
|
||||
@@ -34,7 +34,7 @@ trace-time = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
ethcore-io = { path = "../../util/io", features = ["mio"] }
|
||||
ethkey = { path = "../../ethkey" }
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
kvdb-memorydb = "0.1"
|
||||
ethcore-private-tx = { path = "../private-tx" }
|
||||
ethcore = { path = "..", features = ["test-helpers"] }
|
||||
|
||||
@@ -6,7 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethjson = { path = "../../json" }
|
||||
ethkey = { path = "../../ethkey" }
|
||||
ethkey = { path = "../../accounts/ethkey" }
|
||||
evm = { path = "../evm" }
|
||||
heapsize = "0.4"
|
||||
keccak-hash = "0.1"
|
||||
|
||||
@@ -6,7 +6,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
rlp_derive = { path = "../../util/rlp_derive" }
|
||||
rlp_derive = { path = "../../util/rlp-derive" }
|
||||
parity-bytes = "0.1"
|
||||
ethereum-types = "0.4"
|
||||
ethjson = { path = "../../json" }
|
||||
|
||||
Reference in New Issue
Block a user