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_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)",
|
||||
"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)",
|
||||
]
|
||||
|
||||
|
@ -73,6 +73,8 @@ pub struct TestBlockChainClient {
|
||||
pub spec: Spec,
|
||||
/// VM Factory
|
||||
pub vm_factory: EvmFactory,
|
||||
/// Timestamp assigned to latest sealed block
|
||||
pub latest_block_timestamp: RwLock<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -114,6 +116,7 @@ impl TestBlockChainClient {
|
||||
miner: Arc::new(Miner::with_spec(&spec)),
|
||||
spec: spec,
|
||||
vm_factory: EvmFactory::new(VMType::Interpreter),
|
||||
latest_block_timestamp: RwLock::new(10_000_000),
|
||||
};
|
||||
client.add_blocks(1, EachBlockWith::Nothing); // add genesis block
|
||||
client.genesis_hash = client.last_hash.read().clone();
|
||||
@ -155,6 +158,11 @@ impl TestBlockChainClient {
|
||||
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.
|
||||
pub fn add_blocks(&self, count: usize, with: EachBlockWith) {
|
||||
let len = self.numbers.read().len();
|
||||
@ -279,7 +287,7 @@ impl MiningBlockChainClient for TestBlockChainClient {
|
||||
extra_data
|
||||
).expect("Opening block for tests will not fail.");
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ serde_macros = { version = "0.7.0", optional = true }
|
||||
clippy = { version = "0.0.85", optional = true}
|
||||
json-ipc-server = { git = "https://github.com/ethcore/json-ipc-server.git" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
time = "0.1"
|
||||
|
||||
[build-dependencies]
|
||||
serde_codegen = { version = "0.7.0", optional = true }
|
||||
|
@ -35,6 +35,7 @@ extern crate ethsync;
|
||||
extern crate transient_hashmap;
|
||||
extern crate json_ipc_server as ipc;
|
||||
extern crate ethcore_ipc;
|
||||
extern crate time;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate ethjson;
|
||||
|
@ -30,6 +30,7 @@ mod codes {
|
||||
pub const UNSUPPORTED_REQUEST: i64 = -32000;
|
||||
pub const NO_WORK: i64 = -32001;
|
||||
pub const NO_AUTHOR: i64 = -32002;
|
||||
pub const NO_NEW_WORK: i64 = -32003;
|
||||
pub const UNKNOWN_ERROR: i64 = -32009;
|
||||
pub const TRANSACTION_ERROR: i64 = -32010;
|
||||
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 {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::NO_AUTHOR),
|
||||
|
@ -23,6 +23,7 @@ use std::process::{Command, Stdio};
|
||||
use std::thread;
|
||||
use std::time::{Instant, Duration};
|
||||
use std::sync::{Arc, Weak};
|
||||
use time::get_time;
|
||||
use ethsync::{SyncProvider, SyncState};
|
||||
use ethcore::miner::{MinerService, ExternalMinerService};
|
||||
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> {
|
||||
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);
|
||||
// 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 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());
|
||||
to_value(&(RpcH256::from(pow_hash), RpcH256::from(seed_hash), RpcH256::from(target), block_number))
|
||||
} else {
|
||||
|
@ -30,6 +30,7 @@ use ethsync::SyncState;
|
||||
use v1::{Eth, EthClient, EthClientOptions, EthSigning, EthSigningUnsafeClient};
|
||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService};
|
||||
use rustc_serialize::hex::ToHex;
|
||||
use time::get_time;
|
||||
|
||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||
let client = TestBlockChainClient::new();
|
||||
@ -818,7 +819,7 @@ fn rpc_eth_compile_serpent() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_no_work_if_cant_mine() {
|
||||
fn rpc_get_work_returns_no_work_if_cant_mine() {
|
||||
let eth_tester = EthTester::default();
|
||||
eth_tester.client.set_queue_size(10);
|
||||
|
||||
@ -829,7 +830,7 @@ fn returns_no_work_if_cant_mine() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_correct_work_package() {
|
||||
fn rpc_get_work_returns_correct_work_package() {
|
||||
let eth_tester = EthTester::default();
|
||||
eth_tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
||||
|
||||
@ -840,7 +841,7 @@ fn returns_correct_work_package() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_return_block_number() {
|
||||
fn rpc_get_work_should_not_return_block_number() {
|
||||
let eth_tester = EthTester::new_with_options(EthClientOptions {
|
||||
allow_pending_receipt_query: true,
|
||||
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()));
|
||||
}
|
||||
|
||||
#[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