diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index ca1a8584e2a0b..70a7bb47873f6 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -37,6 +37,7 @@ use std::borrow::Cow; use std::thread; use std::collections::HashMap; use std::marker::PhantomData; +use std::cmp::Ordering; use sc_client_api::{BlockOf, backend::AuxStore}; use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId}; use sp_block_builder::BlockBuilder as BlockBuilderApi; @@ -170,6 +171,19 @@ pub trait PowAlgorithm { ) -> Result, Error> { Ok(None) } + /// Break a fork choice tie. + /// + /// By default this chooses the earliest block seen. Using uniform tie + /// breaking algorithms will help to protect against selfish mining. + /// + /// Returns if the new seal should be considered best block. + fn break_tie( + &self, + _own_seal: &Seal, + _new_seal: &Seal, + ) -> bool { + false + } /// Verify that the difficulty is valid against given seal. fn verify( &self, @@ -194,7 +208,7 @@ pub trait PowAlgorithm { pub struct PowBlockImport { algorithm: Algorithm, inner: I, - select_chain: Option, + select_chain: S, client: Arc, inherent_data_providers: sp_inherents::InherentDataProviders, check_inherents_after: <::Header as HeaderT>::Number, @@ -232,7 +246,7 @@ impl PowBlockImport wher client: Arc, algorithm: Algorithm, check_inherents_after: <::Header as HeaderT>::Number, - select_chain: Option, + select_chain: S, inherent_data_providers: sp_inherents::InherentDataProviders, can_author_with: CAW, ) -> Self { @@ -324,12 +338,9 @@ impl BlockImport for PowBlockImport, new_cache: HashMap>, ) -> Result { - let best_hash = match self.select_chain.as_ref() { - Some(select_chain) => select_chain.best_chain() - .map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))? - .hash(), - None => self.client.info().best_hash, - }; + let best_header = self.select_chain.best_chain() + .map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))?; + let best_hash = best_header.hash(); let parent_hash = *block.header.parent_hash(); let best_aux = PowAux::read::<_, B>(self.client.as_ref(), &best_hash)?; @@ -352,16 +363,7 @@ impl BlockImport for PowBlockImport { - if id == &POW_ENGINE_ID { - seal.clone() - } else { - return Err(Error::::WrongEngine(*id).into()) - } - }, - _ => return Err(Error::::HeaderUnsealed(block.header.hash()).into()), - }; + let inner_seal = fetch_seal::(block.post_digests.last(), block.header.hash())?; let intermediate = block.take_intermediate::>( INTERMEDIATE_KEY @@ -391,7 +393,18 @@ impl BlockImport for PowBlockImport best_aux.total_difficulty + match aux.total_difficulty.cmp(&best_aux.total_difficulty) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + let best_inner_seal = fetch_seal::( + best_header.digest().logs.last(), + best_hash, + )?; + + self.algorithm.break_tie(&best_inner_seal, &inner_seal) + }, + } )); } @@ -729,3 +742,20 @@ fn find_pre_digest(header: &B::Header) -> Result>, Err Ok(pre_digest) } + +/// Fetch PoW seal. +fn fetch_seal( + digest: Option<&DigestItem>, + hash: B::Hash, +) -> Result, Error> { + match digest { + Some(DigestItem::Seal(id, seal)) => { + if id == &POW_ENGINE_ID { + Ok(seal.clone()) + } else { + return Err(Error::::WrongEngine(*id).into()) + } + }, + _ => return Err(Error::::HeaderUnsealed(hash).into()), + } +}