diff --git a/crates/ethcore/src/engines/authority_round/mod.rs b/crates/ethcore/src/engines/authority_round/mod.rs index 18ff69c39..fd2d285fa 100644 --- a/crates/ethcore/src/engines/authority_round/mod.rs +++ b/crates/ethcore/src/engines/authority_round/mod.rs @@ -1557,6 +1557,43 @@ impl Engine for AuthorityRound { } } + // Mostly is the same as `fn sealing_state(&self)` except that it does not + // check whether the node is a step proposer. + fn is_allowed_to_seal(&self) -> bool { + let our_addr = match *self.signer.read() { + Some(ref signer) => signer.address(), + None => return false, + }; + + let client = match self.upgrade_client_or("Not preparing block") { + Ok(client) => client, + Err(_) => return false, + }; + + let parent = match client.as_full_client() { + Some(full_client) => full_client.best_block_header(), + None => { + return false; + } + }; + + let validators = if self.immediate_transitions { + CowLike::Borrowed(&*self.validators) + } else { + let mut epoch_manager = self.epoch_manager.lock(); + if !epoch_manager.zoom_to_after( + &*client, + &self.machine, + &*self.validators, + parent.hash(), + ) { + return false; + } + CowLike::Owned(epoch_manager.validators().clone()) + }; + validators.contains(&parent.hash(), &our_addr) + } + fn sealing_state(&self) -> SealingState { let our_addr = match *self.signer.read() { Some(ref signer) => signer.address(), diff --git a/crates/ethcore/src/engines/mod.rs b/crates/ethcore/src/engines/mod.rs index c96284179..1e940f37e 100644 --- a/crates/ethcore/src/engines/mod.rs +++ b/crates/ethcore/src/engines/mod.rs @@ -470,6 +470,14 @@ pub trait Engine: Sync + Send { /// Register a component which signs consensus messages. fn set_signer(&self, _signer: Option>) {} + /// Returns whether the current node is a validator and + /// actually may seal a block if AuRa engine is used. + /// + /// Used by `eth_mining` rpc call. + fn is_allowed_to_seal(&self) -> bool { + true + } + /// Sign using the EngineSigner, to be used for consensus tx signing. fn sign(&self, _hash: H256) -> Result { unimplemented!() diff --git a/crates/ethcore/src/miner/miner.rs b/crates/ethcore/src/miner/miner.rs index dfc908176..f9515ac03 100644 --- a/crates/ethcore/src/miner/miner.rs +++ b/crates/ethcore/src/miner/miner.rs @@ -1377,7 +1377,7 @@ impl miner::MinerService for Miner { } fn is_currently_sealing(&self) -> bool { - self.sealing.lock().enabled + self.sealing.lock().enabled && self.engine.is_allowed_to_seal() } fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> @@ -1608,7 +1608,9 @@ mod tests { use client::{ChainInfo, EachBlockWith, ImportSealedBlock, TestBlockChainClient}; use miner::{MinerService, PendingOrdering}; - use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec}; + use test_helpers::{ + dummy_engine_signer_with_address, generate_dummy_client, generate_dummy_client_with_spec, + }; use types::transaction::{Transaction, TypedTransaction}; #[test] @@ -2078,6 +2080,31 @@ mod tests { assert!(miner.is_currently_sealing()); } + #[test] + fn should_not_mine_if_is_not_allowed_to_seal() { + let spec = Spec::new_test_round(); + let miner = Miner::new_for_tests_force_sealing(&spec, None, true); + assert!(!miner.is_currently_sealing()); + } + + #[test] + fn should_mine_if_is_allowed_to_seal() { + let verifier: Address = [ + 0x7d, 0x57, 0x7a, 0x59, 0x7b, 0x27, 0x42, 0xb4, 0x98, 0xcb, 0x5c, 0xf0, 0xc2, 0x6c, + 0xdc, 0xd7, 0x26, 0xd3, 0x9e, 0x6e, + ] + .into(); + + let spec = Spec::new_test_round(); + let client: Arc = generate_dummy_client(2); + + let miner = Miner::new_for_tests_force_sealing(&spec, None, true); + miner.engine.register_client(Arc::downgrade(&client)); + miner.set_author(Author::Sealer(dummy_engine_signer_with_address(verifier))); + + assert!(miner.is_currently_sealing()); + } + #[test] fn should_set_new_minimum_gas_price() { // Creates a new GasPricer::Fixed behind the scenes diff --git a/crates/ethcore/src/test_helpers.rs b/crates/ethcore/src/test_helpers.rs index 9650ce444..d011761db 100644 --- a/crates/ethcore/src/test_helpers.rs +++ b/crates/ethcore/src/test_helpers.rs @@ -46,6 +46,8 @@ use block::{Drain, OpenBlock}; use client::{ ChainInfo, ChainMessageType, ChainNotify, Client, ClientConfig, ImportBlock, PrepareOpenBlock, }; +use engines::EngineSigner; +use ethjson::crypto::publickey::{Public, Signature}; use factory::Factories; use miner::Miner; use spec::Spec; @@ -644,3 +646,38 @@ impl ChainNotify for TestNotify { self.messages.write().push(data); } } + +/// Returns engine signer with specified address +pub fn dummy_engine_signer_with_address(addr: Address) -> Box { + struct TestEngineSigner(Address); + + impl TestEngineSigner { + fn with_address(addr: Address) -> Self { + Self(addr) + } + } + + impl EngineSigner for TestEngineSigner { + fn sign(&self, _hash: H256) -> Result { + unimplemented!() + } + + fn address(&self) -> Address { + self.0 + } + + fn decrypt( + &self, + _auth_data: &[u8], + _cipher: &[u8], + ) -> Result, parity_crypto::publickey::Error> { + unimplemented!() + } + + fn public(&self) -> Option { + unimplemented!() + } + } + + Box::new(TestEngineSigner::with_address(addr)) +}