Add timeout for eth_getWork call (#1975)
This commit is contained in:
parent
59ede63eda
commit
2a550c2adf
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -426,6 +426,7 @@ dependencies = [
|
|||||||
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_codegen 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -73,6 +73,8 @@ pub struct TestBlockChainClient {
|
|||||||
pub spec: Spec,
|
pub spec: Spec,
|
||||||
/// VM Factory
|
/// VM Factory
|
||||||
pub vm_factory: EvmFactory,
|
pub vm_factory: EvmFactory,
|
||||||
|
/// Timestamp assigned to latest sealed block
|
||||||
|
pub latest_block_timestamp: RwLock<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -114,6 +116,7 @@ impl TestBlockChainClient {
|
|||||||
miner: Arc::new(Miner::with_spec(&spec)),
|
miner: Arc::new(Miner::with_spec(&spec)),
|
||||||
spec: spec,
|
spec: spec,
|
||||||
vm_factory: EvmFactory::new(VMType::Interpreter),
|
vm_factory: EvmFactory::new(VMType::Interpreter),
|
||||||
|
latest_block_timestamp: RwLock::new(10_000_000),
|
||||||
};
|
};
|
||||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||||
client.genesis_hash = client.last_hash.read().clone();
|
client.genesis_hash = client.last_hash.read().clone();
|
||||||
@ -155,6 +158,11 @@ impl TestBlockChainClient {
|
|||||||
self.queue_size.store(size, AtomicOrder::Relaxed);
|
self.queue_size.store(size, AtomicOrder::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set timestamp assigned to latest sealed block
|
||||||
|
pub fn set_latest_block_timestamp(&self, ts: u64) {
|
||||||
|
*self.latest_block_timestamp.write() = ts;
|
||||||
|
}
|
||||||
|
|
||||||
/// Add blocks to test client.
|
/// Add blocks to test client.
|
||||||
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
|
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
|
||||||
let len = self.numbers.read().len();
|
let len = self.numbers.read().len();
|
||||||
@ -279,7 +287,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
|||||||
extra_data
|
extra_data
|
||||||
).expect("Opening block for tests will not fail.");
|
).expect("Opening block for tests will not fail.");
|
||||||
// TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks)
|
// TODO [todr] Override timestamp for predictability (set_timestamp_now kind of sucks)
|
||||||
open_block.set_timestamp(10_000_000);
|
open_block.set_timestamp(*self.latest_block_timestamp.read());
|
||||||
open_block
|
open_block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ serde_macros = { version = "0.7.0", optional = true }
|
|||||||
clippy = { version = "0.0.85", optional = true}
|
clippy = { version = "0.0.85", optional = true}
|
||||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||||
ethcore-ipc = { path = "../ipc/rpc" }
|
ethcore-ipc = { path = "../ipc/rpc" }
|
||||||
|
time = "0.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde_codegen = { version = "0.7.0", optional = true }
|
serde_codegen = { version = "0.7.0", optional = true }
|
||||||
|
@ -35,6 +35,7 @@ extern crate ethsync;
|
|||||||
extern crate transient_hashmap;
|
extern crate transient_hashmap;
|
||||||
extern crate json_ipc_server as ipc;
|
extern crate json_ipc_server as ipc;
|
||||||
extern crate ethcore_ipc;
|
extern crate ethcore_ipc;
|
||||||
|
extern crate time;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate ethjson;
|
extern crate ethjson;
|
||||||
|
@ -30,6 +30,7 @@ mod codes {
|
|||||||
pub const UNSUPPORTED_REQUEST: i64 = -32000;
|
pub const UNSUPPORTED_REQUEST: i64 = -32000;
|
||||||
pub const NO_WORK: i64 = -32001;
|
pub const NO_WORK: i64 = -32001;
|
||||||
pub const NO_AUTHOR: i64 = -32002;
|
pub const NO_AUTHOR: i64 = -32002;
|
||||||
|
pub const NO_NEW_WORK: i64 = -32003;
|
||||||
pub const UNKNOWN_ERROR: i64 = -32009;
|
pub const UNKNOWN_ERROR: i64 = -32009;
|
||||||
pub const TRANSACTION_ERROR: i64 = -32010;
|
pub const TRANSACTION_ERROR: i64 = -32010;
|
||||||
pub const ACCOUNT_LOCKED: i64 = -32020;
|
pub const ACCOUNT_LOCKED: i64 = -32020;
|
||||||
@ -114,6 +115,14 @@ pub fn no_work() -> Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn no_new_work() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::NO_NEW_WORK),
|
||||||
|
message: "Work has not changed.".into(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn no_author() -> Error {
|
pub fn no_author() -> Error {
|
||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(codes::NO_AUTHOR),
|
code: ErrorCode::ServerError(codes::NO_AUTHOR),
|
||||||
|
@ -23,6 +23,7 @@ use std::process::{Command, Stdio};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
use time::get_time;
|
||||||
use ethsync::{SyncProvider, SyncState};
|
use ethsync::{SyncProvider, SyncState};
|
||||||
use ethcore::miner::{MinerService, ExternalMinerService};
|
use ethcore::miner::{MinerService, ExternalMinerService};
|
||||||
use jsonrpc_core::*;
|
use jsonrpc_core::*;
|
||||||
@ -516,7 +517,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
|
|
||||||
fn work(&self, params: Params) -> Result<Value, Error> {
|
fn work(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
try!(expect_no_params(params));
|
let (no_new_work_timeout,) = from_params::<(u64,)>(params).unwrap_or((0,));
|
||||||
|
|
||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
// check if we're still syncing and return empty strings in that case
|
// check if we're still syncing and return empty strings in that case
|
||||||
@ -545,7 +546,9 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
|
let target = Ethash::difficulty_to_boundary(b.block().header().difficulty());
|
||||||
let seed_hash = self.seed_compute.lock().get_seedhash(b.block().header().number());
|
let seed_hash = self.seed_compute.lock().get_seedhash(b.block().header().number());
|
||||||
|
|
||||||
if self.options.send_block_number_in_get_work {
|
if no_new_work_timeout > 0 && b.block().header().timestamp() + no_new_work_timeout < get_time().sec as u64 {
|
||||||
|
Err(errors::no_new_work())
|
||||||
|
} else if self.options.send_block_number_in_get_work {
|
||||||
let block_number = RpcU256::from(b.block().header().number());
|
let block_number = RpcU256::from(b.block().header().number());
|
||||||
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
|
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,6 +30,7 @@ use ethsync::SyncState;
|
|||||||
use v1::{Eth, EthClient, EthClientOptions, EthSigning, EthSigningUnsafeClient};
|
use v1::{Eth, EthClient, EthClientOptions, EthSigning, EthSigningUnsafeClient};
|
||||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||||
use rustc_serialize::hex::ToHex;
|
use rustc_serialize::hex::ToHex;
|
||||||
|
use time::get_time;
|
||||||
|
|
||||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||||
let client = TestBlockChainClient::new();
|
let client = TestBlockChainClient::new();
|
||||||
@ -818,7 +819,7 @@ fn rpc_eth_compile_serpent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_no_work_if_cant_mine() {
|
fn rpc_get_work_returns_no_work_if_cant_mine() {
|
||||||
let eth_tester = EthTester::default();
|
let eth_tester = EthTester::default();
|
||||||
eth_tester.client.set_queue_size(10);
|
eth_tester.client.set_queue_size(10);
|
||||||
|
|
||||||
@ -829,7 +830,7 @@ fn returns_no_work_if_cant_mine() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn returns_correct_work_package() {
|
fn rpc_get_work_returns_correct_work_package() {
|
||||||
let eth_tester = EthTester::default();
|
let eth_tester = EthTester::default();
|
||||||
eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
||||||
|
|
||||||
@ -840,7 +841,7 @@ fn returns_correct_work_package() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_not_return_block_number() {
|
fn rpc_get_work_should_not_return_block_number() {
|
||||||
let eth_tester = EthTester::new_with_options(EthClientOptions {
|
let eth_tester = EthTester::new_with_options(EthClientOptions {
|
||||||
allow_pending_receipt_query: true,
|
allow_pending_receipt_query: true,
|
||||||
send_block_number_in_get_work: false,
|
send_block_number_in_get_work: false,
|
||||||
@ -852,3 +853,28 @@ fn should_not_return_block_number() {
|
|||||||
|
|
||||||
assert_eq!(eth_tester.io.handle_request(request), Some(response.to_owned()));
|
assert_eq!(eth_tester.io.handle_request(request), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rpc_get_work_should_timeout() {
|
||||||
|
let eth_tester = EthTester::default();
|
||||||
|
eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
||||||
|
eth_tester.client.set_latest_block_timestamp(get_time().sec as u64 - 1000); // Set latest block to 1000 seconds ago
|
||||||
|
let hash = eth_tester.miner.map_sealing_work(&*eth_tester.client, |b| b.hash()).unwrap();
|
||||||
|
|
||||||
|
// Request with timeout of 0 seconds. This should work since we're disabling timeout.
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#;
|
||||||
|
let work_response = format!(
|
||||||
|
r#"{{"jsonrpc":"2.0","result":["0x{:?}","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000800000000000000000000000000000000000000000000000000000000000","0x01"],"id":1}}"#,
|
||||||
|
hash,
|
||||||
|
);
|
||||||
|
assert_eq!(eth_tester.io.handle_request(request), Some(work_response.to_owned()));
|
||||||
|
|
||||||
|
// Request with timeout of 10K seconds. This should work.
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": ["10000"], "id": 1}"#;
|
||||||
|
assert_eq!(eth_tester.io.handle_request(request), Some(work_response.to_owned()));
|
||||||
|
|
||||||
|
// Request with timeout of 10 seconds. This should fail.
|
||||||
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": ["10"], "id": 1}"#;
|
||||||
|
let err_response = r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Work has not changed.","data":null},"id":1}"#;
|
||||||
|
assert_eq!(eth_tester.io.handle_request(request), Some(err_response.to_owned()));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user