Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

pow: support uniform tie breaking in fork choice #7073

Merged
3 commits merged into from
Sep 14, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 49 additions & 19 deletions client/consensus/pow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,6 +171,19 @@ pub trait PowAlgorithm<B: BlockT> {
) -> Result<Option<bool>, Error<B>> {
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it really own_seal or old_seal?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

own_seal means the seal we currently have. And new_seal is the seal we're about to import. I guess this is more referring to selfish mining (in which case one would always be choosing one's own seal).

_new_seal: &Seal,
) -> bool {
false
}
/// Verify that the difficulty is valid against given seal.
fn verify(
&self,
Expand All @@ -194,7 +208,7 @@ pub trait PowAlgorithm<B: BlockT> {
pub struct PowBlockImport<B: BlockT, I, C, S, Algorithm, CAW> {
algorithm: Algorithm,
inner: I,
select_chain: Option<S>,
select_chain: S,
client: Arc<C>,
inherent_data_providers: sp_inherents::InherentDataProviders,
check_inherents_after: <<B as BlockT>::Header as HeaderT>::Number,
Expand Down Expand Up @@ -232,7 +246,7 @@ impl<B, I, C, S, Algorithm, CAW> PowBlockImport<B, I, C, S, Algorithm, CAW> wher
client: Arc<C>,
algorithm: Algorithm,
check_inherents_after: <<B as BlockT>::Header as HeaderT>::Number,
select_chain: Option<S>,
select_chain: S,
inherent_data_providers: sp_inherents::InherentDataProviders,
can_author_with: CAW,
) -> Self {
Expand Down Expand Up @@ -324,12 +338,9 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
mut block: BlockImportParams<B, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
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)?;
Expand All @@ -352,16 +363,7 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
block.body = Some(check_block.deconstruct().1);
}

let inner_seal = match block.post_digests.last() {
Some(DigestItem::Seal(id, seal)) => {
if id == &POW_ENGINE_ID {
seal.clone()
} else {
return Err(Error::<B>::WrongEngine(*id).into())
}
},
_ => return Err(Error::<B>::HeaderUnsealed(block.header.hash()).into()),
};
let inner_seal = fetch_seal::<B>(block.post_digests.last(), block.header.hash())?;

let intermediate = block.take_intermediate::<PowIntermediate::<Algorithm::Difficulty>>(
INTERMEDIATE_KEY
Expand Down Expand Up @@ -391,7 +393,18 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
block.auxiliary.push((key, Some(aux.encode())));
if block.fork_choice.is_none() {
block.fork_choice = Some(ForkChoiceStrategy::Custom(
aux.total_difficulty > 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::<B>(
best_header.digest().logs.last(),
best_hash,
)?;

self.algorithm.break_tie(&best_inner_seal, &inner_seal)
},
}
));
}

Expand Down Expand Up @@ -729,3 +742,20 @@ fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<Option<Vec<u8>>, Err

Ok(pre_digest)
}

/// Fetch PoW seal.
fn fetch_seal<B: BlockT>(
digest: Option<&DigestItem<B::Hash>>,
hash: B::Hash,
) -> Result<Vec<u8>, Error<B>> {
match digest {
Some(DigestItem::Seal(id, seal)) => {
if id == &POW_ENGINE_ID {
Ok(seal.clone())
} else {
return Err(Error::<B>::WrongEngine(*id).into())
}
},
_ => return Err(Error::<B>::HeaderUnsealed(hash).into()),
}
}