diff --git a/Cargo.lock b/Cargo.lock index 2b9e3d3d9a..e47d198392 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,6 +248,7 @@ dependencies = [ "codechain-network 0.1.0", "codechain-state 0.1.0", "codechain-stratum 1.11.0", + "codechain-timer 0.1.0", "codechain-types 0.1.0", "codechain-vm 0.1.0", "cuckoo 0.1.0 (git+https://github.com/CodeChain-io/rust-cuckoo.git?rev=280cab9c)", @@ -293,6 +294,7 @@ dependencies = [ "codechain-key 0.1.0", "codechain-logger 0.1.0", "codechain-network 0.1.0", + "codechain-timer 0.1.0", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -532,6 +534,7 @@ dependencies = [ "codechain-merkle 0.1.0", "codechain-network 0.1.0", "codechain-state 0.1.0", + "codechain-timer 0.1.0", "codechain-token-generator 0.1.0", "codechain-types 0.1.0", "hashdb 0.1.1", diff --git a/codechain/codechain.yml b/codechain/codechain.yml index ad73ad1897..10abc86b53 100644 --- a/codechain/codechain.yml +++ b/codechain/codechain.yml @@ -192,6 +192,12 @@ args: takes_value: true conflicts_with: - no-miner + - no-reseal-timer: + long: no-reseal-timer + help: Do not use reseal timer. + takes_value: false + conflicts_with: + - no-miner - work-queue-size: long: work-queue-size value_name: ITEMS diff --git a/codechain/config/mod.rs b/codechain/config/mod.rs index ba9365b71d..61bf69f0b5 100644 --- a/codechain/config/mod.rs +++ b/codechain/config/mod.rs @@ -83,6 +83,7 @@ impl Config { reseal_on_external_parcel, reseal_min_period: Duration::from_millis(self.mining.reseal_min_period.unwrap()), reseal_max_period: Duration::from_millis(self.mining.reseal_max_period.unwrap()), + no_reseal_timer: self.mining.no_reseal_timer.unwrap(), work_queue_size: self.mining.work_queue_size.unwrap(), }) } @@ -218,6 +219,7 @@ pub struct Mining { pub reseal_on_txs: Option, pub reseal_min_period: Option, pub reseal_max_period: Option, + pub no_reseal_timer: Option, pub work_queue_size: Option, } @@ -375,6 +377,9 @@ impl Mining { if other.reseal_max_period.is_some() { self.reseal_max_period = other.reseal_max_period; } + if other.no_reseal_timer.is_some() { + self.no_reseal_timer = other.no_reseal_timer; + } if other.work_queue_size.is_some() { self.work_queue_size = other.work_queue_size; } @@ -412,6 +417,9 @@ impl Mining { if let Some(reseal_max_period) = matches.value_of("reseal-max-period") { self.reseal_max_period = Some(reseal_max_period.parse().map_err(|_| "Invalid period")?); } + if matches.is_present("no-reseal-timer") { + self.no_reseal_timer = Some(true); + } if let Some(work_queue_size) = matches.value_of("work-queue-size") { self.work_queue_size = Some(work_queue_size.parse().map_err(|_| "Invalid size")?); } diff --git a/codechain/config/presets/config.dev.toml b/codechain/config/presets/config.dev.toml index e68556c153..27f76592cf 100644 --- a/codechain/config/presets/config.dev.toml +++ b/codechain/config/presets/config.dev.toml @@ -13,6 +13,7 @@ force_sealing = false reseal_on_txs = "all" reseal_min_period = 0 reseal_max_period = 120000 +no_reseal_timer = false work_queue_size = 20 [network] diff --git a/codechain/config/presets/config.prod.toml b/codechain/config/presets/config.prod.toml index ddd5633b3a..16904c25a5 100644 --- a/codechain/config/presets/config.prod.toml +++ b/codechain/config/presets/config.prod.toml @@ -12,6 +12,7 @@ force_sealing = true reseal_on_txs = "all" reseal_min_period = 4000 reseal_max_period = 120000 +no_reseal_timer = false work_queue_size = 20 [network] diff --git a/codechain/run_node.rs b/codechain/run_node.rs index 2e55217e9b..e04fe7ae31 100644 --- a/codechain/run_node.rs +++ b/codechain/run_node.rs @@ -86,7 +86,12 @@ fn discovery_start(service: &NetworkService, cfg: &config::Network) -> Result<() Ok(()) } -fn client_start(cfg: &config::Operating, scheme: &Scheme, miner: Arc) -> Result { +fn client_start( + timer_loop: &TimerLoop, + cfg: &config::Operating, + scheme: &Scheme, + miner: Arc, +) -> Result { cinfo!(CLIENT, "Starting client"); let db_path = cfg.db_path.as_ref().map(|s| s.as_str()).unwrap(); let client_path = Path::new(db_path); @@ -94,6 +99,9 @@ fn client_start(cfg: &config::Operating, scheme: &Scheme, miner: Arc) -> let service = ClientService::start(&client_config, &scheme, &client_path, miner) .map_err(|e| format!("Client service error: {}", e))?; + let reseal_timer = timer_loop.new_timer("Client reseal timer", service.client()); + service.client().register_reseal_timer(reseal_timer); + Ok(service) } @@ -229,7 +237,7 @@ pub fn run_node(matches: &ArgMatches) -> Result<(), String> { unlock_accounts(&*ap, &pf)?; let miner = new_miner(&config, &scheme, ap.clone())?; - let client = client_start(&config.operating, &scheme, miner.clone())?; + let client = client_start(&timer_loop, &config.operating, &scheme, miner.clone())?; let sync = BlockSyncExtension::new(client.client()); scheme.engine.register_chain_notify(client.client().as_ref()); diff --git a/core/Cargo.toml b/core/Cargo.toml index 65d9b26c79..e4126cd84a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,6 +14,7 @@ codechain-logger = { path = "../util/logger" } codechain-merkle = { path = "../util/merkle" } codechain-network = { path = "../network" } codechain-state = { path = "../state" } +codechain-timer = { path = "../util/timer" } codechain-types = { path = "../types" } codechain-stratum = { path = "../stratum" } codechain-vm = { path = "../vm" } diff --git a/core/src/client/client.rs b/core/src/client/client.rs index 53dba0ca16..dbdea8dcff 100644 --- a/core/src/client/client.rs +++ b/core/src/client/client.rs @@ -25,6 +25,7 @@ use cnetwork::NodeId; use cstate::{ ActionHandler, AssetScheme, AssetSchemeAddress, OwnedAsset, OwnedAssetAddress, StateDB, TopLevelState, TopStateView, }; +use ctimer::{TimeoutHandler, TimerApi, TimerToken}; use ctypes::invoice::Invoice; use ctypes::transaction::Transaction; use ctypes::{BlockNumber, ShardId}; @@ -41,7 +42,8 @@ use super::{ AccountData, AssetClient, Balance, BlockChain as BlockChainTrait, BlockChainClient, BlockChainInfo, BlockInfo, BlockProducer, ChainInfo, ChainNotify, ClientConfig, DatabaseClient, EngineClient, EngineInfo, Error as ClientError, ExecuteClient, ImportBlock, ImportResult, ImportSealedBlock, MiningBlockChainClient, - ParcelInfo, PrepareOpenBlock, RegularKey, RegularKeyOwner, ReopenBlock, Seq, Shard, StateOrBlock, TransactionInfo, + ParcelInfo, PrepareOpenBlock, RegularKey, RegularKeyOwner, ReopenBlock, ResealTimer, Seq, Shard, StateOrBlock, + TransactionInfo, }; use crate::block::{ClosedBlock, IsBlock, OpenBlock, SealedBlock}; use crate::blockchain::{ @@ -79,6 +81,9 @@ pub struct Client { genesis_accounts: Vec
, importer: Importer, + + /// Timer for reseal_min_period/reseal_max_period on miner client + reseal_timer: RwLock>, } impl Client { @@ -121,6 +126,7 @@ impl Client { queue_parcels: AtomicUsize::new(0), genesis_accounts, importer, + reseal_timer: RwLock::new(None), }); // ensure buffered changes are flushed. @@ -128,6 +134,10 @@ impl Client { Ok(client) } + pub fn register_reseal_timer(&self, timer: TimerApi) { + self.register_timer(timer); + } + /// Returns engine reference. pub fn engine(&self) -> &CodeChainEngine { &*self.engine @@ -260,6 +270,53 @@ impl Client { } } +const RESEAL_MAX_TIMER_TOKEN: TimerToken = 0; +const RESEAL_MIN_TIMER_TOKEN: TimerToken = 1; + +impl TimeoutHandler for Client { + fn on_timeout(&self, token: TimerToken) { + match token { + RESEAL_MAX_TIMER_TOKEN => { + // Working in PoW only + if self.engine().seals_internally().is_none() && !self.importer.miner.prepare_work_sealing(self) { + self.update_sealing(true); + } + } + RESEAL_MIN_TIMER_TOKEN => { + // Checking self.ready_parcels() for efficiency + if !self.ready_parcels().is_empty() { + self.update_sealing(false); + } + } + _ => unreachable!(), + } + } +} + +impl ResealTimer for Client { + fn register_timer(&self, timer: TimerApi) { + *self.reseal_timer.write() = Some(timer); + } + + fn set_max_timer(&self) { + if let Some(reseal_timer) = self.reseal_timer.read().as_ref() { + reseal_timer.cancel(RESEAL_MAX_TIMER_TOKEN).expect("Reseal max timer clear succeeds"); + reseal_timer + .schedule_once(self.importer.miner.get_options().reseal_max_period, RESEAL_MAX_TIMER_TOKEN) + .expect("Reseal max timer set succeeds"); + }; + } + + fn set_min_timer(&self) { + if let Some(reseal_timer) = self.reseal_timer.read().as_ref() { + reseal_timer.cancel(RESEAL_MIN_TIMER_TOKEN).expect("Reseal min timer clear succeeds"); + reseal_timer + .schedule_once(self.importer.miner.get_options().reseal_min_period, RESEAL_MIN_TIMER_TOKEN) + .expect("Reseal min timer set succeeds"); + }; + } +} + impl DatabaseClient for Client { fn database(&self) -> Arc { Arc::clone(&self.db()) @@ -367,8 +424,8 @@ impl EngineInfo for Client { impl EngineClient for Client { /// Make a new block and seal it. - fn update_sealing(&self) { - self.importer.miner.update_sealing(self) + fn update_sealing(&self, allow_empty_block: bool) { + self.importer.miner.update_sealing(self, allow_empty_block) } /// Submit a seal for a block in the mining queue. diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 9f09642502..cb44e6f982 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -35,6 +35,7 @@ use ckey::{Address, PlatformAddress, Public}; use cmerkle::Result as TrieResult; use cnetwork::NodeId; use cstate::{ActionHandler, AssetScheme, AssetSchemeAddress, OwnedAsset, TopStateView}; +use ctimer::TimerApi; use ctypes::invoice::Invoice; use ctypes::transaction::Transaction; use ctypes::{BlockNumber, ShardId}; @@ -101,7 +102,7 @@ pub trait EngineInfo: Send + Sync { /// Client facilities used by internally sealing Engines. pub trait EngineClient: Sync + Send + ChainInfo + ImportBlock + BlockInfo { /// Make a new block and seal it. - fn update_sealing(&self); + fn update_sealing(&self, allow_empty_block: bool); /// Submit a seal for a block in the mining queue. fn submit_seal(&self, block_hash: H256, seal: Vec); @@ -183,6 +184,16 @@ pub trait Shard { fn shard_root(&self, shard_id: ShardId, state: StateOrBlock) -> Option; } +/// Provides a timer API for reseal_min_period/reseal_max_period on miner client +pub trait ResealTimer { + /// Register timer API + fn register_timer(&self, timer: TimerApi); + /// Set reseal min timer as reseal_min_period, for creating blocks with parcels which are pending because of reseal_min_period + fn set_min_timer(&self); + /// Set reseal max timer as reseal_max_period, for creating empty blocks every reseal_max_period + fn set_max_timer(&self); +} + /// Provides methods to access account info pub trait AccountData: Seq + Balance {} @@ -200,7 +211,7 @@ pub trait BlockChain: ChainInfo + BlockInfo + ParcelInfo + TransactionInfo {} /// Blockchain database client. Owns and manages a blockchain and a block queue. pub trait BlockChainClient: - Sync + Send + AccountData + BlockChain + ImportBlock + RegularKeyOwner + ChainTimeInfo { + Sync + Send + AccountData + BlockChain + ImportBlock + RegularKeyOwner + ChainTimeInfo + ResealTimer { /// Get block queue information. fn queue_info(&self) -> BlockQueueInfo; diff --git a/core/src/client/test_client.rs b/core/src/client/test_client.rs index 3a85046a34..08ce336fef 100644 --- a/core/src/client/test_client.rs +++ b/core/src/client/test_client.rs @@ -39,6 +39,7 @@ use ckey::{public_to_address, Address, Generator, NetworkId, PlatformAddress, Ra use cmerkle::skewed_merkle_root; use cnetwork::NodeId; use cstate::{ActionHandler, StateDB}; +use ctimer::{TimeoutHandler, TimerApi, TimerToken}; use ctypes::invoice::Invoice; use ctypes::parcel::{Action, Parcel}; use ctypes::transaction::Transaction; @@ -55,8 +56,8 @@ use crate::blockchain_info::BlockChainInfo; use crate::client::ImportResult; use crate::client::{ AccountData, Balance, BlockChain, BlockChainClient, BlockInfo, BlockProducer, BlockStatus, ChainInfo, ImportBlock, - ImportSealedBlock, MiningBlockChainClient, ParcelInfo, PrepareOpenBlock, RegularKeyOwner, ReopenBlock, Seq, - StateOrBlock, TransactionInfo, + ImportSealedBlock, MiningBlockChainClient, ParcelInfo, PrepareOpenBlock, RegularKeyOwner, ReopenBlock, ResealTimer, + Seq, StateOrBlock, TransactionInfo, }; use crate::db::{COL_STATE, NUM_COLUMNS}; use crate::encoded; @@ -529,6 +530,18 @@ impl BlockChainClient for TestBlockChainClient { } } +impl TimeoutHandler for TestBlockChainClient { + fn on_timeout(&self, _token: TimerToken) {} +} + +impl ResealTimer for TestBlockChainClient { + fn register_timer(&self, _timer: TimerApi) {} + + fn set_max_timer(&self) {} + + fn set_min_timer(&self) {} +} + impl ChainTimeInfo for TestBlockChainClient { fn best_block_number(&self) -> u64 { 0 @@ -548,8 +561,8 @@ impl ChainTimeInfo for TestBlockChainClient { } impl super::EngineClient for TestBlockChainClient { - fn update_sealing(&self) { - self.miner.update_sealing(self) + fn update_sealing(&self, allow_empty_block: bool) { + self.miner.update_sealing(self, allow_empty_block) } fn submit_seal(&self, block_hash: H256, seal: Vec) { diff --git a/core/src/consensus/tendermint/mod.rs b/core/src/consensus/tendermint/mod.rs index 0482679a44..4bf10da20f 100644 --- a/core/src/consensus/tendermint/mod.rs +++ b/core/src/consensus/tendermint/mod.rs @@ -25,7 +25,8 @@ use std::sync::{Arc, Weak}; use ccrypto::blake256; use ckey::{public_to_address, recover, Address, Message, Password, Signature}; -use cnetwork::{Api, NetworkExtension, NetworkService, NodeId, TimeoutHandler, TimerToken}; +use cnetwork::{Api, NetworkExtension, NetworkService, NodeId}; +use ctimer::{TimeoutHandler, TimerToken}; use ctypes::machine::WithBalances; use ctypes::util::unexpected::{Mismatch, OutOfBounds}; use ctypes::BlockNumber; @@ -257,7 +258,7 @@ impl Tendermint { fn update_sealing(&self) { if let Some(ref weak) = *self.client.read() { if let Some(c) = weak.upgrade() { - c.update_sealing(); + c.update_sealing(true); } } } diff --git a/core/src/lib.rs b/core/src/lib.rs index faeda531f6..3ac1bad87f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -26,6 +26,7 @@ extern crate codechain_merkle as cmerkle; extern crate codechain_network as cnetwork; extern crate codechain_state as cstate; extern crate codechain_stratum as cstratum; +extern crate codechain_timer as ctimer; extern crate codechain_types as ctypes; extern crate codechain_vm as cvm; extern crate cuckoo; diff --git a/core/src/miner/miner.rs b/core/src/miner/miner.rs index 2c0b9c0080..41e9111da2 100644 --- a/core/src/miner/miner.rs +++ b/core/src/miner/miner.rs @@ -37,6 +37,7 @@ use crate::account_provider::{AccountProvider, SignError}; use crate::block::{Block, ClosedBlock, IsBlock}; use crate::client::{ AccountData, BlockChain, BlockProducer, ImportSealedBlock, MiningBlockChainClient, RegularKey, RegularKeyOwner, + ResealTimer, }; use crate::consensus::{CodeChainEngine, EngineType}; use crate::encoded; @@ -61,6 +62,8 @@ pub struct MinerOptions { pub reseal_min_period: Duration, /// Maximum period between blocks (enables force sealing after that). pub reseal_max_period: Duration, + /// Disable the reseal timer + pub no_reseal_timer: bool, /// Maximum size of the mem pool. pub mem_pool_size: usize, /// Maximum memory usage of parcels in the queue (current / future). @@ -78,6 +81,7 @@ impl Default for MinerOptions { reseal_on_own_parcel: true, reseal_min_period: Duration::from_secs(2), reseal_max_period: Duration::from_secs(120), + no_reseal_timer: false, mem_pool_size: 8192, mem_pool_memory_limit: Some(2 * 1024 * 1024), work_queue_size: 20, @@ -178,6 +182,10 @@ impl Miner { self.map_pending_block(|b| b.header().clone(), latest_block_number) } + pub fn get_options(&self) -> &MinerOptions { + &self.options + } + /// Check is reseal is allowed and necessary. fn requires_reseal(&self, best_block: BlockNumber) -> bool { let has_local_parcels = self.mem_pool.read().has_local_pending_parcels(); @@ -205,8 +213,6 @@ impl Miner { 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 { @@ -344,53 +350,6 @@ impl Miner { }) } - /// Returns true if we had to prepare new pending block. - fn prepare_work_sealing( - &self, - client: &C, - ) -> bool { - ctrace!(MINER, "prepare_work_sealing: entering"); - let prepare_new = { - let mut sealing_work = self.sealing_work.lock(); - let have_work = sealing_work.queue.peek_last_ref().is_some(); - ctrace!(MINER, "prepare_work_sealing: have_work={}", have_work); - if !have_work { - sealing_work.enabled = true; - true - } else { - false - } - }; - if prepare_new { - // -------------------------------------------------------------------------- - // | NOTE Code below requires transaction_queue and sealing_work locks. | - // | Make sure to release the locks before calling that method. | - // -------------------------------------------------------------------------- - match self.prepare_block(client) { - Ok((block, original_work_hash)) => { - self.prepare_work(block, original_work_hash); - } - Err(err) => { - ctrace!(MINER, "prepare_work_sealing: cannot prepare block: {:?}", err); - } - } - } - let mut sealing_block_last_request = self.sealing_block_last_request.lock(); - let best_number = client.chain_info().best_block_number; - if *sealing_block_last_request != best_number { - ctrace!( - MINER, - "prepare_work_sealing: Miner received request (was {}, now {}) - waking up.", - *sealing_block_last_request, - best_number - ); - *sealing_block_last_request = best_number; - } - - // Return if we restarted - prepare_new - } - /// Prepares work which has to be done to seal. fn prepare_work(&self, block: ClosedBlock, original_work_hash: Option) { let (work, is_new) = { @@ -707,14 +666,67 @@ impl MinerService for Miner { self.engine.engine_type() } - fn update_sealing(&self, chain: &C) + fn prepare_work_sealing( + &self, + client: &C, + ) -> bool { + ctrace!(MINER, "prepare_work_sealing: entering"); + let prepare_new = { + let mut sealing_work = self.sealing_work.lock(); + let have_work = sealing_work.queue.peek_last_ref().is_some(); + ctrace!(MINER, "prepare_work_sealing: have_work={}", have_work); + if !have_work { + sealing_work.enabled = true; + true + } else { + false + } + }; + if prepare_new { + // -------------------------------------------------------------------------- + // | NOTE Code below requires transaction_queue and sealing_work locks. | + // | Make sure to release the locks before calling that method. | + // -------------------------------------------------------------------------- + match self.prepare_block(client) { + Ok((block, original_work_hash)) => { + self.prepare_work(block, original_work_hash); + } + Err(err) => { + ctrace!(MINER, "prepare_work_sealing: cannot prepare block: {:?}", err); + } + } + } + let mut sealing_block_last_request = self.sealing_block_last_request.lock(); + let best_number = client.chain_info().best_block_number; + if *sealing_block_last_request != best_number { + ctrace!( + MINER, + "prepare_work_sealing: Miner received request (was {}, now {}) - waking up.", + *sealing_block_last_request, + best_number + ); + *sealing_block_last_request = best_number; + } + + // Return if we restarted + prepare_new + } + + fn update_sealing(&self, chain: &C, allow_empty_block: bool) where - C: AccountData + BlockChain + BlockProducer + ImportSealedBlock + RegularKeyOwner + ChainTimeInfo, { + C: AccountData + BlockChain + BlockProducer + ImportSealedBlock + RegularKeyOwner + ResealTimer + ChainTimeInfo, + { ctrace!(MINER, "update_sealing: preparing a block"); if self.requires_reseal(chain.chain_info().best_block_number) { let (block, original_work_hash) = match self.prepare_block(chain) { - Ok((block, original_work_hash)) => (block, original_work_hash), + Ok((block, original_work_hash)) => { + if !allow_empty_block && block.block().parcels().is_empty() { + ctrace!(MINER, "update_sealing: block is empty, and allow_empty_block is false"); + return + } + (block, original_work_hash) + } Err(err) => { ctrace!(MINER, "update_sealing: cannot prepare block: {:?}", err); return @@ -728,12 +740,26 @@ impl MinerService for Miner { ctrace!(MINER, "update_sealing: imported internally sealed block"); } } - Some(false) => ctrace!(MINER, "update_sealing: engine is not keen to seal internally right now"), + Some(false) => { + ctrace!(MINER, "update_sealing: engine is not keen to seal internally right now"); + return + } None => { ctrace!(MINER, "update_sealing: engine does not seal internally, preparing work"); - self.prepare_work(block, original_work_hash) + self.prepare_work(block, original_work_hash); + // Set the reseal max timer, for creating empty blocks every reseal_max_period + // Not related to next_mandatory_reseal, which is used in seal_and_import_block_internally + if !self.options.no_reseal_timer { + chain.set_max_timer(); + } } } + + // Sealing successful + *self.next_allowed_reseal.lock() = Instant::now() + self.options.reseal_min_period; + if !self.options.no_reseal_timer { + chain.set_min_timer(); + } } } @@ -793,7 +819,7 @@ impl MinerService for Miner { // | NOTE Code below requires mem_pool and sealing_queue locks. | // | Make sure to release the locks before calling that method. | // ------------------------------------------------------------------ - self.update_sealing(client); + self.update_sealing(client, false); } results } @@ -838,7 +864,7 @@ impl MinerService for Miner { // If new block has not been prepared (means we already had one) // or Engine might be able to seal internally, // we need to update sealing. - self.update_sealing(chain); + self.update_sealing(chain, false); } imported } @@ -906,7 +932,7 @@ impl MinerService for Miner { // ------------------------------------------------------------------ if self.parcel_reseal_allowed() { cdebug!(MINER, "Update sealing"); - self.update_sealing(client); + self.update_sealing(client, true); } } diff --git a/core/src/miner/mod.rs b/core/src/miner/mod.rs index bc8b029e36..b10f5b5c5a 100644 --- a/core/src/miner/mod.rs +++ b/core/src/miner/mod.rs @@ -34,6 +34,7 @@ use crate::account_provider::{AccountProvider, SignError}; use crate::block::ClosedBlock; use crate::client::{ AccountData, BlockChain, BlockProducer, ImportSealedBlock, MiningBlockChainClient, RegularKey, RegularKeyOwner, + ResealTimer, }; use crate::consensus::EngineType; use crate::error::Error; @@ -79,10 +80,15 @@ pub trait MinerService: Send + Sync { /// Get the type of consensus engine. fn engine_type(&self) -> EngineType; + /// Returns true if we had to prepare new pending block. + fn prepare_work_sealing(&self, &C) -> bool + where + C: AccountData + BlockChain + BlockProducer + RegularKeyOwner + ChainTimeInfo; + /// New chain head event. Restart mining operation. - fn update_sealing(&self, chain: &C) + fn update_sealing(&self, chain: &C, allow_empty_block: bool) where - C: AccountData + BlockChain + BlockProducer + ImportSealedBlock + RegularKeyOwner + ChainTimeInfo; + C: AccountData + BlockChain + BlockProducer + ImportSealedBlock + RegularKeyOwner + ResealTimer + ChainTimeInfo; /// 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. diff --git a/discovery/Cargo.toml b/discovery/Cargo.toml index 93c3539b67..753a9d9196 100644 --- a/discovery/Cargo.toml +++ b/discovery/Cargo.toml @@ -8,6 +8,7 @@ codechain-crypto = { path = "../crypto" } codechain-key = { path = "../key" } codechain-logger = { path = "../util/logger" } codechain-network = { path = "../network" } +codechain-timer = { path = "../util/timer" } log = "0.4.6" parking_lot = "0.6.0" primitives = { path = "../util/primitives" } diff --git a/discovery/src/kademlia/extension.rs b/discovery/src/kademlia/extension.rs index 89480dc6a3..93cb53823d 100644 --- a/discovery/src/kademlia/extension.rs +++ b/discovery/src/kademlia/extension.rs @@ -17,7 +17,8 @@ use std::collections::HashSet; use std::sync::Arc; -use cnetwork::{Api, DiscoveryApi, IntoSocketAddr, NetworkExtension, NodeId, RoutingTable, TimeoutHandler, TimerToken}; +use cnetwork::{Api, DiscoveryApi, IntoSocketAddr, NetworkExtension, NodeId, RoutingTable}; +use ctimer::{TimeoutHandler, TimerToken}; use parking_lot::RwLock; use rlp::{Decodable, Encodable, UntrustedRlp}; use time::Duration; diff --git a/discovery/src/lib.rs b/discovery/src/lib.rs index b434b13c29..b31d8dad60 100644 --- a/discovery/src/lib.rs +++ b/discovery/src/lib.rs @@ -29,6 +29,7 @@ extern crate codechain_key as ckey; #[macro_use] extern crate codechain_logger as clogger; extern crate codechain_network as cnetwork; +extern crate codechain_timer as ctimer; mod kademlia; mod unstructured; diff --git a/discovery/src/unstructured/extension.rs b/discovery/src/unstructured/extension.rs index bdc7947efa..d1a5578b3e 100644 --- a/discovery/src/unstructured/extension.rs +++ b/discovery/src/unstructured/extension.rs @@ -17,7 +17,8 @@ use std::collections::HashSet; use std::sync::Arc; -use cnetwork::{Api, DiscoveryApi, IntoSocketAddr, NetworkExtension, NodeId, RoutingTable, TimeoutHandler, TimerToken}; +use cnetwork::{Api, DiscoveryApi, IntoSocketAddr, NetworkExtension, NodeId, RoutingTable}; +use ctimer::{TimeoutHandler, TimerToken}; use parking_lot::RwLock; use rand::prelude::SliceRandom; use rand::thread_rng; diff --git a/network/src/extension.rs b/network/src/extension.rs index 13d87721f7..a94f4cd3bf 100644 --- a/network/src/extension.rs +++ b/network/src/extension.rs @@ -22,7 +22,7 @@ use cio::IoError; use time::Duration; use crate::NodeId; -pub use ctimer::{TimeoutHandler, TimerScheduleError, TimerToken}; +use ctimer::{TimeoutHandler, TimerScheduleError, TimerToken}; #[derive(Debug)] pub enum Error { diff --git a/network/src/lib.rs b/network/src/lib.rs index 8069183590..4faa16049a 100644 --- a/network/src/lib.rs +++ b/network/src/lib.rs @@ -62,7 +62,6 @@ pub use crate::control::{Control as NetworkControl, Error as NetworkControlError pub use crate::discovery::Api as DiscoveryApi; pub use crate::extension::{ Api, Error as NetworkExtensionError, Extension as NetworkExtension, Result as NetworkExtensionResult, - TimeoutHandler, TimerToken, }; pub use crate::node_id::{IntoSocketAddr, NodeId}; pub use crate::service::{Error as NetworkServiceError, Service as NetworkService}; diff --git a/network/src/test/client.rs b/network/src/test/client.rs index 846d0e2828..98501cd4c1 100644 --- a/network/src/test/client.rs +++ b/network/src/test/client.rs @@ -18,10 +18,11 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::ops::Deref; use std::sync::{Arc, Weak}; +use ctimer::TimerToken; use parking_lot::Mutex; use time::Duration; -use crate::extension::{Api, Extension, Result, TimerToken}; +use crate::extension::{Api, Extension, Result}; use crate::NodeId; #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] diff --git a/sync/Cargo.toml b/sync/Cargo.toml index ef242d09a9..59e2661f15 100644 --- a/sync/Cargo.toml +++ b/sync/Cargo.toml @@ -13,6 +13,7 @@ codechain-merkle = { path = "../util/merkle" } codechain-network = { path = "../network" } codechain-state = { path = "../state" } codechain-token-generator = { path = "../util/token_generator" } +codechain-timer = { path = "../util/timer" } codechain-types = { path = "../types" } journaldb = { path = "../util/journaldb" } kvdb = { path = "../util/kvdb" } diff --git a/sync/src/block/extension.rs b/sync/src/block/extension.rs index c6bdda7707..e4b1610af5 100644 --- a/sync/src/block/extension.rs +++ b/sync/src/block/extension.rs @@ -23,8 +23,9 @@ use ccore::{ Block, BlockChainClient, BlockId, BlockImportError, BlockInfo, ChainInfo, ChainNotify, Client, Header, ImportBlock, ImportError, Seal, UnverifiedParcel, }; -use cnetwork::{Api, NetworkExtension, NodeId, TimeoutHandler, TimerToken}; +use cnetwork::{Api, NetworkExtension, NodeId}; use cstate::find_handler_for_id; +use ctimer::{TimeoutHandler, TimerToken}; use ctoken_generator::TokenGenerator; use ctypes::parcel::Action; use ctypes::BlockNumber; diff --git a/sync/src/lib.rs b/sync/src/lib.rs index 7178b6db28..db46041732 100644 --- a/sync/src/lib.rs +++ b/sync/src/lib.rs @@ -22,6 +22,7 @@ extern crate codechain_merkle as cmerkle; extern crate codechain_logger as clogger; extern crate codechain_network as cnetwork; extern crate codechain_state as cstate; +extern crate codechain_timer as ctimer; extern crate codechain_token_generator as ctoken_generator; extern crate codechain_types as ctypes; diff --git a/sync/src/parcel/extension.rs b/sync/src/parcel/extension.rs index 731b523c1c..e1e0e2be4c 100644 --- a/sync/src/parcel/extension.rs +++ b/sync/src/parcel/extension.rs @@ -18,7 +18,8 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::sync::Arc; use ccore::BlockChainClient; -use cnetwork::{Api, NetworkExtension, NodeId, TimeoutHandler, TimerToken}; +use cnetwork::{Api, NetworkExtension, NodeId}; +use ctimer::{TimeoutHandler, TimerToken}; use parking_lot::RwLock; use primitives::H256; use rlp::{Encodable, UntrustedRlp}; diff --git a/test/src/integration.long/timelock.test.ts b/test/src/integration.long/timelock.test.ts index d4c0d7815a..1f032a2651 100644 --- a/test/src/integration.long/timelock.test.ts +++ b/test/src/integration.long/timelock.test.ts @@ -33,7 +33,7 @@ describe("Timelock", function() { beforeEach(async function() { node = new CodeChain({ - argv: ["--force-sealing"], + argv: ["--force-sealing", "--no-reseal-timer"], base: BASE }); await node.start(); @@ -157,9 +157,10 @@ describe("Timelock", function() { value: 3 }); - expect(await node.getBestBlockNumber()).to.equal(2); + expect(await node.getBestBlockNumber()).to.equal(1); await checkTx(txhash, false); + await node.sdk.rpc.devel.startSealing(); await node.sdk.rpc.devel.startSealing(); expect(await node.getBestBlockNumber()).to.equal(3); @@ -173,7 +174,7 @@ describe("Timelock", function() { value: 3 }); - for (let i = 2; i <= 3; i++) { + for (let i = 1; i <= 3; i++) { expect(await node.getBestBlockNumber()).to.equal(i); await checkTx(txhash, false); @@ -278,9 +279,10 @@ describe("Timelock", function() { await node.signTransactionInput(tx, 1); await node.sendTransaction(tx, { awaitInvoice: false }); - expect(await node.getBestBlockNumber()).to.equal(3); + expect(await node.getBestBlockNumber()).to.equal(2); await checkTx(tx.hash(), false); + await node.sdk.rpc.devel.startSealing(); await node.sdk.rpc.devel.startSealing(); expect(await node.getBestBlockNumber()).to.equal(4); await checkTx(tx.hash(), false); @@ -314,9 +316,10 @@ describe("Timelock", function() { await node.signTransactionInput(tx, 1); await node.sendTransaction(tx, { awaitInvoice: false }); - expect(await node.getBestBlockNumber()).to.equal(3); + expect(await node.getBestBlockNumber()).to.equal(2); await checkTx(tx.hash(), false); + await node.sdk.rpc.devel.startSealing(); await node.sdk.rpc.devel.startSealing(); expect(await node.getBestBlockNumber()).to.equal(4); await checkTx(tx.hash(), false); @@ -350,9 +353,10 @@ describe("Timelock", function() { await node.signTransactionInput(tx, 1); await node.sendTransaction(tx, { awaitInvoice: false }); - expect(await node.getBestBlockNumber()).to.equal(3); + expect(await node.getBestBlockNumber()).to.equal(2); await checkTx(tx.hash(), false); + await node.sdk.rpc.devel.startSealing(); await node.sdk.rpc.devel.startSealing(); expect(await node.getBestBlockNumber()).to.equal(4); await checkTx(tx.hash(), true); @@ -381,14 +385,15 @@ describe("Timelock", function() { await node.signTransactionInput(tx, 1); await node.sendTransaction(tx, { awaitInvoice: false }); - expect(await node.getBestBlockNumber()).to.equal(3); + expect(await node.getBestBlockNumber()).to.equal(2); await checkTx(tx.hash(), false); + await node.sdk.rpc.devel.startSealing(); await node.sdk.rpc.devel.startSealing(); expect(await node.getBestBlockNumber()).to.equal(4); await checkTx(tx.hash(), false); - await wait(3000); + await wait(3_000); await node.sdk.rpc.devel.startSealing(); await node.sdk.rpc.devel.startSealing(); diff --git a/test/src/integration/mempool.test.ts b/test/src/integration/mempool.test.ts index 30a0300bf3..9bd8a0b598 100644 --- a/test/src/integration/mempool.test.ts +++ b/test/src/integration/mempool.test.ts @@ -80,7 +80,7 @@ describe("Timelock", function() { beforeEach(async function() { node = new CodeChain({ - argv: ["--force-sealing"] + argv: ["--force-sealing", "--no-reseal-timer"] }); await node.start(); }); @@ -144,6 +144,8 @@ describe("Timelock", function() { await node.sdk.rpc.devel.startSealing(); expect(await node.getBestBlockNumber()).to.equal(2); + await checkTx(txhash1, false); + await checkTx(txhash2, false); await node.sdk.rpc.devel.startSealing(); expect(await node.getBestBlockNumber()).to.equal(3);