split requires_reseal, add test and new test miner

This commit is contained in:
keorn 2016-09-13 12:52:14 +02:00
parent 4bfdeea9e5
commit e41b6c410f
3 changed files with 83 additions and 65 deletions

View File

@ -73,18 +73,12 @@ mod tests {
use spec::Spec; use spec::Spec;
use block::*; use block::*;
/// Create a new test chain spec with `BasicAuthority` consensus engine.
fn new_test_instant() -> Spec {
let bytes: &[u8] = include_bytes!("../../res/instant_seal.json");
Spec::load(bytes).expect("invalid chain spec")
}
#[test] #[test]
fn instant_can_seal() { fn instant_can_seal() {
let tap = AccountProvider::transient_provider(); let tap = AccountProvider::transient_provider();
let addr = tap.insert_account("".sha3(), "").unwrap(); let addr = tap.insert_account("".sha3(), "").unwrap();
let spec = new_test_instant(); let spec = Spec::new_test_instant();
let engine = &*spec.engine; let engine = &*spec.engine;
let genesis_header = spec.genesis_header(); let genesis_header = spec.genesis_header();
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
@ -100,7 +94,7 @@ mod tests {
#[test] #[test]
fn instant_cant_verify() { fn instant_cant_verify() {
let engine = new_test_instant().engine; let engine = Spec::new_test_instant().engine;
let mut header: Header = Header::default(); let mut header: Header = Header::default();
assert!(engine.verify_block_basic(&header, None).is_ok()); assert!(engine.verify_block_basic(&header, None).is_ok());

View File

@ -34,6 +34,7 @@ use miner::{MinerService, MinerStatus, TransactionQueue, AccountDetails, Transac
use miner::work_notify::WorkPoster; use miner::work_notify::WorkPoster;
use client::TransactionImportResult; use client::TransactionImportResult;
use miner::price_info::PriceInfo; use miner::price_info::PriceInfo;
use header::BlockNumber;
/// Different possible definitions for pending transaction set. /// Different possible definitions for pending transaction set.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -334,6 +335,36 @@ impl Miner {
(block, original_work_hash) (block, original_work_hash)
} }
/// Check is reseal is allowed and necessary.
fn requires_reseal(&self, best_block: BlockNumber) -> bool {
let has_local_transactions = self.transaction_queue.lock().has_local_pending_transactions();
let mut sealing_work = self.sealing_work.lock();
if sealing_work.enabled {
trace!(target: "miner", "requires_reseal: sealing enabled");
let last_request = *self.sealing_block_last_request.lock();
let should_disable_sealing = !self.forced_sealing()
&& !has_local_transactions
&& best_block > last_request
&& best_block - last_request > SEALING_TIMEOUT_IN_BLOCKS;
trace!(target: "miner", "requires_reseal: should_disable_sealing={}; best_block={}, last_request={}", should_disable_sealing, best_block, last_request);
if should_disable_sealing {
trace!(target: "miner", "Miner sleeping (current {}, last {})", best_block, last_request);
sealing_work.enabled = false;
sealing_work.queue.reset();
false
} else {
// sealing enabled and we don't want to sleep.
*self.next_allowed_reseal.lock() = Instant::now() + self.options.reseal_min_period;
true
}
} else {
// sealing is disabled.
false
}
}
/// Attempts to perform internal sealing (one that does not require work) to return Ok(sealed), /// Attempts to perform internal sealing (one that does not require work) to return Ok(sealed),
/// Err(Some(block)) returns for unsuccesful sealing while Err(None) indicates misspecified engine. /// Err(Some(block)) returns for unsuccesful sealing while Err(None) indicates misspecified engine.
fn seal_block_internally(&self, block: ClosedBlock) -> Result<SealedBlock, Option<ClosedBlock>> { fn seal_block_internally(&self, block: ClosedBlock) -> Result<SealedBlock, Option<ClosedBlock>> {
@ -777,41 +808,13 @@ impl MinerService for Miner {
self.transaction_queue.lock().last_nonce(address) self.transaction_queue.lock().last_nonce(address)
} }
/// Update sealing if required. /// Update sealing if required.
/// Prepare the block and work if the Engine does not seal internally. /// Prepare the block and work if the Engine does not seal internally.
fn update_sealing(&self, chain: &MiningBlockChainClient) { fn update_sealing(&self, chain: &MiningBlockChainClient) {
trace!(target: "miner", "update_sealing"); trace!(target: "miner", "update_sealing");
let requires_reseal = {
let has_local_transactions = self.transaction_queue.lock().has_local_pending_transactions();
let mut sealing_work = self.sealing_work.lock();
if sealing_work.enabled {
trace!(target: "miner", "update_sealing: sealing enabled");
let current_no = chain.chain_info().best_block_number;
let last_request = *self.sealing_block_last_request.lock();
let should_disable_sealing = !self.forced_sealing()
&& !has_local_transactions
&& current_no > last_request
&& current_no - last_request > SEALING_TIMEOUT_IN_BLOCKS;
trace!(target: "miner", "update_sealing: should_disable_sealing={}; current_no={}, last_request={}", should_disable_sealing, current_no, last_request); if self.requires_reseal(chain.chain_info().best_block_number) {
if should_disable_sealing {
trace!(target: "miner", "Miner sleeping (current {}, last {})", current_no, last_request);
sealing_work.enabled = false;
sealing_work.queue.reset();
false
} else {
// sealing enabled and we don't want to sleep.
*self.next_allowed_reseal.lock() = Instant::now() + self.options.reseal_min_period;
true
}
} else {
// sealing is disabled.
false
}
};
if requires_reseal {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// | NOTE Code below requires transaction_queue and sealing_work locks. | // | NOTE Code below requires transaction_queue and sealing_work locks. |
// | Make sure to release the locks before calling that method. | // | Make sure to release the locks before calling that method. |
@ -937,11 +940,12 @@ mod tests {
use super::*; use super::*;
use util::*; use util::*;
use ethkey::{Generator, Random}; use ethkey::{Generator, Random};
use client::{TestBlockChainClient, EachBlockWith}; use client::{BlockChainClient, TestBlockChainClient, EachBlockWith, TransactionImportResult};
use client::{TransactionImportResult}; use header::BlockNumber;
use types::transaction::{Transaction, Action}; use types::transaction::{Transaction, SignedTransaction, Action};
use block::*; use block::*;
use spec::Spec; use spec::Spec;
use tests::helpers::{generate_dummy_client};
#[test] #[test]
fn should_prepare_block_to_seal() { fn should_prepare_block_to_seal() {
@ -995,12 +999,7 @@ mod tests {
)).ok().expect("Miner was just created.") )).ok().expect("Miner was just created.")
} }
#[test] fn transaction() -> SignedTransaction {
fn should_make_pending_block_when_importing_own_transaction() {
// given
let client = TestBlockChainClient::default();
let miner = miner();
let transaction = {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
Transaction { Transaction {
action: Action::Create, action: Action::Create,
@ -1010,8 +1009,14 @@ mod tests {
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::zero(), nonce: U256::zero(),
}.sign(keypair.secret()) }.sign(keypair.secret())
}; }
#[test]
fn should_make_pending_block_when_importing_own_transaction() {
// given
let client = TestBlockChainClient::default();
let miner = miner();
let transaction = transaction();
// when // when
let res = miner.import_own_transaction(&client, transaction); let res = miner.import_own_transaction(&client, transaction);
@ -1030,18 +1035,7 @@ mod tests {
// given // given
let client = TestBlockChainClient::default(); let client = TestBlockChainClient::default();
let miner = miner(); let miner = miner();
let transaction = { let transaction = transaction();
let keypair = Random.generate().unwrap();
Transaction {
action: Action::Create,
value: U256::zero(),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::zero(),
nonce: U256::zero(),
}.sign(keypair.secret())
};
// when // when
let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap(); let res = miner.import_external_transactions(&client, vec![transaction]).pop().unwrap();
@ -1054,4 +1048,29 @@ mod tests {
// This method will let us know if pending block was created (before calling that method) // This method will let us know if pending block was created (before calling that method)
assert_eq!(miner.prepare_work_sealing(&client), true); assert_eq!(miner.prepare_work_sealing(&client), true);
} }
#[test]
fn internal_seals_without_work() {
let miner = Miner::with_spec(&Spec::new_test_instant());
{
let mut sealing_work = miner.sealing_work.lock();
sealing_work.enabled = true;
}
let c = generate_dummy_client(2);
let client = c.reference().as_ref();
assert_eq!(miner.import_external_transactions(client, vec![transaction()]).pop().unwrap().unwrap(), TransactionImportResult::Current);
miner.update_sealing(client);
client.flush_queue();
assert!(miner.pending_block().is_none());
assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber);
assert_eq!(miner.import_own_transaction(client, transaction()).unwrap(), TransactionImportResult::Current);
miner.update_sealing(client);
client.flush_queue();
assert!(miner.pending_block().is_none());
assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber);
}
} }

View File

@ -260,6 +260,11 @@ impl Spec {
pub fn new_null() -> Self { pub fn new_null() -> Self {
Spec::load(include_bytes!("../../res/null.json") as &[u8]).expect("null.json is invalid") Spec::load(include_bytes!("../../res/null.json") as &[u8]).expect("null.json is invalid")
} }
/// Create a new Spec with InstantSeal consensus which does internal sealing (not requiring work).
pub fn new_test_instant() -> Self {
Spec::load(include_bytes!("../../res/instant_seal.json") as &[u8]).expect("instant_seal.json is invalid")
}
} }
#[cfg(test)] #[cfg(test)]