Miner tweaks (#1797)
* Mining fixes. - Use queue to determine whether we're mining - Kick stale hash rates Fixes #1794 Fixes #1641 * Fix tests. * Address grumbles.
This commit is contained in:
parent
1b507e0147
commit
9de579366a
@ -25,12 +25,6 @@ use std::mem;
|
|||||||
use ipc::binary::BinaryConvertError;
|
use ipc::binary::BinaryConvertError;
|
||||||
use std::collections::{VecDeque, HashMap, BTreeMap};
|
use std::collections::{VecDeque, HashMap, BTreeMap};
|
||||||
|
|
||||||
impl From<String> for Error {
|
|
||||||
fn from(s: String) -> Error {
|
|
||||||
Error::RocksDb(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum WriteCacheEntry {
|
enum WriteCacheEntry {
|
||||||
Remove,
|
Remove,
|
||||||
Write(Vec<u8>),
|
Write(Vec<u8>),
|
||||||
|
@ -31,8 +31,8 @@ pub struct KeyValue {
|
|||||||
pub value: Vec<u8>,
|
pub value: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Binary)]
|
#[derive(Debug, Binary)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
AlreadyOpen,
|
AlreadyOpen,
|
||||||
IsClosed,
|
IsClosed,
|
||||||
RocksDb(String),
|
RocksDb(String),
|
||||||
@ -41,6 +41,12 @@ pub struct KeyValue {
|
|||||||
UncommitedTransactions,
|
UncommitedTransactions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<String> for Error {
|
||||||
|
fn from(s: String) -> Error {
|
||||||
|
Error::RocksDb(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Database configuration
|
/// Database configuration
|
||||||
#[derive(Binary)]
|
#[derive(Binary)]
|
||||||
pub struct DatabaseConfig {
|
pub struct DatabaseConfig {
|
||||||
@ -68,7 +74,7 @@ impl DatabaseConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DatabaseService : Sized {
|
pub trait DatabaseService : Sized {
|
||||||
/// Opens database in the specified path
|
/// Opens database in the specified path
|
||||||
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;
|
fn open(&self, config: DatabaseConfig, path: String) -> Result<(), Error>;
|
||||||
|
|
||||||
|
@ -178,15 +178,15 @@ impl Client {
|
|||||||
db_config.compaction = config.db_compaction.compaction_profile();
|
db_config.compaction = config.db_compaction.compaction_profile();
|
||||||
db_config.wal = config.db_wal;
|
db_config.wal = config.db_wal;
|
||||||
|
|
||||||
let db = Arc::new(Database::open(&db_config, &path.to_str().unwrap()).expect("Error opening database"));
|
let db = Arc::new(try!(Database::open(&db_config, &path.to_str().unwrap()).map_err(ClientError::Database)));
|
||||||
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, db.clone()));
|
let chain = Arc::new(BlockChain::new(config.blockchain, &gb, db.clone()));
|
||||||
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, db.clone(), chain.clone())));
|
let tracedb = Arc::new(try!(TraceDB::new(config.tracing, db.clone(), chain.clone())));
|
||||||
|
|
||||||
let mut state_db = journaldb::new(db.clone(), config.pruning, DB_COL_STATE);
|
let mut state_db = journaldb::new(db.clone(), config.pruning, DB_COL_STATE);
|
||||||
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
|
if state_db.is_empty() && spec.ensure_db_good(state_db.as_hashdb_mut()) {
|
||||||
let batch = DBTransaction::new(&db);
|
let batch = DBTransaction::new(&db);
|
||||||
state_db.commit(&batch, 0, &spec.genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
|
try!(state_db.commit(&batch, 0, &spec.genesis_header().hash(), None));
|
||||||
db.write(batch).expect("Error writing genesis state to state DB");
|
try!(db.write(batch).map_err(ClientError::Database));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.contains(h.state_root())) {
|
if !chain.block_header(&chain.best_block_hash()).map_or(true, |h| state_db.contains(h.state_root())) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use trace::Error as TraceError;
|
use trace::Error as TraceError;
|
||||||
|
use util::UtilError;
|
||||||
use std::fmt::{Display, Formatter, Error as FmtError};
|
use std::fmt::{Display, Formatter, Error as FmtError};
|
||||||
|
|
||||||
/// Client configuration errors.
|
/// Client configuration errors.
|
||||||
@ -6,6 +7,10 @@ use std::fmt::{Display, Formatter, Error as FmtError};
|
|||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// TraceDB configuration error.
|
/// TraceDB configuration error.
|
||||||
Trace(TraceError),
|
Trace(TraceError),
|
||||||
|
/// Database error
|
||||||
|
Database(String),
|
||||||
|
/// Util error
|
||||||
|
Util(UtilError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TraceError> for Error {
|
impl From<TraceError> for Error {
|
||||||
@ -14,10 +19,18 @@ impl From<TraceError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<UtilError> for Error {
|
||||||
|
fn from(err: UtilError) -> Self {
|
||||||
|
Error::Util(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||||
match *self {
|
match *self {
|
||||||
Error::Trace(ref err) => write!(f, "{}", err)
|
Error::Trace(ref err) => write!(f, "{}", err),
|
||||||
|
Error::Util(ref err) => write!(f, "{}", err),
|
||||||
|
Error::Database(ref s) => write!(f, "Database error: {}", s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::{RwLock, U256, H256};
|
use std::time::{Instant, Duration};
|
||||||
|
use util::{Mutex, U256, H256};
|
||||||
|
|
||||||
/// External miner interface.
|
/// External miner interface.
|
||||||
pub trait ExternalMinerService: Send + Sync {
|
pub trait ExternalMinerService: Send + Sync {
|
||||||
@ -25,50 +26,50 @@ pub trait ExternalMinerService: Send + Sync {
|
|||||||
|
|
||||||
/// Total hashrate.
|
/// Total hashrate.
|
||||||
fn hashrate(&self) -> U256;
|
fn hashrate(&self) -> U256;
|
||||||
|
|
||||||
/// Returns true if external miner is mining.
|
|
||||||
fn is_mining(&self) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// External Miner.
|
/// External Miner.
|
||||||
pub struct ExternalMiner {
|
pub struct ExternalMiner {
|
||||||
hashrates: Arc<RwLock<HashMap<H256, U256>>>,
|
hashrates: Arc<Mutex<HashMap<H256, (Instant, U256)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ExternalMiner {
|
impl Default for ExternalMiner {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ExternalMiner {
|
ExternalMiner {
|
||||||
hashrates: Arc::new(RwLock::new(HashMap::new())),
|
hashrates: Arc::new(Mutex::new(HashMap::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternalMiner {
|
impl ExternalMiner {
|
||||||
/// Creates new external miner with prefilled hashrates.
|
/// Creates new external miner with prefilled hashrates.
|
||||||
pub fn new(hashrates: Arc<RwLock<HashMap<H256, U256>>>) -> Self {
|
pub fn new(hashrates: Arc<Mutex<HashMap<H256, (Instant, U256)>>>) -> Self {
|
||||||
ExternalMiner {
|
ExternalMiner {
|
||||||
hashrates: hashrates
|
hashrates: hashrates,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ENTRY_TIMEOUT: u64 = 2;
|
||||||
|
|
||||||
impl ExternalMinerService for ExternalMiner {
|
impl ExternalMinerService for ExternalMiner {
|
||||||
fn submit_hashrate(&self, hashrate: U256, id: H256) {
|
fn submit_hashrate(&self, hashrate: U256, id: H256) {
|
||||||
self.hashrates.write().insert(id, hashrate);
|
self.hashrates.lock().insert(id, (Instant::now() + Duration::from_secs(ENTRY_TIMEOUT), hashrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hashrate(&self) -> U256 {
|
fn hashrate(&self) -> U256 {
|
||||||
self.hashrates.read().iter().fold(U256::from(0), |sum, (_, v)| sum + *v)
|
let mut hashrates = self.hashrates.lock();
|
||||||
}
|
let h = hashrates.drain().filter(|&(_, (t, _))| t > Instant::now()).collect();
|
||||||
|
*hashrates = h;
|
||||||
fn is_mining(&self) -> bool {
|
hashrates.iter().fold(U256::from(0), |sum, (_, &(_, v))| sum + v)
|
||||||
!self.hashrates.read().is_empty()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
use util::{H256, U256};
|
use util::{H256, U256};
|
||||||
|
|
||||||
fn miner() -> ExternalMiner {
|
fn miner() -> ExternalMiner {
|
||||||
@ -76,16 +77,18 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_that_is_mining_if_there_is_at_least_one_entry() {
|
fn it_should_forget_old_hashrates() {
|
||||||
// given
|
// given
|
||||||
let m = miner();
|
let m = miner();
|
||||||
assert_eq!(m.is_mining(), false);
|
assert_eq!(m.hashrate(), U256::from(0));
|
||||||
|
m.submit_hashrate(U256::from(10), H256::from(1));
|
||||||
|
assert_eq!(m.hashrate(), U256::from(10));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
m.submit_hashrate(U256::from(10), H256::from(1));
|
sleep(Duration::from_secs(3));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(m.is_mining(), true);
|
assert_eq!(m.hashrate(), U256::from(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -780,6 +780,10 @@ impl MinerService for Miner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_sealing(&self) -> bool {
|
||||||
|
self.sealing_work.lock().queue.is_in_use()
|
||||||
|
}
|
||||||
|
|
||||||
fn map_sealing_work<F, T>(&self, chain: &MiningBlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
|
fn map_sealing_work<F, T>(&self, chain: &MiningBlockChainClient, f: F) -> Option<T> where F: FnOnce(&ClosedBlock) -> T {
|
||||||
trace!(target: "miner", "map_sealing_work: entering");
|
trace!(target: "miner", "map_sealing_work: entering");
|
||||||
self.enable_and_prepare_sealing(chain);
|
self.enable_and_prepare_sealing(chain);
|
||||||
|
@ -150,6 +150,9 @@ pub trait MinerService : Send + Sync {
|
|||||||
/// Returns highest transaction nonce for given address.
|
/// Returns highest transaction nonce for given address.
|
||||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||||
|
|
||||||
|
/// Is it currently sealing?
|
||||||
|
fn is_sealing(&self) -> bool;
|
||||||
|
|
||||||
/// Suggested gas price.
|
/// Suggested gas price.
|
||||||
fn sensible_gas_price(&self) -> U256 { 20000000000u64.into() }
|
fn sensible_gas_price(&self) -> U256 { 20000000000u64.into() }
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ impl<C, S: ?Sized, M, EM> Eth for EthClient<C, S, M, EM> where
|
|||||||
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
fn is_mining(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match params {
|
match params {
|
||||||
Params::None => to_value(&self.external_miner.is_mining()),
|
Params::None => to_value(&(take_weak!(self.miner).is_sealing())),
|
||||||
_ => Err(Error::invalid_params())
|
_ => Err(Error::invalid_params())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,6 +205,10 @@ impl MinerService for TestMinerService {
|
|||||||
self.last_nonces.read().get(address).cloned()
|
self.last_nonces.read().get(address).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_sealing(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
/// Submit `seal` as a valid solution for the header of `pow_hash`.
|
||||||
/// Will check the seal, but not actually insert the block into the chain.
|
/// Will check the seal, but not actually insert the block into the chain.
|
||||||
fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
fn submit_seal(&self, _chain: &MiningBlockChainClient, _pow_hash: H256, _seal: Vec<Bytes>) -> Result<(), Error> {
|
||||||
|
@ -17,10 +17,11 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::{Instant, Duration};
|
||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use util::hash::{Address, H256, FixedHash};
|
use util::hash::{Address, H256, FixedHash};
|
||||||
use util::numbers::{Uint, U256};
|
use util::numbers::{Uint, U256};
|
||||||
use util::RwLock;
|
use util::Mutex;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
|
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
|
||||||
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
use ethcore::log_entry::{LocalizedLogEntry, LogEntry};
|
||||||
@ -57,7 +58,7 @@ struct EthTester {
|
|||||||
pub sync: Arc<TestSyncProvider>,
|
pub sync: Arc<TestSyncProvider>,
|
||||||
pub accounts_provider: Arc<AccountProvider>,
|
pub accounts_provider: Arc<AccountProvider>,
|
||||||
pub miner: Arc<TestMinerService>,
|
pub miner: Arc<TestMinerService>,
|
||||||
hashrates: Arc<RwLock<HashMap<H256, U256>>>,
|
hashrates: Arc<Mutex<HashMap<H256, (Instant, U256)>>>,
|
||||||
pub io: IoHandler,
|
pub io: IoHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ impl Default for EthTester {
|
|||||||
let sync = sync_provider();
|
let sync = sync_provider();
|
||||||
let ap = accounts_provider();
|
let ap = accounts_provider();
|
||||||
let miner = miner_service();
|
let miner = miner_service();
|
||||||
let hashrates = Arc::new(RwLock::new(HashMap::new()));
|
let hashrates = Arc::new(Mutex::new(HashMap::new()));
|
||||||
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
||||||
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner, true).to_delegate();
|
let eth = EthClient::new(&client, &sync, &ap, &miner, &external_miner, true).to_delegate();
|
||||||
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
|
let sign = EthSigningUnsafeClient::new(&client, &ap, &miner).to_delegate();
|
||||||
@ -133,9 +134,9 @@ fn rpc_eth_syncing() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn rpc_eth_hashrate() {
|
fn rpc_eth_hashrate() {
|
||||||
let tester = EthTester::default();
|
let tester = EthTester::default();
|
||||||
tester.hashrates.write().insert(H256::from(0), U256::from(0xfffa));
|
tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffa)));
|
||||||
tester.hashrates.write().insert(H256::from(0), U256::from(0xfffb));
|
tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffb)));
|
||||||
tester.hashrates.write().insert(H256::from(1), U256::from(0x1));
|
tester.hashrates.lock().insert(H256::from(1), (Instant::now() + Duration::from_secs(2), U256::from(0x1)));
|
||||||
|
|
||||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1}"#;
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":"0xfffc","id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":"0xfffc","id":1}"#;
|
||||||
@ -158,8 +159,8 @@ fn rpc_eth_submit_hashrate() {
|
|||||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||||
|
|
||||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||||
assert_eq!(tester.hashrates.read().get(&H256::from("0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c")).cloned(),
|
assert_eq!(tester.hashrates.lock().get(&H256::from("0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c")).cloned().unwrap().1,
|
||||||
Some(U256::from(0x500_000)));
|
U256::from(0x500_000));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -210,16 +211,11 @@ fn rpc_eth_author() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn rpc_eth_mining() {
|
fn rpc_eth_mining() {
|
||||||
let tester = EthTester::default();
|
let tester = EthTester::default();
|
||||||
|
tester.miner.set_author(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap());
|
||||||
|
|
||||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#;
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
|
||||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
||||||
|
|
||||||
tester.hashrates.write().insert(H256::from(1), U256::from(0x1));
|
|
||||||
|
|
||||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_mining", "params": [], "id": 1}"#;
|
|
||||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
|
||||||
assert_eq!(tester.io.handle_request(request), Some(response.to_owned()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -71,6 +71,9 @@ impl<T> UsingQueue<T> where T: Clone {
|
|||||||
self.pending = Some(b);
|
self.pending = Some(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is there anything in the queue currently?
|
||||||
|
pub fn is_in_use(&self) -> bool { self.in_use.len() > 0 }
|
||||||
|
|
||||||
/// Clears everything; the queue is entirely reset.
|
/// Clears everything; the queue is entirely reset.
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.pending = None;
|
self.pending = None;
|
||||||
|
Loading…
Reference in New Issue
Block a user