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

Cumulative fixes to make working with consensus-pow easier #3617

Merged
merged 29 commits into from
Oct 3, 2019
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0f894f0
consensus-pow: add difficulty data to auxiliary
sorpaas Sep 14, 2019
debbb5b
Timestamp api
sorpaas Sep 14, 2019
62fb99a
Implement FinalityProofProvider for ()
sorpaas Sep 14, 2019
fcb81d7
Add DifficultyApi
sorpaas Sep 17, 2019
74b3634
Remove assumption that Difficulty is u128
sorpaas Sep 19, 2019
a1b49b1
Use a separate trait for add instead of hard-code it as Saturating
sorpaas Sep 19, 2019
9775ece
Some convenience functions to work with PowVerifier
sorpaas Sep 20, 2019
526b860
Try to fix mining unstability
sorpaas Sep 20, 2019
912c032
Fix generic resolution
sorpaas Sep 20, 2019
4beec1a
Unused best_header variable
sorpaas Sep 20, 2019
ff7a3f8
Fix hash calculation
sorpaas Sep 20, 2019
a484830
Remove artificial sleep
sorpaas Sep 20, 2019
58395dc
Tweak proposer waiting time
sorpaas Sep 20, 2019
f431d63
Revert sleep removal
sorpaas Sep 20, 2019
53e8bea
Pass sync oracle to mining
sorpaas Sep 20, 2019
e417b3f
Expose build time as a parameter
sorpaas Sep 20, 2019
547112b
Merge branch 'master' of https://github.com/paritytech/substrate into…
sorpaas Sep 21, 2019
f9b76ec
Update lock file
sorpaas Sep 21, 2019
fb71f77
Fix compile
sorpaas Sep 21, 2019
8888f16
Support skipping check_inherents for ancient blocks
sorpaas Sep 22, 2019
23bbb53
Move difficulty fetch function out of loop
sorpaas Sep 23, 2019
b8745ba
Remove seed from mining
sorpaas Sep 24, 2019
bf26edf
Better comments
sorpaas Sep 24, 2019
d94fe48
Add TotalDifficulty definition for U256 and u128
sorpaas Sep 24, 2019
1719905
Update core/consensus/pow/src/lib.rs
sorpaas Oct 3, 2019
96d6a7e
Rename TotalDifficulty::add -> increment
sorpaas Oct 3, 2019
bf76102
Use SelectChain to fetch the best header/hash
sorpaas Oct 3, 2019
dd428de
Merge branch 'master' of https://github.com/paritytech/substrate into…
sorpaas Oct 3, 2019
7229cd0
Update lock file
sorpaas Oct 3, 2019
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions core/consensus/pow/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -18,4 +19,5 @@ std = [
"substrate-client/std",
"sr-primitives/std",
"primitives/std",
"codec/std",
]
45 changes: 37 additions & 8 deletions core/consensus/pow/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>;

/// Define methods that total difficulty should implement.
pub trait TotalDifficulty {
fn add(&mut self, other: Self);
}

impl TotalDifficulty for primitives::U256 {
fn add(&mut self, other: Self) {
let ret = self.saturating_add(other);
*self = ret;
}
}

impl TotalDifficulty for u128 {
fn add(&mut self, other: Self) {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
let ret = self.saturating_add(other);
*self = ret;
}
}

decl_runtime_apis! {
/// API necessary for timestamp-based difficulty adjustment algorithms.
pub trait TimestampApi<Moment: Decode> {
/// 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<Difficulty: Decode> {
/// Return the target difficulty of the next block.
fn difficulty() -> Difficulty;
}
}
118 changes: 82 additions & 36 deletions core/consensus/pow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,11 @@ 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,
};
use consensus_common::import_queue::{BoxBlockImport, BasicQueue, Verifier};
use codec::{Encode, Decode};
Expand All @@ -63,59 +62,76 @@ fn aux_key(hash: &H256) -> Vec<u8> {

/// Auxiliary storage data for PoW.
#[derive(Encode, Decode, Clone, Debug, Default)]
pub struct PowAux {
/// Total difficulty.
pub struct PowAux<Difficulty> {
/// Difficulty of the current block.
pub difficulty: Difficulty,
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
/// Total difficulty up to current block.
pub total_difficulty: Difficulty,
}

impl PowAux {
impl<Difficulty> PowAux<Difficulty> where
Difficulty: Decode + Default,
{
/// Read the auxiliary from client.
pub fn read<C: AuxStore>(client: &C, hash: &H256) -> Result<Self, String> {
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<B: BlockT> {
/// Difficulty for the algorithm.
type Difficulty: TotalDifficulty + Default + Encode + Decode + Ord + Clone + Copy;

/// Get the next block's difficulty.
fn difficulty(&self, parent: &BlockId<B>) -> Result<Difficulty, String>;
fn difficulty(&self, parent: &BlockId<B>) -> Result<Self::Difficulty, String>;
/// Verify proof of work against the given difficulty.
fn verify(
&self,
parent: &BlockId<B>,
pre_hash: &H256,
seal: &Seal,
difficulty: Difficulty,
difficulty: Self::Difficulty,
) -> Result<bool, String>;
/// Mine a seal that satisfy the given difficulty.
fn mine(
&self,
parent: &BlockId<B>,
pre_hash: &H256,
seed: &H256,
difficulty: Difficulty,
difficulty: Self::Difficulty,
round: u32,
) -> Result<Option<Seal>, String>;
}

/// A verifier for PoW blocks.
pub struct PowVerifier<C, Algorithm> {
pub struct PowVerifier<B: BlockT<Hash=H256>, C, Algorithm> {
client: Arc<C>,
algorithm: Algorithm,
inherent_data_providers: inherents::InherentDataProviders,
check_inherents_after: <<B as BlockT>::Header as HeaderT>::Number,
}

impl<C, Algorithm> PowVerifier<C, Algorithm> {
fn check_header<B: BlockT<Hash=H256>>(
impl<B: BlockT<Hash=H256>, C, Algorithm> PowVerifier<B, C, Algorithm> {
pub fn new(
client: Arc<C>,
algorithm: Algorithm,
check_inherents_after: <<B as BlockT>::Header as HeaderT>::Number,
inherent_data_providers: inherents::InherentDataProviders,
) -> Self {
Self { client, algorithm, inherent_data_providers, check_inherents_after }
}

fn check_header(
&self,
mut header: B::Header,
parent_block_id: BlockId<B>,
) -> Result<(B::Header, Difficulty, DigestItem<H256>), String> where
) -> Result<(B::Header, Algorithm::Difficulty, DigestItem<H256>), String> where
Algorithm: PowAlgorithm<B>,
{
let hash = header.hash();
Expand Down Expand Up @@ -146,7 +162,7 @@ impl<C, Algorithm> PowVerifier<C, Algorithm> {
Ok((header, difficulty, seal))
}

fn check_inherents<B: BlockT<Hash=H256>>(
fn check_inherents(
&self,
block: B,
block_id: BlockId<B>,
Expand All @@ -157,6 +173,10 @@ impl<C, Algorithm> PowVerifier<C, Algorithm> {
{
const MAX_TIMESTAMP_DRIFT_SECS: u64 = 60;

if *block.header().number() < self.check_inherents_after {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did we need to add this? Do we have to deal with any existing deployment?

Copy link
Member Author

Choose a reason for hiding this comment

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

I added this parameter because runtime upgrades may break older inherent data check. For example, when the inherent data format is changed completely or an old inherent module is removed. In those cases, some chains might want to completely skip the check, because the older the block is, the more "finalized" it is because of PoW built on top of it.

This branch can be disabled by simply setting check_inherents_after to 0.

return Ok(())
}

let inherent_res = self.client.runtime_api().check_inherents(
&block_id,
block,
Expand All @@ -183,7 +203,7 @@ impl<C, Algorithm> PowVerifier<C, Algorithm> {
}
}

impl<B: BlockT<Hash=H256>, C, Algorithm> Verifier<B> for PowVerifier<C, Algorithm> where
impl<B: BlockT<Hash=H256>, C, Algorithm> Verifier<B> for PowVerifier<B, C, Algorithm> where
C: ProvideRuntimeApi + Send + Sync + HeaderBackend<B> + AuxStore + ProvideCache<B> + BlockOf,
C::Api: BlockBuilderApi<B>,
Algorithm: PowAlgorithm<B> + Send + Sync,
Expand All @@ -205,11 +225,12 @@ impl<B: BlockT<Hash=H256>, C, Algorithm> Verifier<B> for PowVerifier<C, Algorith
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::<B>(
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.add(difficulty);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why was this changed from a saturating_add()?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the add function from TotalDifficulty trait. The idea here is that some Difficulty definitions may want to use its own customized method for calculating the total difficulty from current difficulty. Most of the time TotalDifficulty::add will still be implemented as saturating_add.

Copy link
Contributor

Choose a reason for hiding this comment

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

A small nit: my brain treats add as a function which takes &self and returns Self. For this case, it seems increase(&mut self, other: Self) → () would be more appropriate, don't you think so?


if let Some(inner_body) = body.take() {
let block = B::new(checked_header.clone(), inner_body);
Expand Down Expand Up @@ -241,7 +262,7 @@ impl<B: BlockT<Hash=H256>, C, Algorithm> Verifier<B> for PowVerifier<C, Algorith
}

/// Register the PoW inherent data provider, if not registered already.
fn register_pow_inherent_data_provider(
pub fn register_pow_inherent_data_provider(
inherent_data_providers: &InherentDataProviders,
) -> Result<(), consensus_common::Error> {
if !inherent_data_providers.has_provider(&srml_timestamp::INHERENT_IDENTIFIER) {
Expand All @@ -262,6 +283,7 @@ pub fn import_queue<B, C, Algorithm>(
block_import: BoxBlockImport<B>,
client: Arc<C>,
algorithm: Algorithm,
check_inherents_after: <<B as BlockT>::Header as HeaderT>::Number,
inherent_data_providers: InherentDataProviders,
) -> Result<PowImportQueue<B>, consensus_common::Error> where
B: BlockT<Hash=H256>,
Expand All @@ -272,11 +294,12 @@ pub fn import_queue<B, C, Algorithm>(
{
register_pow_inherent_data_provider(&inherent_data_providers)?;

let verifier = PowVerifier {
client: client.clone(),
let verifier = PowVerifier::new(
client.clone(),
algorithm,
check_inherents_after,
inherent_data_providers,
};
);

Ok(BasicQueue::new(
verifier,
Expand All @@ -296,19 +319,22 @@ pub fn import_queue<B, C, Algorithm>(
/// 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<B: BlockT<Hash=H256>, C, Algorithm, E>(
pub fn start_mine<B: BlockT<Hash=H256>, C, Algorithm, E, SO>(
mut block_import: BoxBlockImport<B>,
client: Arc<C>,
algorithm: Algorithm,
mut env: E,
preruntime: Option<Vec<u8>>,
round: u32,
mut sync_oracle: SO,
build_time: std::time::Duration,
inherent_data_providers: inherents::InherentDataProviders,
) where
C: HeaderBackend<B> + AuxStore + 'static,
Algorithm: PowAlgorithm<B> + Send + Sync + 'static,
E: Environment<B> + Send + Sync + 'static,
E::Error: std::fmt::Debug,
SO: SyncOracle + Send + Sync + 'static,
{
if let Err(_) = register_pow_inherent_data_provider(&inherent_data_providers) {
warn!("Registering inherent data provider for timestamp failed");
Expand All @@ -323,6 +349,8 @@ pub fn start_mine<B: BlockT<Hash=H256>, C, Algorithm, E>(
&mut env,
preruntime.as_ref(),
round,
&mut sync_oracle,
build_time.clone(),
&inherent_data_providers
) {
Ok(()) => (),
Expand All @@ -331,27 +359,35 @@ pub fn start_mine<B: BlockT<Hash=H256>, C, Algorithm, E>(
e
),
}

std::thread::sleep(std::time::Duration::new(1, 0));
}
});
}

fn mine_loop<B: BlockT<Hash=H256>, C, Algorithm, E>(
fn mine_loop<B: BlockT<Hash=H256>, C, Algorithm, E, SO>(
block_import: &mut BoxBlockImport<B>,
client: &C,
algorithm: &Algorithm,
env: &mut E,
preruntime: Option<&Vec<u8>>,
round: u32,
sync_oracle: &mut SO,
build_time: std::time::Duration,
inherent_data_providers: &inherents::InherentDataProviders,
) -> Result<(), String> where
C: HeaderBackend<B> + AuxStore,
Algorithm: PowAlgorithm<B>,
E: Environment<B>,
E::Error: std::fmt::Debug,
SO: SyncOracle,
{
'outer: loop {
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 = client.info().best_hash;
let best_header = client.header(BlockId::Hash(best_hash))
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
.map_err(|e| format!("Fetching best header failed: {:?}", e))?
Expand All @@ -368,21 +404,19 @@ fn mine_loop<B: BlockT<Hash=H256>, 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,
)?;
Expand All @@ -397,10 +431,22 @@ fn mine_loop<B: BlockT<Hash=H256>, C, Algorithm, E>(
}
};

aux.total_difficulty = aux.total_difficulty.saturating_add(difficulty);
let hash = header.hash();
aux.difficulty = difficulty;
aux.total_difficulty.add(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 = client.info().best_hash;
let best_aux = PowAux::<Algorithm::Difficulty>::read(client, &best_hash)?;

if best_aux.total_difficulty > aux.total_difficulty {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
continue 'outer
}

let import_block = BlockImportParams {
origin: BlockOrigin::Own,
header,
Expand Down
6 changes: 6 additions & 0 deletions core/network/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ pub trait FinalityProofProvider<Block: BlockT>: Send + Sync {
fn prove_finality(&self, for_block: Block::Hash, request: &[u8]) -> Result<Option<Vec<u8>>, Error>;
}

impl<Block: BlockT> FinalityProofProvider<Block> for () {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the finality proof provider is optional when creating the service.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll try to provide a reproducible test case, but I think what I had trouble with is that if I remove .with_finality_proof_provider for the service builder, the whole type resolution will fail. But maybe we should fix that instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I see, it will fail to infer the generic type of the optional finality proof provider.

fn prove_finality(&self, _for_block: Block::Hash, _request: &[u8]) -> Result<Option<Vec<u8>>, Error> {
Ok(None)
}
}

impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
B: client::backend::Backend<Block, Blake2Hasher> + Send + Sync + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
Expand Down