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

Commit 0cb9b5a

Browse files
sorpaasbkchr
andauthored
pow: support uniform tie breaking in fork choice (#7073)
* pow: support uniform tie breaking in fork choice * Update client/consensus/pow/src/lib.rs Co-authored-by: Bastian Köcher <[email protected]> * Refactor fetch seal Co-authored-by: Bastian Köcher <[email protected]>
1 parent 5f955c5 commit 0cb9b5a

File tree

1 file changed

+49
-19
lines changed
  • client/consensus/pow/src

1 file changed

+49
-19
lines changed

client/consensus/pow/src/lib.rs

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use std::borrow::Cow;
3737
use std::thread;
3838
use std::collections::HashMap;
3939
use std::marker::PhantomData;
40+
use std::cmp::Ordering;
4041
use sc_client_api::{BlockOf, backend::AuxStore};
4142
use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId};
4243
use sp_block_builder::BlockBuilder as BlockBuilderApi;
@@ -170,6 +171,19 @@ pub trait PowAlgorithm<B: BlockT> {
170171
) -> Result<Option<bool>, Error<B>> {
171172
Ok(None)
172173
}
174+
/// Break a fork choice tie.
175+
///
176+
/// By default this chooses the earliest block seen. Using uniform tie
177+
/// breaking algorithms will help to protect against selfish mining.
178+
///
179+
/// Returns if the new seal should be considered best block.
180+
fn break_tie(
181+
&self,
182+
_own_seal: &Seal,
183+
_new_seal: &Seal,
184+
) -> bool {
185+
false
186+
}
173187
/// Verify that the difficulty is valid against given seal.
174188
fn verify(
175189
&self,
@@ -194,7 +208,7 @@ pub trait PowAlgorithm<B: BlockT> {
194208
pub struct PowBlockImport<B: BlockT, I, C, S, Algorithm, CAW> {
195209
algorithm: Algorithm,
196210
inner: I,
197-
select_chain: Option<S>,
211+
select_chain: S,
198212
client: Arc<C>,
199213
inherent_data_providers: sp_inherents::InherentDataProviders,
200214
check_inherents_after: <<B as BlockT>::Header as HeaderT>::Number,
@@ -232,7 +246,7 @@ impl<B, I, C, S, Algorithm, CAW> PowBlockImport<B, I, C, S, Algorithm, CAW> wher
232246
client: Arc<C>,
233247
algorithm: Algorithm,
234248
check_inherents_after: <<B as BlockT>::Header as HeaderT>::Number,
235-
select_chain: Option<S>,
249+
select_chain: S,
236250
inherent_data_providers: sp_inherents::InherentDataProviders,
237251
can_author_with: CAW,
238252
) -> Self {
@@ -324,12 +338,9 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
324338
mut block: BlockImportParams<B, Self::Transaction>,
325339
new_cache: HashMap<CacheKeyId, Vec<u8>>,
326340
) -> Result<ImportResult, Self::Error> {
327-
let best_hash = match self.select_chain.as_ref() {
328-
Some(select_chain) => select_chain.best_chain()
329-
.map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))?
330-
.hash(),
331-
None => self.client.info().best_hash,
332-
};
341+
let best_header = self.select_chain.best_chain()
342+
.map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))?;
343+
let best_hash = best_header.hash();
333344

334345
let parent_hash = *block.header.parent_hash();
335346
let best_aux = PowAux::read::<_, B>(self.client.as_ref(), &best_hash)?;
@@ -352,16 +363,7 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
352363
block.body = Some(check_block.deconstruct().1);
353364
}
354365

355-
let inner_seal = match block.post_digests.last() {
356-
Some(DigestItem::Seal(id, seal)) => {
357-
if id == &POW_ENGINE_ID {
358-
seal.clone()
359-
} else {
360-
return Err(Error::<B>::WrongEngine(*id).into())
361-
}
362-
},
363-
_ => return Err(Error::<B>::HeaderUnsealed(block.header.hash()).into()),
364-
};
366+
let inner_seal = fetch_seal::<B>(block.post_digests.last(), block.header.hash())?;
365367

366368
let intermediate = block.take_intermediate::<PowIntermediate::<Algorithm::Difficulty>>(
367369
INTERMEDIATE_KEY
@@ -391,7 +393,18 @@ impl<B, I, C, S, Algorithm, CAW> BlockImport<B> for PowBlockImport<B, I, C, S, A
391393
block.auxiliary.push((key, Some(aux.encode())));
392394
if block.fork_choice.is_none() {
393395
block.fork_choice = Some(ForkChoiceStrategy::Custom(
394-
aux.total_difficulty > best_aux.total_difficulty
396+
match aux.total_difficulty.cmp(&best_aux.total_difficulty) {
397+
Ordering::Less => false,
398+
Ordering::Greater => true,
399+
Ordering::Equal => {
400+
let best_inner_seal = fetch_seal::<B>(
401+
best_header.digest().logs.last(),
402+
best_hash,
403+
)?;
404+
405+
self.algorithm.break_tie(&best_inner_seal, &inner_seal)
406+
},
407+
}
395408
));
396409
}
397410

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

730743
Ok(pre_digest)
731744
}
745+
746+
/// Fetch PoW seal.
747+
fn fetch_seal<B: BlockT>(
748+
digest: Option<&DigestItem<B::Hash>>,
749+
hash: B::Hash,
750+
) -> Result<Vec<u8>, Error<B>> {
751+
match digest {
752+
Some(DigestItem::Seal(id, seal)) => {
753+
if id == &POW_ENGINE_ID {
754+
Ok(seal.clone())
755+
} else {
756+
return Err(Error::<B>::WrongEngine(*id).into())
757+
}
758+
},
759+
_ => return Err(Error::<B>::HeaderUnsealed(hash).into()),
760+
}
761+
}

0 commit comments

Comments
 (0)