diff --git a/Cargo.lock b/Cargo.lock index 9d80227547e01..70433417519d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4861,6 +4861,7 @@ dependencies = [ name = "substrate-consensus-pow-primitives" version = "2.0.0" dependencies = [ + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "substrate-client 2.0.0", diff --git a/core/consensus/pow/primitives/Cargo.toml b/core/consensus/pow/primitives/Cargo.toml index a7e0d284b09fa..021e4c80a754c 100644 --- a/core/consensus/pow/primitives/Cargo.toml +++ b/core/consensus/pow/primitives/Cargo.toml @@ -10,6 +10,7 @@ substrate-client = { path = "../../../client", default-features = false } rstd = { package = "sr-std", path = "../../../sr-std", default-features = false } sr-primitives = { path = "../../../sr-primitives", default-features = false } primitives = { package = "substrate-primitives", path = "../../../primitives", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } [features] default = ["std"] @@ -18,4 +19,5 @@ std = [ "substrate-client/std", "sr-primitives/std", "primitives/std", + "codec/std", ] diff --git a/core/consensus/pow/primitives/src/lib.rs b/core/consensus/pow/primitives/src/lib.rs index 807a7b2df2c90..2079b1cbe7e88 100644 --- a/core/consensus/pow/primitives/src/lib.rs +++ b/core/consensus/pow/primitives/src/lib.rs @@ -20,17 +20,46 @@ use rstd::vec::Vec; use sr_primitives::ConsensusEngineId; +use codec::Decode; +use substrate_client::decl_runtime_apis; /// The `ConsensusEngineId` of PoW. pub const POW_ENGINE_ID: ConsensusEngineId = [b'p', b'o', b'w', b'_']; -/// Type of difficulty. -/// -/// For runtime designed for Substrate, it's always possible to fit its total -/// difficulty range under `u128::max_value()` because it can be freely scaled -/// up or scaled down. Very few PoW chains use difficulty values -/// larger than `u128::max_value()`. -pub type Difficulty = u128; - /// Type of seal. pub type Seal = Vec; + +/// Define methods that total difficulty should implement. +pub trait TotalDifficulty { + fn increment(&mut self, other: Self); +} + +impl TotalDifficulty for primitives::U256 { + fn increment(&mut self, other: Self) { + let ret = self.saturating_add(other); + *self = ret; + } +} + +impl TotalDifficulty for u128 { + fn increment(&mut self, other: Self) { + let ret = self.saturating_add(other); + *self = ret; + } +} + +decl_runtime_apis! { + /// API necessary for timestamp-based difficulty adjustment algorithms. + pub trait TimestampApi { + /// Return the timestamp in the current block. + fn timestamp() -> Moment; + } + + /// API for those chains that put their difficulty adjustment algorithm directly + /// onto runtime. Note that while putting difficulty adjustment algorithm to + /// runtime is safe, putting the PoW algorithm on runtime is not. + pub trait DifficultyApi { + /// Return the target difficulty of the next block. + fn difficulty() -> Difficulty; + } +} diff --git a/core/consensus/pow/src/lib.rs b/core/consensus/pow/src/lib.rs index 8bc0d3593a026..7f282e20523bf 100644 --- a/core/consensus/pow/src/lib.rs +++ b/core/consensus/pow/src/lib.rs @@ -41,12 +41,12 @@ use sr_primitives::Justification; use sr_primitives::generic::{BlockId, Digest, DigestItem}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, ProvideRuntimeApi}; use srml_timestamp::{TimestampInherentData, InherentError as TIError}; -use pow_primitives::{Difficulty, Seal, POW_ENGINE_ID}; +use pow_primitives::{Seal, TotalDifficulty, POW_ENGINE_ID}; use primitives::H256; use inherents::{InherentDataProviders, InherentData}; use consensus_common::{ - BlockImportParams, BlockOrigin, ForkChoiceStrategy, - Environment, Proposer, + BlockImportParams, BlockOrigin, ForkChoiceStrategy, SyncOracle, Environment, Proposer, + SelectChain, }; use consensus_common::import_queue::{BoxBlockImport, BasicQueue, Verifier}; use codec::{Encode, Decode}; @@ -63,59 +63,78 @@ fn aux_key(hash: &H256) -> Vec { /// Auxiliary storage data for PoW. #[derive(Encode, Decode, Clone, Debug, Default)] -pub struct PowAux { - /// Total difficulty. +pub struct PowAux { + /// Difficulty of the current block. + pub difficulty: Difficulty, + /// Total difficulty up to current block. pub total_difficulty: Difficulty, } -impl PowAux { +impl PowAux where + Difficulty: Decode + Default, +{ /// Read the auxiliary from client. pub fn read(client: &C, hash: &H256) -> Result { let key = aux_key(hash); match client.get_aux(&key).map_err(|e| format!("{:?}", e))? { - Some(bytes) => PowAux::decode(&mut &bytes[..]).map_err(|e| format!("{:?}", e)), - None => Ok(PowAux::default()), + Some(bytes) => Self::decode(&mut &bytes[..]) + .map_err(|e| format!("{:?}", e)), + None => Ok(Self::default()), } } } /// Algorithm used for proof of work. pub trait PowAlgorithm { + /// Difficulty for the algorithm. + type Difficulty: TotalDifficulty + Default + Encode + Decode + Ord + Clone + Copy; + /// Get the next block's difficulty. - fn difficulty(&self, parent: &BlockId) -> Result; + fn difficulty(&self, parent: &BlockId) -> Result; /// Verify proof of work against the given difficulty. fn verify( &self, parent: &BlockId, pre_hash: &H256, seal: &Seal, - difficulty: Difficulty, + difficulty: Self::Difficulty, ) -> Result; /// Mine a seal that satisfy the given difficulty. fn mine( &self, parent: &BlockId, pre_hash: &H256, - seed: &H256, - difficulty: Difficulty, + difficulty: Self::Difficulty, round: u32, ) -> Result, String>; } /// A verifier for PoW blocks. -pub struct PowVerifier { +pub struct PowVerifier, C, S, Algorithm> { client: Arc, algorithm: Algorithm, inherent_data_providers: inherents::InherentDataProviders, + select_chain: Option, + check_inherents_after: <::Header as HeaderT>::Number, } -impl PowVerifier { - fn check_header>( +impl, C, S, Algorithm> PowVerifier { + pub fn new( + client: Arc, + algorithm: Algorithm, + check_inherents_after: <::Header as HeaderT>::Number, + select_chain: Option, + inherent_data_providers: inherents::InherentDataProviders, + ) -> Self { + Self { client, algorithm, inherent_data_providers, select_chain, check_inherents_after } + } + + fn check_header( &self, mut header: B::Header, parent_block_id: BlockId, - ) -> Result<(B::Header, Difficulty, DigestItem), String> where + ) -> Result<(B::Header, Algorithm::Difficulty, DigestItem), String> where Algorithm: PowAlgorithm, { let hash = header.hash(); @@ -146,7 +165,7 @@ impl PowVerifier { Ok((header, difficulty, seal)) } - fn check_inherents>( + fn check_inherents( &self, block: B, block_id: BlockId, @@ -157,6 +176,10 @@ impl PowVerifier { { const MAX_TIMESTAMP_DRIFT_SECS: u64 = 60; + if *block.header().number() < self.check_inherents_after { + return Ok(()) + } + let inherent_res = self.client.runtime_api().check_inherents( &block_id, block, @@ -183,9 +206,10 @@ impl PowVerifier { } } -impl, C, Algorithm> Verifier for PowVerifier where +impl, C, S, Algorithm> Verifier for PowVerifier where C: ProvideRuntimeApi + Send + Sync + HeaderBackend + AuxStore + ProvideCache + BlockOf, C::Api: BlockBuilderApi, + S: SelectChain, Algorithm: PowAlgorithm + Send + Sync, { fn verify( @@ -199,17 +223,23 @@ impl, C, Algorithm> Verifier for PowVerifier select_chain.best_chain() + .map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))? + .hash(), + None => self.client.info().best_hash, + }; let hash = header.hash(); let parent_hash = *header.parent_hash(); let best_aux = PowAux::read(self.client.as_ref(), &best_hash)?; let mut aux = PowAux::read(self.client.as_ref(), &parent_hash)?; - let (checked_header, difficulty, seal) = self.check_header::( + let (checked_header, difficulty, seal) = self.check_header( header, BlockId::Hash(parent_hash), )?; - aux.total_difficulty = aux.total_difficulty.saturating_add(difficulty); + aux.difficulty = difficulty; + aux.total_difficulty.increment(difficulty); if let Some(inner_body) = body.take() { let block = B::new(checked_header.clone(), inner_body); @@ -241,7 +271,7 @@ impl, C, Algorithm> Verifier for PowVerifier Result<(), consensus_common::Error> { if !inherent_data_providers.has_provider(&srml_timestamp::INHERENT_IDENTIFIER) { @@ -258,10 +288,12 @@ fn register_pow_inherent_data_provider( pub type PowImportQueue = BasicQueue; /// Import queue for PoW engine. -pub fn import_queue( +pub fn import_queue( block_import: BoxBlockImport, client: Arc, algorithm: Algorithm, + check_inherents_after: <::Header as HeaderT>::Number, + select_chain: Option, inherent_data_providers: InherentDataProviders, ) -> Result, consensus_common::Error> where B: BlockT, @@ -269,14 +301,17 @@ pub fn import_queue( C: Send + Sync + AuxStore + 'static, C::Api: BlockBuilderApi, Algorithm: PowAlgorithm + Send + Sync + 'static, + S: SelectChain + 'static, { register_pow_inherent_data_provider(&inherent_data_providers)?; - let verifier = PowVerifier { - client: client.clone(), + let verifier = PowVerifier::new( + client.clone(), algorithm, + check_inherents_after, + select_chain, inherent_data_providers, - }; + ); Ok(BasicQueue::new( verifier, @@ -296,19 +331,24 @@ pub fn import_queue( /// information, or just be a graffiti. `round` is for number of rounds the /// CPU miner runs each time. This parameter should be tweaked so that each /// mining round is within sub-second time. -pub fn start_mine, C, Algorithm, E>( +pub fn start_mine, C, Algorithm, E, SO, S>( mut block_import: BoxBlockImport, client: Arc, algorithm: Algorithm, mut env: E, preruntime: Option>, round: u32, + mut sync_oracle: SO, + build_time: std::time::Duration, + select_chain: Option, inherent_data_providers: inherents::InherentDataProviders, ) where C: HeaderBackend + AuxStore + 'static, Algorithm: PowAlgorithm + Send + Sync + 'static, E: Environment + Send + Sync + 'static, E::Error: std::fmt::Debug, + SO: SyncOracle + Send + Sync + 'static, + S: SelectChain + 'static, { if let Err(_) = register_pow_inherent_data_provider(&inherent_data_providers) { warn!("Registering inherent data provider for timestamp failed"); @@ -323,6 +363,9 @@ pub fn start_mine, C, Algorithm, E>( &mut env, preruntime.as_ref(), round, + &mut sync_oracle, + build_time.clone(), + select_chain.as_ref(), &inherent_data_providers ) { Ok(()) => (), @@ -331,31 +374,52 @@ pub fn start_mine, C, Algorithm, E>( e ), } - std::thread::sleep(std::time::Duration::new(1, 0)); } }); } -fn mine_loop, C, Algorithm, E>( +fn mine_loop, C, Algorithm, E, SO, S>( block_import: &mut BoxBlockImport, client: &C, algorithm: &Algorithm, env: &mut E, preruntime: Option<&Vec>, round: u32, + sync_oracle: &mut SO, + build_time: std::time::Duration, + select_chain: Option<&S>, inherent_data_providers: &inherents::InherentDataProviders, ) -> Result<(), String> where C: HeaderBackend + AuxStore, Algorithm: PowAlgorithm, E: Environment, E::Error: std::fmt::Debug, + SO: SyncOracle, + S: SelectChain, { 'outer: loop { - let best_hash = client.info().best_hash; - let best_header = client.header(BlockId::Hash(best_hash)) - .map_err(|e| format!("Fetching best header failed: {:?}", e))? - .ok_or("Best header does not exist")?; + if sync_oracle.is_major_syncing() { + debug!(target: "pow", "Skipping proposal due to sync."); + std::thread::sleep(std::time::Duration::new(1, 0)); + continue 'outer + } + + let (best_hash, best_header) = match select_chain { + Some(select_chain) => { + let header = select_chain.best_chain() + .map_err(|e| format!("Fetching best header failed using select chain: {:?}", e))?; + let hash = header.hash(); + (hash, header) + }, + None => { + let hash = client.info().best_hash; + let header = client.header(BlockId::Hash(hash)) + .map_err(|e| format!("Fetching best header failed: {:?}", e))? + .ok_or("Best header does not exist")?; + (hash, header) + }, + }; let mut aux = PowAux::read(client, &best_hash)?; let mut proposer = env.init(&best_header).map_err(|e| format!("{:?}", e))?; @@ -368,21 +432,19 @@ fn mine_loop, C, Algorithm, E>( let block = futures::executor::block_on(proposer.propose( inherent_data, inherent_digest, - std::time::Duration::new(0, 0) + build_time.clone(), )).map_err(|e| format!("Block proposing error: {:?}", e))?; let (header, body) = block.deconstruct(); - let seed = H256::random(); let (difficulty, seal) = { - loop { - let difficulty = algorithm.difficulty( - &BlockId::Hash(best_hash), - )?; + let difficulty = algorithm.difficulty( + &BlockId::Hash(best_hash), + )?; + loop { let seal = algorithm.mine( &BlockId::Hash(best_hash), &header.hash(), - &seed, difficulty, round, )?; @@ -397,10 +459,28 @@ fn mine_loop, C, Algorithm, E>( } }; - aux.total_difficulty = aux.total_difficulty.saturating_add(difficulty); - let hash = header.hash(); + aux.difficulty = difficulty; + aux.total_difficulty.increment(difficulty); + let hash = { + let mut header = header.clone(); + header.digest_mut().push(DigestItem::Seal(POW_ENGINE_ID, seal.clone())); + header.hash() + }; let key = aux_key(&hash); + let best_hash = match select_chain { + Some(select_chain) => select_chain.best_chain() + .map_err(|e| format!("Fetch best hash failed via select chain: {:?}", e))? + .hash(), + None => client.info().best_hash, + }; + let best_aux = PowAux::::read(client, &best_hash)?; + + // if the best block has changed in the meantime drop our proposal + if best_aux.total_difficulty > aux.total_difficulty { + continue 'outer + } + let import_block = BlockImportParams { origin: BlockOrigin::Own, header, diff --git a/core/network/src/chain.rs b/core/network/src/chain.rs index 1a1f649cae2dd..f68942fd96d38 100644 --- a/core/network/src/chain.rs +++ b/core/network/src/chain.rs @@ -83,6 +83,12 @@ pub trait FinalityProofProvider: Send + Sync { fn prove_finality(&self, for_block: Block::Hash, request: &[u8]) -> Result>, Error>; } +impl FinalityProofProvider for () { + fn prove_finality(&self, _for_block: Block::Hash, _request: &[u8]) -> Result>, Error> { + Ok(None) + } +} + impl Client for SubstrateClient where B: client::backend::Backend + Send + Sync + 'static, E: CallExecutor + Send + Sync + 'static,